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

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

Attempt at fixing problem with window height not being known at settings frame generation

File size: 31.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.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 = null;
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.