source: trunk/org/expeditee/gui/FrameGraphics.java@ 4

Last change on this file since 4 was 4, checked in by davidb, 16 years ago

Starting source code to Expeditee

File size: 19.1 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Dimension;
5import java.awt.Font;
6import java.awt.Graphics;
7import java.awt.Graphics2D;
8import java.awt.GraphicsEnvironment;
9import java.awt.RenderingHints;
10import java.awt.image.VolatileImage;
11import java.util.LinkedList;
12import java.util.List;
13
14import org.expeditee.actions.Misc;
15import org.expeditee.items.Dot;
16import org.expeditee.items.Item;
17import org.expeditee.items.Line;
18import org.expeditee.items.Picture;
19import org.expeditee.items.Text;
20
21public class FrameGraphics {
22 public static final int MESSAGE_BUFFER_HEIGHT = 100;
23
24 private static final int MESSAGE_LINK_Y_OFFSET = 100;
25
26 private static final int MESSAGE_LINK_X = 50;
27
28 // the graphics used to paint with
29 private static Graphics2D _DisplayGraphics;
30
31 // the maximum size that can be used to paint on
32 private static Dimension _MaxSize;
33
34 // messages shown in the message window
35 public static Text[] Messages = new Text[3];
36
37 // buffer of the message window
38 private static VolatileImage _MessageBuffer = null;
39
40 // font used for the messages
41 private static Font _MessageFont = Font.decode("Serif-Plain-16");
42
43 // the number of messages currently shown (used for scrolling up)
44 private static int _MessageCount = 1;
45
46 // modes
47 public static final int MODE_NORMAL = 0;
48
49 public static final int MODE_AUDIENCE = 1;
50
51 public static final int MODE_XRAY = 2;
52
53 private static int _Mode = MODE_NORMAL;
54
55 // if true, error messages are not shown to the user
56 private static boolean _SupressErrors = false;
57
58 // The link to the message frameset
59 public static Text MessageLink = new Text(-2, "Messages");
60
61 // Date\time formatter for timestamping the messages
62 /*
63 * private static SimpleDateFormat _formatter = new SimpleDateFormat( "
64 * [ddMMMyyyy:HHmm]");
65 */
66
67 // creator for creating the message frames
68 private static FrameCreator _creator;
69
70 /**
71 * Returns the Graphics2D object currently being used to draw with, this may
72 * be null if no Graphics2D has been assigned yet.
73 *
74 * @return the Graphics2D object being used to draw, or null.
75 */
76 public static Graphics2D getGraphics() {
77 return _DisplayGraphics;
78 }
79
80 /**
81 * If Audience Mode is on this method will toggle it to be off, or
82 * vice-versa. This results in the Frame being re-parsed and repainted.
83 */
84 public static void ToggleAudienceMode() {
85 if (_Mode == MODE_AUDIENCE)
86 _Mode = MODE_NORMAL;
87 else
88 _Mode = MODE_AUDIENCE;
89
90 FrameUtils.Parse(DisplayIO.getCurrentFrame());
91 DisplayIO.UpdateTitle();
92 Repaint();
93 }
94
95 /**
96 * If X-Ray Mode is on this method will toggle it to be off, or vice-versa.
97 * This results in the Frame being re-parsed and repainted.
98 */
99 public static void ToggleXRayMode() {
100 if (_Mode == MODE_XRAY)
101 _Mode = MODE_NORMAL;
102 else
103 _Mode = MODE_XRAY;
104
105 FrameUtils.Parse(DisplayIO.getCurrentFrame());
106 Repaint();
107 }
108
109 /**
110 * @return True if Audience Mode is currently on, False otherwise.
111 */
112 public static boolean isAudienceMode() {
113 return _Mode == MODE_AUDIENCE;
114 }
115
116 /**
117 * @return True if X-Ray Mode is currently on, False otherwise.
118 */
119 public static boolean isXRayMode() {
120 return _Mode == MODE_XRAY;
121 }
122
123 public static void setMaxSize(Dimension max) {
124 if (_MaxSize == null)
125 _MaxSize = max;
126
127 // Mike assumes this is the height of the Message window?
128 _MaxSize.setSize(max.width, max.height - DisplayIO.getOffset()
129 - MESSAGE_BUFFER_HEIGHT);
130 if (DisplayIO.getCurrentFrame() != null) {
131 DisplayIO.getCurrentFrame().setBuffer(null);
132 DisplayIO.getCurrentFrame().setMaxSize(max);
133 }
134
135 _MessageBuffer = null;
136
137 for (int i = 0; i < Messages.length; i++) {
138 if (Messages[i] != null) {
139 Messages[i].setOffset(0, -_MaxSize.height);
140 Messages[i].setMaxSize(_MaxSize);
141 }
142 }
143
144 MessageLink.setOffset(0, -_MaxSize.height);
145 MessageLink.setMaxSize(_MaxSize);
146 MessageLink.setPosition(_MaxSize.width - MESSAGE_LINK_Y_OFFSET,
147 MESSAGE_LINK_X);
148
149 Repaint();
150 }
151
152 public static Dimension getMaxSize() {
153 return _MaxSize;
154 }
155
156 public static Dimension getMaxFrameSize() {
157 if (DisplayIO.isTwinFramesOn()) {
158 return new Dimension((_MaxSize.width / 2), _MaxSize.height);
159 } else
160 return _MaxSize;
161 }
162
163 /**
164 * Sets the Graphics2D object that should be used for all painting tasks.
165 * Note: Actual painting is done by using g.create() to create temporary
166 * instances that are then disposed of using g.dispose().
167 *
168 * @param g
169 * The Graphics2D object to use for all painting
170 */
171 public static void setDisplayGraphics(Graphics2D g) {
172 _DisplayGraphics = g;
173 }
174
175 /*
176 * Displays the given Item on the screen
177 */
178 private static void PaintItem(Graphics2D g, Item i) {
179 if (i == null || g == null)
180 return;
181
182 // do not paint annotation items in audience mode
183 if (g != null
184 && (!isAudienceMode()
185 || (isAudienceMode() && !i.isAnnotation()) || i == FrameUtils.LastEdited))
186 // do not paint the name of overlay frames
187 if (i.getParent() == null
188 || i.getParent() == DisplayIO.getCurrentFrame()
189 || i.getParent() == DisplayIO.getOppositeFrame()
190 || i != i.getParent().getName()) {
191 Graphics2D tg = (Graphics2D) g.create();
192 i.paint(tg);
193 tg.dispose();
194
195 }
196 }
197
198 private static void AddAllOverlayItems(List<Item> items, Frame overlay,
199 List<Frame> seenOverlays) {
200 if (seenOverlays.contains(overlay))
201 return;
202
203 seenOverlays.add(overlay);
204
205 for (Overlay o : overlay.getOverlays())
206 AddAllOverlayItems(items, o.Frame, seenOverlays);
207
208 items.addAll(overlay.getItems());
209 }
210
211 private static VolatileImage Paint(Frame toPaint) {
212 return Paint(toPaint, true);
213 }
214
215 private static VolatileImage Paint(Frame toPaint, boolean paintOverlay) {
216 if (toPaint == null)
217 return null;
218
219 // the buffer is not valid, so it must be recreated
220 if (!toPaint.isBufferValid()) {
221 VolatileImage buffer = toPaint.getBuffer();
222 if (buffer == null) {
223 GraphicsEnvironment ge = GraphicsEnvironment
224 .getLocalGraphicsEnvironment();
225 buffer = ge.getDefaultScreenDevice().getDefaultConfiguration()
226 .createCompatibleVolatileImage(_MaxSize.width,
227 _MaxSize.height);
228 toPaint.setBuffer(buffer);
229 }
230
231 Graphics2D bg = (Graphics2D) buffer.getGraphics();
232 // Text items should automatically be Anti-Aliased now
233 /*
234 * bg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
235 * RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
236 */
237
238 // Nicer looking lines, but may be too jerky while
239 // rubber-banding on older machines
240 if (UserSettings.AntiAlias)
241 ((Graphics2D) bg).setRenderingHint(
242 RenderingHints.KEY_ANTIALIASING,
243 RenderingHints.VALUE_ANTIALIAS_ON);
244
245 bg.setColor(toPaint.getPaintBackgroundColor());
246 bg.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
247
248 List<Item> paintItems = new LinkedList<Item>();
249 List<Frame> seenOverlays = new LinkedList<Frame>();
250 if (paintOverlay)
251 AddAllOverlayItems(paintItems, toPaint, seenOverlays);
252 else {
253 paintItems.addAll(toPaint.getItems());
254 paintItems.remove(toPaint.getFrameNameItem());
255 }
256 PaintPictures(bg, paintItems);
257
258 if (toPaint == DisplayIO.getCurrentFrame())
259 PaintPictures(bg, Frame.FreeItems);
260
261 PaintNonLinesNonPicture(bg, paintItems);
262 PaintLines(bg, paintItems);
263
264 if (toPaint == DisplayIO.getCurrentFrame())
265 PaintNonLinesNonPicture(bg, Frame.FreeItems);
266
267 // toPaint.setBufferValid(true);
268
269 if (DisplayIO.isTwinFramesOn()) {
270 List<Item> lines = new LinkedList<Item>();
271 for (Item i : Frame.FreeItems) {
272 if (i instanceof Line) {
273 Line line = (Line) i;
274
275 if (toPaint != DisplayIO.getCurrentFrame()) {
276 if (line.getEndItem().isFloating()
277 ^ line.getStartItem().isFloating()) {
278 lines.add(TransposeLine(line,
279 line.getEndItem(), toPaint, DisplayIO
280 .getRealMouseX(), -DisplayIO
281 .getMiddle()));
282 lines.add(TransposeLine(line, line
283 .getStartItem(), toPaint, DisplayIO
284 .getRealMouseX(), -DisplayIO
285 .getMiddle()));
286 }
287 } else {
288 if (line.getEndItem().isFloating()
289 ^ line.getStartItem().isFloating()) {
290 Line l = TransposeLine(line, line.getEndItem(),
291 toPaint, 0, 0);
292 if (l == null)
293 l = TransposeLine(line,
294 line.getStartItem(), toPaint, 0, 0);
295 if (l == null)
296 l = line;
297 lines.add(l);
298 } else
299 lines.add(line);
300 }
301 }
302 }
303 PaintLines(bg, lines);
304 } else {
305 // PaintPictures(bg, Frame.FreeItems);
306 // PaintNonLinesNonPicture(bg, Frame.FreeItems);
307 PaintLines(bg, Frame.FreeItems);
308 }
309
310 bg.dispose();
311 }
312
313 return toPaint.getBuffer();
314 }
315
316 // creates a new line so that lines are shown correctly when spanning
317 // across frames in TwinFrames mode
318 private static Line TransposeLine(Line line, Item d, Frame toPaint,
319 int base, int adj) {
320 Line nl = null;
321
322 if (toPaint != DisplayIO.getCurrentFrame() && d.getParent() == null
323 && line.getOppositeEnd(d).getParent() == toPaint) {
324 nl = line.copy();
325 if (d == line.getStartItem())
326 d = nl.getStartItem();
327 else
328 d = nl.getEndItem();
329
330 if (DisplayIO.FrameOnSide(toPaint) == 0)
331 d.setX(base);
332 else
333 d.setX(base + adj);
334
335 } else if (toPaint == DisplayIO.getCurrentFrame()
336 && d.getParent() == null
337 && line.getOppositeEnd(d).getParent() != toPaint) {
338 nl = line.copy();
339
340 if (d == line.getStartItem())
341 d = nl.getEndItem();
342 else
343 d = nl.getStartItem();
344
345 if (DisplayIO.FrameOnSide(toPaint) == 1)
346 d.setX(d.getX() - DisplayIO.getMiddle());
347 else
348 d.setX(d.getX() + DisplayIO.getMiddle());
349 }
350
351 return nl;
352 }
353
354 private static void Paint(VolatileImage left, VolatileImage right,
355 Color background) {
356 if (_MessageBuffer == null) {
357 GraphicsEnvironment ge = GraphicsEnvironment
358 .getLocalGraphicsEnvironment();
359 _MessageBuffer = ge.getDefaultScreenDevice()
360 .getDefaultConfiguration().createCompatibleVolatileImage(
361 _MaxSize.width, MESSAGE_BUFFER_HEIGHT);
362 }
363
364 paintMessage(_MessageBuffer.createGraphics(), background);
365 Graphics g = _DisplayGraphics.create();
366
367 // if TwinFrames mode is on, then clipping etc has to be set
368 if (DisplayIO.isTwinFramesOn()) {
369 // draw the two lines down the middle of the window
370 if (left != null)
371 left.getGraphics().drawLine(DisplayIO.getMiddle() - 2, 0,
372 DisplayIO.getMiddle() - 2, _MaxSize.height);
373
374 if (right != null)
375 right.getGraphics().drawLine(0, 0, 0, _MaxSize.height);
376
377 // set the clipping area
378 ((Graphics2D) g).setClip(0, 0, DisplayIO.getMiddle() - 1,
379 _MaxSize.height + DisplayIO.getOffset());
380 g.drawImage(left, 0, DisplayIO.getOffset(),
381 DisplayIO.DEFAULT_BACKGROUND, null);
382 ((Graphics2D) g).setClip(null);
383 g.drawImage(right, DisplayIO.getMiddle() + 1,
384 DisplayIO.getOffset(), DisplayIO.DEFAULT_BACKGROUND, null);
385
386 // otherwise, just draw whichever side is active
387 } else {
388 if (DisplayIO.getCurrentSide() == 0)
389 g.drawImage(left, 0, DisplayIO.getOffset(),
390 DisplayIO.DEFAULT_BACKGROUND, null);
391 else
392 g.drawImage(right, 0, DisplayIO.getOffset(),
393 DisplayIO.DEFAULT_BACKGROUND, null);
394 }
395 // draw the message area
396 g.drawImage(_MessageBuffer, 0, _MaxSize.height + DisplayIO.getOffset(),
397 null);
398 g.dispose();
399 }
400
401 private static void paintMessage(Graphics2D g, Color background) {
402 ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
403 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
404 g.setColor(background);
405 g.fillRect(0, 0, _MaxSize.width, MESSAGE_BUFFER_HEIGHT);
406 g.setFont(_MessageFont);
407 g.setColor(Color.BLACK);
408 g.drawLine(0, 0, _MaxSize.width, 0);
409 for (Text t : Messages)
410 PaintItem(g, t);
411 if (MessageLink.getLink() != null)
412 PaintItem(g, MessageLink);
413 g.dispose();
414 }
415
416 public static void Clear() {
417 Graphics g = _DisplayGraphics.create();
418 g.setColor(Color.WHITE);
419 g.fillRect(0, 0, _MaxSize.width, _MaxSize.height);
420 g.dispose();
421 }
422
423 /**
424 * Called to refresh the display screen.
425 *
426 */
427 public static void Repaint() {
428 if (_DisplayGraphics == null)
429 return;
430
431 if (UserSettings.Threading) {
432 if (painter == null) {
433 painter = new FrameGraphics().new Repainter();
434
435 painter.setDaemon(true);
436 painter.setPriority(Thread.MIN_PRIORITY);
437
438 painter.start();
439 } else
440 painter.run();
441 } else {
442 Frame[] toPaint = DisplayIO.getFrames();
443
444 if (toPaint != null) {
445 VolatileImage left = Paint(toPaint[0]);
446 VolatileImage right = Paint(toPaint[1]);
447 Paint(left, right, DisplayIO.DEFAULT_BACKGROUND);
448 }
449 }
450 }
451
452 private static Repainter painter = null;
453
454 private static void PaintNonLinesNonPicture(Graphics2D g, List<Item> toPaint) {
455 for (Item i : toPaint)
456 if (!(i instanceof Line) && !(i instanceof Picture))
457 PaintItem(g, i);
458 }
459
460 private static void PaintLines(Graphics2D g, List<Item> toPaint) {
461 for (Item i : toPaint)
462 if (i instanceof Line)
463 PaintItem(g, i);
464 }
465
466 private static void PaintPictures(Graphics2D g, List<Item> toPaint) {
467 for (Item i : toPaint) {
468 if (i instanceof Picture)
469 PaintItem(g, i);
470 else if (i instanceof Dot)
471 ((Dot) i).paintFill(g);
472 }
473 }
474
475 /**
476 * Highlights an item on the screen Note: All graphics are handled by the
477 * Item itself.
478 *
479 * @param i
480 * The Item to highlight.
481 * @param val
482 * True if the highlighting is being shown, false if it is being
483 * erased.
484 */
485 public static void Highlight(Item i, boolean val, boolean changeMouse) {
486 if ((i instanceof Line/* Dot */)) {
487 List<Item> connected = i.getAllConnected();
488
489 for (Item conn : connected)
490 conn.showHighlight(val);
491 } else {
492 i.showHighlight(val);
493 }
494
495 Repaint();
496 }
497
498 public static void OverwriteMessage(String message) {
499 for (int ind = Messages.length - 1; ind >= 0; ind--) {
500 if (Messages[ind] != null) {
501 Messages[ind].setText(message);
502 Repaint();
503 return;
504 }
505 }
506
507 // if we have not returned, then there are no messages yet
508 DisplayMessage(message);
509 }
510
511 /**
512 * Displays the given message in the message area of the Frame, any previous
513 * message is cleared from the screen.
514 *
515 * @param message
516 * The message to display to the user in the message area
517 */
518 public static void DisplayMessage(String message) {
519 DisplayMessage(message, Color.BLACK);
520 }
521
522 public static void DisplayMessage(String message, Color textColor) {
523 displayMessage(message, null, textColor);
524 Misc.Beep();
525 }
526
527 public static void DisplayMessage(Text message) {
528 displayMessage(message.getFirstLine(), message.getLink(), message
529 .getColor());
530 Misc.Beep();
531 }
532
533 public static void DisplayMessageAlways(String message) {
534 _lastMessage = null;
535 displayMessage(message, null, Color.BLACK);
536 Misc.Beep();
537 }
538
539 public static void WarningMessage(String message) {
540 displayMessage(message, null, Color.MAGENTA);
541 Misc.Beep();
542 }
543
544 private static String _lastMessage = null;
545
546 private static void displayMessage(String message, String link, Color color) {
547 // add timestamp to message
548 if (message.equals(_lastMessage))
549 return;
550 _lastMessage = message;
551
552 // message += _formatter.format(Calendar.getInstance().getTime());
553
554 // if the creator needs to be initialised (happens on first message)
555 if (_creator == null) {
556 _creator = new FrameCreator("Messages", true);
557
558 // set up 'Messages' link on the right hand side
559 MessageLink.setPosition(_MaxSize.width - MESSAGE_LINK_Y_OFFSET,
560 MESSAGE_LINK_X);
561 MessageLink.setOffset(0, -_MaxSize.height);
562 }
563
564 // if the message slots have not all been used yet
565 if (_MessageCount < 4) {
566 int pos = 15;
567 // find the next empty slot, and create the new message
568 for (int i = 0; i < Messages.length; i++) {
569 if (Messages[i] == null) {
570 Messages[i] = new Text(-1, _MessageCount++ + ": " + message);
571 Messages[i].setPosition(20, pos);
572 Messages[i].setOffset(0, -_MaxSize.height);
573 Messages[i].setMaxSize(_MaxSize);
574 Messages[i].setColor(color);
575 Messages[i].setLink(link);
576
577 _creator.addItem(Messages[i].copy());
578 MessageLink.setLink(_creator.getCurrent());
579 Repaint();
580 return;
581 }
582
583 pos += 25;
584 }
585 }
586
587 // if we have not returned then all message slots are used
588 for (int i = 0; i < Messages.length - 1; i++) {
589 Messages[i].setText(Messages[i + 1].getFirstLine());
590 Messages[i].setColor(Messages[i + 1].getColor());
591 Messages[i].setLink(Messages[i + 1].getLink());
592 }
593
594 // show the new message
595 Text last = Messages[Messages.length - 1];
596 last.setColor(color);
597 last.setText(_MessageCount++ + ": " + message);
598 last.setLink(link);
599
600 _creator.addItem(last.copy());
601 // update the link to the latest message frame
602 MessageLink.setLink(_creator.getCurrent());
603 Repaint();
604 }
605
606 /**
607 * Checks if the error message ends with a frame name after the
608 * frameNameSeparator symbol
609 *
610 * @param message
611 * the message to be displayed
612 */
613 public static void LinkedErrorMessage(String message) {
614 if (_SupressErrors)
615 return;
616 Misc.Beep();
617 String[] tokens = message.split(Text.FRAME_NAME_SEPARATOR);
618 String link = null;
619 if (tokens.length > 1)
620 link = tokens[tokens.length - 1];
621 displayMessage(message, link, Color.RED);
622 }
623
624 public static void ErrorMessage(String message) {
625 if (_SupressErrors)
626 return;
627 Misc.Beep();
628 displayMessage(message, null, Color.RED);
629
630 // Michael is confused about why this code is gettting into infinite
631 // loop
632
633 /*
634 * Exception e = new Exception();
635 *
636 * try{ throw e; }catch(Exception ex){ StackTraceElement[] trace =
637 * ex.getStackTrace(); message += " (" + trace[1].getFileName() + ":" +
638 * trace[1].getLineNumber() + ")";
639 *
640 * String source = trace[1].getClassName(); source = "src.java." +
641 * source; source = source.replace(".", File.separator); source +=
642 * ".java";
643 *
644 * try{ BufferedReader reader = new BufferedReader(new
645 * FileReader(source)); FrameCreator creator = new
646 * FrameCreator("Messages", false);
647 *
648 * int min = trace[1].getLineNumber() - 10; int max =
649 * trace[1].getLineNumber() + 3;
650 *
651 * int line = 0; while(reader.ready()){ if(line > min && line < max)
652 * if(line == trace[1].getLineNumber() - 1)
653 * creator.addText(reader.readLine(), Color.RED); else
654 * creator.addText(reader.readLine()); else if(line > max) break; else
655 * reader.readLine();
656 *
657 * line++; }
658 *
659 * reader.close();
660 *
661 * displayMessage(message, creator.getCurrent(), Color.RED);
662 * }catch(Exception ioe){ ioe.printStackTrace(); } }
663 */
664 }
665
666 private class Repainter extends Thread {
667 public boolean isPainting = false;
668
669 public void run() {
670 isPainting = true;
671
672 Frame[] toPaint = DisplayIO.getFrames();
673
674 if (toPaint != null) {
675 VolatileImage left = Paint(toPaint[0]);
676 VolatileImage right = Paint(toPaint[1]);
677 Paint(left, right, DisplayIO.DEFAULT_BACKGROUND);
678 }
679
680 isPainting = false;
681 }
682 }
683
684 /**
685 * Invalidates the buffered image of the current Frame and forces it to be
686 * repainted on to the screen.
687 */
688 public static void ForceRepaint() {
689 Frame current = DisplayIO.getCurrentFrame();
690
691 if (current == null)
692 return;
693
694 Repaint();
695 }
696
697 /**
698 * Repaints the buffer of the given Frame.
699 *
700 * @param toUpdate
701 * The Frame whose buffer is to be repainted.
702 */
703
704 public static void UpdateBuffer(Frame toUpdate, boolean paintOverlays) {
705 if (toUpdate == null)
706 return;
707
708 VolatileImage vi = Paint(toUpdate, paintOverlays);
709 toUpdate.setBuffer(vi);
710 }
711
712 public static void SupressErrors(boolean val) {
713 _SupressErrors = val;
714 }
715}
Note: See TracBrowser for help on using the repository browser.