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

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