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

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

Added DebugFrame action
Added GetCometStats
Lots of bug fixes

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