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

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