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

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