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

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

Changed all the (int)(x+0.5) float conversions to use the Math.round(x) which does Math.floor(x+0.5F) so that negative numbers are rounded properly

File size: 26.2 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Component;
5import java.awt.Container;
6import java.awt.Dimension;
7import java.awt.Font;
8import java.awt.Graphics;
9import java.awt.Graphics2D;
10import java.awt.GraphicsEnvironment;
11import java.awt.Image;
12import java.awt.Point;
13import java.awt.RenderingHints;
14import java.awt.Stroke;
15import java.awt.image.BufferedImage;
16import java.awt.image.VolatileImage;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.Comparator;
20import java.util.HashSet;
21import java.util.Iterator;
22import java.util.LinkedList;
23import java.util.List;
24
25import javax.swing.JPopupMenu;
26import javax.swing.SwingUtilities;
27
28import org.expeditee.actions.Misc;
29import org.expeditee.items.InteractiveWidget;
30import org.expeditee.items.Item;
31import org.expeditee.items.ItemUtils;
32import org.expeditee.items.Line;
33import org.expeditee.items.Picture;
34import org.expeditee.items.Text;
35import org.expeditee.items.WidgetEdge;
36
37public class FrameGraphics {
38 public static final int MESSAGE_BUFFER_HEIGHT = 100;
39
40 private static final int MESSAGE_LINK_Y_OFFSET = 100;
41
42 private static final int MESSAGE_LINK_X = 50;
43
44 // the graphics used to paint with
45 private static Graphics2D _DisplayGraphics;
46
47 // the maximum size that can be used to paint on
48 private static Dimension _MaxSize;
49
50 // messages shown in the message window
51 public static Text[] Messages = new Text[4];
52
53 // buffer of the message window
54 private static VolatileImage _MessageBuffer = null;
55
56 // font used for the messages
57 private static Font _MessageFont = Font.decode("Serif-Plain-16");
58
59 // the number of messages currently shown (used for scrolling up)
60 private static int _MessageCount = 0;
61
62 // modes
63 public static final int MODE_NORMAL = 0;
64
65 public static final int MODE_AUDIENCE = 1;
66
67 public static final int MODE_XRAY = 2;
68
69 public static final Color ERROR_COLOR = Color.red;
70
71 private static int _Mode = MODE_NORMAL;
72
73 // if true, error messages are not shown to the user
74 private static boolean _SupressMessages = false;
75
76 public static String MESSAGES_FRAMESET_NAME = "Messages";
77
78 // The link to the message frameset
79 public static Item MessageLink = new Text(-2, "@" + MESSAGES_FRAMESET_NAME,
80 Color.black, Color.white);
81
82 // creator for creating the message frames
83 private static FrameCreator _creator;
84
85 /**
86 * If Audience Mode is on this method will toggle it to be off, or
87 * vice-versa. This results in the Frame being re-parsed and repainted.
88 */
89 public static void ToggleAudienceMode() {
90 Frame current = DisplayIO.getCurrentFrame();
91 if (_Mode == MODE_AUDIENCE)
92 _Mode = MODE_NORMAL;
93 else {
94 _Mode = MODE_AUDIENCE;
95 ItemUtils.UpdateConnectedToAnnotations(current.getItems());
96 for (Overlay o : current.getOverlaysDeep()) {
97 ItemUtils.UpdateConnectedToAnnotations(o.Frame.getItems());
98 }
99 for (Vector v : current.getVectorsDeep()) {
100 ItemUtils.UpdateConnectedToAnnotations(v.Frame.getItems());
101 }
102 }
103 FrameUtils.Parse(current);
104 DisplayIO.UpdateTitle();
105 setMaxSize(new Dimension(_MaxSize.width, _MessageBuffer.getHeight()
106 + _MaxSize.height));
107 Repaint();
108 }
109
110 /**
111 * If X-Ray Mode is on this method will toggle it to be off, or vice-versa.
112 * This results in the Frame being re-parsed and repainted.
113 */
114 public static void ToggleXRayMode() {
115 if (_Mode == MODE_XRAY)
116 _Mode = MODE_NORMAL;
117 else
118 _Mode = MODE_XRAY;
119
120 FrameUtils.Parse(DisplayIO.getCurrentFrame());
121 DisplayIO.UpdateTitle();
122 Repaint();
123 }
124
125 /**
126 * @return True if Audience Mode is currently on, False otherwise.
127 */
128 public static boolean isAudienceMode() {
129 return _Mode == MODE_AUDIENCE;
130 }
131
132 /**
133 * @return True if X-Ray Mode is currently on, False otherwise.
134 */
135 public static boolean isXRayMode() {
136 return _Mode == MODE_XRAY;
137 }
138
139 public static void setMaxSize(Dimension max) {
140 if (_MaxSize == null)
141 _MaxSize = max;
142
143 // Hide the message buffer if in audience mode
144 int newMaxHeight = max.height
145 - (isAudienceMode() ? 0 : MESSAGE_BUFFER_HEIGHT);
146 if (newMaxHeight > 0) {
147 _MaxSize.setSize(max.width, newMaxHeight);
148 }
149
150 if (DisplayIO.getCurrentFrame() != null) {
151 DisplayIO.getCurrentFrame().setBuffer(null);
152 DisplayIO.getCurrentFrame().setMaxSize(max);
153 }
154
155 if (newMaxHeight > 0) {
156 _MessageBuffer = null;
157
158 for (int i = 0; i < Messages.length; i++) {
159 if (Messages[i] != null) {
160 Messages[i].setOffset(0, -_MaxSize.height);
161 Messages[i].setMaxSize(_MaxSize);
162 }
163 }
164
165 MessageLink.setOffset(0, -_MaxSize.height);
166 MessageLink.setMaxSize(_MaxSize);
167 MessageLink.setPosition(_MaxSize.width - MESSAGE_LINK_Y_OFFSET,
168 MESSAGE_LINK_X);
169 }
170 // Repaint();
171 }
172
173 public static Dimension getMaxSize() {
174 return _MaxSize;
175 }
176
177 public static Dimension getMaxFrameSize() {
178 if (DisplayIO.isTwinFramesOn()) {
179 return new Dimension((_MaxSize.width / 2), _MaxSize.height);
180 } else
181 return _MaxSize;
182 }
183
184 /**
185 * Sets the Graphics2D object that should be used for all painting tasks.
186 * Note: Actual painting is done by using g.create() to create temporary
187 * instances that are then disposed of using g.dispose().
188 *
189 * @param g
190 * The Graphics2D object to use for all painting
191 */
192 public static void setDisplayGraphics(Graphics2D g) {
193 _DisplayGraphics = g;
194 }
195
196 /*
197 * Displays the given Item on the screen
198 */
199 private static void PaintItem(Graphics2D g, Item i) {
200 if (i == null || g == null)
201 return;
202
203 // do not paint annotation items in audience mode
204 if (!isAudienceMode()
205 || (isAudienceMode() && !i.isConnectedToAnnotation() && !i
206 .isAnnotation()) || i == FrameUtils.LastEdited) {
207
208 Graphics2D tg = (Graphics2D) g.create();
209 i.paint(tg);
210 tg.dispose();
211 }
212 }
213
214 public static void AddAllOverlayItems(List<Item> items, Frame overlay,
215 List<Frame> seenOverlays) {
216 if (seenOverlays.contains(overlay))
217 return;
218
219 seenOverlays.add(overlay);
220
221 for (Overlay o : overlay.getOverlays())
222 AddAllOverlayItems(items, o.Frame, seenOverlays);
223
224 items.addAll(overlay.getItems());
225 }
226
227 /**
228 * Adds all the scaled vector items for a frame into a list. It uses
229 * recursion to get the items from nested vector frames.
230 *
231 * @param items
232 * the list into which the vector items will be placed.
233 * @param vector
234 * the frame containing vecor items.
235 * @param seenVectors
236 * the vectors which should be ignored to prevent infinate loops.
237 * @param origin
238 * start point for this frame or null if it is a top level frame.
239 * @param scale
240 * the factor by which the item on the vector frame are to be
241 * scaled.
242 */
243 public static void AddAllVectorItems(List<Item> items, Frame vector,
244 Collection<Frame> seenVectors, Point origin, Float scale,
245 Color defaultForeground, Color defaultBackground) {
246
247 // Check all the vector items and add the items on the vectors
248 if (seenVectors.contains(vector))
249 return;
250 seenVectors.add(vector);
251
252 int originX = origin == null ? 0 : origin.x;
253 int originY = origin == null ? 0 : origin.y;
254
255 for (Vector o : vector.getVectors())
256 AddAllVectorItems(items, o.Frame, new HashSet<Frame>(seenVectors),
257 new Point(originX + Math.round(o.Origin.x * scale), originY
258 + Math.round(o.Origin.y * scale)), o.Scale * scale,
259 o.Foreground, o.Background);
260 // if its the original frame then were done
261 if (origin == null) {
262 ItemUtils.EnclosedCheck(items);
263 return;
264 }
265 // Put copies of the items shifted to the origin of the VectorTag
266 int dx = origin.x;
267 int dy = origin.y;
268 List<Item> copies = ItemUtils.CopyItems(vector.getItems());
269 // FrameMouseActions
270 Iterator<Item> iterator = copies.iterator();
271 while (iterator.hasNext()) {
272 Item i = iterator.next();
273 // Dont paint annotation items for @v
274 if (i.isAnnotation()) {
275 iterator.remove();
276 continue;
277 }
278 i.setLinkMark(false);
279 i.setActionMark(false);
280 if (!(i instanceof Line)) {
281 i.setXY(Math.round(i.getX() * scale + dx), Math.round(i.getY()
282 * scale + dy));
283 i.setThickness(Math.round(i.getThickness() * scale));
284 i
285 .setArrowheadLength(Math.round(i.getArrowheadLength()
286 * scale));
287 if (i.getColor() == null) {
288 i.setColor(defaultForeground);
289 }
290 if (i.getBackgroundColor() == null) {
291 i.setBackgroundColor(defaultBackground);
292 }
293 if (i.getFillColor() == null) {
294 i.setFillColor(defaultBackground);
295 }
296 }
297 if (i instanceof Text)
298 i.setSize(Math.round(i.getSize() * scale));
299 items.add(i);
300 }
301 }
302
303 /**
304 * Recursive function similar to AddAllOverlayItems.
305 *
306 * @param widgets
307 * The collection the widgets will be added to
308 * @param overlay
309 * An "overlay" frame - this intially will be the parent frame
310 * @param seenOverlays
311 * Used for state in the recursion stack. Pass as an empty
312 * (non-null) list.
313 */
314 public static void AddAllOverlayWidgets(List<InteractiveWidget> widgets,
315 Frame overlay, List<Frame> seenOverlays) {
316 if (seenOverlays.contains(overlay))
317 return;
318
319 seenOverlays.add(overlay);
320
321 for (Overlay o : overlay.getOverlays())
322 AddAllOverlayWidgets(widgets, o.Frame, seenOverlays);
323
324 widgets.addAll(overlay.getInteractiveWidgets());
325 }
326
327 private static Image Paint(Frame toPaint) {
328 return Paint(toPaint, true);
329 }
330
331 private static Image Paint(Frame toPaint, boolean isActualFrame) {
332 if (toPaint == null)
333 return null;
334
335 // the buffer is not valid, so it must be recreated
336 if (!toPaint.isBufferValid()) {
337 Image buffer = toPaint.getBuffer();
338 if (buffer == null) {
339 GraphicsEnvironment ge = GraphicsEnvironment
340 .getLocalGraphicsEnvironment();
341 if (!isActualFrame) {
342 buffer = new BufferedImage(_MaxSize.width, _MaxSize.height,
343 BufferedImage.TYPE_INT_ARGB);
344 } else {
345 buffer = ge.getDefaultScreenDevice()
346 .getDefaultConfiguration()
347 .createCompatibleVolatileImage(_MaxSize.width,
348 _MaxSize.height);
349 }
350 toPaint.setBuffer(buffer);
351 }
352
353 Graphics2D bg = (Graphics2D) buffer.getGraphics();
354
355 // Nicer looking lines, but may be too jerky while
356 // rubber-banding on older machines
357 if (UserSettings.AntiAlias)
358 ((Graphics2D) bg).setRenderingHint(
359 RenderingHints.KEY_ANTIALIASING,
360 RenderingHints.VALUE_ANTIALIAS_ON);
361 // If we are doing @f etc... then have a clear background if its the
362 // default background color
363 Color backgroundColor = null;
364 // Need to allow transparency for frameImages
365 if (!isActualFrame) {
366 backgroundColor = toPaint.getBackgroundColor();
367 if (backgroundColor == null)
368 backgroundColor = Item.TRANSPARENT;
369 } else {
370 backgroundColor = toPaint.getPaintBackgroundColor();
371 }
372
373 bg.setColor(backgroundColor);
374 bg.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
375
376 List<Item> paintItems = new LinkedList<Item>();
377 List<InteractiveWidget> paintWidgets;
378
379 AddAllVectorItems(paintItems, toPaint, new LinkedList<Frame>(),
380 null, 1F, null, null);
381
382 if (isActualFrame) {
383 AddAllOverlayItems(paintItems, toPaint, new LinkedList<Frame>());
384
385 paintWidgets = new LinkedList<InteractiveWidget>();
386 AddAllOverlayWidgets(paintWidgets, toPaint,
387 new LinkedList<Frame>());
388
389 } else {
390 paintItems.addAll(toPaint.getItems());
391 paintWidgets = toPaint.getInteractiveWidgets();
392 }
393
394 // FIRST: Paint widgets swing gui (not expeditee gui) .
395 // Note that these are the ancored widgets
396 for (InteractiveWidget iw : paintWidgets) {
397 iw.paint(bg);
398 }
399
400 PaintPictures(bg, paintItems);
401
402 if (toPaint == DisplayIO.getCurrentFrame())
403 PaintPictures(bg, Frame.FreeItems);
404
405 PaintLines(bg, paintItems);
406 PaintNonLinesNonPicture(bg, paintItems);
407
408 // toPaint.setBufferValid(true);
409
410 if (DisplayIO.isTwinFramesOn()) {
411 List<Item> lines = new LinkedList<Item>();
412 for (Item i : Frame.FreeItems) {
413 if (i instanceof Line) {
414 Line line = (Line) i;
415
416 if (toPaint != DisplayIO.getCurrentFrame()) {
417 if (line.getEndItem().isFloating()
418 ^ line.getStartItem().isFloating()) {
419 lines.add(TransposeLine(line,
420 line.getEndItem(), toPaint,
421 FrameMouseActions.getY(), -DisplayIO
422 .getMiddle()));
423 lines.add(TransposeLine(line, line
424 .getStartItem(), toPaint,
425 FrameMouseActions.getY(), -DisplayIO
426 .getMiddle()));
427 }
428 } else {
429 if (line.getEndItem().isFloating()
430 ^ line.getStartItem().isFloating()) {
431 Line l = TransposeLine(line, line.getEndItem(),
432 toPaint, 0, 0);
433 if (l == null)
434 l = TransposeLine(line,
435 line.getStartItem(), toPaint, 0, 0);
436 if (l == null)
437 l = line;
438 lines.add(l);
439 } else
440 lines.add(line);
441 }
442 }
443 }
444 PaintLines(bg, lines);
445 } else {
446 // PaintPictures(bg, Frame.FreeItems);
447 // PaintNonLinesNonPicture(bg, Frame.FreeItems);
448 PaintLines(bg, Frame.FreeItems);
449 }
450
451 if (toPaint == DisplayIO.getCurrentFrame())
452 PaintNonLinesNonPicture(bg, Frame.FreeItems);
453
454 if (isActualFrame && !isAudienceMode()) {
455 PaintItem(bg, toPaint.getNameItem());
456 }
457
458 // BROOK: Ensure popups are repainted
459 if (Browser._theBrowser != null)
460 repaintPopups(Browser._theBrowser.getLayeredPane(), bg);
461
462 bg.dispose();
463 }
464
465 return toPaint.getBuffer();
466 }
467
468 // creates a new line so that lines are shown correctly when spanning
469 // across frames in TwinFrames mode
470 private static Line TransposeLine(Line line, Item d, Frame toPaint,
471 int base, int adj) {
472 Line nl = null;
473
474 if (toPaint != DisplayIO.getCurrentFrame() && d.getParent() == null
475 && line.getOppositeEnd(d).getParent() == toPaint) {
476 nl = line.copy();
477 if (d == line.getStartItem())
478 d = nl.getStartItem();
479 else
480 d = nl.getEndItem();
481
482 if (DisplayIO.FrameOnSide(toPaint) == 0)
483 d.setX(base);
484 else
485 d.setX(base + adj);
486
487 } else if (toPaint == DisplayIO.getCurrentFrame()
488 && d.getParent() == null
489 && line.getOppositeEnd(d).getParent() != toPaint) {
490 nl = line.copy();
491
492 if (d == line.getStartItem())
493 d = nl.getEndItem();
494 else
495 d = nl.getStartItem();
496
497 if (DisplayIO.FrameOnSide(toPaint) == 1)
498 d.setX(d.getX() - DisplayIO.getMiddle());
499 else
500 d.setX(d.getX() + DisplayIO.getMiddle());
501 }
502
503 return nl;
504 }
505
506 private static void Paint(Image left, Image right, Color background) {
507 if (_MessageBuffer == null) {
508 GraphicsEnvironment ge = GraphicsEnvironment
509 .getLocalGraphicsEnvironment();
510 _MessageBuffer = ge.getDefaultScreenDevice()
511 .getDefaultConfiguration().createCompatibleVolatileImage(
512 _MaxSize.width,
513 (isAudienceMode() ? 0 : MESSAGE_BUFFER_HEIGHT));
514 }
515
516 paintMessage(_MessageBuffer.createGraphics(), background);
517 Graphics g = _DisplayGraphics.create();
518
519 // if TwinFrames mode is on, then clipping etc has to be set
520 if (DisplayIO.isTwinFramesOn()) {
521 // draw the two lines down the middle of the window
522 if (left != null)
523 left.getGraphics().drawLine(DisplayIO.getMiddle() - 2, 0,
524 DisplayIO.getMiddle() - 2, _MaxSize.height);
525
526 if (right != null)
527 right.getGraphics().drawLine(0, 0, 0, _MaxSize.height);
528
529 // set the clipping area
530 ((Graphics2D) g).setClip(0, 0, DisplayIO.getMiddle() - 1,
531 _MaxSize.height);
532 g.drawImage(left, 0, 0, Item.DEFAULT_BACKGROUND, null);
533 ((Graphics2D) g).setClip(null);
534 g.drawImage(right, DisplayIO.getMiddle() + 1, 0,
535 Item.DEFAULT_BACKGROUND, null);
536
537 // otherwise, just draw whichever side is active
538 } else {
539 if (DisplayIO.getCurrentSide() == 0)
540 g.drawImage(left, 0, 0, Item.DEFAULT_BACKGROUND, null);
541 else
542 g.drawImage(right, 0, 0, Item.DEFAULT_BACKGROUND, null);
543 }
544 // Dont display the message area in audience mode
545 if (!isAudienceMode()) {
546 // draw the message area
547 g.drawImage(_MessageBuffer, 0, _MaxSize.height, null);
548 }
549 g.dispose();
550
551 }
552
553 /**
554 * Paints the message area
555 *
556 * @param g
557 * @param background
558 */
559 private static void paintMessage(Graphics2D g, Color background) {
560 ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
561 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
562 g.setColor(background);
563 g.fillRect(0, 0, _MaxSize.width, MESSAGE_BUFFER_HEIGHT);
564 g.setFont(_MessageFont);
565 g.setColor(Color.BLACK);
566 g.drawLine(0, 0, _MaxSize.width, 0);
567 for (Item t : Messages)
568 PaintItem(g, t);
569 if (MessageLink.getLink() != null)
570 PaintItem(g, MessageLink);
571 g.dispose();
572 }
573
574 public static void Clear() {
575 Graphics g = _DisplayGraphics.create();
576 g.setColor(Color.WHITE);
577 g.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
578 g.dispose();
579 }
580
581 /**
582 * Called to refresh the display screen.
583 *
584 */
585 public static void Repaint() {
586 Runtime.getRuntime();
587 if (_DisplayGraphics == null)
588 return;
589
590 if (UserSettings.Threading) {
591 if (painter == null) {
592 painter = new FrameGraphics().new Repainter();
593
594 painter.setDaemon(true);
595 painter.setPriority(Thread.MIN_PRIORITY);
596
597 painter.start();
598 } else
599 painter.run();
600 } else {
601 Frame[] toPaint = DisplayIO.getFrames();
602
603 if (toPaint != null) {
604 Image left = Paint(toPaint[0]);
605 Image right = Paint(toPaint[1]);
606 Paint(left, right, Item.DEFAULT_BACKGROUND);
607 }
608 }
609 }
610
611 private static Repainter painter = null;
612
613 private static void PaintNonLinesNonPicture(Graphics2D g, List<Item> toPaint) {
614 for (Item i : toPaint)
615 if (!(i instanceof Line) && !(i instanceof Picture))
616 PaintItem(g, i);
617 }
618
619 private static void PaintLines(Graphics2D g, List<Item> toPaint) {
620 Stroke oldStroke = g.getStroke();
621 // Use this set to keep track of the items that have been painted
622 Collection<Item> done = new HashSet<Item>();
623 for (Item i : toPaint)
624 if (i instanceof Line) {
625 Line l = (Line)i;
626 if (done.contains(l)) {
627 l.paintArrows(g);
628 } else {
629 // When painting a line all connected lines are painted too
630 done.addAll(l.getAllConnected());
631 if (l.getStartItem().getEnclosedArea() == 0)
632 PaintItem(g, i);
633 }
634 }
635 g.setStroke(oldStroke);
636 }
637
638 private static void PaintPictures(Graphics2D g, List<Item> toPaint) {
639 // Use this set to keep track of the items that dont need to be
640 // repainted
641 Collection<Item> done = new HashSet<Item>();
642 List<Item> toFill = new LinkedList<Item>();
643 for (Item i : toPaint) {
644 // Ignore items that have already been done!
645 if (done.contains(i))
646 continue;
647 if (i instanceof Picture) {
648 PaintItem(g, i);
649 } else if (i.isLineEnd()
650 && (!isAudienceMode() || !i.isConnectedToAnnotation())) {
651 toFill.add(i);
652 done.addAll(i.getAllConnected());
653 }
654 }
655 //Sort the items to fill
656 Collections.sort(toFill, new Comparator<Item>() {
657 public int compare(Item a, Item b) {
658 Integer aArea = a.getEnclosedArea();
659 Integer bArea = b.getEnclosedArea();
660 return aArea.compareTo(bArea) * -1;
661 }
662 });
663 for(Item i: toFill){
664 i.paintFill(g);
665 PaintItem(g, i.getLines().get(0));
666 }
667 }
668
669 /**
670 * Highlights an item on the screen Note: All graphics are handled by the
671 * Item itself.
672 *
673 * @param i
674 * The Item to highlight.
675 * @param val
676 * True if the highlighting is being shown, false if it is being
677 * erased.
678 * @return the item that was highlighted
679 */
680 public static Item Highlight(Item i) {
681 if ((i instanceof Line)) {
682 // Check if within 20% of the end of the line
683 Line l = (Line) i;
684 Item toDisconnect = l.getEndPointToDisconnect(Math
685 .round(FrameMouseActions.MouseX), Math
686 .round(FrameMouseActions.MouseY));
687
688 // Brook: Widget Edges do not have such a context
689 if (toDisconnect != null && !(i instanceof WidgetEdge)) {
690 Item.SelectedMode newMode = toDisconnect.getSelectedMode();
691 if (Frame.itemAttachedToCursor())
692 newMode = Item.SelectedMode.Normal;
693 // unhighlight all the other dots
694 for (Item conn : toDisconnect.getAllConnected()) {
695 conn.setSelectedMode(Item.SelectedMode.None);
696 }
697 l.setSelectedMode(newMode);
698 // highlight the dot that will be in disconnect mode
699 toDisconnect.setSelectedMode(newMode);
700 i = toDisconnect;
701 } else {
702 Collection<Item> connected = i.getAllConnected();
703 for (Item conn : connected) {
704 conn.setSelectedMode(Item.SelectedMode.Connected);
705 }
706 }
707 } else {
708 //FrameGraphics.ChangeSelectionMode(i,
709 // Item.SelectedMode.Normal);
710 //For polygons need to make sure all other endpoints are unHighlighted
711 for (Item conn : i.getAllConnected()) {
712 conn.setSelectedMode(Item.SelectedMode.None);
713 }
714 i.setSelectedMode(Item.SelectedMode.Normal);
715 }
716 Repaint();
717 return i;
718 }
719
720 public static void ChangeSelectionMode(Item item, Item.SelectedMode newMode) {
721 if (item == null)
722 return;
723
724 for (Item i : item.getAllConnected())
725 i.setSelectedMode(newMode);
726 Repaint();
727 }
728
729 public static void OverwriteMessage(String message) {
730 for (int ind = Messages.length - 1; ind >= 0; ind--) {
731 if (Messages[ind] != null) {
732 Messages[ind].setText(getMessagePrefix(false) + message);
733 Repaint();
734 return;
735 }
736 }
737
738 // if we have not returned, then there are no messages yet
739 DisplayMessage(message, Color.darkGray);
740 }
741
742 /**
743 * Displays the given message in the message area of the Frame, any previous
744 * message is cleared from the screen.
745 *
746 * @param message
747 * The message to display to the user in the message area
748 */
749 public static void DisplayMessage(String message) {
750 DisplayMessageAlways(message);
751 }
752
753 public static void DisplayMessageOnce(String message) {
754 displayMessage(message, null, null, Color.BLACK, false);
755 }
756
757 public static void DisplayMessage(String message, Color textColor) {
758 displayMessage(message, null, null, textColor);
759 // Misc.Beep();
760 }
761
762 public static void DisplayMessage(Text message) {
763 displayMessage(message.getFirstLine(), message.getLink(), message
764 .getAction(), message.getColor());
765 // Misc.Beep();
766 }
767
768 public static void DisplayMessageAlways(String message) {
769 displayMessage(message, null, null, Color.BLACK);
770 // Misc.Beep();
771 }
772
773 public static void WarningMessage(String message) {
774 displayMessage(message, null, null, Color.MAGENTA);
775 // Misc.Beep();
776 }
777
778 private static String _lastMessage = null;
779
780 private static void displayMessage(String message, String link,
781 List<String> actions, Color color) {
782 displayMessage(message, link, actions, color, true);
783 }
784
785 private static void displayMessage(String message, String link,
786 List<String> actions, Color color, boolean displayAlways) {
787
788 System.out.println(message);
789 assert (message != null);
790
791 if (_SupressMessages)
792 return;
793
794 if (!displayAlways && message.equals(_lastMessage)) {
795 Misc.Beep();
796 return;
797 }
798 _lastMessage = message;
799
800 if (_creator == null) {
801 _creator = new FrameCreator(MESSAGES_FRAMESET_NAME, true);
802
803 // set up 'Messages' link on the right hand side
804 MessageLink.setPosition(_MaxSize.width - MESSAGE_LINK_Y_OFFSET,
805 MESSAGE_LINK_X);
806 MessageLink.setOffset(0, -_MaxSize.height);
807 }
808
809 // if the message slots have not all been used yet
810 if (_MessageCount <= Messages.length) {
811 int pos = 15;
812 // find the next empty slot, and create the new message
813 for (int i = 0; i < Messages.length; i++) {
814 if (Messages[i] == null) {
815 Messages[i] = new Text(-1, getMessagePrefix(true) + message);
816 Messages[i].setPosition(20, pos);
817 Messages[i].setOffset(0, -_MaxSize.height);
818 Messages[i].setMaxSize(_MaxSize);
819 Messages[i].setColor(color);
820 Messages[i].setLink(link);
821 _creator.addItem(Messages[i].copy());
822 MessageLink.setLink(_creator.getCurrent());
823 Repaint();
824 return;
825 }
826
827 pos += 25;
828 }
829 }
830
831 // if we have not returned then all message slots are used
832 for (int i = 0; i < Messages.length - 1; i++) {
833 Messages[i].setText(Messages[i + 1].getFirstLine());
834 Messages[i].setColor(Messages[i + 1].getColor());
835 Messages[i].setLink(Messages[i + 1].getLink());
836 }
837
838 // show the new message
839 Text last = Messages[Messages.length - 1];
840 last.setColor(color);
841 // Make set the text for the new message
842 last.setText(getMessagePrefix(true) + message);
843 last.setLink(link);
844 last.setActions(actions);
845
846 _creator.addItem(last.copy());
847 // update the link to the latest message frame
848 MessageLink.setLink(_creator.getCurrent());
849 Repaint();
850 }
851
852 private static String getMessagePrefix(boolean incrementCounter) {
853 if (incrementCounter)
854 _MessageCount++;
855 return "@" + _MessageCount + ": ";
856 }
857
858 /**
859 * Checks if the error message ends with a frame name after the
860 * frameNameSeparator symbol
861 *
862 * @param message
863 * the message to be displayed
864 */
865 public static void LinkedErrorMessage(String message) {
866 if (_SupressMessages)
867 return;
868 Misc.Beep();
869 String[] tokens = message.split(Text.FRAME_NAME_SEPARATOR);
870 String link = null;
871 if (tokens.length > 1)
872 link = tokens[tokens.length - 1];
873 displayMessage(message, link, null, ERROR_COLOR);
874 }
875
876 public static void ErrorMessage(String message) {
877 if (_SupressMessages)
878 return;
879 Misc.Beep();
880 displayMessage(message, null, null, ERROR_COLOR);
881 }
882
883 private class Repainter extends Thread {
884 public boolean isPainting = false;
885
886 public void run() {
887 // TODO see if there is any other way to deal with this
888 if (_MaxSize.width <= 0 || _MaxSize.height <= 0) {
889 return;
890 }
891 isPainting = true;
892
893 Frame[] toPaint = DisplayIO.getFrames();
894
895 if (toPaint != null) {
896 Image left = Paint(toPaint[0]);
897 Image right = Paint(toPaint[1]);
898 Paint(left, right, Item.DEFAULT_BACKGROUND);
899 }
900
901 isPainting = false;
902 }
903 }
904
905 /**
906 * Invalidates the buffered image of the current Frame and forces it to be
907 * repainted on to the screen.
908 */
909 public static void ForceRepaint() {
910 Frame current = DisplayIO.getCurrentFrame();
911
912 if (current == null)
913 return;
914
915 Repaint();
916 }
917
918 /**
919 * Repaints the buffer of the given Frame.
920 *
921 * @param toUpdate
922 * The Frame whose buffer is to be repainted.
923 */
924
925 public static void UpdateBuffer(Frame toUpdate, boolean paintOverlays) {
926 if (toUpdate == null)
927 return;
928
929 Image vi = Paint(toUpdate, paintOverlays);
930 toUpdate.setBuffer(vi);
931 }
932
933 public static void SupressMessages(boolean val) {
934 _SupressMessages = val;
935 }
936
937 private static void repaintPopups(Container parent, Graphics g) {
938 for (Component c : parent.getComponents()) {
939 if (c instanceof JPopupMenu && ((JPopupMenu) c).isVisible()) {
940
941 Point p = SwingUtilities.convertPoint(c, c.getLocation(),
942 Browser._theBrowser.getContentPane());
943
944 g.translate(p.x, p.y);
945 c.paint(g);
946 g.translate(-p.x, -p.y);
947 } else if (c instanceof Container
948 && c != Browser._theBrowser.getContentPane()) {
949 repaintPopups((Container) c, g);
950 }
951 }
952 }
953}
Note: See TracBrowser for help on using the repository browser.