source: trunk/src/org/expeditee/gui/FrameGraphics.java@ 170

Last change on this file since 170 was 170, checked in by bjn8, 16 years ago

Fixed widget overlap rendering... now renders same as swing ordering

File size: 25.2 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Dimension;
5import java.awt.EventQueue;
6import java.awt.Graphics;
7import java.awt.Graphics2D;
8import java.awt.GraphicsEnvironment;
9import java.awt.Image;
10import java.awt.Rectangle;
11import java.awt.RenderingHints;
12import java.awt.geom.Area;
13import java.awt.image.BufferedImage;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.Comparator;
17import java.util.HashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.ListIterator;
21
22import org.expeditee.items.Circle;
23import org.expeditee.items.InteractiveWidget;
24import org.expeditee.items.Item;
25import org.expeditee.items.ItemUtils;
26import org.expeditee.items.Line;
27import org.expeditee.items.Permission;
28import org.expeditee.items.WidgetEdge;
29import org.expeditee.items.XRayable;
30
31public class FrameGraphics {
32
33 // the graphics used to paint with
34 private static Graphics2D _DisplayGraphics;
35
36 // the maximum size that can be used to paint on
37 private static Dimension _MaxSize = new Dimension(1000, 1000);
38
39 // modes
40 public static final int MODE_NORMAL = 0;
41
42 public static final int MODE_AUDIENCE = 1;
43
44 public static final int MODE_XRAY = 2;
45
46 // Start in XRay mode so that errors arnt thrown when parsing the profile
47 // frame if it has images on it
48 private static int _Mode = MODE_XRAY;
49
50 private FrameGraphics() {
51 // util constructor
52 }
53
54 /**
55 * If Audience Mode is on this method will toggle it to be off, or
56 * vice-versa. This results in the Frame being re-parsed and repainted.
57 */
58 public static void ToggleAudienceMode() {
59 Frame current = DisplayIO.getCurrentFrame();
60 if (_Mode == MODE_AUDIENCE)
61 _Mode = MODE_NORMAL;
62 else {
63 _Mode = MODE_AUDIENCE;
64 ItemUtils.UpdateConnectedToAnnotations(current.getItems());
65 for (Overlay o : current.getOverlays()) {
66 ItemUtils.UpdateConnectedToAnnotations(o.Frame.getItems());
67 }
68 for (Vector v : current.getVectorsDeep()) {
69 ItemUtils.UpdateConnectedToAnnotations(v.Frame.getItems());
70 }
71 }
72 FrameUtils.Parse(current);
73 DisplayIO.UpdateTitle();
74 setMaxSize(new Dimension(_MaxSize.width, MessageBay
75 .getMessageBufferHeight()
76 + _MaxSize.height));
77 refresh(false);
78 }
79
80 /**
81 * If X-Ray Mode is on this method will toggle it to be off, or vice-versa.
82 * This results in the Frame being re-parsed and repainted.
83 */
84 public static void ToggleXRayMode() {
85 if (_Mode == MODE_XRAY)
86 setMode(MODE_NORMAL, true);
87 else
88 setMode(MODE_XRAY, true);
89 DisplayIO.UpdateTitle();
90 FrameMouseActions.getInstance().refreshHighlights();
91 refresh(false);
92 }
93
94 public static void setMode(int mode, boolean parse) {
95 if (_Mode == mode)
96 return;
97 _Mode = mode;
98 if (parse)
99 FrameUtils.Parse(DisplayIO.getCurrentFrame());
100 }
101
102 /**
103 * @return True if Audience Mode is currently on, False otherwise.
104 */
105 public static boolean isAudienceMode() {
106 return _Mode == MODE_AUDIENCE;
107 }
108
109 /**
110 * @return True if X-Ray Mode is currently on, False otherwise.
111 */
112 public static boolean isXRayMode() {
113 return _Mode == MODE_XRAY;
114 }
115
116 public static void setMaxSize(Dimension max) {
117 if (_MaxSize == null)
118 _MaxSize = max;
119
120 // Hide the message buffer if in audience mode
121 int newMaxHeight = max.height
122 - (isAudienceMode() ? 0 : MessageBay.MESSAGE_BUFFER_HEIGHT);
123 if (newMaxHeight > 0) {
124 _MaxSize.setSize(max.width, newMaxHeight);
125 }
126 Frame current = DisplayIO.getCurrentFrame();
127 if (current != null) {
128 current.setBuffer(null);
129 current.refreshSize(getMaxFrameSize());
130 if (DisplayIO.isTwinFramesOn()) {
131 Frame opposite = DisplayIO.getOppositeFrame();
132 //if (opposite != null) {
133 opposite.setBuffer(null);
134 opposite.refreshSize(getMaxFrameSize());
135 //}
136 }
137 }
138
139 if (newMaxHeight > 0) {
140 MessageBay.updateSize();
141 }
142 }
143
144 public static Dimension getMaxSize() {
145 return _MaxSize;
146 }
147
148 public static Dimension getMaxFrameSize() {
149 if (DisplayIO.isTwinFramesOn()) {
150 return new Dimension((_MaxSize.width / 2), _MaxSize.height);
151 } else
152 return _MaxSize;
153 }
154
155 /**
156 * Sets the Graphics2D object that should be used for all painting tasks.
157 * Note: Actual painting is done by using g.create() to create temporary
158 * instances that are then disposed of using g.dispose().
159 *
160 * @param g
161 * The Graphics2D object to use for all painting
162 */
163 public static void setDisplayGraphics(Graphics2D g) {
164 _DisplayGraphics = g;
165 }
166
167 /*
168 * Displays the given Item on the screen
169 */
170 static void PaintItem(Graphics2D g, Item i) {
171 if (i == null || g == null)
172 return;
173
174 // do not paint annotation items in audience mode
175 if (!isAudienceMode()
176 || (isAudienceMode() && !i.isConnectedToAnnotation() && !i
177 .isAnnotation()) || i == FrameUtils.getLastEdited()) {
178
179 Graphics2D tg = (Graphics2D) g.create();
180 i.paint(tg);
181 tg.dispose();
182 }
183 }
184
185 /**
186 * Adds all the scaled vector items for a frame into a list. It uses
187 * recursion to get the items from nested vector frames.
188 *
189 * @param items
190 * the list into which the vector items will be placed.
191 * @param vector
192 * the frame containing vecor items.
193 * @param seenVectors
194 * the vectors which should be ignored to prevent infinate loops.
195 * @param origin
196 * start point for this frame or null if it is a top level frame.
197 * @param scale
198 * the factor by which the item on the vector frame are to be
199 * scaled.
200 */
201 // public static void AddAllVectorItems(List<Item> items, Vector vector,
202 // Collection<Frame> seenVectors) {
203 // // Check all the vector items and add the items on the vectors
204 // if (seenVectors.contains(vector))
205 // return;
206 // seenVectors.add(vector);
207 //
208 // float originX = origin == null ? 0 : origin.x;
209 // float originY = origin == null ? 0 : origin.y;
210 //
211 // for (Vector o : vector.getVectors())
212 // AddAllVectorItems(items, o.Frame, new HashSet<Frame>(seenVectors),
213 // new Point2D.Float(originX + o.Origin.x * scale, originY
214 // + o.Origin.y * scale), o.Scale * scale,
215 // o.Foreground, o.Background);
216 // // if its the original frame then were done
217 // if (origin == null) {
218 // ItemUtils.EnclosedCheck(items);
219 // return;
220 // }
221 // // Put copies of the items shifted to the origin of the VectorTag
222 // items.addAll(ItemUtils
223 // .CopyItems(vector.getNonAnnotationItems(), vector));
224 //
225 // }
226 /**
227 * Recursive function similar to AddAllOverlayItems.
228 *
229 * @param widgets
230 * The collection the widgets will be added to
231 * @param overlay
232 * An "overlay" frame - this intially will be the parent frame
233 * @param seenOverlays
234 * Used for state in the recursion stack. Pass as an empty
235 * (non-null) list.
236 */
237 public static void AddAllOverlayWidgets(List<InteractiveWidget> widgets,
238 Frame overlay, List<Frame> seenOverlays) {
239 if (seenOverlays.contains(overlay))
240 return;
241
242 seenOverlays.add(overlay);
243
244 for (Overlay o : overlay.getOverlays())
245 AddAllOverlayWidgets(widgets, o.Frame, seenOverlays);
246
247 widgets.addAll(overlay.getInteractiveWidgets());
248 }
249
250 private static Image Paint(Frame toPaint, Area clip) {
251 return Paint(toPaint, clip, true);
252 }
253
254 /**
255 *
256 * @param toPaint
257 * @param clip
258 * If null, then no clip applied.
259 * @param isActualFrame
260 * @return
261 */
262 private static Image Paint(Frame toPaint, Area clip, boolean isActualFrame) {
263 if (toPaint == null)
264 return null;
265
266 // the buffer is not valid, so it must be recreated
267 if (!toPaint.isBufferValid()) {
268 Image buffer = toPaint.getBuffer();
269 if (buffer == null) {
270 GraphicsEnvironment ge = GraphicsEnvironment
271 .getLocalGraphicsEnvironment();
272 if (!isActualFrame) {
273 buffer = new BufferedImage(_MaxSize.width, _MaxSize.height,
274 BufferedImage.TYPE_INT_ARGB);
275 } else {
276 buffer = ge.getDefaultScreenDevice()
277 .getDefaultConfiguration()
278 .createCompatibleVolatileImage(_MaxSize.width,
279 _MaxSize.height);
280 }
281 toPaint.setBuffer(buffer);
282 }
283
284 Graphics2D bg = (Graphics2D) buffer.getGraphics();
285 bg.setClip(clip);
286
287 // TODO: Revise images and clip - VERY IMPORTANT
288
289 // Nicer looking lines, but may be too jerky while
290 // rubber-banding on older machines
291 if (UserSettings.AntiAlias)
292 bg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
293 RenderingHints.VALUE_ANTIALIAS_ON);
294 // If we are doing @f etc... then have a clear background if its the
295 // default background color
296 Color backgroundColor = null;
297 // Need to allow transparency for frameImages
298 if (!isActualFrame) {
299 backgroundColor = toPaint.getBackgroundColor();
300 if (backgroundColor == null)
301 backgroundColor = Item.TRANSPARENT;
302 } else {
303 backgroundColor = toPaint.getPaintBackgroundColor();
304 }
305
306 bg.setColor(backgroundColor);
307 // bg.setColor(Color.pink); // TODO: TEMP FOR DEBUGGING
308
309 bg.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
310
311 List<Item> visibleItems = new LinkedList<Item>();
312 List<InteractiveWidget> paintWidgets;
313
314 if (isActualFrame) {
315 // Add all the items for this frame and any other from other
316 // frames
317 visibleItems.addAll(toPaint.getAllItems());
318 paintWidgets = new LinkedList<InteractiveWidget>();
319 AddAllOverlayWidgets(paintWidgets, toPaint,
320 new LinkedList<Frame>());
321 } else {
322 visibleItems.addAll(toPaint.getVisibleItems());
323 visibleItems.addAll(toPaint.getVectorItems());
324 paintWidgets = toPaint.getInteractiveWidgets();
325 }
326
327 // FIRST: Paint widgets swing gui (not expeditee gui) .
328 // Note that these are the anchored widgets
329 ListIterator<InteractiveWidget> widgetItor = paintWidgets.listIterator(paintWidgets.size());
330 while (widgetItor.hasPrevious()) { // Paint first-in-last-serve ordering - like swing
331 InteractiveWidget iw = widgetItor.previous();
332 if (clip == null
333 || clip.intersects(iw.getComponant().getBounds()))
334 iw.paint(bg);
335
336 // Paint borders
337 for (Item i : iw.getItems()) {
338 if (clip == null
339 || i.isInDrawingArea(clip))
340 i.paint(bg);
341 }
342
343 }
344
345 // Filter out items that do not need to be painted
346 List<Item> paintItems;
347 HashSet<Item> fillOnlyItems = null; // only contains items that do
348 // not need drawing but fills
349 // might
350
351 if (clip == null) {
352 paintItems = visibleItems;
353 } else {
354 fillOnlyItems = new HashSet<Item>();
355 paintItems = new LinkedList<Item>();
356 for (Item i : visibleItems) {
357 if (i.isInDrawingArea(clip)) {
358 paintItems.add(i);
359 } else if (i.isEnclosed()) {
360 // just add all fill items despite possibility of fills
361 // not being in clip
362 // because it will be faster than having to test twice
363 // for fills that do need
364 // repainting.
365 fillOnlyItems.add(i);
366 }
367 }
368 }
369 // Only paint files and lines once ... between anchored AND free
370 // items
371 HashSet<Item> paintedFillsAndLines = new HashSet<Item>();
372 PaintPictures(bg, paintItems, fillOnlyItems, paintedFillsAndLines);
373 PaintLines(bg, visibleItems);
374
375 // Filter out free items that do not need to be painted
376 // This is efficient in cases with animation while free items exist
377
378 List<Item> freeItemsToPaint = new LinkedList<Item>();
379 // Dont paint the free items for the other frame in twin frames mode
380 // if (toPaint == DisplayIO.getCurrentFrame()) {
381 if (clip == null) {
382 freeItemsToPaint = FreeItems.getInstance();
383 } else {
384 freeItemsToPaint = new LinkedList<Item>();
385 fillOnlyItems.clear();
386 for (Item i : FreeItems.getInstance()) {
387 if (i.isInDrawingArea(clip)) {
388 freeItemsToPaint.add(i);
389 } else if (i.isEnclosed()) {
390 fillOnlyItems.add(i);
391 }
392 }
393 }
394 // }
395
396 if (isActualFrame && toPaint == DisplayIO.getCurrentFrame())
397 PaintPictures(bg, freeItemsToPaint, fillOnlyItems,
398 paintedFillsAndLines);
399 // TODO if we can get transparency with FreeItems.getInstance()...
400 // then text can
401 // be done before freeItems
402 PaintNonLinesNonPicture(bg, paintItems);
403
404 // toPaint.setBufferValid(true);
405
406 if (isActualFrame && !isAudienceMode()) {
407 PaintItem(bg, toPaint.getNameItem());
408 }
409
410 if (DisplayIO.isTwinFramesOn()) {
411 List<Item> lines = new LinkedList<Item>();
412 for (Item i : freeItemsToPaint) {
413 if (i instanceof Line) {
414 Line line = (Line) i;
415
416 if (toPaint == DisplayIO.getCurrentFrame()) {
417 // If exactly one end of the line is floating...
418
419 if (line.getEndItem().isFloating()
420 ^ line.getStartItem().isFloating()) {
421 // Line l = TransposeLine(line,
422 // line.getEndItem(),
423 // toPaint, 0, 0);
424 // if (l == null)
425 // l = TransposeLine(line,
426 // line.getStartItem(), toPaint, 0, 0);
427 // if (l == null)
428 // l = line;
429 // lines.add(l);
430 } else {
431 // lines.add(line);
432 }
433 } else {
434 // if (line.getEndItem().isFloating()
435 // ^ line.getStartItem().isFloating()) {
436 // lines.add(TransposeLine(line,
437 // line.getEndItem(), toPaint,
438 // FrameMouseActions.getY(), -DisplayIO
439 // .getMiddle()));
440 // lines.add(TransposeLine(line, line
441 // .getStartItem(), toPaint,
442 // FrameMouseActions.getY(), -DisplayIO
443 // .getMiddle()));
444 // }
445 }
446 }
447 }
448 if (isActualFrame)
449 PaintLines(bg, lines);
450 } else {
451 // Dont paint the
452 if (isActualFrame)
453 PaintLines(bg, freeItemsToPaint);
454 }
455
456 if (isActualFrame && toPaint == DisplayIO.getCurrentFrame())
457 PaintNonLinesNonPicture(bg, freeItemsToPaint);
458
459 // Repaint popups / drags...
460 if (isActualFrame)
461 PopupManager.getInstance().paintLayeredPane(bg, clip);
462
463 bg.dispose();
464 }
465
466 return toPaint.getBuffer();
467 }
468
469 // creates a new line so that lines are shown correctly when spanning
470 // across frames in TwinFrames mode
471 private static Line TransposeLine(Line line, Item d, Frame toPaint,
472 int base, int adj) {
473 Line nl = null;
474
475 if (toPaint != DisplayIO.getCurrentFrame() && d.getParent() == null
476 && line.getOppositeEnd(d).getParent() == toPaint) {
477 nl = line.copy();
478 if (d == line.getStartItem())
479 d = nl.getStartItem();
480 else
481 d = nl.getEndItem();
482
483 if (DisplayIO.FrameOnSide(toPaint) == 0)
484 d.setX(base);
485 else
486 d.setX(base + adj);
487
488 } else if (toPaint == DisplayIO.getCurrentFrame()
489 && d.getParent() == null
490 && line.getOppositeEnd(d).getParent() != toPaint) {
491 nl = line.copy();
492
493 if (d == line.getStartItem())
494 d = nl.getEndItem();
495 else
496 d = nl.getStartItem();
497
498 if (DisplayIO.FrameOnSide(toPaint) == 1)
499 d.setX(d.getX() - DisplayIO.getMiddle());
500 else
501 d.setX(d.getX() + DisplayIO.getMiddle());
502 }
503 if (nl != null) {
504 nl.invalidateAll();
505 FrameGraphics.requestRefresh(true);
506 }
507 return nl;
508 }
509
510 private static void Paint(Graphics g, Image left, Image right,
511 Color background) {
512
513 // if TwinFrames mode is on, then clipping etc has to be set
514 if (DisplayIO.isTwinFramesOn()) {
515 // draw the two lines down the middle of the window
516 if (left != null)
517 left.getGraphics().drawLine(DisplayIO.getMiddle() - 2, 0,
518 DisplayIO.getMiddle() - 2, _MaxSize.height);
519
520 if (right != null)
521 right.getGraphics().drawLine(0, 0, 0, _MaxSize.height);
522
523 // set the clipping area
524 ((Graphics2D) g).setClip(0, 0, DisplayIO.getMiddle() - 1,
525 _MaxSize.height);
526 g.drawImage(left, 0, 0, Item.DEFAULT_BACKGROUND, null);
527 ((Graphics2D) g).setClip(null);
528 g.drawImage(right, DisplayIO.getMiddle() + 1, 0,
529 Item.DEFAULT_BACKGROUND, null);
530
531 // otherwise, just draw whichever side is active
532 } else {
533 if (DisplayIO.getCurrentSide() == 0)
534 g.drawImage(left, 0, 0, Item.DEFAULT_BACKGROUND, null);
535 else
536 g.drawImage(right, 0, 0, Item.DEFAULT_BACKGROUND, null);
537 }
538
539 }
540
541 public static void Clear() {
542 Graphics g = _DisplayGraphics.create();
543 g.setColor(Color.WHITE);
544 g.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
545 g.dispose();
546 }
547
548 private static void PaintNonLinesNonPicture(Graphics2D g, List<Item> toPaint) {
549 for (Item i : toPaint)
550 if (!(i instanceof Line) && !(i instanceof XRayable))
551 PaintItem(g, i);
552 }
553
554 /**
555 * Paint the lines that are not part of an enclosure.
556 *
557 * @param g
558 * @param toPaint
559 */
560 private static void PaintLines(Graphics2D g, List<Item> toPaint) {
561 // Use this set to keep track of the items that have been painted
562 Collection<Item> done = new HashSet<Item>();
563 for (Item i : toPaint)
564 if (i instanceof Line) {
565 Line l = (Line) i;
566 if (done.contains(l)) {
567 l.paintArrows(g);
568 } else {
569 // When painting a line all connected lines are painted too
570 done.addAll(l.getAllConnected());
571 if (l.getStartItem().getEnclosedArea() == 0)
572 PaintItem(g, i);
573 }
574 }
575 }
576
577 /**
578 * Paint filled areas and their surrounding lines as well as pictures. Note:
579 * floating widgets are painted as fills
580 *
581 * @param g
582 * @param toPaint
583 */
584 private static void PaintPictures(Graphics2D g, List<Item> toPaint,
585 HashSet<Item> fillOnlyItems, HashSet<Item> done) {
586
587 List<Item> toFill = new LinkedList<Item>();
588 for (Item i : toPaint) {
589 // Ignore items that have already been done!
590 // Also ignore invisible items..
591 // TODO possibly ignore invisible items before coming to this
592 // method?
593 if (done.contains(i))
594 continue;
595 if (i instanceof XRayable) {
596 toFill.add(i);
597 done.addAll(i.getConnected());
598 } else if (i.hasEnclosures()) {
599 for (Item enclosure : i.getEnclosures()) {
600 if (!toFill.contains(enclosure))
601 toFill.add(enclosure);
602 }
603 done.addAll(i.getConnected());
604 } else if (i.isLineEnd()
605 && (!isAudienceMode() || !i.isConnectedToAnnotation())) {
606 toFill.add(i);
607 done.addAll(i.getAllConnected());
608 }
609 }
610
611 if (fillOnlyItems != null) {
612 for (Item i : fillOnlyItems) {
613 if (done.contains(i))
614 continue;
615 else if (!isAudienceMode() || !i.isConnectedToAnnotation()) {
616 toFill.add(i);
617 }
618 done.addAll(i.getAllConnected());
619 }
620 }
621
622 // Sort the items to fill
623 Collections.sort(toFill, new Comparator<Item>() {
624 public int compare(Item a, Item b) {
625 Double aArea = a.getEnclosedArea();
626 Double bArea = b.getEnclosedArea();
627 int cmp = aArea.compareTo(bArea);
628 if (cmp == 0) {
629 // System.out.println(a.getEnclosureID() + " " + b.getID());
630 return new Integer(a.getEnclosureID()).compareTo(b
631 .getEnclosureID());
632 }
633 return cmp * -1;
634 }
635 });
636 for (Item i : toFill) {
637 if (i instanceof XRayable) {
638 PaintItem(g, i);
639 } else {
640 // Paint the fill and lines
641 i.paintFill(g);
642 List<Line> lines = i.getLines();
643 if (lines.size() > 0)
644 PaintItem(g, lines.get(0));
645 }
646 }
647 }
648
649 /**
650 * Highlights an item on the screen Note: All graphics are handled by the
651 * Item itself.
652 *
653 * @param i
654 * The Item to highlight.
655 * @param val
656 * True if the highlighting is being shown, false if it is being
657 * erased.
658 * @return the item that was highlighted
659 */
660 public static Item Highlight(Item i) {
661 if ((i instanceof Line)) {
662 // Check if within 20% of the end of the line
663 Line l = (Line) i;
664 Item toDisconnect = l.getEndPointToDisconnect(Math
665 .round(FrameMouseActions.MouseX), Math
666 .round(FrameMouseActions.MouseY));
667
668 // Brook: Widget Edges do not have such a context
669 if (toDisconnect != null && !(i instanceof WidgetEdge)) {
670 Item.HighlightMode newMode = toDisconnect.getHighlightMode();
671 if (FreeItems.itemAttachedToCursor())
672 newMode = Item.HighlightMode.Normal;
673 // unhighlight all the other dots
674 for (Item conn : toDisconnect.getAllConnected()) {
675 conn.setHighlightMode(Item.HighlightMode.None);
676 }
677 l.setHighlightMode(newMode);
678 // highlight the dot that will be in disconnect mode
679 toDisconnect.setHighlightMode(newMode);
680 i = toDisconnect;
681 } else {
682 Collection<Item> connected = i.getAllConnected();
683 for (Item conn : connected) {
684 conn.setHighlightMode(Item.HighlightMode.Connected);
685 }
686 }
687 } else if (i instanceof Circle) {
688 i.setHighlightMode(Item.HighlightMode.Connected);
689 } else {
690 // FrameGraphics.ChangeSelectionMode(i,
691 // Item.SelectedMode.Normal);
692 // For polygons need to make sure all other endpoints are
693 // unHighlighted
694 if (i.hasPermission(Permission.full))
695 changeHighlightMode(i, Item.HighlightMode.Normal,
696 Item.HighlightMode.None);
697 else
698 changeHighlightMode(i, Item.HighlightMode.Connected,
699 Item.HighlightMode.Connected);
700 }
701 Repaint();
702 return i;
703 }
704
705 public static void changeHighlightMode(Item item, Item.HighlightMode newMode) {
706 changeHighlightMode(item, newMode, newMode);
707 }
708
709 public static void changeHighlightMode(Item item,
710 Item.HighlightMode newMode, Item.HighlightMode connectedNewMode) {
711 if (item == null)
712 return;
713 // Mike: TODO comment on why the line below is used!!
714 // I forgot already!!Opps
715 boolean freeItem = FreeItems.getInstance().contains(item);
716 for (Item i : item.getAllConnected()) {
717 if (/* freeItem || */!FreeItems.getInstance().contains(i)) {
718 i.setHighlightMode(connectedNewMode);
719 }
720 }
721 if (!freeItem && newMode != connectedNewMode)
722 item.setHighlightMode(newMode);
723 Repaint();
724 }
725
726 /**
727 * Repaints the buffer of the given Frame.
728 *
729 * @param toUpdate
730 * The Frame whose buffer is to be repainted.
731 */
732
733 public static void UpdateBuffer(Frame toUpdate, boolean paintOverlays) {
734 if (toUpdate == null)
735 return;
736
737 Image vi = Paint(toUpdate, null, paintOverlays);
738 toUpdate.setBuffer(vi);
739 }
740
741 public static int getMode() {
742 return _Mode;
743 }
744
745 public static Graphics createGraphics() {
746 // Error messages on start up will call this message before
747 // _DisplayGraphics has been initialised
748 if (_DisplayGraphics == null)
749 return null;
750 return _DisplayGraphics.create();
751 }
752
753 // Damaged areas pending to render. Accessessed by multiple threads
754 private static HashSet<Rectangle> damagedAreas = new HashSet<Rectangle>();
755
756 /**
757 * Checks that the item is visible (on current frame && overlays) - if
758 * visible then damaged area will be re-rendered on the next refresh.
759 *
760 * @param damagedItem
761 * @param toRepaint
762 */
763 public static void invalidateItem(Item damagedItem, Rectangle toRepaint) {
764 // Only add area to repaint if item is visible...
765 if (ItemUtils.isVisible(damagedItem)) {
766 synchronized (damagedAreas) {
767 damagedAreas.add(toRepaint);
768 }
769 } else if (MessageBay.isMessageItem(damagedItem)) {
770 MessageBay.addDirtyArea(toRepaint);
771 }
772 }
773
774 /**
775 * The given area will be re-rendered in the next refresh. This is the
776 * quicker version and is more useful for re-rendering animated areas.
777 *
778 * @param toRepaint
779 */
780 public static void invalidateArea(Rectangle toRepaint) {
781 synchronized (damagedAreas) {
782 damagedAreas.add(toRepaint);
783 }
784 }
785
786 public static void clearInvalidAreas() {
787 synchronized (damagedAreas) {
788 damagedAreas.clear();
789 }
790 }
791
792 /**
793 * Invalidates the buffered image of the current Frame and forces it to be
794 * repainted on to the screen. Repaints all items. This is more expensive
795 * than refresh.
796 */
797 public static void ForceRepaint() { // TODO: TEMP: Use refresh
798 Frame current = DisplayIO.getCurrentFrame();
799
800 if (current == null)
801 return;
802 refresh(false);
803 }
804
805 public static void Repaint() { // TODO: TEMP: Use refresh
806 refresh(true);
807 }
808
809 /**
810 * Called to refresh the display screen. Thread safe.
811 */
812 public static void refresh(boolean useInvalidation) {
813
814 if (_DisplayGraphics == null || _MaxSize.width <= 0
815 || _MaxSize.height <= 0)
816 return;
817
818 Area clip = null;
819 if (useInvalidation) { // build clip
820
821 synchronized (damagedAreas) {
822 if (!damagedAreas.isEmpty()) {
823
824 for (Rectangle r : damagedAreas) {
825 if (clip == null)
826 clip = new Area(r);
827 else
828 clip.add(new Area(r));
829 }
830 damagedAreas.clear();
831
832 } else if (MessageBay.isDirty()) {
833 // Paint dirty message bay
834 Graphics dg = _DisplayGraphics.create();
835 MessageBay.refresh(true, dg, Item.DEFAULT_BACKGROUND);
836 return;
837
838 } else return; // nothing to render
839 }
840
841 } else {
842 synchronized (damagedAreas) {
843 damagedAreas.clear();
844 }
845 // System.out.println("FULLSCREEN REFRESH"); // TODO: REMOVE
846 }
847
848 Frame[] toPaint = DisplayIO.getFrames();
849 Image left = Paint(toPaint[0], clip);
850 Image right = Paint(toPaint[1], clip);
851
852 Graphics dg = _DisplayGraphics.create();
853
854 // Paint frame to window
855 Paint(dg, left, right, Item.DEFAULT_BACKGROUND);
856
857 // Paint any animations
858 PopupManager.getInstance().paintAnimations();
859
860 // Paint message bay
861 MessageBay.refresh(useInvalidation, dg, Item.DEFAULT_BACKGROUND);
862
863 dg.dispose();
864 }
865
866 /**
867 * If wanting to refresh from another thread - other than the main thread
868 * that handles the expeditee datamodel (modifying / accessing / rendering).
869 * Use this method for thread safety.
870 */
871 public static synchronized void requestRefresh(boolean useInvalidation) {
872
873 _requestMarsheller._useInvalidation = useInvalidation;
874
875 if (_requestMarsheller._isQueued) {
876 return;
877 }
878
879 _requestMarsheller._isQueued = true;
880 EventQueue.invokeLater(_requestMarsheller); // Render on AWT thread
881 }
882
883 private static RenderRequestMarsheller _requestMarsheller = new FrameGraphics().new RenderRequestMarsheller();
884
885 /**
886 * Used for marshelling render requests from foreign threads to the event
887 * dispatcher thread... (AWT)
888 *
889 * @author Brook Novak
890 */
891 private class RenderRequestMarsheller implements Runnable {
892
893 boolean _useInvalidation = true;
894
895 boolean _isQueued = false;
896
897 public void run() {
898 refresh(_useInvalidation);
899 _isQueued = false;
900 _useInvalidation = true;
901 }
902
903 }
904
905}
Note: See TracBrowser for help on using the repository browser.