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

Last change on this file since 298 was 298, checked in by ra33, 16 years ago

Adding networking stuff for peer to peer sharing of frames

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