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

Last change on this file since 1062 was 1062, checked in by davidb, 8 years ago

Fix from Bryce of up-down arrow keys in text item

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