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

Last change on this file since 427 was 427, checked in by ra33, 15 years ago
File size: 29.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.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 if (FreeItems.hasCursor()
518 && DisplayIO.getCursor() == Item.DEFAULT_CURSOR)
519 PaintNonLinesNonPicture(bg, FreeItems.getCursor());
520 }
521
522 // creates a new line so that lines are shown correctly when spanning
523 // across frames in TwinFrames mode
524 // private static Line TransposeLine(Line line, Item d, Frame toPaint,
525 // int base, int adj) {
526 // Line nl = null;
527 //
528 // if (toPaint != DisplayIO.getCurrentFrame() && d.getParent() == null
529 // && line.getOppositeEnd(d).getParent() == toPaint) {
530 // nl = line.copy();
531 // if (d == line.getStartItem())
532 // d = nl.getStartItem();
533 // else
534 // d = nl.getEndItem();
535 //
536 // if (DisplayIO.FrameOnSide(toPaint) == 0)
537 // d.setX(base);
538 // else
539 // d.setX(base + adj);
540 //
541 // } else if (toPaint == DisplayIO.getCurrentFrame()
542 // && d.getParent() == null
543 // && line.getOppositeEnd(d).getParent() != toPaint) {
544 // nl = line.copy();
545 //
546 // if (d == line.getStartItem())
547 // d = nl.getEndItem();
548 // else
549 // d = nl.getStartItem();
550 //
551 // if (DisplayIO.FrameOnSide(toPaint) == 1)
552 // d.setX(d.getX() - DisplayIO.getMiddle());
553 // else
554 // d.setX(d.getX() + DisplayIO.getMiddle());
555 // }
556 // if (nl != null) {
557 // nl.invalidateAll();
558 // FrameGraphics.requestRefresh(true);
559 // }
560 // return nl;
561 // }
562
563 private static void Paint(Graphics g, Image left, Image right,
564 Color background) {
565
566 // if TwinFrames mode is on, then clipping etc has to be set
567 if (DisplayIO.isTwinFramesOn()) {
568 // draw the two lines down the middle of the window
569 if (left != null)
570 left.getGraphics().drawLine(DisplayIO.getMiddle() - 2, 0,
571 DisplayIO.getMiddle() - 2, _MaxSize.height);
572
573 if (right != null)
574 right.getGraphics().drawLine(0, 0, 0, _MaxSize.height);
575
576 // set the clipping area
577 ((Graphics2D) g).setClip(0, 0, DisplayIO.getMiddle() - 1,
578 _MaxSize.height);
579 g.drawImage(left, 0, 0, Item.DEFAULT_BACKGROUND, null);
580 ((Graphics2D) g).setClip(null);
581 g.drawImage(right, DisplayIO.getMiddle() + 1, 0,
582 Item.DEFAULT_BACKGROUND, null);
583
584 // otherwise, just draw whichever side is active
585 } else {
586 if (DisplayIO.getCurrentSide() == 0)
587 g.drawImage(left, 0, 0, Item.DEFAULT_BACKGROUND, null);
588 else
589 g.drawImage(right, 0, 0, Item.DEFAULT_BACKGROUND, null);
590 }
591
592 }
593
594 public static void Clear() {
595 Graphics g = _DisplayGraphics.create();
596 g.setColor(Color.WHITE);
597 g.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
598 g.dispose();
599 }
600
601 private static void PaintNonLinesNonPicture(Graphics2D g, List<Item> toPaint) {
602 for (Item i : toPaint)
603 if (!(i instanceof Line) && !(i instanceof XRayable))
604 PaintItem(g, i);
605 }
606
607 /**
608 * Paint the lines that are not part of an enclosure.
609 *
610 * @param g
611 * @param toPaint
612 */
613 private static void PaintLines(Graphics2D g, List<Item> toPaint) {
614 // Use this set to keep track of the items that have been painted
615 Collection<Item> done = new HashSet<Item>();
616 for (Item i : toPaint)
617 if (i instanceof Line) {
618 Line l = (Line) i;
619 if (done.contains(l)) {
620 l.paintArrows(g);
621 } else {
622 // When painting a line all connected lines are painted too
623 done.addAll(l.getAllConnected());
624 if (l.getStartItem().getEnclosedArea() == 0)
625 PaintItem(g, i);
626 }
627 }
628 }
629
630 /**
631 * Paint filled areas and their surrounding lines as well as pictures. Note:
632 * floating widgets are painted as fills
633 *
634 * @param g
635 * @param toPaint
636 */
637 private static void PaintPictures(Graphics2D g, List<Item> toPaint,
638 HashSet<Item> fillOnlyItems, HashSet<Item> done) {
639
640 List<Item> toFill = new LinkedList<Item>();
641 for (Item i : toPaint) {
642 // Ignore items that have already been done!
643 // Also ignore invisible items..
644 // TODO possibly ignore invisible items before coming to this
645 // method?
646 if (done.contains(i))
647 continue;
648 if (i instanceof XRayable) {
649 toFill.add(i);
650 done.addAll(i.getConnected());
651 } else if (i.hasEnclosures()) {
652 for (Item enclosure : i.getEnclosures()) {
653 if (!toFill.contains(enclosure))
654 toFill.add(enclosure);
655 }
656 done.addAll(i.getConnected());
657 } else if (i.isLineEnd()
658 && (!isAudienceMode() || !i.isConnectedToAnnotation())) {
659 toFill.add(i);
660 done.addAll(i.getAllConnected());
661 }
662 }
663
664 if (fillOnlyItems != null) {
665 for (Item i : fillOnlyItems) {
666 if (done.contains(i))
667 continue;
668 else if (!isAudienceMode() || !i.isConnectedToAnnotation()) {
669 toFill.add(i);
670 }
671 done.addAll(i.getAllConnected());
672 }
673 }
674
675 // Sort the items to fill
676 Collections.sort(toFill, new Comparator<Item>() {
677 public int compare(Item a, Item b) {
678 Double aArea = a.getEnclosedArea();
679 Double bArea = b.getEnclosedArea();
680 int cmp = aArea.compareTo(bArea);
681 if (cmp == 0) {
682 // System.out.println(a.getEnclosureID() + " " +
683 // b.getID());\
684 // Shapes to the left go underneath
685 Polygon pA = a.getEnclosedShape();
686 Polygon pB = b.getEnclosedShape();
687 if (pA == null || pB == null)
688 return 0;
689 return new Integer(pA.getBounds().x).compareTo(pB
690 .getBounds().x);
691 }
692 return cmp * -1;
693 }
694 });
695 for (Item i : toFill) {
696 if (i instanceof XRayable) {
697 PaintItem(g, i);
698 } else {
699 // Paint the fill and lines
700 i.paintFill(g);
701 List<Line> lines = i.getLines();
702 if (lines.size() > 0)
703 PaintItem(g, lines.get(0));
704 }
705 }
706 }
707
708 /**
709 * Highlights an item on the screen Note: All graphics are handled by the
710 * Item itself.
711 *
712 * @param i
713 * The Item to highlight.
714 * @param val
715 * True if the highlighting is being shown, false if it is being
716 * erased.
717 * @return the item that was highlighted
718 */
719 public static Item Highlight(Item i) {
720 if ((i instanceof Line)) {
721 // Check if within 20% of the end of the line
722 Line l = (Line) i;
723 Item toDisconnect = l.getEndPointToDisconnect(Math
724 .round(FrameMouseActions.MouseX), Math
725 .round(FrameMouseActions.MouseY));
726
727 // Brook: Widget Edges do not have such a context
728 if (toDisconnect != null && !(i instanceof WidgetEdge)) {
729 Item.HighlightMode newMode = toDisconnect.getHighlightMode();
730 if (FreeItems.itemsAttachedToCursor())
731 newMode = Item.HighlightMode.Normal;
732 // unhighlight all the other dots
733 for (Item conn : toDisconnect.getAllConnected()) {
734 conn.setHighlightMode(Item.HighlightMode.None);
735 }
736 l.setHighlightMode(newMode);
737 // highlight the dot that will be in disconnect mode
738 toDisconnect.setHighlightMode(newMode);
739 i = toDisconnect;
740 } else {
741 Collection<Item> connected = i.getAllConnected();
742 for (Item conn : connected) {
743 conn.setHighlightMode(Item.HighlightMode.Connected);
744 }
745 }
746 } else if (i instanceof Circle) {
747 i.setHighlightMode(Item.HighlightMode.Connected);
748 } else if (!i.isVisible()) {
749 changeHighlightMode(i, Item.HighlightMode.Connected, null);
750 } else {
751 // FrameGraphics.ChangeSelectionMode(i,
752 // Item.SelectedMode.Normal);
753 // For polygons need to make sure all other endpoints are
754 // unHighlighted
755 if (i.hasPermission(Permission.full))
756 changeHighlightMode(i, Item.HighlightMode.Normal,
757 Item.HighlightMode.None);
758 else
759 changeHighlightMode(i, Item.HighlightMode.Connected,
760 Item.HighlightMode.Connected);
761 }
762 Repaint();
763 return i;
764 }
765
766 public static void changeHighlightMode(Item item, Item.HighlightMode newMode) {
767 changeHighlightMode(item, newMode, newMode);
768 }
769
770 public static void changeHighlightMode(Item item,
771 Item.HighlightMode newMode, Item.HighlightMode connectedNewMode) {
772 if (item == null)
773 return;
774
775 if (item.hasVector()) {
776 for (Item i : item.getParentOrCurrentFrame().getVectorItems()) {
777 if (i.getEditTarget() == item) {
778 i.setHighlightMode(newMode);
779 }
780 }
781 item.setHighlightMode(newMode);
782 } else {
783 // Mike: TODO comment on why the line below is used!!
784 // I forgot already!!Opps
785 boolean freeItem = FreeItems.getInstance().contains(item);
786 for (Item i : item.getAllConnected()) {
787 if (/* freeItem || */!FreeItems.getInstance().contains(i)) {
788 i.setHighlightMode(connectedNewMode);
789 }
790 }
791 if (!freeItem && newMode != connectedNewMode)
792 item.setHighlightMode(newMode);
793 }
794 Repaint();
795 }
796
797 /**
798 * Repaints the buffer of the given Frame.
799 *
800 * @param toUpdate
801 * The Frame whose buffer is to be repainted.
802 */
803
804 public static void UpdateBuffer(Frame toUpdate, boolean paintOverlays,
805 boolean useVolitile) {
806 toUpdate.setBuffer(getBuffer(toUpdate, paintOverlays, useVolitile));
807 }
808
809 public static Image getBuffer(Frame toUpdate, boolean paintOverlays,
810 boolean useVolitile) {
811 if (toUpdate == null)
812 return null;
813
814 return Paint(toUpdate, null, paintOverlays, useVolitile);
815 }
816
817 public static int getMode() {
818 return _Mode;
819 }
820
821 public static Graphics createGraphics() {
822 // Error messages on start up will call this message before
823 // _DisplayGraphics has been initialised
824 if (_DisplayGraphics == null)
825 return null;
826 return _DisplayGraphics.create();
827 }
828
829 // Damaged areas pending to render. Accessessed by multiple threads
830 private static HashSet<Rectangle> damagedAreas = new HashSet<Rectangle>();
831
832 /** The clip used while paiting */
833 private static Area currentClip;
834
835 /**
836 * The current clip that is used for painting at this instant.
837 *
838 * Intention: for extra clipping within an items paint method - the clip is
839 * lost in the graphics object for which can be regained via this method.
840 *
841 * @return The current clip. Null if no clip (e.g. full screen render).
842 */
843 public static Area getCurrentClip() {
844 return (currentClip != null) ? (Area) currentClip.clone() : null;
845 }
846
847 /**
848 * Checks that the item is visible (on current frame && overlays) - if
849 * visible then damaged area will be re-rendered on the next refresh.
850 *
851 * @param damagedItem
852 * @param toRepaint
853 */
854 public static void invalidateItem(Item damagedItem, Rectangle toRepaint) {
855 // Only add area to repaint if item is visible...
856 if (ItemUtils.isVisible(damagedItem)) {
857 synchronized (damagedAreas) {
858 damagedAreas.add(toRepaint);
859 }
860 } else if (MessageBay.isMessageItem(damagedItem)) {
861 MessageBay.addDirtyArea(toRepaint);
862 }
863 }
864
865 /**
866 * The given area will be re-rendered in the next refresh. This is the
867 * quicker version and is more useful for re-rendering animated areas.
868 *
869 * @param toRepaint
870 */
871 public static void invalidateArea(Rectangle toRepaint) {
872 synchronized (damagedAreas) {
873 damagedAreas.add(toRepaint);
874 }
875 }
876
877 public static void clearInvalidAreas() {
878 synchronized (damagedAreas) {
879 damagedAreas.clear();
880 }
881 }
882
883 /**
884 * Invalidates the buffered image of the current Frame and forces it to be
885 * repainted on to the screen. Repaints all items. This is more expensive
886 * than refresh.
887 */
888 public static void ForceRepaint() { // TODO: TEMP: Use refresh
889 Frame current = DisplayIO.getCurrentFrame();
890
891 if (current == null)
892 return;
893 refresh(false);
894 }
895
896 public static void Repaint() { // TODO: TEMP: Use refresh
897 refresh(true);
898 }
899
900 /**
901 * Called to refresh the display screen. Thread safe.
902 */
903 public static void refresh(boolean useInvalidation) {
904
905 if (_DisplayGraphics == null || _MaxSize.width <= 0
906 || _MaxSize.height <= 0)
907 return;
908
909 currentClip = null;
910 if (useInvalidation) { // build clip
911
912 synchronized (damagedAreas) {
913 if (!damagedAreas.isEmpty()) {
914
915 for (Rectangle r : damagedAreas) {
916 if (currentClip == null)
917 currentClip = new Area(r);
918 else
919 currentClip.add(new Area(r));
920 }
921 damagedAreas.clear();
922
923 } else if (MessageBay.isDirty()) {
924 // Paint dirty message bay
925 Graphics dg = _DisplayGraphics.create();
926 MessageBay.refresh(true, dg, Item.DEFAULT_BACKGROUND);
927 return;
928
929 } else
930 return; // nothing to render
931 }
932
933 } else {
934 synchronized (damagedAreas) {
935 damagedAreas.clear();
936 }
937 // System.out.println("FULLSCREEN REFRESH"); // TODO: REMOVE
938 }
939
940 Frame[] toPaint = DisplayIO.getFrames();
941 Image left = Paint(toPaint[0], currentClip);
942 Image right = Paint(toPaint[1], currentClip);
943
944 Graphics dg = _DisplayGraphics.create();
945
946 // Paint frame to window
947 Paint(dg, left, right, Item.DEFAULT_BACKGROUND);
948
949 // Paint any animations
950 PopupManager.getInstance().paintAnimations();
951
952 // Paint message bay
953 MessageBay.refresh(useInvalidation, dg, Item.DEFAULT_BACKGROUND);
954
955 dg.dispose();
956 }
957
958 /**
959 * If wanting to refresh from another thread - other than the main thread
960 * that handles the expeditee datamodel (modifying / accessing / rendering).
961 * Use this method for thread safety.
962 */
963 public static synchronized void requestRefresh(boolean useInvalidation) {
964
965 _requestMarsheller._useInvalidation = useInvalidation;
966
967 if (_requestMarsheller._isQueued) {
968 return;
969 }
970
971 _requestMarsheller._isQueued = true;
972 EventQueue.invokeLater(_requestMarsheller); // Render on AWT thread
973 }
974
975 private static RenderRequestMarsheller _requestMarsheller = new FrameGraphics().new RenderRequestMarsheller();
976
977 /**
978 * Used for marshelling render requests from foreign threads to the event
979 * dispatcher thread... (AWT)
980 *
981 * @author Brook Novak
982 */
983 private class RenderRequestMarsheller implements Runnable {
984
985 boolean _useInvalidation = true;
986
987 boolean _isQueued = false;
988
989 public void run() {
990 refresh(_useInvalidation);
991 _isQueued = false;
992 _useInvalidation = true;
993 }
994
995 }
996
997 /**
998 * Adds a FinalFrameRenderPass to the frame-render pipeline...
999 *
1000 * Note that the last added FinalFrameRenderPass will be rendered at the
1001 * very top.
1002 *
1003 * @param pass
1004 * The pass to add. If already added then nothing results in the
1005 * call.
1006 *
1007 * @throws NullPointerException
1008 * If pass is null.
1009 */
1010 public static void addFrameRenderPass(FrameRenderPass pass) {
1011 if (pass == null)
1012 throw new NullPointerException("pass");
1013
1014 if (!_frameRenderPasses.contains(pass))
1015 _frameRenderPasses.add(pass);
1016 }
1017
1018 /**
1019 * Adds a FinalFrameRenderPass to the frame-render pipeline...
1020 *
1021 * Note that the last added FinalFrameRenderPass will be rendered at the
1022 * very top.
1023 *
1024 * @param pass
1025 * The pass to remove
1026 *
1027 */
1028 public static void removeFrameRenderPass(FrameRenderPass pass) {
1029 _frameRenderPasses.remove(pass);
1030 }
1031
1032 /**
1033 * A FinalFrameRenderPass is invoked at the very final stages for rendering
1034 * a frame: that is, after the popups are drawn.
1035 *
1036 * There can be many applications for FinalFrameRenderPass. Such as tool
1037 * tips, balloons, or drawing items at the highest Z-order in special
1038 * situations.
1039 *
1040 * Although if there are multiples FinalFrameRenderPasses attatach to the
1041 * frame painter then it is not garaunteed to be rendered very last.
1042 *
1043 * @see FrameGraphics#addFinalFrameRenderPass(org.expeditee.gui.FrameGraphics.FrameRenderPass)
1044 * @see FrameGraphics#removeFinalFrameRenderPass(org.expeditee.gui.FrameGraphics.FrameRenderPass)
1045 *
1046 * @author Brook Novak
1047 */
1048 public interface FrameRenderPass {
1049
1050 /**
1051 *
1052 * @param currentClip
1053 *
1054 * @return The clip that the pass should use instead. i.e. if there are
1055 * any effects that cannot invladate prior to paint call.
1056 */
1057 Area paintStarted(Area currentClip);
1058
1059 void paintFinalPass(Graphics g);
1060
1061 void paintPreLayeredPanePass(Graphics g);
1062 }
1063
1064}
Note: See TracBrowser for help on using the repository browser.