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

Last change on this file since 570 was 570, checked in by jts21, 11 years ago

Add settings package which uses reflection to allow changing settings without hard coding the code to change every setting.

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