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

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

Added calculate action

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