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

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

Improvements for widgets and popups

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