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

Last change on this file since 1054 was 1054, checked in by davidb, 8 years ago

Delayed drawing of InteractiveWidgets so they end up on top

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