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

Last change on this file since 468 was 468, checked in by davidb, 11 years ago

Using larger cursors in Java can hit limits specific to an particular implementation. Java provides calls to find out these limits. Code upgrades to make use of this. This change hopefully now fixes the 'strange' needing to press keys twice problem that can occur from time to time (in the noticed case, it was happening when the text items got very large, and so it was trying to add a very large cursor). Mouse-Y methods along similar lines to Mouse-X methods added as well

File size: 25.3 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.AWTException;
4import java.awt.Color;
5import java.awt.Cursor;
6import java.awt.Dimension;
7import java.awt.Image;
8import java.awt.Point;
9import java.awt.Robot;
10import java.awt.Toolkit;
11import java.awt.event.KeyEvent;
12import java.awt.geom.Point2D;
13import java.awt.image.MemoryImageSource;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.HashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Stack;
21
22import javax.swing.JOptionPane;
23
24import org.expeditee.items.Item;
25import org.expeditee.items.ItemParentStateChangedEvent;
26import org.expeditee.items.Picture;
27import org.expeditee.items.Text;
28import org.expeditee.stats.SessionStats;
29import org.expeditee.taskmanagement.EntitySaveManager;
30
31/**
32 * This Interface is used by the Frame to control all display input and output.
33 *
34 * @author jdm18
35 *
36 */
37public class DisplayIO {
38
39 private static final int SMALL_CURSOR_SIZE = 16;
40
41 private static final int MEDIUM_CURSOR_SIZE = 32;
42
43 private static final int LARGE_CURSOR_SIZE = 64;
44
45 /**
46 * The color to be used to highlight the linked parent item, when the user
47 * navigates backwards.
48 */
49 public static final Color BACK_HIGHLIGHT_COLOR = Color.MAGENTA;
50
51 private static Browser _Browser;
52
53 // The current Frame being displayed on the screen.
54 private static Frame _CurrentFrames[] = new Frame[2];
55
56 // Maintains the list of frames visited thus-far for back-tracking
57 @SuppressWarnings("unchecked")
58 private static Stack<String>[] _VisitedFrames = new Stack[2];
59
60 @SuppressWarnings("unchecked")
61 private static Stack<String>[] _BackedUpFrames = new Stack[2];
62
63 // used to change the mouse cursor position on the screen
64 private static Robot _Robot;
65
66 private static boolean _TwinFrames = false;
67
68 /** Notified whenever the frame changes */
69 private static HashSet<DisplayIOObserver> _displayIOObservers = new HashSet<DisplayIOObserver>();
70
71 /**
72 * The title to display in the Title bar.
73 */
74 public static String TITLE = "ExpDev";
75
76 private DisplayIO() {
77 }
78
79 public static void Init(Browser browser) {
80 _Browser = browser;
81 try {
82 _Robot = new Robot();
83 } catch (AWTException e) {
84 e.printStackTrace();
85 }
86
87 Point mouse = _Browser.getMousePosition();
88 if (mouse != null) {
89 FrameMouseActions.MouseX = mouse.x;
90 FrameMouseActions.MouseY = mouse.y;
91 }
92
93 _VisitedFrames[0] = new Stack<String>();
94 _VisitedFrames[1] = new Stack<String>();
95 _BackedUpFrames[0] = new Stack<String>();
96 _BackedUpFrames[1] = new Stack<String>();
97 }
98
99 /**
100 * Notifies observers that the frame has changed.
101 */
102 private static void fireFrameChanged() {
103 for (DisplayIOObserver observer : _displayIOObservers) {
104 observer.frameChanged();
105 }
106 }
107
108 /**
109 * Adds a DisplayIOObserver to the DisplayIO. DisplayIOObserver's are
110 * notified when frame changes.
111 *
112 * @see #removeDisplayIOObserver(DisplayIOObserver)
113 *
114 * @param observer
115 * The observer to add
116 *
117 * @throws NullPointerException
118 * If observer is null.
119 */
120 public static void addDisplayIOObserver(DisplayIOObserver observer) {
121 if (observer == null)
122 throw new NullPointerException("observer");
123 _displayIOObservers.add(observer);
124 }
125
126 /**
127 * Removes a DisplayIOObserver from the DisplayIO.
128 *
129 * @see #addDisplayIOObserver(DisplayIOObserver)
130 *
131 * @param observer
132 * The observer to add
133 *
134 * @throws NullPointerException
135 * If observer is null.
136 */
137 public static void removeDisplayIOObserver(DisplayIOObserver observer) {
138 if (observer == null)
139 throw new NullPointerException("observer");
140 _displayIOObservers.remove(observer);
141 }
142
143 public static void setTextCursor(Text text, int cursorMovement) {
144 setTextCursor(text, cursorMovement, false, false, false, false);
145 }
146
147 public static void setTextCursor(Text text, int cursorMovement,
148 boolean newSize, boolean isShiftDown, boolean isCtrlDown,
149 boolean allowClearSelection) {
150
151 int size = Math.round(text.getSize());
152 if (allowClearSelection && !isShiftDown && text.hasSelection())
153 text.clearSelection();
154
155 Point2D.Float newMouse = text.moveCursor(cursorMovement, DisplayIO
156 .getFloatMouseX(), FrameMouseActions.MouseY, isShiftDown,
157 isCtrlDown);
158
159 if (!newSize && cursorType == Item.TEXT_CURSOR) {
160 if (cursorMovement != 0)
161 DisplayIO.setCursorPosition(newMouse, false);
162 return;
163 }
164
165 cursorType = Item.TEXT_CURSOR;
166
167 // Do some stuff to adjust the cursor size based on the font size
168 final int MEDIUM_CURSOR_CUTOFF = 31;
169 final int LARGE_CURSOR_CUTOFF = 62;
170
171 int cursorSize = LARGE_CURSOR_SIZE;
172 int hotspotPos = 0;
173 int start = 0;
174
175 Toolkit toolkit = Toolkit.getDefaultToolkit();
176 Dimension best_cursor_dim = toolkit.getBestCursorSize(cursorSize,cursorSize);
177 int best_cursor_height = best_cursor_dim.height;
178
179 if (best_cursor_height < cursorSize) {
180 // not able to provide the size of cursor Expeditee wants to
181 // => lock on to 'best_cursor_height' and use this to generate dependent values
182 cursorSize = best_cursor_height; // OS + Java version dependent: most likely MEDIUM_CURSOR_SIZE
183 if (size < best_cursor_height) {
184 start = cursorSize - size - 1;
185 hotspotPos = cursorSize - (size + 1) / 4;
186 }
187 else {
188 start = size - best_cursor_height;
189 hotspotPos = cursorSize -1;
190 }
191
192 }
193 else if (size < MEDIUM_CURSOR_CUTOFF) {
194 cursorSize = MEDIUM_CURSOR_SIZE;
195 start = cursorSize - size - 1;
196 hotspotPos = cursorSize - (size + 1) / 4;
197 } else if (size < LARGE_CURSOR_CUTOFF) {
198 hotspotPos = cursorSize - (size - 5) / 4;
199 start = cursorSize - size - 2;
200 } else {
201 int FIXED_CURSOR_MIN = 77;
202 if (size >= FIXED_CURSOR_MIN) {
203 hotspotPos = cursorSize - 2;
204 } else {
205 hotspotPos = size - (FIXED_CURSOR_MIN - cursorSize);
206 }
207 }
208
209 int[] pixels = new int[cursorSize * cursorSize];
210
211 for (int i = start; i < cursorSize; i++) {
212 pixels[i * cursorSize] = pixels[i * cursorSize + 1] = 0xFF000000;
213 }
214
215 MemoryImageSource memory_image = new MemoryImageSource(cursorSize, cursorSize, pixels, 0, cursorSize);
216 Image image = toolkit.createImage(memory_image);
217
218 Cursor textCursor = toolkit.createCustomCursor(image, new Point(0, hotspotPos), "textcursor");
219 _Browser.setCursor(textCursor);
220
221 if (cursorMovement != Text.NONE) {
222 DisplayIO.setCursorPosition(newMouse, false);
223 }
224 }
225
226 /**
227 * Sets the type of cursor the display should be using
228 *
229 * @param type
230 * The type of cursor to display, using constants defined in the
231 * Cursor class.
232 */
233 public static void setCursor(int type) {
234 // avoid flicker when not changing
235 if (type == cursorType || type == Item.UNCHANGED_CURSOR)
236 return;
237
238 cursorType = type;
239
240 if(_Browser != null){
241 refreshCursor();
242 }
243 }
244
245 public static void refreshCursor() {
246 if (cursorType == Item.HIDDEN_CURSOR
247 || (FreeItems.hasCursor() && cursorType == Item.DEFAULT_CURSOR)) {
248 int[] pixels = new int[SMALL_CURSOR_SIZE * SMALL_CURSOR_SIZE];
249 Image image = Toolkit.getDefaultToolkit().createImage(
250 new MemoryImageSource(SMALL_CURSOR_SIZE, SMALL_CURSOR_SIZE,
251 pixels, 0, SMALL_CURSOR_SIZE));
252 Cursor transparentCursor = Toolkit.getDefaultToolkit()
253 .createCustomCursor(image, new Point(0, 0),
254 "invisiblecursor");
255 _Browser.setCursor(transparentCursor);
256 } else
257 _Browser.setCursor(new Cursor(cursorType));
258 }
259
260 private static int cursorType = Item.DEFAULT_CURSOR;
261
262 public static int getCursor() {
263 return cursorType;
264 }
265
266 /**
267 * Moves the mouse cursor to the given x,y coordinates on the screen
268 *
269 * @param x
270 * The x coordinate
271 * @param y
272 * The y coordinate
273 */
274 public static void setCursorPosition(float x, float y) {
275 setCursorPosition(x, y, true);
276 }
277
278 public static void setCursorPosition(float x, float y, boolean forceArrow) {
279 // Adjust the position to move the mouse to to account for being in
280 // TwinFramesMode
281 if (_TwinFrames) {
282 if (getCurrentSide() == 1) {
283 int middle = getMiddle();
284 x += middle;
285 }
286 }
287
288 float deltax = x - FrameMouseActions.MouseX;
289 float deltay = y - FrameMouseActions.MouseY;
290
291 // When the Robot moves the cursor... a short time later a mouseMoved
292 // event is generated...
293 // We want to ignore this event by remembering the location the robot
294 // was shifted to.
295 FrameMouseActions.setLastRobotMove(x, y);
296
297 if (FreeItems.itemsAttachedToCursor()) {
298 List<Item> toMove = FreeItems.getInstance();
299 for (Item move : toMove) {
300 move.setPosition(move.getX() + deltax, move.getY() + deltay);
301 }
302 }
303
304 // cheat
305 FrameMouseActions.setForceArrow(forceArrow);
306 int mouseX = (int) _Browser.getContentPane().getLocationOnScreen()
307 .getX()
308 + Math.round(x);
309 int mouseY = (int) _Browser.getContentPane().getLocationOnScreen()
310 .getY()
311 + Math.round(y);
312 _Robot.mouseMove(mouseX, mouseY);
313 // System.out.println("MouseMoved: " + x + "," + y);
314 }
315
316 public static void resetCursorOffset() {
317 FrameMouseActions.resetOffset();
318 }
319
320 /**
321 * Sets the current cursor position in the current frame
322 *
323 * @param pos
324 */
325 public static void setCursorPosition(Point2D.Float pos) {
326 setCursorPosition(pos.x, pos.y);
327 }
328
329 public static void setCursorPosition(Point pos) {
330 setCursorPosition(pos.x, pos.y);
331 }
332
333 public static void setCursorPosition(Point pos, boolean forceArrow) {
334 setCursorPosition(pos.x, pos.y, forceArrow);
335 }
336
337 public static void setCursorPosition(Point2D.Float pos, boolean forceArrow) {
338 setCursorPosition(pos.x, pos.y, forceArrow);
339 }
340
341 /**
342 * Returns the top item (last added) of the Back-Stack (which is popped off)
343 *
344 * @return The name of the last Frame added to the back-stack
345 */
346 public static String getLastFrame() {
347 int side = getCurrentSide();
348
349 if (_VisitedFrames[side].size() > 0)
350 return _VisitedFrames[side].pop();
351 else
352 return null;
353 }
354
355 /**
356 * Adds the given Frame to the back-stack
357 *
358 * @param frame
359 * The Frame to add
360 */
361 public static void addToBack(Frame toAdd) {
362 int side = getCurrentSide();
363
364 // // do not allow duplicate frames
365 // if (_VisitedFrames[side].size() > 0)
366 // if (_VisitedFrames[side].peek().equals(toAdd.getName())) {
367 // return;
368 // }
369
370 Item ip = FrameUtils.getCurrentItem();
371 if (ip == null)
372 _VisitedFrames[side].push(toAdd.getName());
373 else
374 _VisitedFrames[side].push(toAdd.getName());
375 // System.out.println("Added: " + _VisitedFrames[side].size());
376 }
377
378 public static String removeFromBack() {
379 int side = getCurrentSide();
380
381 // there must be a frame to go back to
382 if (_VisitedFrames[side].size() > 0) {
383 return _VisitedFrames[side].pop();
384 }
385 return null;
386 }
387
388 /**
389 * Returns a 'peek' at the end element on the back-stack of the current
390 * side. If the back-stack is empty, null is returned.
391 *
392 * @return The name of the most recent Frame added to the back-stack, or
393 * null if the back-stack is empty.
394 */
395 public static String peekFromBackUpStack() {
396 int side = getCurrentSide();
397
398 // check that the stack is not empty
399 if (_VisitedFrames[side].size() > 0)
400 return _VisitedFrames[side].peek();
401
402 // if the stack is empty, return null
403 return null;
404 }
405
406 public static void setCurrentFrame(Frame frame, boolean incrementStats) {
407 if (frame == null)
408 return;
409
410 if (_TwinFrames) {
411 if (_CurrentFrames[0] == null) {
412 _CurrentFrames[0] = frame;
413 fireFrameChanged();
414 return;
415 }
416 if (_CurrentFrames[1] == null) {
417 _CurrentFrames[1] = frame;
418 fireFrameChanged();
419 return;
420 }
421 }
422
423 // if this is already the current frame
424 if (frame == getCurrentFrame()) {
425 FrameGraphics.Repaint();
426 MessageBay.displayMessage(frame.getName()
427 + " is already the current frame.");
428 return;
429 } else if (incrementStats) {
430 SessionStats.AccessedFrame();
431 }
432
433 // Invalidate free items
434 if (!FreeItems.getInstance().isEmpty() && getCurrentFrame() != null) {
435
436 // Empty free items temporarily so that the old frames buffer is
437 // repainted
438 // without the free items.
439 ArrayList<? extends Item> tmp = (ArrayList<? extends Item>) FreeItems
440 .getInstance().clone();
441 FreeItems.getInstance().clear(); // NOTE: This will invalidate
442 // all the cleared free items
443 FrameGraphics.refresh(true);
444 FreeItems.getInstance().addAll(tmp);
445
446 }
447
448 // Changing frames is a Save point for saveable entities:
449 EntitySaveManager.getInstance().saveAll();
450
451 if (_TwinFrames) {
452 // if the same frame is being shown in both sides, load a fresh
453 // copy from disk
454 if (_CurrentFrames[getOppositeSide()] == frame
455 || _CurrentFrames[getOppositeSide()].hasOverlay(frame)) {
456 FrameIO.SuspendCache();
457 frame = FrameIO.LoadFrame(frame.getName());
458 FrameIO.ResumeCache();
459 }
460
461 // If the frames are the same then the items for the
462 // frame that is just about to hide will still be in view
463 // so only notify items that they are hidden if the
464 // frames differ.
465 if (_CurrentFrames[getCurrentSide()] != null
466 && _CurrentFrames[0] != _CurrentFrames[1]) {
467 for (Item i : _CurrentFrames[getCurrentSide()].getItems()) {
468 i.onParentStateChanged(new ItemParentStateChangedEvent(
469 _CurrentFrames[getCurrentSide()],
470 ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN));
471 }
472 }
473 _CurrentFrames[getCurrentSide()] = frame;
474
475 // BROOK : TODO... overlays and loadable widgets
476 for (Item i : _CurrentFrames[getCurrentSide()].getItems()) {
477 i.onParentStateChanged(new ItemParentStateChangedEvent(
478 _CurrentFrames[getCurrentSide()],
479 ItemParentStateChangedEvent.EVENT_TYPE_SHOWN));
480 }
481 } else {
482
483 // Notifying items on the frame being hidden that they
484 // are about to be hidden.
485 // ie. Widgets use this method to remove themselves from the JPanel
486 List<Frame> currentOnlyOverlays = new LinkedList<Frame>();
487 List<Frame> nextOnlyOverlays = new LinkedList<Frame>();
488 List<Frame> sharedOverlays = new LinkedList<Frame>();
489
490 // Get all overlayed frames seen by the next frame
491 for (Overlay o : frame.getOverlays()) {
492 if (!nextOnlyOverlays.contains(o))
493 nextOnlyOverlays.add(o.Frame);
494 }
495
496 // Get all overlayed frames seen by the current frame
497 if (_CurrentFrames[getCurrentSide()] != null) {
498 for (Overlay o : _CurrentFrames[getCurrentSide()].getOverlays()) {
499 if (!currentOnlyOverlays.contains(o))
500 currentOnlyOverlays.add(o.Frame);
501 }
502 }
503
504 // Extract shared overlays between the current and next frame
505 for (Frame of : currentOnlyOverlays) {
506 if (nextOnlyOverlays.contains(of)) {
507 sharedOverlays.add(of);
508 }
509 }
510
511 // The first set, currentOnlyOverlays, must be notified that they
512 // are hidden
513 Collection<Item> items = new LinkedList<Item>();
514
515 // Notify items that will not be in view any more
516 if (_CurrentFrames[getCurrentSide()] != null) {
517 List<Frame> seen = new LinkedList<Frame>();
518 seen.addAll(sharedOverlays); // Signify that seen all shared
519 // overlays
520 seen.remove(_CurrentFrames[getCurrentSide()]); // must ensure
521 // excluded
522
523 // Get all items seen from the current frame - including all
524 // possible non-shared overlays
525 items = _CurrentFrames[getCurrentSide()].getAllItems();
526 for (Frame f : seen)
527 items.removeAll(f.getAllItems());
528
529 // Notify items that they are hidden
530 for (Item i : items) {
531 i.onParentStateChanged(new ItemParentStateChangedEvent(
532 _CurrentFrames[getCurrentSide()],
533 ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN));
534 }
535 }
536
537 // Set the new frame
538 _CurrentFrames[getCurrentSide()] = frame;
539 frame.refreshSize();
540 // Notify items on the frame being displayed that they are in view
541 // ie. widgets use this method to add themselves to the content pane
542 items.clear();
543
544 // Notify overlay items that they are shown
545 for (Item i : frame.getOverlayItems()) {
546 Overlay owner = frame.getOverlayOwner(i);
547 // if (owner == null) i.onParentFameShown(false, 0);
548 // else ...
549 assert (owner != null);
550 i
551 .onParentStateChanged(new ItemParentStateChangedEvent(
552 frame,
553 ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY,
554 owner.permission));
555 }
556
557 for (Item i : frame.getItems()) {
558 i.onParentStateChanged(new ItemParentStateChangedEvent(frame,
559 ItemParentStateChangedEvent.EVENT_TYPE_SHOWN));
560 }
561 }
562 frame.reset();
563 FrameMouseActions.getInstance().refreshHighlights();
564 FrameGraphics.refresh(false);
565 fireFrameChanged();
566 }
567
568 public static void UpdateTitle() {
569 StringBuffer title = new StringBuffer(TITLE);
570
571 if (FrameGraphics.isAudienceMode())
572 title.append(" - Audience Mode");
573 else if (FrameGraphics.isXRayMode())
574 title.append(" - X-Ray Mode");
575 else
576 title.append(" [").append(SessionStats.getShortStats()).append(']');
577
578 _Browser.setTitle(title.toString());
579 }
580
581 public static int getCurrentSide() {
582 if (_Browser == null)
583 return 0;
584
585 if (_TwinFrames
586 && FrameMouseActions.MouseX >= (_Browser.getWidth() / 2F)
587 && _CurrentFrames[1] != null)
588 return 1;
589
590 if (_CurrentFrames[0] == null && _CurrentFrames[1] != null)
591 return 1;
592
593 return 0;
594 }
595
596 private static int getOppositeSide() {
597 if (getCurrentSide() == 0)
598 return 1;
599
600 return 0;
601 }
602
603 public static int FrameOnSide(Frame toFind) {
604 if (_CurrentFrames[0] == toFind)
605 return 0;
606
607 if (_CurrentFrames[1] == toFind)
608 return 1;
609
610 return -1;
611 }
612
613 /**
614 * Returns the Frame currently being displayed on the screen.
615 *
616 * @return The Frame currently displayed.
617 */
618 public static Frame getCurrentFrame() {
619 return _CurrentFrames[getCurrentSide()];
620 }
621
622 public static Frame getOppositeFrame() {
623 return _CurrentFrames[getOppositeSide()];
624 }
625
626 public static Frame[] getFrames() {
627 return _CurrentFrames;
628 }
629
630 public static int getMiddle() {
631 return _Browser.getWidth() / 2;
632 }
633
634 public static int getHeight() {
635 return _Browser.getHeight();
636 }
637
638 /**
639 * Returns the current mouse X coordinate. This coordinate is relative to
640 * the left edge of the frame the mouse is in. It takes into account the
641 * user being in twin frames mode.
642 *
643 * @return The X coordinate of the mouse.
644 */
645 public static float getFloatMouseX() {
646 if (_TwinFrames
647 && FrameMouseActions.MouseY < FrameGraphics.getMaxSize().height)
648 return FrameMouseActions.MouseX % (_Browser.getWidth() / 2);
649
650 return FrameMouseActions.MouseX;
651 }
652
653
654 /**
655 * Returns the current mouse X coordinate. This coordinate is relative to
656 * the left edge of the frame the mouse is in. It takes into account the
657 * user being in twin frames mode.
658 *
659 * @return The X coordinate of the mouse.
660 */
661 public static int getMouseX() {
662 return Math.round(getFloatMouseX());
663 }
664
665
666 /**
667 * Returns the current mouse Y coordinate. This coordinate is relative to
668 * the top edge of the frame the mouse is in.
669 *
670 * @return The Y coordinate of the mouse.
671 */
672 public static float getFloatMouseY() {
673
674 return FrameMouseActions.MouseY;
675 }
676
677 /**
678 * Returns the current mouse Y coordinate. This coordinate is relative to
679 * the top edge of the frame the mouse is in.
680 *
681 * @return The Y coordinate of the mouse.
682 */
683 public static int getMouseY() {
684 return Math.round(getFloatMouseY());
685 }
686
687 public static boolean Back() {
688 int side = getCurrentSide();
689
690 // there must be a frame to go back to
691 if (_VisitedFrames[side].size() < 1) {
692 MessageBay.displayMessageOnce("You are already on the home frame");
693 return false;
694 }
695
696 if (!FrameUtils.LeavingFrame(getCurrentFrame())) {
697 MessageBay.displayMessage("Error navigating back");
698 return false;
699 }
700
701 String oldFrame = getCurrentFrame().getName().toLowerCase();
702
703 // do not get a cached version (in case it is in the other window)
704 if (isTwinFramesOn())
705 FrameIO.SuspendCache();
706 Frame frame = FrameIO.LoadFrame(removeFromBack());
707 // If the top frame on the backup stack is the current frame go back
708 // again... or if it has been deleted
709 // Recursively backup the stack
710 if (frame == null || frame.equals(getCurrentFrame())) {
711 Back();
712 return false;
713 }
714
715 if (isTwinFramesOn()) {
716 FrameIO.ResumeCache();
717 }
718 _BackedUpFrames[side].push(oldFrame);
719 FrameUtils.DisplayFrame(frame, false, true);
720 FrameMouseActions.setHighlightHold(true);
721
722 for (Item i : frame.getItems()) {
723 if (i.getLink() != null
724 && i.getAbsoluteLink().toLowerCase().equals(oldFrame)) {
725 if (i.getHighlightMode() != Item.HighlightMode.Normal) {
726 i.setHighlightMode(Item.HighlightMode.Normal,
727 BACK_HIGHLIGHT_COLOR);
728 }
729 // check if its an @f item and if so update the buffer
730 if (i instanceof Picture) {
731 Picture p = (Picture) i;
732 p.refresh();
733 }
734 }
735 }
736 FrameGraphics.requestRefresh(true);
737 return true;
738 }
739
740 public static boolean Forward() {
741 int side = getCurrentSide();
742
743 // there must be a frame to go back to
744 if (_BackedUpFrames[side].size() == 0) {
745 return false;
746 }
747
748 if (!FrameUtils.LeavingFrame(getCurrentFrame())) {
749 MessageBay.displayMessage("Error navigating forward");
750 return false;
751 }
752
753 String oldFrame = getCurrentFrame().getName().toLowerCase();
754
755 // do not get a cached version (in case it is in the other window)
756 if (isTwinFramesOn())
757 FrameIO.SuspendCache();
758 Frame frame = FrameIO.LoadFrame(_BackedUpFrames[side].pop());
759 // If the top frame on the backup stack is the current frame go back
760 // again... or if it has been deleted
761 // Recursively backup the stack
762 if (frame == null || frame.equals(getCurrentFrame())) {
763 Forward();
764 return false;
765 }
766
767 if (isTwinFramesOn()) {
768 FrameIO.ResumeCache();
769 }
770 _VisitedFrames[side].push(oldFrame);
771 FrameUtils.DisplayFrame(frame, false, true);
772 FrameGraphics.requestRefresh(true);
773 return true;
774 }
775
776 /**
777 * Toggles the display of frames between TwinFrames mode and Single frame
778 * mode.
779 */
780 public static void ToggleTwinFrames() {
781 // determine which side is the active side
782 int opposite = getOppositeSide();
783 int current = getCurrentSide();
784 _TwinFrames = !_TwinFrames;
785
786 // if TwinFrames is being turned on
787 if (_TwinFrames) {
788 // if this is the first time TwinFrames has been toggled on,
789 // load the user's first frame
790 if (_VisitedFrames[opposite].size() == 0) {
791 FrameIO.SuspendCache();
792 setCurrentFrame(FrameIO.LoadFrame(UserSettings.HomeFrame), true);
793 FrameIO.ResumeCache();
794 } else {
795 // otherwise, restore the frame from the side's back-stack
796 setCurrentFrame(FrameIO.LoadFrame(_VisitedFrames[opposite]
797 .pop()), true);
798 }
799
800 // else, TwinFrames is being turned off
801 } else {
802 // add the frame to the back-stack
803 Frame hiding = _CurrentFrames[opposite];
804 FrameUtils.LeavingFrame(hiding);
805 _VisitedFrames[opposite].add(hiding.getName());
806 _CurrentFrames[opposite] = null;
807 _CurrentFrames[current].refreshSize();
808 }
809 if (_CurrentFrames[current] != null)
810 _CurrentFrames[current].refreshSize();
811 if (_CurrentFrames[opposite] != null)
812 _CurrentFrames[opposite].refreshSize();
813
814 FrameGraphics.Clear();
815 FrameGraphics.requestRefresh(false);
816 FrameGraphics.Repaint();
817 }
818
819 public static boolean isTwinFramesOn() {
820 return _TwinFrames;
821 }
822
823 public static void Reload(int side) {
824 if (side < 0)
825 return;
826
827 FrameIO.SuspendCache();
828 _CurrentFrames[side] = FrameIO
829 .LoadFrame(_CurrentFrames[side].getName());
830 FrameIO.ResumeCache();
831 }
832
833 public static boolean DisplayConfirmDialog(String message, String title,
834 int type, int options, int res) {
835 return JOptionPane.showConfirmDialog(_Browser, message, title, options,
836 type) == res;
837 }
838
839 public static final int RESULT_OK = JOptionPane.OK_OPTION;
840
841 public static final int OPTIONS_OK_CANCEL = JOptionPane.OK_CANCEL_OPTION;
842
843 public static final int TYPE_WARNING = JOptionPane.WARNING_MESSAGE;
844
845 public static void pressMouse(int buttons) {
846 _Robot.mousePress(buttons);
847 }
848
849 public static void releaseMouse(int buttons) {
850 _Robot.mouseRelease(buttons);
851 }
852
853 public static void clickMouse(int buttons) throws InterruptedException {
854 _Robot.mousePress(buttons);
855 Thread.sleep(100);
856 _Robot.mouseRelease(buttons);
857 }
858
859 public static void typeKey(int key) throws InterruptedException {
860 _Robot.keyPress(key);
861 // _Robot.waitForIdle();
862 _Robot.keyRelease(key);
863 // _Robot.waitForIdle();
864 Thread.sleep(200);
865 }
866
867 public static void typeText(String s) throws InterruptedException {
868 for (int i = 0; i < s.length(); i++) {
869 char c = s.charAt(i);
870 if (Character.isUpperCase(c))
871 _Robot.keyPress(KeyEvent.VK_SHIFT);
872 typeKey(getKeyCode(c));
873 if (Character.isUpperCase(c))
874 _Robot.keyRelease(KeyEvent.VK_SHIFT);
875 }
876 }
877
878 protected static int getKeyCode(char c) {
879 switch (c) {
880 case '\n':
881 return KeyEvent.VK_ENTER;
882 case ' ':
883 return KeyEvent.VK_SPACE;
884 }
885
886 if (Character.isSpaceChar(c))
887 return KeyEvent.VK_SPACE;
888 else if (Character.isDigit(c)) {
889 return (int) (KeyEvent.VK_0 + c - '0');
890 } else if (Character.isUpperCase(c)) {
891 return c;
892 }
893
894 return (int) (KeyEvent.VK_A + c - 'a');
895 }
896
897 /**
898 * Moves the cursor the end of this item.
899 *
900 * @param i
901 */
902 public static void MoveCursorToEndOfItem(Item i) {
903 setTextCursor((Text) i, Text.END, true, false, false, false);
904 }
905
906 public static void translateCursor(int deltaX, int deltaY) {
907 setCursorPosition(FrameMouseActions.MouseX + deltaX,
908 FrameMouseActions.MouseY + deltaY, false);
909 }
910
911 public static void clearBackedUpFrames() {
912 _BackedUpFrames[getCurrentSide()].clear();
913 }
914
915 /**
916 * @param secondsDelay
917 * @param s
918 * @throws InterruptedException
919 */
920 public static void typeStringDirect(double secondsDelay, String s)
921 throws InterruptedException {
922 for (int i = 0; i < s.length(); i++) {
923 FrameKeyboardActions.processChar(s.charAt(i), false);
924 Thread.sleep((int) (secondsDelay * 1000));
925 }
926 }
927
928 public static List<String> getUnmodifiableVisitedList() {
929 return Collections.unmodifiableList(_VisitedFrames[getCurrentSide()]);
930 }
931}
Note: See TracBrowser for help on using the repository browser.