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

Last change on this file since 137 was 137, checked in by ra33, 16 years ago

Fixed a few problems with painting etc.. and finished search stuff

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