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

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

Added anchorRight and anchorBottom properties to items...
Fixed bugs with @i item display...
Can now inject properties into enclosed items at the same time

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