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

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