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

Last change on this file since 669 was 669, checked in by jts21, 10 years ago

generateSettingsTree() now uses FrameCreator, and also now uses a single column of settings with info alongside instead of multiple columns with tooltips.

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