source: trunk/src/org/expeditee/gui/FrameKeyboardActions.java@ 885

Last change on this file since 885 was 885, checked in by csl14, 10 years ago

Made changes to Exploratory Search interface and minor fixes to various things

File size: 63.4 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Rectangle;
5import java.awt.Toolkit;
6import java.awt.datatransfer.StringSelection;
7import java.awt.event.KeyEvent;
8import java.awt.event.KeyListener;
9import java.awt.geom.Point2D;
10import java.text.NumberFormat;
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashSet;
15import java.util.LinkedList;
16import java.util.List;
17
18import org.expeditee.actions.Actions;
19import org.expeditee.actions.Misc;
20import org.expeditee.actions.Navigation;
21import org.expeditee.actions.Simple;
22import org.expeditee.agents.ScaleFrameset;
23import org.expeditee.io.ItemSelection;
24import org.expeditee.items.Circle;
25import org.expeditee.items.Dot;
26import org.expeditee.items.Item;
27import org.expeditee.items.ItemUtils;
28import org.expeditee.items.Line;
29import org.expeditee.items.Text;
30import org.expeditee.items.UserAppliedPermission;
31import org.expeditee.items.XRayable;
32import org.expeditee.items.widgets.InteractiveWidget;
33import org.expeditee.items.widgets.WidgetCorner;
34import org.expeditee.items.widgets.WidgetEdge;
35import org.expeditee.settings.UserSettings;
36import org.expeditee.settings.templates.TemplateSettings;
37import org.expeditee.stats.Formatter;
38import org.expeditee.stats.Logger;
39import org.expeditee.stats.SessionStats;
40
41public class FrameKeyboardActions implements KeyListener {
42
43 private static FrameKeyboardActions _instance = new FrameKeyboardActions();
44
45 protected FrameKeyboardActions() {
46 }
47
48 public static FrameKeyboardActions getInstance() {
49 return _instance;
50 }
51
52 private static Text _toRemove = null;
53
54 private static Collection<Item> _enclosedItems = null;
55
56 public static void resetEnclosedItems() {
57 _enclosedItems = null;
58 }
59
60 public synchronized void keyTyped(KeyEvent e) {
61
62 if (Simple.isProgramRunning()) {
63 if (e.isControlDown()
64 && (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.getKeyChar() == KeyEvent.VK_C)) {
65 Simple.stop();
66 return;
67 } else if (e.isControlDown() && e.getKeyChar() == KeyEvent.VK_SPACE) {
68 Simple.nextStatement();
69 return;
70 } else {
71 Simple.KeyStroke(e.getKeyChar());
72 }
73 if (Simple.consumeKeyboardInput())
74 return;
75 }
76
77 // ignore escape character and control characters
78 if (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.isControlDown()) {
79 return;
80 }
81
82 // Deal with splitting text items when typing too fast
83 // Mike: thinks this problem may have been solved and was due to
84 // rounding errors in the text class...
85 // It may have been fixed by changing to the use of floats instead of
86 // ints for text positioning etc
87 // if (FrameMouseActions.isWaitingForRobot()) {
88 // System.out.println("Waiting: " + e.getKeyChar());
89 // return;
90 // }
91 e.consume();
92 char ch = e.getKeyChar();
93 // System.out.println(ch);
94
95 if (e.isAltDown()) {
96
97 } else {
98 processChar(ch, e.isShiftDown());
99 }
100 // FrameGraphics.Repaint();
101 }
102
103 public static void processChar(char ch, boolean isShiftDown) {
104 Navigation.ResetLastAddToBack();
105 Item on = FrameUtils.getCurrentItem();
106
107 // permission check
108 if (on != null && !on.hasPermission(UserAppliedPermission.full)) {
109 MessageBay
110 .displayMessage("Insufficient permission to edit this item");
111 return;
112 }
113
114 if (_toRemove != null && on != _toRemove) {
115 assert (_toRemove.getLength() == 0);
116 // This line is to protect mistaken removal of items if there is a
117 // bug...
118 if (_toRemove.getLength() == 0)
119 DisplayIO.getCurrentFrame().removeItem(_toRemove);
120 }
121 _toRemove = null;
122
123 // ignore delete and backspace if in free space
124 if ((on == null || !(on instanceof Text))
125 && (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_TAB || ch == KeyEvent.VK_DELETE))
126 return;
127
128 SessionStats.TypedChar(ch);
129
130 // check for dot's being replaced with text
131 if (on != null && on instanceof Dot && !(on instanceof WidgetCorner)) {
132 if (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE) {
133 return;
134 }
135 replaceDot((Item) on, ch);
136 return;
137 }
138
139 // only text can interact with keyboard events
140 if (on != null && !(on instanceof Text))
141 on = null;
142
143 // DisplayIO.UpdateTitle();
144
145 Text text = (Text) on;
146 // if this text is empty but has not been removed (such as from
147 // ESC-pushdown)
148 if (text != null && text.isEmpty()
149 && (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE)) {
150 if (text.getLines().size() > 0)
151 replaceText(text);
152 else {
153 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
154 }
155 return;
156 }
157
158 // if the user is in free space, create a new text item
159 /*
160 * MikeSays: Why do we have to check is highlighted... doing so causes
161 * problems if you type characters to fast, they turn into multiple text
162 * items. ie. JK together on the Linux laptop.
163 */
164 if (on == null /* || !on.isHighlighted() */) {
165 // DisplayIO.UpdateTitle();
166 text = createText(ch);
167 text.justify(false);
168
169 FrameUtils.setLastEdited(text);
170 DisplayIO.setTextCursor(text, Text.NONE);
171 return;
172 } else {
173 FrameUtils.setLastEdited(text);
174 }
175
176 float oldY = FrameMouseActions.MouseY;
177
178 DisplayIO.setTextCursor(text, Text.NONE);
179 Point2D.Float newMouse = null;
180 if (ch == '\t') {
181 if (isShiftDown) {
182 newMouse = text.removeTab(ch, DisplayIO.getFloatMouseX(),
183 FrameMouseActions.MouseY);
184 } else {
185 newMouse = text.insertTab(ch, DisplayIO.getFloatMouseX(),
186 FrameMouseActions.MouseY);
187 }
188 } else {
189 newMouse = text.insertChar(ch, DisplayIO.getFloatMouseX(),
190 FrameMouseActions.MouseY);
191 }
192 /*
193 * check if the user hit enter and the cursor is now on another text
194 * item
195 */
196 if (oldY < newMouse.y) {
197 // float diff = newMouse.y - oldY;
198 // System.out.print("c");
199 Rectangle rect = text.getPolygon().getBounds();
200
201 // Text lastEdited = FrameUtils.getLastEdited();
202 // FrameUtils.setLastEdited(null);
203
204 Item justBelow = FrameUtils.onItem(DisplayIO.getCurrentFrame(),
205 text.getX() + 10, rect.y + rect.height + 1, false);
206
207 // FrameUtils.setLastEdited(lastEdited);
208
209 // Dont drop unless
210 if (justBelow != null && justBelow instanceof Text
211 && justBelow != on) {
212 // Drop all the items below it down!
213 // Get the list of items that must be dropped
214 List<Text> column = DisplayIO.getCurrentFrame().getColumn(on);
215 FrameUtils.Align(column, false, 0);
216 }
217 }
218
219 DisplayIO.setCursorPosition(newMouse.x, newMouse.y, false);
220
221 // This repaint is needed for WINDOWS only?!?!? Mike is not sure why!
222 if (ch == KeyEvent.VK_DELETE)
223 FrameGraphics.requestRefresh(true);
224
225 // a change has occured to the Frame
226 text.getParent().setChanged(true);
227
228 // check that the Text item still exists (hasn't been deleted\backspaced
229 // away)
230 if (text.isEmpty()) {
231 _toRemove = text;
232
233 if (text.hasAction())
234 text.setActionMark(true);
235 else if (text.getLink() != null)
236 text.setLinkMark(true);
237 else if (text.getLines().size() > 0)
238 replaceText(text);
239 else {
240 // DisplayIO.getCurrentFrame().removeItem(text);
241 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
242 }
243 }
244 }
245
246 public static Text replaceDot(Item dot, char ch) {
247 Text text = createText(ch);
248 Item.DuplicateItem(dot, text);
249 FrameUtils.setLastEdited(text);
250
251 // Copy the lines list so it can be modified
252 List<Line> lines = new LinkedList<Line>(dot.getLines());
253 for (Line line : lines)
254 line.replaceLineEnd(dot, text);
255 Frame current = dot.getParentOrCurrentFrame();
256 current.removeItem(dot);
257 ItemUtils.EnclosedCheck(current.getItems());
258 return text;
259 }
260
261 /**
262 * Replaces the given text item with a dot
263 */
264 public static Item replaceText(Item text) {
265 Item dot = new Dot(text.getX(), text.getY(), text.getID());
266 Item.DuplicateItem(text, dot);
267
268 List<Line> lines = new LinkedList<Line>();
269 lines.addAll(text.getLines());
270 if (lines.size() > 0)
271 dot.setColor(lines.get(0).getColor());
272 for (Line line : lines) {
273 line.replaceLineEnd(text, dot);
274 }
275 text.delete();
276 Frame current = text.getParentOrCurrentFrame();
277 current.addItem(dot);
278 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
279 ItemUtils.EnclosedCheck(current.getItems());
280 return dot;
281 }
282
283 /**
284 * Creates a new Text Item whose text contains the given character. This
285 * method also moves the mouse cursor to be pointing at the newly created
286 * Text Item ready to insert the next character.
287 *
288 * @param start
289 * The character to use as the initial text of this Item.
290 * @return The newly created Text Item
291 */
292 private static Text createText(char start) {
293 Text t = DisplayIO.getCurrentFrame().createBlankText("" + start);
294
295 Point2D.Float newMouse = t.insertChar(start, DisplayIO.getMouseX(),
296 FrameMouseActions.getY());
297 DisplayIO.setCursorPosition(newMouse.x, newMouse.y, false);
298
299 return t;
300 }
301
302 /**
303 * Creates a new Text Item with no text. The newly created Item is a copy of
304 * any ItemTemplate if one is present, and inherits all the attributes of
305 * the Template
306 *
307 * @return The newly created Text Item
308 */
309 private static Text createText() {
310 return DisplayIO.getCurrentFrame().createNewText();
311 }
312
313 private void move(int direction, boolean isShiftDown, boolean isCtrlDown) {
314 Item on = FrameUtils.getCurrentItem();
315
316 if (on == null) {
317 navigateFrame(direction);
318 return;
319 }
320
321 if (on instanceof Text) {
322 Text text = (Text) on;
323 // When the user hits the left and right button with mouse
324 // positions over the the frame name navigation occurs
325 if (text.isFrameName()) {
326 navigateFrame(direction);
327 return;
328 } else {
329 FrameUtils.setLastEdited(text);
330 DisplayIO.setTextCursor(text, direction, false, isShiftDown,
331 isCtrlDown, true);
332 }
333 }
334 }
335
336 private void navigateFrame(int direction) {
337 switch (direction) {
338 case Text.RIGHT:
339 case Text.PAGE_UP:
340 Navigation.NextFrame(false);
341 break;
342 case Text.LEFT:
343 case Text.PAGE_DOWN:
344 Navigation.PreviousFrame(false);
345 break;
346 case Text.HOME:
347 case Text.LINE_HOME:
348 Navigation.ZeroFrame();
349 break;
350 case Text.END:
351 case Text.LINE_END:
352 Navigation.LastFrame();
353 break;
354 }
355 }
356
357 /**
358 * Receives and processes any Function, Control, and Escape key presses
359 *
360 * @param e
361 * The KeyEvent received from the keyboard
362 */
363 public void keyPressed(KeyEvent e) {
364 int keyCode = e.getKeyCode();
365
366 if (keyCode != KeyEvent.VK_F1 && keyCode != KeyEvent.VK_F2) {
367 resetEnclosedItems();
368 }
369
370 SessionStats.AddFrameEvent("k" + KeyEvent.getKeyText(keyCode));
371
372 FrameUtils.ResponseTimer.restart();
373 // e.consume();
374
375 if (Actions.isAgentRunning()) {
376 if (keyCode == KeyEvent.VK_ESCAPE)
377 Actions.stopAgent();
378 else
379 Actions.interruptAgent();
380 return;
381 } else if (Simple.consumeKeyboardInput()) {
382 return;
383 }
384
385 if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12) {
386 functionKey(FunctionKey.values()[keyCode - KeyEvent.VK_F1 + 1], e
387 .isShiftDown(), e.isControlDown());
388 return;
389 } else if (e.isAltDown()) {
390 int distance = e.isShiftDown() ? 1 : 20;
391 switch (keyCode) {
392 case KeyEvent.VK_1:
393 FrameMouseActions.leftButton();
394 // DisplayIO.clickMouse(InputEvent.BUTTON1_MASK);
395 break;
396 case KeyEvent.VK_2:
397 // DisplayIO.clickMouse(InputEvent.BUTTON2_MASK);
398 FrameMouseActions.middleButton();
399 break;
400 case KeyEvent.VK_3:
401 // DisplayIO.clickMouse(InputEvent.BUTTON3_MASK);
402 FrameMouseActions.rightButton();
403 break;
404 case KeyEvent.VK_LEFT:
405 DisplayIO.translateCursor(-distance, 0);
406 break;
407 case KeyEvent.VK_RIGHT:
408 DisplayIO.translateCursor(distance, 0);
409 break;
410 case KeyEvent.VK_UP:
411 DisplayIO.translateCursor(0, -distance);
412 break;
413 case KeyEvent.VK_DOWN:
414 DisplayIO.translateCursor(0, distance);
415 break;
416 }
417 return;
418 }
419 switch (keyCode) {
420 case KeyEvent.VK_CONTROL:
421 FrameMouseActions.control(e);
422 break;
423 case KeyEvent.VK_SHIFT:
424 FrameMouseActions.shift(e);
425 break;
426 }
427
428 if (e.isControlDown()) {
429 controlChar(e.getKeyCode(), e.isShiftDown());
430 return;
431 }
432
433 switch (keyCode) {
434 case KeyEvent.VK_ESCAPE:
435 // Do escape after control so Ctl+Escape does not perform DropDown
436 functionKey(FunctionKey.DropDown, e.isShiftDown(), e
437 .isControlDown());
438 SessionStats.Escape();
439 break;
440 case KeyEvent.VK_LEFT:
441 move(Text.LEFT, e.isShiftDown(), e.isControlDown());
442 break;
443 case KeyEvent.VK_RIGHT:
444 move(Text.RIGHT, e.isShiftDown(), e.isControlDown());
445 break;
446 case KeyEvent.VK_PAGE_DOWN:
447 navigateFrame(Text.PAGE_DOWN);
448 break;
449 case KeyEvent.VK_PAGE_UP:
450 navigateFrame(Text.PAGE_UP);
451 break;
452 case KeyEvent.VK_UP:
453 if (e.isControlDown()) {
454 NextTextItem(FrameUtils.getCurrentItem(), false);
455 } else {
456 move(Text.UP, e.isShiftDown(), e.isControlDown());
457 }
458 break;
459 case KeyEvent.VK_DOWN:
460 if (e.isControlDown()) {
461 NextTextItem(FrameUtils.getCurrentItem(), true);
462 } else {
463 move(Text.DOWN, e.isShiftDown(), e.isControlDown());
464 }
465 break;
466 case KeyEvent.VK_END:
467 if (e.isControlDown())
468 move(Text.END, e.isShiftDown(), e.isControlDown());
469 else
470 move(Text.LINE_END, e.isShiftDown(), e.isControlDown());
471 break;
472 case KeyEvent.VK_HOME:
473 if (e.isControlDown())
474 move(Text.HOME, e.isShiftDown(), e.isControlDown());
475 else
476 move(Text.LINE_HOME, e.isShiftDown(), e.isControlDown());
477 break;
478 // TODO remove this when upgrading Java
479 // This is a patch because Java6 wont trigger KeyTyped event for
480 // Shift+Tab
481 case KeyEvent.VK_TAB:
482 if (e.isShiftDown()) {
483 e.setKeyChar('\t');
484 keyTyped(e);
485 }
486 break;
487 }
488 }
489
490 /**
491 * Moves the cursor to the next text item on the frame
492 *
493 * @param currentItem
494 * @param direction
495 * move up if direction is negative, down if direction is
496 * positive
497 */
498 public static void NextTextItem(Item currentItem, boolean down) {
499 // Move the cursor to the next text item
500 Frame current = DisplayIO.getCurrentFrame();
501 Text title = current.getTitleItem();
502
503 Collection<Text> currentItems = FrameUtils.getCurrentTextItems();
504 List<Text> textItems = new ArrayList<Text>();
505 // Move to the next text item in the box if
506 if (currentItems.contains(currentItem)) {
507 textItems.addAll(currentItems);
508 } else {
509 if (title != null)
510 textItems.add(title);
511 textItems.addAll(current.getBodyTextItems(true));
512 }
513
514 Collections.sort(textItems);
515
516 if (textItems.size() == 0) {
517 // If there are no text items on the frame its a NoOp
518 if (title == null)
519 return;
520 if (title != null)
521 DisplayIO.MoveCursorToEndOfItem(title);
522 FrameGraphics.Repaint();
523 return;
524 }
525
526 // If the user is mouse wheeling in free space...
527 if (currentItem == null) {
528 // find the nearest item in the correct direction
529 int currY = FrameMouseActions.getY();
530 for (int i = 0; i < textItems.size(); i++) {
531 Item t = textItems.get(i);
532 if (currY < t.getY()) {
533 if (down) {
534 DisplayIO.MoveCursorToEndOfItem(t);
535 } else {
536 if (i == 0) {
537 DisplayIO.MoveCursorToEndOfItem(current
538 .getTitleItem());
539 } else {
540 DisplayIO.MoveCursorToEndOfItem(textItems
541 .get(i - 1));
542 }
543 }
544 FrameGraphics.Repaint();
545 return;
546 }
547 }
548 // If we are at the botton of the screen and the user scrolls down
549 // then scroll backup to the title
550 if (textItems.size() > 0) {
551 DisplayIO.MoveCursorToEndOfItem(textItems
552 .get(textItems.size() - 1));
553 }
554 return;
555 }
556
557 // Find the current item... then move to the next item
558 int i = textItems.indexOf(currentItem);
559
560 int nextIndex = i + (down ? 1 : -1);
561 if (nextIndex >= 0 && nextIndex < textItems.size()) {
562 DisplayIO.MoveCursorToEndOfItem(textItems.get(nextIndex));
563 } else {
564 DisplayIO.MoveCursorToEndOfItem(currentItem);
565 }
566 return;
567
568 }
569
570 public void keyReleased(KeyEvent e) {
571 if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
572 FrameMouseActions.control(e);
573 } else if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
574 FrameMouseActions.shift(e);
575 } else if (e.isAltDown() || e.isControlDown()) {
576 // switch (e.getKeyCode()) {
577 // case KeyEvent.VK_1:
578 // DisplayIO.releaseMouse(InputEvent.BUTTON1_MASK);
579 // break;
580 // case KeyEvent.VK_2:
581 // DisplayIO.releaseMouse(InputEvent.BUTTON2_MASK);
582 // break;
583 // case KeyEvent.VK_3:
584 // DisplayIO.releaseMouse(InputEvent.BUTTON3_MASK);
585 // break;
586 // }
587 }
588 }
589
590 private static void copyItemToClipboard(Item on) {
591 if (on == null || !(on instanceof Text))
592 return;
593
594 Text text = (Text) on;
595 String string = text.copySelectedText();
596
597 if (string == null || string.length() == 0)
598 string = text.getText();
599
600 // add the text of the item to the clipboard
601 StringSelection selection = new StringSelection(string);
602 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection,
603 null);
604 }
605
606 /**
607 * Processes all control character keystrokes. Currently Ctrl+C and Ctrl+V
608 * are copy and paste, all other keystrokes are ignored.
609 *
610 * @param ch
611 * The character being pressed along with the control key
612 */
613 private void controlChar(int key, boolean isShiftDown) {
614 Logger.Log(Logger.USER, Logger.CONTROL_CHAR, "User pressing: Ctrl+"
615 + KeyEvent.getKeyText(key));
616 //
617 // if (FrameUtils.getCurrentItem() == null
618 // && !Frame.itemAttachedToCursor()) {
619 // Item t = DisplayIO.getCurrentFrame().createNewText(ch + ": ");
620 // FrameMouseActions.pickup(t);
621 // } else {
622 // remove the link for ctrl+l
623 Item current = FrameUtils.getCurrentItem();
624 Frame currentFrame = DisplayIO.getCurrentFrame();
625 int distance = isShiftDown ? 1 : 20;
626 switch (key) {
627 case KeyEvent.VK_HOME:
628 if (current != null && current instanceof Text) {
629 move(Text.HOME, isShiftDown, true);
630 } else {
631 while (DisplayIO.Back())
632 ;
633 }
634 break;
635 case KeyEvent.VK_END:
636 if (current != null && current instanceof Text) {
637 move(Text.END, isShiftDown, true);
638 } else {
639 while (DisplayIO.Forward())
640 ;
641 }
642 break;
643 case KeyEvent.VK_PAGE_UP:
644 DisplayIO.Back();
645 break;
646 case KeyEvent.VK_PAGE_DOWN:
647 DisplayIO.Forward();
648 break;
649 case KeyEvent.VK_TAB:
650 calculateItem(current);
651 break;
652 case KeyEvent.VK_ESCAPE:
653 // Do escape after control so Ctl+Escape does not perform DropDown
654 functionKey(FunctionKey.DropDown, isShiftDown, true);
655 SessionStats.Escape();
656 break;
657 case KeyEvent.VK_1:
658 FrameMouseActions.leftButton();
659 // DisplayIO.clickMouse(InputEvent.BUTTON1_MASK);
660 break;
661 case KeyEvent.VK_2:
662 FrameMouseActions.middleButton();
663 // DisplayIO.clickMouse(InputEvent.BUTTON2_MASK);
664 break;
665 case KeyEvent.VK_3:
666 FrameMouseActions.rightButton();
667 // DisplayIO.clickMouse(InputEvent.BUTTON3_MASK);
668 break;
669 case KeyEvent.VK_LEFT:
670 if (current instanceof Text) {
671 DisplayIO.setTextCursor((Text) current, Text.LEFT, false,
672 isShiftDown, true, true);
673 } else {
674 DisplayIO.translateCursor(-distance, 0);
675 }
676 break;
677 case KeyEvent.VK_RIGHT:
678 if (current instanceof Text) {
679 DisplayIO.setTextCursor((Text) current, Text.RIGHT, false,
680 isShiftDown, true, true);
681 } else {
682 DisplayIO.translateCursor(distance, 0);
683 }
684 break;
685 case KeyEvent.VK_UP:
686 // if (current instanceof Text) {
687 NextTextItem(FrameUtils.getCurrentItem(), false);
688 // } else {
689 // DisplayIO.translateCursor(0, -distance);
690 // }
691 break;
692 case KeyEvent.VK_DOWN:
693 // if (current instanceof Text) {
694 NextTextItem(FrameUtils.getCurrentItem(), true);
695 // } else {
696 // DisplayIO.translateCursor(0, distance);
697 // }
698 break;
699 case KeyEvent.VK_L:
700 // If its not linked then link it to its self
701 if (current instanceof Text && current.getLink() == null) {
702 String text = ((Text) current).getText();
703 // Ignore the annotation if there is one
704 if (text.charAt(0) == '@')
705 text = text.substring(1);
706
707 if (FrameIO.isValidFrameName(text)) {
708 current.setLink(text);
709 } else if (FrameIO.isValidFramesetName(text)) {
710 current.setLink(text + '1');
711 }
712 } else if (current != null) {
713 // If its linked remove the link
714 current.setLink(null);
715 }
716 break;
717 case KeyEvent.VK_G:
718 // If its not linked then link it to its self
719 if (current instanceof Text) {
720 String text = ((Text) current).getText();
721 if (text.charAt(0) == '@')
722 text = text.substring(1);
723
724 if (FrameIO.isValidFrameName(text)) {
725 current.setLink(text);
726 } else if (FrameIO.isValidFramesetName(text)) {
727 current.setLink(text + '1');
728 }
729 }
730 if (current != null && current.getLink() != null) {
731 Navigation.Goto(current.getAbsoluteLink());
732 return;
733 }
734 break;
735 case KeyEvent.VK_A:
736 // If its not linked then link it to its self
737 if (current instanceof Text) {
738 if (!current.hasAction()) {
739 String text = ((Text) current).getText().trim();
740 // first trim the annotation
741 if (text.startsWith("@")) {
742 text = text.substring(1).trim();
743 }
744 // then trim the action
745 String lowerCaseText = text.toLowerCase();
746 if (lowerCaseText.startsWith("a:")) {
747 text = text.substring("a:".length()).trim();
748 } else if (lowerCaseText.startsWith("action:")) {
749 text = text.substring("action:".length()).trim();
750
751 }
752 current.setAction(text);
753 } else {
754 // If its linked remove the link
755 current.setActions(null);
756 }
757 }
758 break;
759 case KeyEvent.VK_B:
760 if (current instanceof Text) {
761 ((Text) current).toggleBold();
762 }
763 break;
764 case KeyEvent.VK_I:
765 if (current instanceof Text) {
766 ((Text) current).toggleItalics();
767 }
768 break;
769 case KeyEvent.VK_V:
770 ItemSelection.paste();
771 return;
772 case KeyEvent.VK_C:
773 if(FreeItems.itemsAttachedToCursor()) {
774 ItemSelection.copyClone();
775 return;
776 }
777 if(current instanceof Text) {
778 copyItemToClipboard(current);
779 }
780 Text item = null;
781 // Check if its a line to be turned into a circle
782 if (current instanceof Dot && current.getLines().size() == 1) {
783 item = replaceDot(current, '@');
784 } else if (current instanceof Line
785 && current.getAllConnected().size() == 3) {
786 Item end = ((Line) current).getEndItem();
787 if (end instanceof Dot) {
788 item = replaceDot(end, '@');
789 } else if (end instanceof Text) {
790 item = (Text) end;
791 }
792 }
793 if (item != null) {
794 item.setText("@c");
795 DisplayIO.setCursorPosition(item.getX(), item.getY());
796 FrameUtils.setLastEdited(null);
797 Refresh();
798 }
799 return;
800 case KeyEvent.VK_X:
801 ItemSelection.cut();
802 return;
803 case KeyEvent.VK_M:
804 if (current == null)
805 return;
806 if (current != null && !current.hasPermission(UserAppliedPermission.full)) {
807 MessageBay
808 .displayMessage("Insufficient permission toggle the items mark");
809 return;
810 }
811 boolean newValue = !(current.getLinkMark() || current
812 .getActionMark());
813 current.setLinkMark(newValue);
814 current.setActionMark(newValue);
815 break;
816 case KeyEvent.VK_Z:
817 if(isShiftDown) {
818 DisplayIO.getCurrentFrame().redo();
819 } else {
820 DisplayIO.getCurrentFrame().undo();
821 }
822 return;
823 case KeyEvent.VK_D:
824 // perform a delete operation
825 processChar((char) KeyEvent.VK_DELETE, isShiftDown);
826 break;
827 case KeyEvent.VK_DELETE:
828 // perform a delete operation
829 FrameMouseActions.delete(current);
830 break;
831 case KeyEvent.VK_SPACE:
832 if (isShiftDown) {
833 FrameMouseActions.rightButton();
834 } else {
835 FrameMouseActions.middleButton();
836 }
837 break;
838 case KeyEvent.VK_F:
839 // perform a format operation
840 if (isShiftDown) {
841 Actions.PerformActionCatchErrors(currentFrame, null, "HFormat");
842 } else {
843 Actions.PerformActionCatchErrors(currentFrame, null, "Format");
844 }
845 return;
846 case KeyEvent.VK_J:
847 Text text = getCurrentTextItem();
848 if (text == null) {
849 for (Text t : currentFrame.getBodyTextItems(false)) {
850 t.justify(true);
851 }
852
853 return;
854 }
855
856 // if (text.getWidth() < 0)
857 // text.setWidth(text.getBoundsWidth() - Item.MARGIN_RIGHT
858 // - UserSettings.Gravity);
859 text.justify(true);
860 break;
861
862 case KeyEvent.VK_R:
863 Text textCurrent = getCurrentTextItem();
864 if (textCurrent == null) {
865 for (Text t : currentFrame.getBodyTextItems(false)) {
866 t.setWidth(null);
867 t.justify(true);
868 }
869
870 return;
871 }
872 textCurrent.setWidth(null);
873 textCurrent.justify(true);
874 break;
875 case KeyEvent.VK_S:
876 /*
877 * Only split when shift is down... it is too easy to accidentally
878 * hit Ctrl+S after completing a paragraph because this is the
879 * shortcut for saving a document in most word processors and text
880 * editors!
881 *
882 */
883 if (!isShiftDown) {
884 Save();
885 return;
886 }
887 Text text2 = getCurrentTextItem();
888 // split the current text item
889 if (text2 == null)
890 return;
891 List<String> textLines = text2.getTextList();
892 if (textLines.size() <= 1)
893 return;
894 // remove all except the first line of text from the item being
895 // split
896 text2.setText(textLines.get(0));
897 int y = text2.getY();
898 for (int i = 1; i < textLines.size(); i++) {
899 Text newText = text2.copy();
900 newText.setText(textLines.get(i));
901 y += newText.getBoundsHeight();
902 newText.setY(y);
903 // update the items ID to prevent conflicts with the current
904 // frame
905 newText.setID(currentFrame.getNextItemID());
906 currentFrame.addItem(newText);
907 }
908 break;
909 case KeyEvent.VK_ENTER:
910 FrameMouseActions.leftButton();
911 break;
912 case KeyEvent.VK_BACK_SPACE:
913 DisplayIO.Back();
914 break;
915 }
916 FrameGraphics.Repaint();
917 }
918
919 /**
920 * Gets the currently selected item if the user is allowed to modify it.
921 *
922 * @return null if the currently selected item is not a Text item that the
923 * user has permission to modify
924 */
925 private static Text getCurrentTextItem() {
926 Item item = FrameUtils.getCurrentItem();
927
928 if (item != null && !item.hasPermission(UserAppliedPermission.full)) {
929 MessageBay
930 .displayMessage("Insufficient permission to copy that item");
931 return null;
932 }
933
934 Item on = null;
935 if (item != null)
936 on = item;
937
938 if (on == null || !(on instanceof Text))
939 return null;
940
941 return (Text) on;
942 }
943
944 public static void functionKey(FunctionKey key, boolean isShiftDown,
945 boolean isControlDown) {
946 functionKey(key, 1, isShiftDown, isControlDown);
947 }
948
949 /**
950 * Called when a Function key has been pressed, and performs the specific
951 * action based on the key.
952 */
953 public static void functionKey(FunctionKey key, int repeat,
954 boolean isShiftDown, boolean isControlDown) {
955 // get whatever the user is pointing at
956 Item on = FrameUtils.getCurrentItem();
957 String displayMessage = "F" + key.ordinal() + ": " + key.toString();
958 // check for enclosed mode
959 if (on == null && key.ordinal() < FunctionKey.AudienceMode.ordinal()) {
960 Collection<Item> enclosed = FrameUtils.getCurrentItems(on);
961
962 if (enclosed != null && enclosed.size() > 0) {
963 // ensure only one dot\line is present in the list
964 Collection<Item> lineEnds = FrameUtils.getEnclosingLineEnds();
965 Item firstConnected = lineEnds.iterator().next();
966 Collection<Item> connected = firstConnected.getAllConnected();
967
968 switch (key) {
969 case DropDown:
970 // Get the last text item and drop from in
971 Item lastText = null;
972 for (Item i : enclosed) {
973 if (i instanceof Text) {
974 lastText = i;
975 }
976 }
977 // Drop from the item if there was a text item in the box
978 if (lastText != null) {
979 Drop(lastText, false);
980 } else {
981 // Move to the top of the box
982 Rectangle rect = firstConnected.getEnclosedShape()
983 .getBounds();
984 int newX = rect.x + Text.MARGIN_LEFT;
985 int newY = Text.MARGIN_LEFT
986 + rect.y
987 + DisplayIO.getCurrentFrame().getItemTemplate()
988 .getBoundsHeight();
989 moveCursorAndFreeItems(newX, newY);
990 // TODO can resetOffset be put inside
991 // moveCursorAndFreeItems
992 FrameMouseActions.resetOffset();
993 }
994 break;
995 case SizeUp:
996 SetSize(firstConnected, repeat, false, true, isControlDown);
997 break;
998 case SizeDown:
999 SetSize(firstConnected, -repeat, false, true, isControlDown);
1000 break;
1001 case ChangeColor:
1002 if (connected.size() > 0) {
1003 for (Item d : lineEnds) {
1004 if (isControlDown)
1005 SetGradientColor(d, isShiftDown);
1006 else
1007 SetFillColor(d, isShiftDown);
1008 break;
1009 }
1010 }
1011 break;
1012 case ToggleAnnotation:
1013 ToggleAnnotation(firstConnected);
1014 break;
1015 case ChangeFontStyle:
1016 ToggleFontStyle(firstConnected);
1017 break;
1018 case ChangeFontFamily:
1019 ToggleFontFamily(firstConnected);
1020 break;
1021 case InsertDate:
1022 AddDate(firstConnected);
1023 break;
1024 case Save:
1025 Save();
1026 MessageBay.displayMessage(displayMessage);
1027 break;
1028 }
1029 return;
1030 }
1031 }
1032 // Show a description of the function key pressed if the user is in free
1033 // space and return for the F keys that dont do anything in free space.
1034 if (on == null) {
1035 switch (key) {
1036 // These function keys still work in free space
1037 case DropDown:
1038 case InsertDate:
1039 case XRayMode:
1040 case AudienceMode:
1041 case Refresh:
1042 case Save:
1043 break;
1044 case SizeDown:
1045 zoomFrame(DisplayIO.getCurrentFrame(), 0.909090909f);
1046 DisplayIO.getCurrentFrame().refreshSize();
1047 FrameKeyboardActions.Refresh();
1048 return;
1049// if (isControlDown) {
1050// UserSettings.ScaleFactor.set(UserSettings.ScaleFactor.get() - 0.05f);
1051// Misc.repaint();
1052// return;
1053// }
1054 case SizeUp:
1055 zoomFrame(DisplayIO.getCurrentFrame(), 1.1f);
1056 DisplayIO.getCurrentFrame().refreshSize();
1057 FrameKeyboardActions.Refresh();
1058 return;
1059// if (isControlDown) {
1060// UserSettings.ScaleFactor.set(UserSettings.ScaleFactor.get() + 0.05f);
1061// Misc.repaint();
1062// return;
1063// }
1064 default:
1065 MessageBay.displayMessageOnce(displayMessage);
1066 return;
1067 }
1068 }
1069
1070 switch (key) {
1071 case DropDown:
1072 if (isShiftDown || isControlDown) {
1073 if (on != null) {
1074 calculateItem(on);
1075 }
1076 }
1077 Drop(on, false);
1078 return;
1079 case SizeUp:
1080 SetSize(on, repeat, true, false, isControlDown);
1081 if (on instanceof Text) {
1082 DisplayIO.setTextCursor((Text) on, Text.NONE, true, false,
1083 false, true);
1084 }
1085 break;
1086 case SizeDown:
1087 SetSize(on, -repeat, true, false, isControlDown);
1088 if (on instanceof Text) {
1089 DisplayIO.setTextCursor((Text) on, Text.NONE, true, false,
1090 false, true);
1091 }
1092 break;
1093 case ChangeColor:
1094 SetColor(on, isShiftDown, isControlDown);
1095 break;
1096 case ToggleAnnotation:
1097 ToggleAnnotation(on);
1098 break;
1099 case ChangeFontStyle:
1100 ToggleFontStyle(on);
1101 break;
1102 case ChangeFontFamily:
1103 ToggleFontFamily(on);
1104 break;
1105 case InsertDate:
1106 AddDate(on);
1107 return;
1108 case NewFrameset:
1109 CreateFrameset(on);
1110 break;
1111 case XRayMode:
1112 FrameGraphics.ToggleXRayMode();
1113 break;
1114 case AudienceMode:
1115 FrameGraphics.ToggleAudienceMode();
1116 break;
1117 case Refresh:
1118 Refresh();
1119 break;
1120 case Save:
1121 Save();
1122 break;
1123 }
1124 on = FrameUtils.getCurrentItem();
1125 Collection<Item> enclosed = FrameUtils.getCurrentItems(on);
1126 if (on == null && (enclosed == null || enclosed.size() == 0))
1127 MessageBay.displayMessage(displayMessage);
1128 }
1129
1130 private static void calculateItem(Item toCalculate) {
1131 if (toCalculate == null)
1132 return;
1133
1134 if (!toCalculate.update()) {
1135 toCalculate.setFormula(null);
1136 MessageBay.errorMessage("Can not calculate formula ["
1137 + toCalculate.getText() + ']');
1138 }
1139 }
1140
1141 private static void Save() {
1142 Frame current = DisplayIO.getCurrentFrame();
1143 current.change();
1144 FrameIO.SaveFrame(current, true, true);
1145 }
1146
1147 public static final String DEFAULT_NEW_ITEM_TEXT = "";
1148
1149 /**
1150 * Performs the dropping action: If the cursor is in free space then: the
1151 * cursor is repositioned below the last non-annotation text item. If the
1152 * cursor is on an item, and has items attached then: the cusor is
1153 * positioned below the pointed to item, and the items below are 'pushed
1154 * down' to make room.
1155 *
1156 * @param toDropFrom
1157 * The Item being pointed at by the mouse, may be null to
1158 * indicate the cursor is in free space.
1159 */
1160 public static boolean Drop(Item toDropFrom, boolean bPasting) {
1161 try {
1162 FrameUtils.setLastEdited(null);
1163
1164 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1165
1166 // if a line is being rubber-banded, this is a no-op
1167 if (Frame.rubberbandingLine())
1168 return false; // No-op
1169
1170 // if the cursor is in free space then the drop will happen from the
1171 // last non annotation text item on the frame
1172 if (toDropFrom == null) {
1173 toDropFrom = DisplayIO.getCurrentFrame()
1174 .getLastNonAnnotationTextItem();
1175 }
1176
1177 // if no item was found, return
1178 if (toDropFrom == null) {
1179 MessageBay.errorMessage("No item could be found to drop from");
1180 return false;
1181 }
1182
1183 if (!(toDropFrom instanceof Text)) {
1184 MessageBay
1185 .displayMessage("Only text items can be dropped from");
1186 return false;
1187 }
1188
1189 // Get the list of items that must be dropped
1190 List<Text> column = DisplayIO.getCurrentFrame().getColumn(
1191 toDropFrom);
1192
1193 if (column == null) {
1194 MessageBay.errorMessage("No column found to align items to");
1195 return false;
1196 }
1197
1198 Item title = DisplayIO.getCurrentFrame().getTitleItem();
1199
1200 // We wont do auto bulleting when dropping from titles
1201 if (!bPasting && toDropFrom != title) {
1202 newItemText = getAutoBullet(((Text) toDropFrom).getFirstLine());
1203 }
1204
1205 Text dummyItem = null;
1206 if (!bPasting && FreeItems.textOnlyAttachedToCursor()) {
1207 dummyItem = (Text) FreeItems.getItemAttachedToCursor();
1208 String autoBullet = getAutoBullet(dummyItem.getText());
1209
1210 if (autoBullet.length() > 0)
1211 newItemText = "";
1212 dummyItem.setText(newItemText + dummyItem.getText());
1213 }
1214
1215 dummyItem = createText();
1216 if (FreeItems.textOnlyAttachedToCursor()) {
1217 Text t = (Text) FreeItems.getItemAttachedToCursor();
1218 dummyItem.setSize(t.getSize());
1219 int lines = t.getTextList().size();
1220 for (int i = 0; i < lines; i++) {
1221 newItemText += '\n';
1222 }
1223 }
1224
1225 dummyItem.setText(newItemText);
1226
1227 // If the only item on the frame is the title and the frame name
1228 // goto the zero frame and drop to the @start if there is one
1229 // or a fixed amount if there is not
1230 if (column.size() == 0) {
1231 Frame current = DisplayIO.getCurrentFrame();
1232 // Item itemTemplate = current.getItemTemplate();
1233 int xPos = title.getX() + FrameCreator.INDENT_FROM_TITLE;
1234 int yPos = FrameCreator.getYStart(title);
1235 // Check for @start on the zero frame
1236 Frame zero = FrameIO.LoadFrame(current.getFramesetName() + '0');
1237 Text start = zero.getAnnotation("start");
1238 if (start != null) {
1239 xPos = start.getX();
1240 yPos = start.getY();
1241 }
1242
1243 dummyItem.setPosition(xPos, yPos);
1244 // DisplayIO.setCursorPosition(xPos, yPos);
1245
1246 checkMovingCursor(dummyItem);
1247 } else {
1248 int yPos = column.get(0).getY() + 1;
1249 int xPos = column.get(0).getX();
1250 // Either position the new item below the title or just above
1251 // the first item below the title
1252 if (toDropFrom == title && column.get(0) != title) {
1253 // If dropping from the title position just above top item
1254 yPos = column.get(0).getY() - 1;
1255
1256 Frame current = DisplayIO.getCurrentFrame();
1257 // Check for @start on the zero frame
1258 Frame zero = FrameIO
1259 .LoadFrame(current.getFramesetName() + '0');
1260 Text start = zero.getAnnotation("start");
1261 if (start != null) {
1262 yPos = Math.min(yPos, start.getY());
1263 }
1264 }
1265 dummyItem.setPosition(xPos, yPos);
1266 column.add(dummyItem);
1267 FrameUtils.Align(column, false, 0);
1268 // Check if it will be outside the frame area
1269 if (dummyItem.getY() < 0
1270 || dummyItem.getY() > FrameGraphics.getMaxFrameSize()
1271 .getHeight()) {
1272 // Check for the 'next' tag!
1273 Frame current = DisplayIO.getCurrentFrame();
1274 Item next = current.getAnnotation("next");
1275 Item prev = current.getAnnotation("previous");
1276 // Check for an unlinked next tag
1277 if ((next != null && !next.hasLink())
1278 || (prev != null && prev.hasLink())) {
1279 Frame firstFrame = current;
1280 if (next != null)
1281 next.delete();
1282 FrameCreator frameCreator = new FrameCreator(null);
1283 // Add the next button
1284 next = frameCreator.addNextButton(current, null);
1285
1286 // Create the new frame linked to the next tag
1287 boolean mouseMoved = FrameMouseActions.tdfc(next);
1288 Frame moreFrame = DisplayIO.getCurrentFrame();
1289
1290 // Add previous button to the new frame
1291 frameCreator.addPreviousButton(moreFrame, firstFrame
1292 .getName());
1293 Item first = current.getAnnotation("first");
1294 if (first != null) {
1295 frameCreator.addFirstButton(moreFrame, first
1296 .getLink());
1297 } else {
1298 frameCreator.addFirstButton(moreFrame, firstFrame
1299 .getName());
1300 }
1301 // Add the @next if we are pasting
1302 // if (bPasting) {
1303 // Item copy = next.copy();
1304 // copy.setLink(null);
1305 // moreFrame.addItem(copy);
1306 // }
1307
1308 moreFrame.setTitle(firstFrame.getTitleItem().getText());
1309 // need to move the mouse to the top of the frame if
1310 // there wasnt an @start on it
1311 if (!mouseMoved) {
1312 Item moreTitle = moreFrame.getTitleItem();
1313 moreTitle.setOverlayPermission(UserAppliedPermission.full);
1314 Drop(moreTitle, bPasting);
1315 }
1316 // Add the bullet text to the item
1317 dummyItem.setPosition(DisplayIO.getMouseX(),
1318 FrameMouseActions.getY());
1319 } else {
1320 MessageBay
1321 .warningMessage("Can not create items outside the frame area");
1322 // ensures correct repainting when items don't move
1323 DisplayIO.setCursorPosition(DisplayIO.getMouseX(),
1324 FrameMouseActions.getY());
1325 return false;
1326 }
1327 }
1328 if (!FreeItems.textOnlyAttachedToCursor()
1329 && !dummyItem.isEmpty()) {
1330 DisplayIO.getCurrentFrame().addItem(dummyItem);
1331 }
1332
1333 checkMovingCursor(dummyItem);
1334 }
1335 if (dummyItem.getText().length() == 0
1336 || FreeItems.itemsAttachedToCursor()) {
1337 dummyItem.getParentOrCurrentFrame().removeItem(dummyItem);
1338 dummyItem.setRightMargin(FrameGraphics.getMaxFrameSize().width,
1339 false);
1340 } else {
1341 dummyItem.setWidth(toDropFrom.getWidth());
1342 }
1343
1344 DisplayIO.resetCursorOffset();
1345 FrameGraphics.Repaint();
1346 } catch (RuntimeException e) {
1347 // MessageBay.errorMessage(e.getMessage());
1348 e.printStackTrace();
1349 return false;
1350 }
1351 return true;
1352 }
1353
1354 /**
1355 * @param dummyItem
1356 */
1357 private static void checkMovingCursor(Text dummyItem) {
1358 // Move the item to the cursor position
1359 if (FreeItems.itemsAttachedToCursor()) {
1360 moveCursorAndFreeItems(dummyItem.getX(), dummyItem.getY());
1361 } else {
1362 DisplayIO.MoveCursorToEndOfItem(dummyItem);
1363 }
1364 }
1365
1366 /**
1367 * @param dummyItem
1368 */
1369 public static void moveCursorAndFreeItems(int x, int y) {
1370 int oldX = FrameMouseActions.getX();
1371 int oldY = FrameMouseActions.getY();
1372
1373 if (oldX == x && oldY == y)
1374 return;
1375
1376 DisplayIO.setCursorPosition(x, y);
1377 Item firstItem = FreeItems.getItemAttachedToCursor();
1378
1379 if (firstItem == null) {
1380 firstItem = null;
1381 return;
1382 }
1383
1384 int deltaX = firstItem.getX() - x;
1385 int deltaY = firstItem.getY() - y;
1386
1387 for (Item i : FreeItems.getInstance()) {
1388 i.setPosition(i.getX() - deltaX, i.getY() - deltaY);
1389 }
1390 }
1391
1392 /**
1393 * Gets the next letter sequence for a given string to be used in auto
1394 * lettering.
1395 *
1396 * @param s
1397 * a sequence of letters
1398 * @return the next sequence of letters
1399 */
1400 static private String nextLetterSequence(String s) {
1401 if (s.length() > 1)
1402 return s;
1403
1404 if (s.equals("z"))
1405 return "a";
1406
1407 return (char) ((int) s.charAt(0) + 1) + "";
1408 }
1409
1410 public static String getBullet(String s) {
1411 return getBullet(s, false);
1412 }
1413
1414 public static String getAutoBullet(String s) {
1415 return getBullet(s, true);
1416 }
1417
1418 private static String getBullet(String s, boolean nextBullet) {
1419 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1420
1421 if (s == null)
1422 return newItemText;
1423 /*
1424 * Item i = ItemUtils.FindTag(DisplayIO.getCurrentFrame().getItems(),
1425 * "@NoAutoBullets"); if (i != null) return newItemText;
1426 */
1427 // Separate the space at the start of the text item
1428 String preceedingSpace = "";
1429 for (int i = 0; i < s.length(); i++) {
1430 if (!Character.isSpaceChar(s.charAt(i))) {
1431 preceedingSpace = s.substring(0, i);
1432 s = s.substring(i);
1433 break;
1434 }
1435 }
1436
1437 // figure out the type of the text item
1438 // This allows us to do auto bulleting
1439 if (s != null && s.length() > 1) {
1440 // First check for text beginning with * @ # etc
1441 // These are simple auto bullets
1442 if (!Character.isLetterOrDigit(s.charAt(0))
1443 && !Character.isSpaceChar(s.charAt(0))) {
1444 if (Text.isBulletChar(s.charAt(0))) {
1445 int nonSpaceIndex = 1;
1446 // Find the end of the bullet and space after the bullet
1447 while (nonSpaceIndex < s.length()
1448 && s.charAt(nonSpaceIndex) == ' ') {
1449 nonSpaceIndex++;
1450 }
1451 // we must have a special char followed by >= 1 space
1452 if (nonSpaceIndex > 1)
1453 newItemText = s.substring(0, nonSpaceIndex);
1454 }
1455 // Auto numbering and lettering
1456 } else {
1457 if (Character.isDigit(s.charAt(0))) {
1458 newItemText = getAutoNumber(s, nextBullet);
1459 // Auto lettering
1460 } else if (Character.isLetter(s.charAt(0))) {
1461 newItemText = getAutoLetter(s, nextBullet);
1462 }
1463 }
1464 }
1465 return preceedingSpace + newItemText;
1466 }
1467
1468 private static boolean isAutoNumberOrLetterChar(char c) {
1469 return c == ':' || c == '-' || c == '.' || c == ')' || c == '>';
1470 }
1471
1472 /**
1473 * Gets the string to be used to start the next auto numbered text item.
1474 *
1475 * @param s
1476 * the previous text item
1477 * @return the beginning of the next auto numbered text item
1478 */
1479 private static String getAutoNumber(String s, boolean nextBullet) {
1480 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1481
1482 int nonDigitIndex = 1;
1483 while (Character.isDigit(s.charAt(nonDigitIndex))) {
1484 nonDigitIndex++;
1485
1486 if (nonDigitIndex + 1 >= s.length())
1487 return DEFAULT_NEW_ITEM_TEXT;
1488 }
1489
1490 if (isAutoNumberOrLetterChar(s.charAt(nonDigitIndex))) {
1491
1492 // we must have a number followed one non letter
1493 // then one or more spaces
1494 int nonSpaceIndex = nonDigitIndex + 1;
1495 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1496 nonSpaceIndex++;
1497 }
1498
1499 if (nonSpaceIndex > nonDigitIndex + 1) {
1500 if (nextBullet)
1501 newItemText = (Integer.parseInt(s.substring(0,
1502 nonDigitIndex)) + 1)
1503 + s.substring(nonDigitIndex, nonSpaceIndex);
1504 else
1505 newItemText = s.substring(0, nonSpaceIndex);
1506 }
1507 }
1508 return newItemText;
1509 }
1510
1511 /**
1512 * Gets the string to be used to start the next auto lettered text item.
1513 *
1514 * @param s
1515 * the previous text items
1516 * @return the initial text for the new text item
1517 */
1518 private static String getAutoLetter(String s, boolean nextBullet) {
1519 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1520
1521 int nonLetterIndex = 1;
1522
1523 if (isAutoNumberOrLetterChar(s.charAt(nonLetterIndex))) {
1524
1525 // Now search for the next non space character
1526 int nonSpaceIndex = nonLetterIndex + 1;
1527 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1528 nonSpaceIndex++;
1529 }
1530
1531 // If there was a space then we have reached the end of our auto
1532 // text
1533 if (nonSpaceIndex > nonLetterIndex + 1) {
1534 if (nextBullet)
1535 newItemText = nextLetterSequence(s.substring(0,
1536 nonLetterIndex))
1537 + s.substring(nonLetterIndex, nonSpaceIndex);
1538 else
1539 newItemText = s.substring(0, nonSpaceIndex);
1540 }
1541 }
1542 return newItemText;
1543 }
1544
1545 private static boolean refreshAnchors(Collection<Item> items) {
1546 boolean bReparse = false;
1547
1548 for(Item i : items) {
1549 Float anchorLeft = i.getAnchorLeft();
1550 Float anchorRight = i.getAnchorRight();
1551 Float anchorTop = i.getAnchorTop();
1552 Float anchorBottom = i.getAnchorBottom();
1553
1554
1555 if (anchorLeft != null) {
1556 i.setAnchorLeft(anchorLeft);
1557 if (i.hasVector()) {
1558 bReparse = true;
1559 }
1560 }
1561
1562 if (anchorRight != null) {
1563 i.setAnchorRight(anchorRight);
1564 if (i.hasVector()) {
1565 bReparse = true;
1566 }
1567 }
1568
1569 if (anchorTop != null) {
1570 i.setAnchorTop(anchorTop);
1571 if (i.hasVector()) {
1572 bReparse = true;
1573 }
1574 }
1575
1576 if (anchorBottom != null) {
1577 i.setAnchorBottom(anchorBottom);
1578 if (i.hasVector()) {
1579 bReparse = true;
1580 }
1581 }
1582 }
1583 return bReparse;
1584 }
1585
1586 private static void zoomFrame(Frame frame, double scaleFactor) {
1587
1588 int x = FrameMouseActions.getX(), y = FrameMouseActions.getY();
1589
1590 if (frame == null) {
1591 return;
1592 }
1593
1594 Collection<Item> items = frame.getVisibleItems();
1595
1596 for(Item item : items) {
1597 if(item instanceof Text && item.getSize() <= Text.MINIMUM_FONT_SIZE && scaleFactor < 1) {
1598 return;
1599 }
1600 }
1601
1602 for (Vector v : frame.getVectors()) {
1603 v.Source.scale((float) scaleFactor, x, y);
1604 }
1605
1606 for (Item item : items) {
1607 // This line is only needed for circles!!
1608 // Need to really fix up the way this works!!
1609 if (item.hasEnclosures())
1610 continue;
1611 if (!item.hasPermission(UserAppliedPermission.full))
1612 continue;
1613 item.invalidateAll();
1614 if (!(item instanceof Line)) {
1615 item.scale((float) scaleFactor, x, y);
1616 }
1617 }
1618
1619 for (Item item : items) {
1620 if (!item.hasPermission(UserAppliedPermission.full))
1621 continue;
1622 // if (!(item instanceof Line))
1623 item.updatePolygon();
1624
1625 if (item instanceof Line) {
1626 ((Line) item).refreshStroke(item.getThickness());
1627 }
1628
1629 item.invalidateAll();
1630 }
1631 }
1632
1633 /**
1634 * Adjusts the size of the given Item, by the given amount. Note: The amount
1635 * is relative and can be positive or negative.
1636 *
1637 * @param toSet
1638 * The Item whose size is to be adjusted
1639 * @param diff
1640 * The amount to adjust the Item's size by
1641 * @param moveCursor
1642 * true if the cursor position should be automatically adjusted
1643 * with resizing
1644 */
1645 public static void SetSize(Item item, int diff, boolean moveCursor,
1646 boolean insideEnclosure, boolean isControlDown) {
1647 Collection<Item> toSize = new HashSet<Item>();
1648 Collection<InteractiveWidget> widgets = new HashSet<InteractiveWidget>();
1649 // the mouse is only moved when the Item is on the frame, not free
1650 // boolean moveMouse = false;
1651 Item toSet = null;
1652
1653 // if the user is not pointing to any item
1654 if (item == null) {
1655 if (FreeItems.itemsAttachedToCursor())
1656 toSize.addAll(FreeItems.getInstance());
1657 else {
1658 MessageBay
1659 .displayMessage("There are no Items selected on the Frame or on the Cursor");
1660 return;
1661 }
1662 } else {
1663 if (item.isFrameName()) {
1664 // scale the entire frame
1665 if(diff != 0) {
1666 zoomFrame(DisplayIO.getCurrentFrame(), diff > 0 ? 1.1 : 0.909090909);
1667 DisplayIO.getCurrentFrame().refreshSize();
1668 FrameKeyboardActions.Refresh();
1669 }
1670 // MessageBay.displayMessage("Can not resize the frame name");
1671 return;
1672 }
1673 // check permissions
1674 if (!item.hasPermission(UserAppliedPermission.full)) {
1675 Item editTarget = item.getEditTarget();
1676 if (editTarget != item
1677 && editTarget.hasPermission(UserAppliedPermission.full)) {
1678 item = editTarget;
1679 } else {
1680 MessageBay
1681 .displayMessage("Insufficient permission to change the size of that item");
1682 return;
1683 }
1684 }
1685 toSet = item;
1686 // For resizing enclosures pick up everything that is attached to
1687 // items partly in the enclosure
1688 // TODO make this only pick up stuff COMPLETELY enclosed... if we
1689 // change copying to copy only the stuff completely enclosed
1690 if (insideEnclosure) {
1691 if (_enclosedItems == null) {
1692 for (Item i : FrameUtils.getCurrentItems(toSet)) {
1693 if (i.hasPermission(UserAppliedPermission.full)
1694 && !toSize.contains(i))
1695 toSize.addAll(i.getAllConnected());
1696 }
1697 _enclosedItems = toSize;
1698 } else {
1699 toSize = _enclosedItems;
1700 }
1701
1702 }// Enclosed circle centers are resized with the center as origin
1703 // Just add the circle center to the list of items to size
1704 else if (!toSet.hasEnclosures() && !(toSet instanceof Text)
1705 && toSet.isLineEnd()) {
1706 toSize.addAll(toSet.getLines());
1707 } else if (toSet instanceof Line) {
1708
1709 Line line = (Line) toSet;
1710
1711 if (!(toSet instanceof WidgetEdge)
1712 || ((WidgetEdge) toSet).getWidgetSource()
1713 .isWidgetEdgeThicknessAdjustable()) {
1714
1715 float current = Math.abs(line.getThickness());
1716 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
1717 line.setThickness(current);
1718 FrameGraphics.Repaint();
1719 return;
1720
1721 }
1722
1723 } else {
1724 toSize.add(toSet);
1725 }
1726 }
1727
1728 // add widgets to notify
1729 for(Item i : toSize) {
1730 if(i instanceof WidgetEdge) {
1731 widgets.add(((WidgetEdge) i).getWidgetSource());
1732 } else if(i instanceof WidgetCorner) {
1733 widgets.add(((WidgetCorner) i).getWidgetSource());
1734 }
1735 }
1736
1737 Point2D origin = new Point2D.Float(FrameMouseActions.MouseX,
1738 FrameMouseActions.MouseY);
1739 // Inside enclosures increase the size of the enclosure
1740 double ratio = (100.0 + diff * 2) / 100.0;
1741 if (insideEnclosure) {
1742 Collection<Item> done = new HashSet<Item>();
1743 // adjust the size of all the items
1744 for (Item i : toSize) {
1745 if (done.contains(i))
1746 continue;
1747
1748 if (i.isLineEnd()) {
1749
1750 if (!(i instanceof WidgetCorner)
1751 || !((WidgetCorner) i).getWidgetSource()
1752 .isFixedSize()) { // don't size fixed
1753 // widgets
1754
1755 Collection<Item> allConnected = i.getAllConnected();
1756 done.addAll(allConnected);
1757 for (Item it : allConnected) {
1758 it.translate(origin, ratio);
1759 it.setArrowheadLength((float) (it
1760 .getArrowheadLength() * ratio));
1761 }
1762 i.setThickness((float) (i.getThickness() * ratio));
1763 }
1764 } else if (i instanceof XRayable) {
1765 XRayable xRay = (XRayable) i;
1766 Text source = xRay.getSource();
1767 // Ensure that the source is done before the XRayable
1768 if (!done.contains(source)) {
1769 scaleText(insideEnclosure, origin, ratio, done, source);
1770 }
1771
1772 i.translate(origin, ratio);
1773 i.setThickness((float) (i.getThickness() * ratio));
1774 done.add(i);
1775 } else if (i.hasVector()) {
1776 // TODO Improve the effiency of resizing vectors... ie...
1777 // dont want to have to reparse all the time
1778 assert (i instanceof Text);
1779 Text text = (Text) i;
1780 AttributeValuePair avp = new AttributeValuePair(text
1781 .getText());
1782 double scale = 1F;
1783 try {
1784 scale = avp.getDoubleValue();
1785 } catch (Exception e) {
1786 }
1787 scale *= ratio;
1788 NumberFormat nf = Vector.getNumberFormatter();
1789 text.setAttributeValue(nf.format(scale));
1790 text.translate(origin, ratio);
1791 item.getParent().parse();
1792 } else if (i instanceof Text) {
1793 scaleText(insideEnclosure, origin, ratio, done, (Text) i);
1794 }
1795 }
1796 // refresh anchored items
1797 if(refreshAnchors(toSize)) {
1798 FrameUtils.Parse(DisplayIO.getCurrentFrame(), false);
1799 }
1800 // notify widgets they were resized
1801 for(InteractiveWidget iw : widgets) {
1802 iw.onResized();
1803 }
1804 FrameGraphics.refresh(true);
1805 return;
1806 }
1807
1808 // adjust the size of all the items
1809 for (Item i : toSize) {
1810 // Lines and dots use thickness, not size
1811 if (i.hasEnclosures()) {
1812 Circle c = (Circle) i.getEnclosures().iterator().next();
1813 c.setSize(c.getSize() * (float) ratio);
1814 } else if (i instanceof Line || i instanceof Circle
1815 && !insideEnclosure) {
1816 float current = Math.abs(i.getThickness());
1817 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
1818 i.setThickness(current);
1819 } else if (i instanceof Dot) {
1820 Item dot = (Item) i;
1821 float current = Math.abs(dot.getThickness());
1822 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
1823 dot.setThickness(current);
1824 } else if (i.hasVector()) {
1825 assert (item instanceof Text);
1826 Text text = (Text) item;
1827 AttributeValuePair avp = new AttributeValuePair(text.getText());
1828 double scale = 1F;
1829 try {
1830 scale = avp.getDoubleValue();
1831 } catch (Exception e) {
1832 }
1833 scale *= ratio;
1834 NumberFormat nf = Vector.getNumberFormatter();
1835 text.setAttributeValue(nf.format(scale));
1836 text.translate(origin, ratio);
1837 item.getParent().parse();
1838 } else {
1839 float oldSize = Math.abs(i.getSize());
1840 float newSize = Math
1841 .max(oldSize + diff, Item.MINIMUM_THICKNESS);
1842 float resizeRatio = newSize / oldSize;
1843 // Set size for Picture also translates
1844 i.setSize(newSize);
1845 if (i instanceof Text && i.getSize() != oldSize) {
1846 if (toSize.size() == 1 && !isControlDown) {
1847 moveCursorAndFreeItems(i.getX(), i.getY());
1848 } else {
1849 i.translate(origin, resizeRatio);
1850 if (i.isLineEnd()) {
1851 i.setPosition(i.getPosition());
1852 }
1853 }
1854 }
1855 }
1856 }
1857
1858 if (toSet != null)
1859 toSet.getParent().setChanged(true);
1860
1861 // refresh anchored items
1862 if(refreshAnchors(toSize)) {
1863 FrameUtils.Parse(DisplayIO.getCurrentFrame(), false);
1864 }
1865
1866 // notify widgets they were resized
1867 for(InteractiveWidget iw : widgets) {
1868 iw.onResized();
1869 }
1870 FrameGraphics.refresh(true);
1871 }
1872
1873 /**
1874 * @param origin
1875 * @param ratio
1876 * @param done
1877 * @param source
1878 */
1879 private static void scaleText(boolean insideEnclosure, Point2D origin,
1880 double ratio, Collection<Item> done, Text source) {
1881 if (insideEnclosure)
1882 source.setWidth(Math.round((float) (source.getWidth() * ratio)));
1883 source.translate(origin, ratio);
1884 source.setSize((float) (source.getSize() * ratio));
1885 done.add(source);
1886 }
1887
1888 private static void SetFillColor(Item item, boolean setTransparent) {
1889 if (item == null)
1890 return;
1891
1892 if (!item.hasPermission(UserAppliedPermission.full)) {
1893 MessageBay
1894 .displayMessage("Insufficient permission to change fill color");
1895 return;
1896 }
1897
1898 Item toSet = item;
1899 Color color = toSet.getFillColor();
1900 if (setTransparent)
1901 color = null;
1902 else
1903 color = ColorUtils.getNextColor(color, TemplateSettings.FillColorWheel.get(), toSet
1904 .getGradientColor());
1905
1906 // if (color == null) {
1907 // MessageBay.displayMessage("FillColor is now transparent");
1908 // }
1909
1910 toSet.setFillColor(color);
1911 toSet.getParent().setChanged(true);
1912
1913 FrameGraphics.Repaint();
1914 }
1915
1916 private static void SetGradientColor(Item item, boolean setTransparent) {
1917 if (item == null)
1918 return;
1919
1920 if (!item.hasPermission(UserAppliedPermission.full)) {
1921 MessageBay
1922 .displayMessage("Insufficient permission to change gradient color");
1923 return;
1924 }
1925
1926 Item toSet = item;
1927 Color color = toSet.getGradientColor();
1928 if (setTransparent)
1929 color = null;
1930 else
1931 color = ColorUtils.getNextColor(color, TemplateSettings.ColorWheel.get(), toSet
1932 .getFillColor());
1933
1934 // if (color == null) {
1935 // MessageBay.displayMessage("FillColor is now transparent");
1936 // }
1937
1938 toSet.setGradientColor(color);
1939 toSet.getParent().setChanged(true);
1940
1941 FrameGraphics.Repaint();
1942 }
1943
1944 /**
1945 * Sets the colour of the current Item based on its current colour. The
1946 * colours proceed in the order stored in COLOR_WHEEL.
1947 *
1948 * @param toSet
1949 * The Item whose colour is to be changed
1950 */
1951 private static void SetColor(Item item, boolean setTransparent,
1952 boolean setBackgroundColor) {
1953 // first determine the next color
1954 Color color = null;
1955 Frame currentFrame = DisplayIO.getCurrentFrame();
1956 if (item == null) {
1957 if (FreeItems.itemsAttachedToCursor()) {
1958 color = FreeItems.getInstance().get(0).getColor();
1959 } else {
1960 return;
1961 }
1962 // change the background color if the user is pointing on the
1963 // frame name
1964 } else if (item == currentFrame.getNameItem()) {
1965 // check permissions
1966 if (!item.hasPermission(UserAppliedPermission.full)) {
1967 MessageBay
1968 .displayMessage("Insufficient permission to the frame's background color");
1969 return;
1970 }
1971 if (setTransparent)
1972 currentFrame.setBackgroundColor(null);
1973 else
1974 currentFrame.toggleBackgroundColor();
1975 // Display a message if the color has changed to transparent
1976 // if (currentFrame.getBackgroundColor() == null)
1977 // FrameGraphics
1978 // .displayMessage("Background color is now transparent");
1979 FrameGraphics.Repaint();
1980 return;
1981 } else {
1982 // check permissions
1983 if (!item.hasPermission(UserAppliedPermission.full)) {
1984 Item editTarget = item.getEditTarget();
1985 if (editTarget != item
1986 && editTarget.hasPermission(UserAppliedPermission.full)) {
1987 item = editTarget;
1988 } else {
1989 MessageBay
1990 .displayMessage("Insufficient permission to change color");
1991 return;
1992 }
1993 }
1994 // Toggling color of circle center changes the circle fill color
1995 if (item.hasEnclosures()) {
1996 if (setBackgroundColor) {
1997 SetGradientColor(item.getEnclosures().iterator().next(),
1998 setTransparent);
1999 } else {
2000 SetFillColor(item.getEnclosures().iterator().next(),
2001 setTransparent);
2002 }
2003 } else if (setBackgroundColor) {
2004 color = item.getPaintBackgroundColor();
2005 } else {
2006 color = item.getPaintColor();
2007 }
2008 }
2009 if (setTransparent)
2010 color = null;
2011 else if (setBackgroundColor) {
2012 color = ColorUtils.getNextColor(color, TemplateSettings.FillColorWheel.get(), item
2013 .getPaintColor());
2014 } else {
2015 color = ColorUtils.getNextColor(color, TemplateSettings.ColorWheel.get(),
2016 currentFrame.getPaintBackgroundColor());
2017 }
2018 // if (currentFrame.getPaintForegroundColor().equals(color))
2019 // color = null;
2020
2021 // if color is being set to default display a message to indicate that
2022 // if (color == null) {
2023 // MessageBay.displayMessage("Color is set to default");
2024 // }
2025
2026 if (setBackgroundColor) {
2027 if (item == null && FreeItems.itemsAttachedToCursor()) {
2028 for (Item i : FreeItems.getInstance())
2029 i.setBackgroundColor(color);
2030 } else {
2031 item.setBackgroundColor(color);
2032 item.getParent().setChanged(true);
2033 }
2034 } else {
2035 if (item == null && FreeItems.itemsAttachedToCursor()) {
2036 for (Item i : FreeItems.getInstance())
2037 i.setColor(color);
2038 } else {
2039 item.setColor(color);
2040 item.getParent().setChanged(true);
2041 }
2042 }
2043 FrameGraphics.Repaint();
2044 }
2045
2046 /**
2047 * Toggles the given Item's annotation status on\off.
2048 *
2049 * @param toToggle
2050 * The Item to toggle
2051 */
2052 private static void ToggleAnnotation(Item toToggle) {
2053 if (toToggle == null) {
2054 MessageBay.displayMessage("There is no Item selected to toggle");
2055 return;
2056 }
2057
2058 // check permissions
2059 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2060 MessageBay
2061 .displayMessage("Insufficient permission to toggle that item's annotation");
2062 return;
2063 }
2064 toToggle.setAnnotation(!toToggle.isAnnotation());
2065
2066 toToggle.getParent().setChanged(true);
2067 FrameGraphics.Repaint();
2068 }
2069
2070 /**
2071 * Toggles the face style of a text item
2072 *
2073 * @param toToggle
2074 * The Item to toggle
2075 */
2076 private static void ToggleFontStyle(Item toToggle) {
2077 if (toToggle == null) {
2078 MessageBay.displayMessage("There is no Item selected to toggle");
2079 return;
2080 }
2081
2082 // check permissions
2083 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2084 MessageBay
2085 .displayMessage("Insufficient permission to toggle that item's annotation");
2086 return;
2087 }
2088
2089 if (toToggle instanceof Text) {
2090 Text text = (Text) toToggle;
2091 text.toggleFontStyle();
2092
2093 text.getParent().setChanged(true);
2094 FrameGraphics.Repaint();
2095 }
2096 }
2097
2098 /**
2099 * Toggles the face style of a text item
2100 *
2101 * @param toToggle
2102 * The Item to toggle
2103 */
2104 private static void ToggleFontFamily(Item toToggle) {
2105 if (toToggle == null) {
2106 MessageBay.displayMessage("There is no Item selected to toggle");
2107 return;
2108 }
2109
2110 // check permissions
2111 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2112 MessageBay
2113 .displayMessage("Insufficient permission to toggle that item's annotation");
2114 return;
2115 }
2116
2117 if (toToggle instanceof Text) {
2118 Text text = (Text) toToggle;
2119 text.toggleFontFamily();
2120
2121 text.getParent().setChanged(true);
2122 FrameGraphics.Repaint();
2123 }
2124 }
2125
2126 /**
2127 * If the given Item is null, then a new Text item is created with the
2128 * current date If the given Item is not null, then the current date is
2129 * prepended to the Item's text
2130 *
2131 * @param toAdd
2132 * The Item to prepend the date to, or null
2133 */
2134 public static void AddDate(Item toAdd) {
2135 String date1 = Formatter.getDateTime();
2136 String date2 = Formatter.getDate();
2137 final String leftSeparator = " :";
2138 final String rightSeparator = ": ";
2139 String dateToAdd = date1 + rightSeparator;
2140 boolean prepend = false;
2141 boolean append = false;
2142
2143 // if the user is pointing at an item, add the date where ever the
2144 // cursor is pointing
2145 if (toAdd != null && toAdd instanceof Text) {
2146 // permission check
2147 if (!toAdd.hasPermission(UserAppliedPermission.full)) {
2148 MessageBay
2149 .displayMessage("Insufficicent permission to add the date to that item");
2150 return;
2151 }
2152
2153 Text textItem = (Text) toAdd;
2154
2155 String text = textItem.getText();
2156
2157 // check if the default date has already been put on this item
2158 if (text.startsWith(date1 + rightSeparator)) {
2159 textItem.removeText(date1 + rightSeparator);
2160 dateToAdd = date2 + rightSeparator;
2161 prepend = true;
2162 } else if (text.startsWith(date2 + rightSeparator)) {
2163 textItem.removeText(date2 + rightSeparator);
2164 dateToAdd = leftSeparator + date2;
2165 append = true;
2166 } else if (text.endsWith(leftSeparator + date2)) {
2167 textItem.removeEndText(leftSeparator + date2);
2168 append = true;
2169 dateToAdd = leftSeparator + date1;
2170 } else if (text.endsWith(leftSeparator + date1)) {
2171 textItem.removeEndText(leftSeparator + date1);
2172 if (textItem.getLength() > 0) {
2173 dateToAdd = "";
2174 prepend = true;
2175 } else {
2176 // use the default date format
2177 prepend = true;
2178 }
2179 }
2180
2181 if (prepend) {
2182 // add the date to the text item
2183 textItem.prependText(dateToAdd);
2184 if (dateToAdd.length() == textItem.getLength())
2185 DisplayIO.setCursorPosition(textItem
2186 .getParagraphEndPosition());
2187 } else if (append) {
2188 textItem.appendText(dateToAdd);
2189 if (dateToAdd.length() == textItem.getLength())
2190 DisplayIO.setCursorPosition(textItem.getPosition());
2191 } else {
2192 for (int i = 0; i < date1.length(); i++) {
2193 processChar(date1.charAt(i), false);
2194 }
2195 }
2196
2197 textItem.getParent().setChanged(true);
2198 FrameGraphics.Repaint();
2199 // } else {
2200 // MessageBay
2201 // .displayMessage("Only text items can have the date prepended to
2202 // them");
2203 // }
2204 // otherwise, create a new text item
2205 } else {
2206 Text newText = createText();
2207 newText.setText(dateToAdd);
2208 DisplayIO.getCurrentFrame().addItem(newText);
2209 DisplayIO.getCurrentFrame().setChanged(true);
2210 FrameGraphics.Repaint();
2211
2212 DisplayIO.setCursorPosition(newText.getParagraphEndPosition());
2213 }
2214
2215 }
2216
2217 /**
2218 * Creates a new Frameset with the name given by the Item
2219 *
2220 * @param name
2221 */
2222 private static void CreateFrameset(Item item) {
2223 if (item == null) {
2224 MessageBay
2225 .displayMessage("There is no selected item to use for the frameset name");
2226 return;
2227 }
2228
2229 if (!(item instanceof Text)) {
2230 MessageBay
2231 .displayMessage("Framesets can only be created from text items");
2232 return;
2233 }
2234
2235 // dont create frameset if the item is linked
2236 if (item.getLink() != null) {
2237 MessageBay
2238 .displayMessage("A frameset can not be created from a linked item");
2239 return;
2240 }
2241
2242 // check permissions
2243 if (!item.hasPermission(UserAppliedPermission.full)) {
2244 MessageBay
2245 .displayMessage("Insufficient permission to create a frameset from this item");
2246 return;
2247 }
2248
2249 Text text = (Text) item;
2250 try {
2251 // create the new frameset
2252 Frame linkTo = FrameIO.CreateNewFrameset(text.getFirstLine());
2253 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2254 text.setLink(linkTo.getName());
2255 text.getParent().setChanged(true);
2256 FrameUtils.DisplayFrame(linkTo, true, true);
2257 linkTo.moveMouseToDefaultLocation();
2258 // this needs to be done if the user doesnt move the mouse before
2259 // doing Tdfc while the cursor is set to the text cursor
2260 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2261 } catch (Exception e) {
2262 MessageBay.errorMessage(e.getMessage());
2263 }
2264 }
2265
2266 /**
2267 * Forces a re-parse and repaint of the current Frame.
2268 */
2269 public static void Refresh() {
2270 Frame currentFrame = DisplayIO.getCurrentFrame();
2271
2272 if(FrameMouseActions.isShiftDown()) {
2273 currentFrame.refreshSize();
2274 }
2275
2276 // Refresh widgets that use its self as a data source
2277 currentFrame.notifyObservers(true);
2278
2279 if (FrameIO.isProfileFrame(currentFrame)) {
2280 // TODO ensure that users can not delete the first frame in a
2281 // frameset...
2282 // TODO handle the case when users manually delete the first frame
2283 // in a frameset from the filesystem
2284 Frame profile = FrameIO.LoadFrame(currentFrame.getFramesetName()
2285 + "1");
2286 assert (profile != null);
2287 FrameUtils.Parse(currentFrame);
2288 FrameUtils.ParseProfile(profile);
2289 } else {
2290 FrameUtils.Parse(currentFrame);
2291 }
2292 // Need to update the cursor for when text items change to @b pictures
2293 // etc and the text cursor is showing
2294 FrameMouseActions.updateCursor();
2295 FrameMouseActions.getInstance().refreshHighlights();
2296 FrameGraphics.ForceRepaint();
2297 }
2298}
Note: See TracBrowser for help on using the repository browser.