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

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

Added SearchItem and SearchStr

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