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

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

Repaint now works with cropping and changing color of @b and @v

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