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

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

added functionality for dockable @v's

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