source: trunk/src/org/expeditee/gui/DisplayIO.java@ 55

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

Fixed bug with widgets movement and scaling

File size: 18.9 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.AWTException;
4import java.awt.Color;
5import java.awt.Cursor;
6import java.awt.Image;
7import java.awt.Point;
8import java.awt.Robot;
9import java.awt.Toolkit;
10import java.awt.image.MemoryImageSource;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.Stack;
14
15import javax.swing.JOptionPane;
16
17import org.expeditee.items.Item;
18import org.expeditee.items.ItemParentStateChangedEvent;
19import org.expeditee.items.Picture;
20import org.expeditee.stats.SessionStats;
21
22/**
23 * This Interface is used by the Frame to control all display input and output.
24 *
25 * @author jdm18
26 *
27 */
28public class DisplayIO {
29
30 private static final int SMALL_CURSOR_SIZE = 16;
31
32 private static final int MEDIUM_CURSOR_SIZE = 32;
33
34 private static final int LARGE_CURSOR_SIZE = 64;
35
36 public static final Color DEFAULT_BACKGROUND = Color.WHITE;
37
38 /**
39 * The color to be used to highlight the linked parent item, when the user
40 * navigates backwards.
41 */
42 public static final Color BACK_HIGHLIGHT_COLOR = Color.MAGENTA;
43
44 private static Browser _Browser;
45
46 // The current Frame being displayed on the screen.
47 private static Frame _CurrentFrames[] = new Frame[2];
48
49 // Maintains the list of frames visited thus-far for back-tracking
50 @SuppressWarnings("unchecked")
51 private static Stack<String>[] _VisitedFrames = new Stack[2];
52
53 // used to change the mouse cursor position on the screen
54 private static Robot _Robot;
55
56 // public static JMenuBar _MenuBar = new JMenuBar();
57
58 private static boolean _TwinFrames = false;
59
60 /**
61 * The title to display in the Title bar.
62 */
63 public static final String TITLE = "Exp15May2008A";
64
65 private DisplayIO() {
66 }
67
68 public static void Init(Browser browser) {
69 _Browser = browser;
70 try {
71 _Robot = new Robot();
72 } catch (AWTException e) {
73 e.printStackTrace();
74 }
75
76 Point mouse = _Browser.getMousePosition();
77 if (mouse != null) {
78 FrameMouseActions.MouseX = mouse.x;
79 FrameMouseActions.MouseY = mouse.y;
80 }
81
82 _VisitedFrames[0] = new Stack<String>();
83 _VisitedFrames[1] = new Stack<String>();
84 }
85
86 public static void setTextCursor(int size) {
87 if (cursorType == Item.TEXT_CURSOR)
88 return;
89
90 cursorType = Item.TEXT_CURSOR;
91
92 // Do some stuff to adjust the cursor size based on the font size
93 final int MEDIUM_CURSOR_CUTOFF = 31;
94 final int LARGE_CURSOR_CUTOFF = 62;
95
96 int cursorSize = LARGE_CURSOR_SIZE;
97 int hotspotPos = 0;
98 int start = 0;
99
100 if (size < MEDIUM_CURSOR_CUTOFF) {
101 cursorSize = MEDIUM_CURSOR_SIZE;
102 start = cursorSize - size - 2;
103 hotspotPos = cursorSize - (size + 2) / 4;
104 } else if (size < LARGE_CURSOR_CUTOFF) {
105 hotspotPos = cursorSize - (size - 5) / 4;
106 start = cursorSize - size - 2;
107 } else {
108 int FIXED_CURSOR_MIN = 77;
109 if (size >= FIXED_CURSOR_MIN) {
110 hotspotPos = cursorSize - 2;
111 } else {
112 hotspotPos = size - (FIXED_CURSOR_MIN - cursorSize);
113 }
114 }
115
116 /*
117 * System.out.println("hs: " + hotspotPos + " start: " + start + " font: " +
118 * size + " cursor: " + (cursorSize - start));
119 */
120
121 int[] pixels = new int[cursorSize * cursorSize];
122
123 for (int i = start; i < cursorSize; i++)
124 pixels[i * cursorSize] = pixels[(i * cursorSize) + 1] = 0xFF000000;
125
126 Image image = Toolkit.getDefaultToolkit().createImage(
127 new MemoryImageSource(cursorSize, cursorSize, pixels, 0,
128 cursorSize));
129 Cursor textCursor = Toolkit.getDefaultToolkit().createCustomCursor(
130 image, new Point(0, hotspotPos), "textcursor");
131 _Browser.setCursor(textCursor);
132 }
133
134 /**
135 * Sets the type of cursor the display should be using
136 *
137 * @param type
138 * The type of cursor to display, using constants defined in the
139 * Cursor class.
140 */
141 public static void setCursor(int type) {
142 // avoid flicker when not changing
143 if (type == cursorType || type == Item.UNCHANGED_CURSOR)
144 return;
145
146 cursorType = type;
147
148 if (type == Item.HIDDEN_CURSOR) {
149 int[] pixels = new int[SMALL_CURSOR_SIZE * SMALL_CURSOR_SIZE];
150 Image image = Toolkit.getDefaultToolkit().createImage(
151 new MemoryImageSource(SMALL_CURSOR_SIZE, SMALL_CURSOR_SIZE,
152 pixels, 0, SMALL_CURSOR_SIZE));
153 Cursor transparentCursor = Toolkit.getDefaultToolkit()
154 .createCustomCursor(image, new Point(0, 0),
155 "invisiblecursor");
156 _Browser.setCursor(transparentCursor);
157 } else
158 _Browser.setCursor(new Cursor(type));
159 }
160
161 private static int cursorType = Item.DEFAULT_CURSOR;
162
163 public static int getCursor() {
164 return cursorType;
165 }
166
167 /**
168 * Moves the mouse cursor to the given x,y coordinates on the screen
169 *
170 * @param x
171 * The x coordinate
172 * @param y
173 * The y coordinate
174 */
175 public static void setCursorPosition(int x, int y) {
176 setCursorPosition(x, y, true);
177 }
178
179 public static void setCursorPosition(int x, int y, boolean forceArrow) {
180 int deltax = x - FrameMouseActions.MouseX;
181 int deltay = y - FrameMouseActions.MouseY;
182
183 if (_TwinFrames) {
184 if (getCurrentSide() == 1)
185 x += getMiddle();
186 }
187
188 // ensures the cursor doesn't fall behind while waiting for robot
189 FrameMouseActions.MouseX = x;
190 FrameMouseActions.MouseY = y;
191
192 if (Frame.itemAttachedToCursor()) {
193 List<Item> toMove = Frame.FreeItems;
194 for (Item move : toMove) {
195 move.setPosition(move.getX() + deltax, move.getY() + deltay);
196 }
197 }
198
199 // cheat
200 FrameMouseActions.setForceArrow(forceArrow);
201 _Robot.mouseMove((int) _Browser.getContentPane().getLocationOnScreen()
202 .getX()
203 + x, (int) _Browser.getContentPane().getLocationOnScreen()
204 .getY()
205 + y);
206 }
207
208 public static void resetCursorOffset() {
209 FrameMouseActions.resetOffset();
210 }
211
212 public static void setCursorPosition(Point pos) {
213 setCursorPosition(pos.x, pos.y);
214 }
215
216 public static void setCursorPosition(Point pos, boolean forceArrow) {
217 setCursorPosition(pos.x, pos.y, forceArrow);
218 }
219
220 private static boolean _typed = false;
221
222 public static void setKeyTyped(boolean val) {
223 _typed = val;
224 }
225
226 public static boolean getKeyTyped() {
227 return _typed;
228 }
229
230 /**
231 * Forces a complete repaint of the GUI and Frame
232 */
233 public static void repaint() {
234 _Browser.repaint();
235 }
236
237 /**
238 * Returns the top item (last added) of the Back-Stack (which is popped off)
239 *
240 * @return The name of the last Frame added to the back-stack
241 */
242 public static String getLastFrame() {
243 int side = getCurrentSide();
244
245 if (_VisitedFrames[side].size() > 0)
246 return _VisitedFrames[side].pop();
247 else
248 return null;
249 }
250
251 /**
252 * Adds the given Frame to the back-stack
253 *
254 * @param frame
255 * The Frame to add
256 */
257 public static void addToBack(Frame toAdd) {
258 int side = getCurrentSide();
259
260 // do not allow duplicate frames
261 if (_VisitedFrames[side].size() > 0)
262 if (_VisitedFrames[side].peek().equals(toAdd.getFrameName())) {
263 return;
264 }
265
266 Item ip = FrameUtils.getCurrentItem();
267 if (ip == null)
268 _VisitedFrames[side].push(toAdd.getFrameName());
269 else
270 _VisitedFrames[side].push(toAdd.getFrameName());
271 }
272
273 /**
274 * Returns a 'peek' at the end element on the back-stack of the current
275 * side. If the back-stack is empty, null is returned.
276 *
277 * @return The name of the most recent Frame added to the back-stack, or
278 * null if the back-stack is empty.
279 */
280 public static String peekFromBack() {
281 int side = getCurrentSide();
282
283 // check that the stack is not empty
284 if (_VisitedFrames[side].size() > 0)
285 return _VisitedFrames[side].peek();
286
287 // if the stack is empty, return null
288 return null;
289 }
290
291 /**
292 * Sets the currently displayed Frame to the given Frame
293 *
294 * @param frame
295 * The Frame to display on the screen
296 */
297 public static void setCurrentFrame(Frame frame) {
298 if (frame == null)
299 return;
300
301 if (_TwinFrames) {
302 if (_CurrentFrames[0] == null) {
303 _CurrentFrames[0] = frame;
304 return;
305 }
306 if (_CurrentFrames[1] == null) {
307 _CurrentFrames[1] = frame;
308 return;
309 }
310 }
311
312 // if this is already the current frame
313 if (frame == getCurrentFrame()) {
314 FrameGraphics.Repaint();
315 FrameGraphics.DisplayMessage(frame.getFrameName()
316 + " is already the current frame.");
317 return;
318 } else {
319 SessionStats.AccessedFrame();
320 }
321
322 if (_TwinFrames) {
323 // if the same frame is being shown in both sides, load a fresh
324 // copy
325 // from disk
326 if (_CurrentFrames[getOppositeSide()] == frame
327 || _CurrentFrames[getOppositeSide()].getOverlays()
328 .contains(new Overlay(frame, 1))) {
329 FrameIO.SuspendCache();
330 frame = FrameIO.LoadFrame(frame.getFrameName());
331 FrameIO.ResumeCache();
332 }
333
334 // If the frames are the same then the items for the
335 // frame that is just about to hide will still be in view
336 // so only notify items that they are hidden if the
337 // frames differ.
338 if (_CurrentFrames[getCurrentSide()] != null
339 && _CurrentFrames[0] != _CurrentFrames[1]) {
340 for (Item i : _CurrentFrames[getCurrentSide()].getItems()) {
341 i.onParentStateChanged(new ItemParentStateChangedEvent(
342 _CurrentFrames[getCurrentSide()],
343 ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN));
344 }
345 }
346 _CurrentFrames[getCurrentSide()] = frame;
347
348 // BROOK : TODO...
349 for (Item i : _CurrentFrames[getCurrentSide()].getItems()) {
350 i.onParentStateChanged(new ItemParentStateChangedEvent(
351 _CurrentFrames[getCurrentSide()],
352 ItemParentStateChangedEvent.EVENT_TYPE_SHOWN));
353 }
354 } else {
355
356 // Notifying items on the frame being hidden that they
357 // are about to be hidden.
358 // ie. Widgets use this method to remove themselves from the JPanel
359 List<Frame> currentOnlyOverlays = new LinkedList<Frame>();
360 List<Frame> nextOnlyOverlays = new LinkedList<Frame>();
361 List<Frame> sharedOverlays = new LinkedList<Frame>();
362
363 // Get all overlayed frames seen by the next frame
364 for (Overlay o : frame.getOverlaysDeep()) {
365 if (!nextOnlyOverlays.contains(o))
366 nextOnlyOverlays.add(o.Frame);
367 }
368
369 // Get all overlayed frames seen by the current frame
370 if (_CurrentFrames[getCurrentSide()] != null) {
371 for (Overlay o : _CurrentFrames[getCurrentSide()]
372 .getOverlaysDeep()) {
373 if (!currentOnlyOverlays.contains(o))
374 currentOnlyOverlays.add(o.Frame);
375 }
376 }
377
378 // Extract shared overlays between the current and next frame
379 for (Frame of : currentOnlyOverlays) {
380 if (nextOnlyOverlays.contains(of)) {
381 sharedOverlays.add(of);
382 }
383 }
384
385 // The first set, currentOnlyOverlays, must be notified that they
386 // are hidden
387 List<Item> items = new LinkedList<Item>();
388
389 // Notify items that will not be in view any more
390 if (_CurrentFrames[getCurrentSide()] != null) {
391 List<Frame> seen = new LinkedList<Frame>();
392 seen.addAll(sharedOverlays); // Signify that seen all shared
393 // overlays
394 seen.remove(_CurrentFrames[getCurrentSide()]); // must ensure
395 // excluded
396
397 // Get all items seen from the current frame - including all
398 // possible non-shared overlays
399 FrameGraphics.AddAllOverlayItems(items,
400 _CurrentFrames[getCurrentSide()], seen);
401
402 // Notify items that they are hidden
403 for (Item i : items) {
404 i.onParentStateChanged(new ItemParentStateChangedEvent(
405 _CurrentFrames[getCurrentSide()],
406 ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN));
407 }
408 }
409
410 // Set the new frame
411 _CurrentFrames[getCurrentSide()] = frame;
412 frame.setMaxSize(FrameGraphics.getMaxFrameSize());
413
414 // Notify items on the frame being displayed that they are in view
415 // ie. widgets use this method to add themselves to the content pane
416 items.clear();
417
418 // Get all items seen from the new frame - including all possible
419 // non-shared overlays
420 FrameGraphics.AddAllOverlayItems(items,
421 _CurrentFrames[getCurrentSide()], new LinkedList<Frame>());
422
423 // Seperate current frames items from its overlayed items
424 List<Item> nonOverlayItems = _CurrentFrames[getCurrentSide()]
425 .getItems();
426 items.removeAll(nonOverlayItems);
427
428 // Notify overlay items that they are shown
429 for (Item i : items) {
430 Overlay owner = _CurrentFrames[getCurrentSide()]
431 .getOverlayOwner(i);
432 // if (owner == null) i.onParentFameShown(false, 0);
433 // else ...
434 assert (owner != null);
435 i.onParentStateChanged(new ItemParentStateChangedEvent(
436 _CurrentFrames[getCurrentSide()],
437 ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY,
438 owner.Level));
439 }
440
441 // Notify non-overlay items that they are shown
442 for (Item i : nonOverlayItems) {
443 i.onParentStateChanged(new ItemParentStateChangedEvent(
444 _CurrentFrames[getCurrentSide()],
445 ItemParentStateChangedEvent.EVENT_TYPE_SHOWN));
446 }
447 }
448
449 FrameGraphics.Repaint();
450 }
451
452 public static void UpdateTitle() {
453 StringBuffer title = new StringBuffer(TITLE + " [");
454
455 if (FrameGraphics.isAudienceMode())
456 title.append(" - Audience Mode");
457 else
458 title.append(SessionStats.getShortStats()).append(']');
459
460 _Browser.setTitle(title.toString());
461 }
462
463 public static int getCurrentSide() {
464 if (_Browser == null)
465 return 0;
466
467 if (_TwinFrames
468 && FrameMouseActions.MouseX >= (_Browser.getWidth() / 2)
469 && _CurrentFrames[1] != null)
470 return 1;
471
472 if (_CurrentFrames[0] == null && _CurrentFrames[1] != null)
473 return 1;
474
475 return 0;
476 }
477
478 private static int getOppositeSide() {
479 if (getCurrentSide() == 0)
480 return 1;
481
482 return 0;
483 }
484
485 public static int FrameOnSide(Frame toFind) {
486 if (_CurrentFrames[0] == toFind)
487 return 0;
488
489 if (_CurrentFrames[1] == toFind)
490 return 1;
491
492 return -1;
493 }
494
495 /**
496 * Returns the Frame currently being displayed on the screen.
497 *
498 * @return The Frame currently displayed.
499 */
500 public static Frame getCurrentFrame() {
501 return _CurrentFrames[getCurrentSide()];
502 }
503
504 public static Frame getOppositeFrame() {
505 return _CurrentFrames[getOppositeSide()];
506 }
507
508 public static Frame[] getFrames() {
509 return _CurrentFrames;
510 }
511
512 public static int getMiddle() {
513 return _Browser.getWidth() / 2;
514 }
515
516 public static int getHeight() {
517 return _Browser.getHeight();
518 }
519
520 /**
521 * Adds the given JMenu to the JMenuBar of the GUI
522 *
523 * @param menu
524 * The menu to add
525 */
526 /*
527 * public static void addMenu(JMenu menu) { if (menu.getItemCount() == 0)
528 * return;
529 *
530 * _MenuBar.add(menu); // show progress _Browser.loadedMenu(menu.getText()); }
531 */
532
533 /**
534 * Performs the same action as if the user had clicked on the MenuItem at
535 * the given index in the Menu at the given index.
536 *
537 * @param menu
538 * The index of the menu (starts from 0)
539 * @param index
540 * The index of the Item to simulate a click on (starts from 0)
541 */
542 /*
543 * public static void activateMenuItem(int menu, int index) { if
544 * (_MenuBar.getComponentCount() < menu) return;
545 *
546 * JMenu act = _MenuBar.getMenu(menu);
547 *
548 * if (act.getComponentCount() < index) return;
549 *
550 * act.getItem(index).doClick(); }
551 *
552 * public static int getMenuHeight() { if (_MenuBar.getHeight() < 5) return
553 * 21;
554 *
555 * return _MenuBar.getHeight() - 5; }
556 */
557
558 /**
559 * Returns the current mouse X coordinate. This coordinate is relative to
560 * the left edge of the frame the mouse is in. It takes into account the
561 * user being in twin frames mode.
562 *
563 * @return The X coordinate of the mouse.
564 */
565 public static int getMouseX() {
566 if (_TwinFrames && getMouseY() < FrameGraphics.getMaxSize().height)
567 return FrameMouseActions.MouseX % (_Browser.getWidth() / 2);
568
569 return FrameMouseActions.MouseX;
570 }
571
572 /**
573 * Returns the current mouse Y coordinate. This is relative to the top of
574 * the frame.
575 *
576 * @return The Y coordinate of the mouse.
577 */
578 public static int getMouseY() {
579 return FrameMouseActions.MouseY;
580 }
581
582 public static int getRealMouseX() {
583 return FrameMouseActions.MouseX;
584 }
585
586 public static int getRealMouseY() {
587 return FrameMouseActions.MouseY;
588 }
589
590 public static void Back() {
591 int side = getCurrentSide();
592
593 // there must be a frame to go back to
594 if (_VisitedFrames[side].size() < 1) {
595 FrameGraphics.DisplayMessage("You are already on the home frame");
596 return;
597 }
598
599 if (!FrameUtils.LeavingFrame(getCurrentFrame())) {
600 FrameGraphics.DisplayMessage("Back operation cancelled");
601 return;
602 }
603
604 String oldFrame = getCurrentFrame().getFrameName().toLowerCase();
605
606 // do not get a cached version (in case it is in the other window)
607 if (isTwinFramesOn())
608 FrameIO.SuspendCache();
609 Frame frame = FrameIO.LoadFrame(_VisitedFrames[side].pop());
610 if (isTwinFramesOn())
611 FrameIO.ResumeCache();
612
613 FrameUtils.DisplayFrame(frame, false);
614 FrameMouseActions.setHighlightHold(true);
615
616 for (Item i : frame.getItems())
617 if (i.getLink() != null
618 && i.getAbsoluteLink().toLowerCase().equals(oldFrame)) {
619 i.setSelectedMode(Item.SelectedMode.Normal,
620 BACK_HIGHLIGHT_COLOR);
621 // check if its an @f item and if so update the buffer
622 if (i instanceof Picture) {
623 Picture p = (Picture) i;
624 p.refresh();
625 }
626 }
627
628 FrameGraphics.Repaint();
629 }
630
631 /**
632 * Toggles the display of frames between TwinFrames mode and Single frame
633 * mode.
634 */
635 public static void ToggleTwinFrames() {
636 // determine which side is the active side
637 int opposite = getOppositeSide();
638 int current = getCurrentSide();
639 _TwinFrames = !_TwinFrames;
640
641 // if TwinFrames is being turned on
642 if (_TwinFrames) {
643 // if this is the first time TwinFrames has been toggled on,
644 // load
645 // the user's first frame
646 if (_VisitedFrames[opposite].size() == 0) {
647 FrameIO.SuspendCache();
648 setCurrentFrame(FrameIO.LoadFrame(UserSettings.FirstFrame));
649 FrameIO.ResumeCache();
650 } else {
651 // otherwise, restore the frame from the side's back-stack
652 setCurrentFrame(FrameIO.LoadFrame(_VisitedFrames[opposite]
653 .pop()));
654 }
655
656 // else, TwinFrames is being turned off
657 } else {
658 // add the frame to the back-stack
659 Frame hiding = _CurrentFrames[opposite];
660 FrameUtils.LeavingFrame(hiding);
661 _VisitedFrames[opposite].add(hiding.getFrameName());
662 _CurrentFrames[opposite] = null;
663 _CurrentFrames[current].setMaxSize(FrameGraphics.getMaxFrameSize());
664 }
665 if (_CurrentFrames[current] != null)
666 _CurrentFrames[current].setMaxSize(FrameGraphics.getMaxFrameSize());
667 if (_CurrentFrames[opposite] != null)
668 _CurrentFrames[opposite]
669 .setMaxSize(FrameGraphics.getMaxFrameSize());
670
671 FrameGraphics.Clear();
672 FrameGraphics.Repaint();
673 }
674
675 public static boolean isTwinFramesOn() {
676 return _TwinFrames;
677 }
678
679 public static void Reload(int side) {
680 if (side < 0)
681 return;
682
683 FrameIO.SuspendCache();
684 _CurrentFrames[side] = FrameIO.LoadFrame(_CurrentFrames[side]
685 .getFrameName());
686 FrameIO.ResumeCache();
687 }
688
689 public static boolean DisplayConfirmDialog(String message, String title,
690 int type, int options, int res) {
691 return JOptionPane.showConfirmDialog(_Browser, message, title, options,
692 type) == res;
693 }
694
695 public static final int RESULT_OK = JOptionPane.OK_OPTION;
696
697 public static final int OPTIONS_OK_CANCEL = JOptionPane.OK_CANCEL_OPTION;
698
699 public static final int TYPE_WARNING = JOptionPane.WARNING_MESSAGE;
700
701 public static void pressMouse(int buttons) {
702 _Robot.mousePress(buttons);
703 }
704
705 public static void releaseMouse(int buttons) {
706 _Robot.mouseRelease(buttons);
707 }
708
709 public static void clickMouse(int buttons) {
710 _Robot.mousePress(buttons);
711 _Robot.mouseRelease(buttons);
712 }
713}
Note: See TracBrowser for help on using the repository browser.