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

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

Added additional mail functionality...
Also added REMINDER feature

File size: 56.6 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.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.HashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.StringTokenizer;
19
20import org.expeditee.actions.Actions;
21import org.expeditee.actions.NavigationActions;
22import org.expeditee.actions.Simple;
23import org.expeditee.io.Logger;
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.Permission;
30import org.expeditee.items.StringUtils;
31import org.expeditee.items.Text;
32import org.expeditee.items.XRayable;
33import org.expeditee.items.widgets.WidgetCorner;
34import org.expeditee.stats.SessionStats;
35
36public class FrameKeyboardActions implements KeyListener {
37
38 private static Text _toRemove = null;
39
40 private static Collection<Item> _enclosedItems = null;
41
42 public static void resetEnclosedItems() {
43 _enclosedItems = null;
44 }
45
46 public synchronized void keyTyped(KeyEvent e) {
47 if (Simple.isProgramRunning()) {
48 if (e.isControlDown()
49 && (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.getKeyChar() == KeyEvent.VK_C)) {
50 Simple.stop();
51 return;
52 } else if (e.isControlDown() && e.getKeyChar() == KeyEvent.VK_SPACE) {
53 Simple.nextStatement();
54 return;
55 } else {
56 Simple.KeyStroke(e.getKeyChar());
57 }
58 if (Simple.consumeKeyboardInput())
59 return;
60 }
61
62 // ignore escape character and control characters
63 if (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.isControlDown()) {
64 return;
65 }
66
67 // Deal with splitting text items when typing too fast
68 // Mike: thinks this problem may have been solved and was due to
69 // rounding errors in the text class...
70 // It may have been fixed by changing to the use of floats instead of
71 // ints for text positioning etc
72 // if (FrameMouseActions.isWaitingForRobot()) {
73 // System.out.println("Waiting: " + e.getKeyChar());
74 // return;
75 // }
76 e.consume();
77 char ch = e.getKeyChar();
78 // System.out.println(ch);
79
80 if (e.isAltDown()) {
81
82 } else {
83 processChar(ch, e.isShiftDown());
84 }
85 // FrameGraphics.Repaint();
86 }
87
88 public static void processChar(char ch, boolean isShiftDown) {
89 NavigationActions.ResetLastAddToBack();
90 Item on = FrameUtils.getCurrentItem();
91
92 // permission check
93 if (on != null && !on.hasPermission(Permission.full)) {
94 MessageBay
95 .displayMessage("Insufficient permission to edit this item");
96 return;
97 }
98
99 if (_toRemove != null && on != _toRemove) {
100 assert (_toRemove.getLength() == 0);
101 // This line is to protect mistaken removal of items if there is a
102 // bug...
103 if (_toRemove.getLength() == 0)
104 DisplayIO.getCurrentFrame().removeItem(_toRemove);
105 }
106 _toRemove = null;
107
108 // ignore delete and backspace if in free space
109 if ((on == null || !(on instanceof Text))
110 && (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_TAB || ch == KeyEvent.VK_DELETE))
111 return;
112
113 SessionStats.TypedChar(ch);
114
115 // check for dot's being replaced with text
116 if (on != null && on instanceof Dot && !(on instanceof WidgetCorner)) {
117 if (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE) {
118 return;
119 }
120 replaceDot((Item) on, ch);
121 return;
122 }
123
124 // only text can interact with keyboard events
125 if (on != null && !(on instanceof Text))
126 on = null;
127
128 // DisplayIO.UpdateTitle();
129
130 Text text = (Text) on;
131 // if this text is empty but has not been removed (such as from
132 // ESC-pushdown)
133 if (text != null && text.isEmpty()
134 && (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE)) {
135 if (text.getLines().size() > 0)
136 replaceText(text);
137 else {
138 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
139 }
140 return;
141 }
142
143 FrameUtils.setLastEdited(text);
144
145 // if the user is in free space, create a new text item
146 if (on == null || !on.isHighlighted()) {
147 // DisplayIO.UpdateTitle();
148 text = createText(ch);
149 justify(text);
150
151 FrameUtils.setLastEdited(text);
152 DisplayIO.setTextCursor(text, Text.NONE);
153 return;
154 }
155
156 DisplayIO.setTextCursor(text, Text.NONE);
157 Point2D.Float newMouse = null;
158 if (ch == '\t') {
159 if (isShiftDown) {
160 newMouse = text.removeTab(ch, DisplayIO.getFloatMouseX(),
161 FrameMouseActions.MouseY);
162 } else {
163 newMouse = text.insertTab(ch, DisplayIO.getFloatMouseX(),
164 FrameMouseActions.MouseY);
165 }
166 } else {
167 newMouse = text.insertChar(ch, DisplayIO.getFloatMouseX(),
168 FrameMouseActions.MouseY);
169 }
170 DisplayIO.setCursorPosition(newMouse.x, newMouse.y, false);
171
172 // This repaint is needed for WINDOWS only?!?!? Mike is not sure why!
173 if (ch == KeyEvent.VK_DELETE)
174 FrameGraphics.requestRefresh(true);
175
176 // a change has occured to the Frame
177 text.getParent().setChanged(true);
178
179 // check that the Text item still exists (hasn't been deleted\backspaced
180 // away)
181 if (text.isEmpty()) {
182 _toRemove = text;
183
184 if (text.hasAction())
185 text.setActionMark(true);
186 else if (text.getLink() != null)
187 text.setLinkMark(true);
188 else if (text.getLines().size() > 0)
189 replaceText(text);
190 else {
191 // DisplayIO.getCurrentFrame().removeItem(text);
192 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
193 }
194 }
195 }
196
197 /**
198 * @param text
199 */
200 private static void justify(Text text) {
201 // Check if that text item is inside an enclosing rectangle...
202 // Set its max width accordingly
203 Polygon enclosure = FrameUtils.getEnlosingPolygon();
204 if (enclosure != null) {
205 Rectangle bounds = enclosure.getBounds();
206 text.setRightMargin(bounds.x + bounds.width);
207 } else {
208 text.setRightMargin(FrameGraphics.getMaxFrameSize().width);
209 }
210 }
211
212 public static Text replaceDot(Item dot, char ch) {
213 Text text = createText(ch);
214 Item.DuplicateItem(dot, text);
215 FrameUtils.setLastEdited(text);
216
217 // Copy the lines list so it can be modified
218 List<Line> lines = new LinkedList<Line>(dot.getLines());
219 for (Line line : lines)
220 line.replaceLineEnd(dot, text);
221 Frame current = dot.getParentOrCurrentFrame();
222 current.removeItem(dot);
223 ItemUtils.EnclosedCheck(current.getItems());
224 return text;
225 }
226
227 /**
228 * Replaces the given text item with a dot
229 */
230 public static Item replaceText(Item text) {
231 Item dot = new Dot(text.getX(), text.getY(), text.getID());
232 Item.DuplicateItem(text, dot);
233
234 List<Line> lines = new LinkedList<Line>();
235 lines.addAll(text.getLines());
236 if (lines.size() > 0)
237 dot.setColor(lines.get(0).getColor());
238 for (Line line : lines) {
239 line.replaceLineEnd(text, dot);
240 }
241 text.delete();
242 Frame current = text.getParentOrCurrentFrame();
243 current.addItem(dot);
244 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
245 ItemUtils.EnclosedCheck(current.getItems());
246 return dot;
247 }
248
249 /**
250 * Creates a new Text Item whose text contains the given character. This
251 * method also moves the mouse cursor to be pointing at the newly created
252 * Text Item ready to insert the next character.
253 *
254 * @param start
255 * The character to use as the initial text of this Item.
256 * @return The newly created Text Item
257 */
258 private static Text createText(char start) {
259 Text t = DisplayIO.getCurrentFrame().createBlankText("" + start);
260
261 Point2D.Float newMouse = t.insertChar(start, DisplayIO.getMouseX(),
262 FrameMouseActions.getY());
263 DisplayIO.setCursorPosition(newMouse.x, newMouse.y, false);
264
265 return t;
266 }
267
268 /**
269 * Creates a new Text Item with no text. The newly created Item is a copy of
270 * any ItemTemplate if one is present, and inherits all the attributes of
271 * the Template
272 *
273 * @return The newly created Text Item
274 */
275 private static Text createText() {
276 return DisplayIO.getCurrentFrame().createNewText();
277 }
278
279 private void move(int direction, boolean isShiftDown) {
280 Item on = FrameUtils.getCurrentItem();
281
282 if (on == null) {
283 navigateFrame(direction);
284 return;
285 }
286
287 if (on instanceof Text) {
288 Text text = (Text) on;
289 // When the user hits the left and right button with mouse
290 // positions over the the frame name navigation occurs
291 if (text.isFrameName()) {
292 navigateFrame(direction);
293 return;
294 } else {
295 FrameUtils.setLastEdited(text);
296 if (!isShiftDown)
297 text.clearSelection();
298 DisplayIO.setTextCursor(text, direction, false, isShiftDown);
299 }
300 }
301 }
302
303 private void navigateFrame(int direction) {
304 switch (direction) {
305 case Text.RIGHT:
306 case Text.PAGE_UP:
307 NavigationActions.NextFrame(false);
308 break;
309 case Text.LEFT:
310 case Text.PAGE_DOWN:
311 NavigationActions.PreviousFrame(false);
312 break;
313 case Text.HOME:
314 case Text.LINE_HOME:
315 NavigationActions.ZeroFrame();
316 break;
317 case Text.END:
318 case Text.LINE_END:
319 NavigationActions.LastFrame();
320 break;
321 }
322 }
323
324 /**
325 * Receives and processes any Function, Control, and Escape key presses
326 *
327 * @param e
328 * The KeyEvent received from the keyboard
329 */
330 public void keyPressed(KeyEvent e) {
331 int keyCode = e.getKeyCode();
332
333 if(keyCode != KeyEvent.VK_F1 && keyCode != KeyEvent.VK_F2){
334 resetEnclosedItems();
335 }
336
337 SessionStats.AddFrameEvent("k" + KeyEvent.getKeyText(keyCode));
338
339 FrameUtils.ResponseTimer.restart();
340 // e.consume();
341
342 if (Actions.isAgentRunning()) {
343 if (keyCode == KeyEvent.VK_ESCAPE)
344 Actions.stopAgent();
345 else
346 Actions.interruptAgent();
347 return;
348 } else if (Simple.consumeKeyboardInput()) {
349 return;
350 }
351
352 if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12) {
353 functionKey(FunctionKey.values()[keyCode - KeyEvent.VK_F1 + 1], e
354 .isShiftDown(), e.isControlDown());
355 return;
356 } else if (e.isAltDown()) {
357 int distance = e.isShiftDown() ? 1 : 20;
358 switch (keyCode) {
359 case KeyEvent.VK_1:
360 FrameMouseActions.leftButton();
361 // DisplayIO.clickMouse(InputEvent.BUTTON1_MASK);
362 break;
363 case KeyEvent.VK_2:
364 // DisplayIO.clickMouse(InputEvent.BUTTON2_MASK);
365 FrameMouseActions.middleButton();
366 break;
367 case KeyEvent.VK_3:
368 // DisplayIO.clickMouse(InputEvent.BUTTON3_MASK);
369 FrameMouseActions.rightButton();
370 break;
371 case KeyEvent.VK_LEFT:
372 DisplayIO.translateCursor(-distance, 0);
373 break;
374 case KeyEvent.VK_RIGHT:
375 DisplayIO.translateCursor(distance, 0);
376 break;
377 case KeyEvent.VK_UP:
378 DisplayIO.translateCursor(0, -distance);
379 break;
380 case KeyEvent.VK_DOWN:
381 DisplayIO.translateCursor(0, distance);
382 break;
383 }
384 return;
385 } else if (e.isControlDown()) {
386 if (keyCode == KeyEvent.VK_CONTROL) {
387 FrameMouseActions.control(e);
388 } else {
389 controlChar(e.getKeyCode(), e.isShiftDown());
390 }
391 return;
392 }
393
394 switch (keyCode) {
395 case KeyEvent.VK_ESCAPE:
396 // Do escape after control so Ctl+Escape does not perform DropDown
397 functionKey(FunctionKey.DropDown, e.isShiftDown(), e
398 .isControlDown());
399 SessionStats.Escape();
400 break;
401 case KeyEvent.VK_LEFT:
402 move(Text.LEFT, e.isShiftDown());
403 break;
404 case KeyEvent.VK_RIGHT:
405 move(Text.RIGHT, e.isShiftDown());
406 break;
407 case KeyEvent.VK_PAGE_DOWN:
408 navigateFrame(Text.PAGE_DOWN);
409 break;
410 case KeyEvent.VK_PAGE_UP:
411 navigateFrame(Text.PAGE_UP);
412 break;
413 case KeyEvent.VK_UP:
414 if (e.isControlDown()) {
415 NextTextItem(FrameUtils.getCurrentItem(), false);
416 } else {
417 move(Text.UP, e.isShiftDown());
418 }
419 break;
420 case KeyEvent.VK_DOWN:
421 if (e.isControlDown()) {
422 NextTextItem(FrameUtils.getCurrentItem(), true);
423 } else {
424 move(Text.DOWN, e.isShiftDown());
425 }
426 break;
427 case KeyEvent.VK_END:
428 if (e.isControlDown())
429 move(Text.END, e.isShiftDown());
430 else
431 move(Text.LINE_END, e.isShiftDown());
432 break;
433 case KeyEvent.VK_HOME:
434 if (e.isControlDown())
435 move(Text.HOME, e.isShiftDown());
436 else
437 move(Text.LINE_HOME, e.isShiftDown());
438 break;
439 // TODO remove this when upgrading Java
440 // This is a patch because Java6 wont trigger KeyTyped event for
441 // Shift+Tab
442 case KeyEvent.VK_TAB:
443 if (e.isShiftDown()) {
444 e.setKeyChar('\t');
445 keyTyped(e);
446 }
447 break;
448 }
449 }
450
451 /**
452 * Moves the cursor to the next text item on the frame
453 *
454 * @param currentItem
455 * @param direction
456 * move up if direction is negative, down if direction is
457 * positive
458 */
459 public static void NextTextItem(Item currentItem, boolean down) {
460 // Move the cursor to the next text item
461 Frame current = DisplayIO.getCurrentFrame();
462 Item title = current.getTitleItem();
463
464 Collection<Text> currentItems = FrameUtils.getCurrentTextItems();
465 List<Text> textItems = new ArrayList<Text>();
466 // Move to the next text item in the box if
467 if (currentItems.contains(currentItem)) {
468 textItems.addAll(currentItems);
469 } else {
470 textItems.add(current.getTitleItem());
471 textItems.addAll(current.getBodyTextItems(true));
472 }
473
474 Collections.sort(textItems);
475
476 if (textItems.size() == 0) {
477 // If there are no text items on the frame its a NoOp
478 if (title == null)
479 return;
480 DisplayIO.MoveCursorToEndOfItem(title);
481 FrameGraphics.Repaint();
482 return;
483 }
484
485 // If the user is mouse wheeling in free space...
486 if (currentItem == null) {
487 // find the nearest item in the correct direction
488 int currY = FrameMouseActions.getY();
489 for (int i = 0; i < textItems.size(); i++) {
490 Item t = textItems.get(i);
491 if (currY < t.getY()) {
492 if (down) {
493 DisplayIO.MoveCursorToEndOfItem(t);
494 } else {
495 if (i == 0) {
496 DisplayIO.MoveCursorToEndOfItem(current
497 .getTitleItem());
498 } else {
499 DisplayIO.MoveCursorToEndOfItem(textItems
500 .get(i - 1));
501 }
502 }
503 FrameGraphics.Repaint();
504 return;
505 }
506 }
507 // If we are at the botton of the screen and the user scrolls down
508 // then scroll backup to the title
509 if (textItems.size() > 0) {
510 DisplayIO.MoveCursorToEndOfItem(textItems
511 .get(textItems.size() - 1));
512 }
513 return;
514 }
515
516 // Find the current item... then move to the next item
517 int i = textItems.indexOf(currentItem);
518
519 int nextIndex = i + (down ? 1 : -1);
520 if (nextIndex >= 0 && nextIndex < textItems.size()) {
521 DisplayIO.MoveCursorToEndOfItem(textItems.get(nextIndex));
522 } else {
523 DisplayIO.MoveCursorToEndOfItem(currentItem);
524 }
525 return;
526
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 LONG_DATE_FORMAT = "ddMMMyyyy[HH:mm]";
1117
1118 /**
1119 * Performs the dropping action: If the cursor is in free space then: the
1120 * cursor is repositioned below the last non-annotation text item. If the
1121 * cursor is on an item, and has items attached then: the cusor is
1122 * positioned below the pointed to item, and the items below are 'pushed
1123 * down' to make room.
1124 *
1125 * @param toDropFrom
1126 * The Item being pointed at by the mouse, may be null to
1127 * indicate the cursor is in free space.
1128 */
1129 public static boolean Drop(Item toDropFrom, boolean bPasting) {
1130 try {
1131 FrameUtils.setLastEdited(null);
1132
1133 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1134
1135 // if a line is being rubber-banded, this is a no-op
1136 if (Frame.rubberbandingLine())
1137 return false; // No-op
1138
1139 // if the cursor is in free space then the drop will happen from the
1140 // last non annotation text item on the frame
1141 if (toDropFrom == null) {
1142 toDropFrom = DisplayIO.getCurrentFrame()
1143 .getLastNonAnnotationTextItem();
1144 }
1145
1146 // if no item was found, return
1147 if (toDropFrom == null) {
1148 MessageBay.errorMessage("No item could be found to drop from");
1149 return false;
1150 }
1151
1152 if (!(toDropFrom instanceof Text)) {
1153 MessageBay
1154 .displayMessage("Only text items can be dropped from");
1155 return false;
1156 }
1157
1158 // Get the list of items that must be dropped
1159 List<Text> column = DisplayIO.getCurrentFrame().getColumn(
1160 toDropFrom);
1161
1162 if (column == null) {
1163 MessageBay.errorMessage("No column found to align items to");
1164 return false;
1165 }
1166
1167 Item title = DisplayIO.getCurrentFrame().getTitleItem();
1168
1169 // We wont do auto bulleting when dropping from titles
1170 if (!bPasting && toDropFrom != title) {
1171 newItemText = getAutoBullet(((Text) toDropFrom).getFirstLine());
1172 }
1173
1174 Text dummyItem = null;
1175 if (!bPasting && FreeItems.textOnlyAttachedToCursor()) {
1176 dummyItem = (Text) FreeItems.getItemAttachedToCursor();
1177 String autoBullet = getAutoBullet(dummyItem.getText());
1178
1179 if (autoBullet.length() > 0)
1180 newItemText = "";
1181 dummyItem.setText(newItemText + dummyItem.getText());
1182 }
1183
1184 dummyItem = createText();
1185 if (FreeItems.textOnlyAttachedToCursor()) {
1186 Text t = (Text) FreeItems.getItemAttachedToCursor();
1187 dummyItem.setSize(t.getSize());
1188 int lines = t.getTextList().size();
1189 for (int i = 0; i < lines; i++) {
1190 newItemText += '\n';
1191 }
1192 }
1193
1194 dummyItem.setText(newItemText);
1195
1196 // If the only item on the frame is the title and the frame name
1197 // goto the zero frame and drop to the @start if there is one
1198 // or a fixed amount if there is not
1199 if (column.size() == 0) {
1200 Frame current = DisplayIO.getCurrentFrame();
1201 //Item itemTemplate = current.getItemTemplate();
1202 int xPos = title.getX() + FrameCreator.INDENT_FROM_TITLE;
1203 int yPos = FrameCreator.getYStart(title);
1204 // Check for @start on the zero frame
1205 Frame zero = FrameIO.LoadFrame(current.getFramesetName() + '0');
1206 Text start = zero.getAnnotation("start");
1207 if (start != null) {
1208 xPos = start.getX();
1209 yPos = start.getY();
1210 }
1211
1212 dummyItem.setPosition(xPos, yPos);
1213 DisplayIO.setCursorPosition(xPos, yPos);
1214 } else {
1215 int yPos = column.get(0).getY() + 1;
1216 // Either position the new item below the title or just above
1217 // the first item below the title
1218 if (toDropFrom == title)
1219 yPos = Math.min(column.get(0).getY() - 1, title.getY()
1220 + title.getBoundsHeight()
1221 + dummyItem.getBoundsHeight());
1222 dummyItem.setPosition(column.get(0).getX(), yPos);
1223 column.add(dummyItem);
1224 FrameUtils.Align(column, false, 0);
1225 // Check if it will be outside the frame area
1226 if (dummyItem.getY() < 0
1227 || dummyItem.getY() > FrameGraphics.getMaxFrameSize()
1228 .getHeight()) {
1229 // Check for the 'next' tag!
1230 Frame current = DisplayIO.getCurrentFrame();
1231 Item next = current.getAnnotation("next");
1232 Item prev = current.getAnnotation("previous");
1233 // Check for an unlinked next tag
1234 if ((next != null && !next.hasLink())
1235 || (prev != null && prev.hasLink())) {
1236 Frame firstFrame = current;
1237 if (next != null)
1238 next.delete();
1239 FrameCreator frameCreator = new FrameCreator(null);
1240 // Add the next button
1241 next = frameCreator.addNextButton(current, null);
1242
1243 // Create the new frame linked to the next tag
1244 boolean mouseMoved = FrameMouseActions.tdfc(next);
1245 Frame moreFrame = DisplayIO.getCurrentFrame();
1246
1247 // Add previous button to the new frame
1248 frameCreator.addPreviousButton(moreFrame, firstFrame
1249 .getName());
1250 Item first = current.getAnnotation("first");
1251 if (first != null) {
1252 frameCreator.addFirstButton(moreFrame, first
1253 .getLink());
1254 } else {
1255 frameCreator.addFirstButton(moreFrame, firstFrame
1256 .getName());
1257 }
1258 // Add the @next if we are pasting
1259 // if (bPasting) {
1260 // Item copy = next.copy();
1261 // copy.setLink(null);
1262 // moreFrame.addItem(copy);
1263 // }
1264
1265 moreFrame.setTitle(firstFrame.getTitleItem().getText());
1266 // need to move the mouse to the top of the frame if
1267 // there wasnt an @start on it
1268 if (!mouseMoved) {
1269 Item moreTitle = moreFrame.getTitleItem();
1270 moreTitle.setPermission(Permission.full);
1271 Drop(moreTitle, bPasting);
1272 }
1273 // Add the bullet text to the item
1274 dummyItem.setPosition(DisplayIO.getMouseX(),
1275 FrameMouseActions.getY());
1276 } else {
1277 MessageBay
1278 .warningMessage("Can not create items outside the frame area");
1279 // ensures correct repainting when items don't move
1280 DisplayIO.setCursorPosition(DisplayIO.getMouseX(),
1281 FrameMouseActions.getY());
1282 return false;
1283 }
1284 }
1285 if (!FreeItems.textOnlyAttachedToCursor()
1286 && !dummyItem.isEmpty()) {
1287 DisplayIO.getCurrentFrame().addItem(dummyItem);
1288 }
1289
1290 // Move the item to the cursor position
1291 if (FreeItems.itemsAttachedToCursor()) {
1292 DisplayIO.setCursorPosition(dummyItem.getX(), dummyItem
1293 .getY());
1294 Item firstItem = FreeItems.getItemAttachedToCursor();
1295 int deltaX = firstItem.getX() - dummyItem.getX();
1296 int deltaY = firstItem.getY() - dummyItem.getY();
1297
1298 for (Item i : FreeItems.getInstance()) {
1299 i.setPosition(i.getX() - deltaX, i.getY() - deltaY);
1300 }
1301 } else {
1302 DisplayIO.MoveCursorToEndOfItem(dummyItem);
1303 }
1304 }
1305 if (dummyItem.getText().length() == 0
1306 || FreeItems.itemsAttachedToCursor()) {
1307 dummyItem.getParentOrCurrentFrame().removeItem(dummyItem);
1308 dummyItem.setRightMargin(FrameGraphics.getMaxFrameSize().width);
1309 } else {
1310 dummyItem.setWidth(toDropFrom.getWidth());
1311 }
1312
1313 DisplayIO.resetCursorOffset();
1314 FrameGraphics.Repaint();
1315 } catch (RuntimeException e) {
1316 // MessageBay.errorMessage(e.getMessage());
1317 e.printStackTrace();
1318 return false;
1319 }
1320 return true;
1321 }
1322
1323 /**
1324 * Gets the next letter sequence for a given string to be used in auto
1325 * lettering.
1326 *
1327 * @param s
1328 * a sequence of letters
1329 * @return the next sequence of letters
1330 */
1331 static private String nextLetterSequence(String s) {
1332 if (s.length() > 1)
1333 return s;
1334
1335 if (s.equals("z"))
1336 return "a";
1337
1338 return (char) ((int) s.charAt(0) + 1) + "";
1339 }
1340
1341 public static String getBullet(String s) {
1342 return getBullet(s, false);
1343 }
1344
1345 public static String getAutoBullet(String s) {
1346 return getBullet(s, true);
1347 }
1348
1349 private static String getBullet(String s, boolean nextBullet) {
1350 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1351
1352 if (s == null)
1353 return newItemText;
1354 /*
1355 * Item i = ItemUtils.FindTag(DisplayIO.getCurrentFrame().getItems(),
1356 * "@NoAutoBullets"); if (i != null) return newItemText;
1357 */
1358 // Separate the space at the start of the text item
1359 String preceedingSpace = "";
1360 for (int i = 0; i < s.length(); i++) {
1361 if (!Character.isSpaceChar(s.charAt(i))) {
1362 preceedingSpace = s.substring(0, i);
1363 s = s.substring(i);
1364 break;
1365 }
1366 }
1367
1368 // figure out the type of the text item
1369 // This allows us to do auto bulleting
1370 if (s != null && s.length() > 1) {
1371 // First check for text beginning with * @ # etc
1372 // These are simple auto bullets
1373 if (!Character.isLetterOrDigit(s.charAt(0))
1374 && !Character.isSpaceChar(s.charAt(0))) {
1375 if (Text.isBulletChar(s.charAt(0))) {
1376 int nonSpaceIndex = 1;
1377 // Find the end of the bullet and space after the bullet
1378 while (nonSpaceIndex < s.length()
1379 && s.charAt(nonSpaceIndex) == ' ') {
1380 nonSpaceIndex++;
1381 }
1382 // we must have a special char followed by >= 1 space
1383 if (nonSpaceIndex > 1)
1384 newItemText = s.substring(0, nonSpaceIndex);
1385 }
1386 // Auto numbering and lettering
1387 } else {
1388 if (Character.isDigit(s.charAt(0))) {
1389 newItemText = getAutoNumber(s, nextBullet);
1390 // Auto lettering
1391 } else if (Character.isLetter(s.charAt(0))) {
1392 newItemText = getAutoLetter(s, nextBullet);
1393 }
1394 }
1395 }
1396 return preceedingSpace + newItemText;
1397 }
1398
1399 private static boolean isAutoNumberOrLetterChar(char c) {
1400 return c == ':' || c == '-' || c == '.' || c == ')' || c == '>';
1401 }
1402
1403 /**
1404 * Gets the string to be used to start the next auto numbered text item.
1405 *
1406 * @param s
1407 * the previous text item
1408 * @return the beginning of the next auto numbered text item
1409 */
1410 private static String getAutoNumber(String s, boolean nextBullet) {
1411 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1412
1413 int nonDigitIndex = 1;
1414 while (Character.isDigit(s.charAt(nonDigitIndex))) {
1415 nonDigitIndex++;
1416
1417 if (nonDigitIndex + 1 >= s.length())
1418 return DEFAULT_NEW_ITEM_TEXT;
1419 }
1420
1421 if (isAutoNumberOrLetterChar(s.charAt(nonDigitIndex))) {
1422
1423 // we must have a number followed one non letter
1424 // then one or more spaces
1425 int nonSpaceIndex = nonDigitIndex + 1;
1426 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1427 nonSpaceIndex++;
1428 }
1429
1430 if (nonSpaceIndex > nonDigitIndex + 1) {
1431 if (nextBullet)
1432 newItemText = (Integer.parseInt(s.substring(0,
1433 nonDigitIndex)) + 1)
1434 + s.substring(nonDigitIndex, nonSpaceIndex);
1435 else
1436 newItemText = s.substring(0, nonSpaceIndex);
1437 }
1438 }
1439 return newItemText;
1440 }
1441
1442 /**
1443 * Gets the string to be used to start the next auto lettered text item.
1444 *
1445 * @param s
1446 * the previous text items
1447 * @return the initial text for the new text item
1448 */
1449 private static String getAutoLetter(String s, boolean nextBullet) {
1450 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1451
1452 int nonLetterIndex = 1;
1453
1454 if (isAutoNumberOrLetterChar(s.charAt(nonLetterIndex))) {
1455
1456 // Now search for the next non space character
1457 int nonSpaceIndex = nonLetterIndex + 1;
1458 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1459 nonSpaceIndex++;
1460 }
1461
1462 // If there was a space then we have reached the end of our auto
1463 // text
1464 if (nonSpaceIndex > nonLetterIndex + 1) {
1465 if (nextBullet)
1466 newItemText = nextLetterSequence(s.substring(0,
1467 nonLetterIndex))
1468 + s.substring(nonLetterIndex, nonSpaceIndex);
1469 else
1470 newItemText = s.substring(0, nonSpaceIndex);
1471 }
1472 }
1473 return newItemText;
1474 }
1475
1476 /**
1477 * Adjusts the size of the given Item, by the given amount. Note: The amount
1478 * is relative and can be positive or negative.
1479 *
1480 * @param toSet
1481 * The Item whose size is to be adjusted
1482 * @param diff
1483 * The amount to adjust the Item's size by
1484 * @param moveCursor
1485 * true if the cursor position should be automatically adjusted
1486 * with resizing
1487 */
1488 public static void SetSize(Item item, int diff, boolean moveCursor,
1489 boolean insideEnclosure) {
1490 Collection<Item> toSize = new HashSet<Item>();
1491 // the mouse is only moved when the Item is on the frame, not free
1492 // boolean moveMouse = false;
1493 Item toSet = null;
1494
1495 // if the user is not pointing to any item
1496 if (item == null) {
1497 if (FreeItems.itemsAttachedToCursor())
1498 toSize.addAll(FreeItems.getInstance());
1499 else {
1500 MessageBay
1501 .displayMessage("There are no Items selected on the Frame or on the Cursor");
1502 return;
1503 }
1504 } else {
1505 if (item.isFrameName()) {
1506 MessageBay.displayMessage("Can not resize the frame name");
1507 return;
1508 }
1509 // check permissions
1510 if (!item.hasPermission(Permission.full)) {
1511 MessageBay
1512 .displayMessage("Insufficient permission to change the size of that item");
1513 return;
1514 }
1515 toSet = item;
1516 // For resizing enclosures pick up everything that is attached to
1517 // items partly in the enclosure
1518 // TODO make this only pick up stuff COMPLETELY enclosed... if we
1519 // change copying to copy only the stuff completely enclosed
1520 if (insideEnclosure) {
1521 if (_enclosedItems == null) {
1522 for (Item i : FrameUtils.getCurrentItems(toSet)) {
1523 if (i.hasPermission(Permission.full)
1524 && !toSize.contains(i))
1525 toSize.addAll(i.getAllConnected());
1526 }
1527 _enclosedItems = toSize;
1528 } else {
1529 toSize = _enclosedItems;
1530 }
1531
1532 }// Enclosed circle centers are resized with the center as origin
1533 // Just add the circle center to the list of items to size
1534 else if (!toSet.hasEnclosures() && !(toSet instanceof Text)
1535 && toSet.isLineEnd()) {
1536 toSize.addAll(toSet.getLines());
1537 } else if (toSet instanceof Line) {
1538 Line line = (Line) toSet;
1539 float current = Math.abs(line.getThickness());
1540 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
1541 line.getStartItem().setThickness(current);
1542 FrameGraphics.Repaint();
1543 return;
1544 } else {
1545 toSize.add(toSet);
1546 }
1547 }
1548
1549 Point2D origin = new Point2D.Float(FrameMouseActions.MouseX,
1550 FrameMouseActions.MouseY);
1551 // Inside enclosures increase the size of the enclosure
1552 double ratio = (100.0 + diff * 2) / 100.0;
1553 if (insideEnclosure) {
1554 Collection<Item> done = new HashSet<Item>();
1555 // adjust the size of all the items
1556 for (Item i : toSize) {
1557 if (done.contains(i))
1558 continue;
1559
1560 if (i.isLineEnd()) {
1561 Collection<Item> allConnected = i.getAllConnected();
1562 done.addAll(allConnected);
1563 for (Item it : allConnected) {
1564 it.translate(origin, ratio);
1565 it
1566 .setArrowheadLength((float) (it
1567 .getArrowheadLength() * ratio));
1568 }
1569 i.setThickness((float) (i.getThickness() * ratio));
1570 } else if (i instanceof XRayable) {
1571 i.translate(origin, ratio);
1572 i.setThickness((float) (i.getThickness() * ratio));
1573 done.add(i);
1574 } else if (i instanceof Text) {
1575 i.translate(origin, ratio);
1576 i.setSize((float) (i.getSize() * ratio));
1577 done.add(i);
1578 }
1579 }
1580 FrameGraphics.refresh(true);
1581 return;
1582 }
1583
1584 // adjust the size of all the items
1585 for (Item i : toSize) {
1586 // Lines and dots use thickness, not size
1587 if (i.hasEnclosures()) {
1588 Circle c = (Circle) i.getEnclosures().iterator().next();
1589 c.setSize(c.getSize() * (float) ratio);
1590 } else if (i instanceof Line || i instanceof Circle
1591 && !insideEnclosure) {
1592 float current = Math.abs(i.getThickness());
1593 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
1594 i.setThickness(current);
1595 } else if (i instanceof Dot) {
1596 Item dot = (Item) i;
1597 float current = Math.abs(dot.getThickness());
1598 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
1599 dot.setThickness(current);
1600 } else {
1601 float oldSize = Math.abs(i.getSize());
1602 float newSize = Math
1603 .max(oldSize + diff, Item.MINIMUM_THICKNESS);
1604 float resizeRatio = newSize / oldSize;
1605 // Set size for Picture also translates
1606 i.setSize(newSize);
1607 if (i instanceof Text && i.getSize() != oldSize) {
1608 i.translate(origin, resizeRatio);
1609 if (i.isLineEnd()) {
1610 i.setPosition(i.getPosition());
1611 }
1612 }
1613 }
1614 }
1615
1616 if (toSet != null)
1617 toSet.getParent().setChanged(true);
1618
1619 FrameGraphics.refresh(true);
1620 }
1621
1622 private static void SetFillColor(Item item, boolean setTransparent) {
1623 if (item == null)
1624 return;
1625
1626 if (!item.hasPermission(Permission.full)) {
1627 MessageBay
1628 .displayMessage("Insufficient permission to change fill color");
1629 return;
1630 }
1631
1632 Item toSet = item;
1633 Color color = toSet.getFillColor();
1634 if (setTransparent)
1635 color = null;
1636 else
1637 color = ColorUtils.getNextColor(color, Item.FILL_COLOR_WHEEL, toSet
1638 .getGradientColor());
1639
1640 // if (color == null) {
1641 // MessageBay.displayMessage("FillColor is now transparent");
1642 // }
1643
1644 toSet.setFillColor(color);
1645 toSet.getParent().setChanged(true);
1646
1647 FrameGraphics.Repaint();
1648 }
1649
1650 private static void SetGradientColor(Item item, boolean setTransparent) {
1651 if (item == null)
1652 return;
1653
1654 if (!item.hasPermission(Permission.full)) {
1655 MessageBay
1656 .displayMessage("Insufficient permission to change gradient color");
1657 return;
1658 }
1659
1660 Item toSet = item;
1661 Color color = toSet.getGradientColor();
1662 if (setTransparent)
1663 color = null;
1664 else
1665 color = ColorUtils.getNextColor(color, Item.COLOR_WHEEL, toSet
1666 .getFillColor());
1667
1668 // if (color == null) {
1669 // MessageBay.displayMessage("FillColor is now transparent");
1670 // }
1671
1672 toSet.setGradientColor(color);
1673 toSet.getParent().setChanged(true);
1674
1675 FrameGraphics.Repaint();
1676 }
1677
1678 /**
1679 * Sets the colour of the current Item based on its current colour. The
1680 * colours proceed in the order stored in COLOR_WHEEL.
1681 *
1682 * @param toSet
1683 * The Item whose colour is to be changed
1684 */
1685 private static void SetColor(Item item, boolean setTransparent,
1686 boolean setBackgroundColor) {
1687 // first determine the next color
1688 Color color = null;
1689 Frame currentFrame = DisplayIO.getCurrentFrame();
1690 if (item == null) {
1691 if (FreeItems.itemsAttachedToCursor()) {
1692 color = FreeItems.getInstance().get(0).getColor();
1693 } else {
1694 return;
1695 }
1696 // change the background color if the user is pointing on the
1697 // frame name
1698 } else if (item == currentFrame.getNameItem()) {
1699 // check permissions
1700 if (!item.hasPermission(Permission.full)) {
1701 MessageBay
1702 .displayMessage("Insufficient permission to the frame's background color");
1703 return;
1704 }
1705 if (setTransparent)
1706 currentFrame.setBackgroundColor(null);
1707 else
1708 currentFrame.toggleBackgroundColor();
1709 // Display a message if the color has changed to transparent
1710 // if (currentFrame.getBackgroundColor() == null)
1711 // FrameGraphics
1712 // .displayMessage("Background color is now transparent");
1713 FrameGraphics.Repaint();
1714 return;
1715 } else {
1716 // check permissions
1717 if (!item.hasPermission(Permission.full)) {
1718 MessageBay
1719 .displayMessage("Insufficient permission to change that item's color");
1720 return;
1721 }
1722 // Toggling color of circle center changes the circle fill color
1723 if (item.hasEnclosures()) {
1724 if (setBackgroundColor) {
1725 SetGradientColor(item.getEnclosures().iterator().next(),
1726 setTransparent);
1727 } else {
1728 SetFillColor(item.getEnclosures().iterator().next(),
1729 setTransparent);
1730 }
1731 } else if (setBackgroundColor) {
1732 color = item.getPaintBackgroundColor();
1733 } else {
1734 color = item.getPaintColor();
1735 }
1736 }
1737 if (setTransparent)
1738 color = null;
1739 else if (setBackgroundColor) {
1740 color = ColorUtils.getNextColor(color, Item.FILL_COLOR_WHEEL, item
1741 .getPaintColor());
1742 } else {
1743 color = ColorUtils.getNextColor(color, Item.COLOR_WHEEL,
1744 currentFrame.getPaintBackgroundColor());
1745 }
1746 // if (currentFrame.getPaintForegroundColor().equals(color))
1747 // color = null;
1748
1749 // if color is being set to default display a message to indicate that
1750 // if (color == null) {
1751 // MessageBay.displayMessage("Color is set to default");
1752 // }
1753
1754 if (setBackgroundColor) {
1755 if (item == null && FreeItems.itemsAttachedToCursor()) {
1756 for (Item i : FreeItems.getInstance())
1757 i.setBackgroundColor(color);
1758 } else {
1759 item.setBackgroundColor(color);
1760 item.getParent().setChanged(true);
1761 }
1762 } else {
1763 if (item == null && FreeItems.itemsAttachedToCursor()) {
1764 for (Item i : FreeItems.getInstance())
1765 i.setColor(color);
1766 } else {
1767 item.setColor(color);
1768 item.getParent().setChanged(true);
1769 }
1770 }
1771 FrameGraphics.Repaint();
1772 }
1773
1774 /**
1775 * Toggles the given Item's annotation status on\off.
1776 *
1777 * @param toToggle
1778 * The Item to toggle
1779 */
1780 private static void ToggleAnnotation(Item toToggle) {
1781 if (toToggle == null) {
1782 MessageBay.displayMessage("There is no Item selected to toggle");
1783 return;
1784 }
1785
1786 // check permissions
1787 if (!toToggle.hasPermission(Permission.full)) {
1788 MessageBay
1789 .displayMessage("Insufficient permission to toggle that item's annotation");
1790 return;
1791 }
1792 toToggle.setAnnotation(!toToggle.isAnnotation());
1793
1794 toToggle.getParent().setChanged(true);
1795 FrameGraphics.Repaint();
1796 }
1797
1798 /**
1799 * Toggles the face style of a text item
1800 *
1801 * @param toToggle
1802 * The Item to toggle
1803 */
1804 private static void ToggleFontStyle(Item toToggle) {
1805 if (toToggle == null) {
1806 MessageBay.displayMessage("There is no Item selected to toggle");
1807 return;
1808 }
1809
1810 // check permissions
1811 if (!toToggle.hasPermission(Permission.full)) {
1812 MessageBay
1813 .displayMessage("Insufficient permission to toggle that item's annotation");
1814 return;
1815 }
1816
1817 if (toToggle instanceof Text) {
1818 Text text = (Text) toToggle;
1819 text.toggleFontStyle();
1820
1821 text.getParent().setChanged(true);
1822 FrameGraphics.Repaint();
1823 }
1824 }
1825
1826 /**
1827 * Toggles the face style of a text item
1828 *
1829 * @param toToggle
1830 * The Item to toggle
1831 */
1832 private static void ToggleFontFamily(Item toToggle) {
1833 if (toToggle == null) {
1834 MessageBay.displayMessage("There is no Item selected to toggle");
1835 return;
1836 }
1837
1838 // check permissions
1839 if (!toToggle.hasPermission(Permission.full)) {
1840 MessageBay
1841 .displayMessage("Insufficient permission to toggle that item's annotation");
1842 return;
1843 }
1844
1845 if (toToggle instanceof Text) {
1846 Text text = (Text) toToggle;
1847 text.toggleFontFamily();
1848
1849 text.getParent().setChanged(true);
1850 FrameGraphics.Repaint();
1851 }
1852 }
1853
1854 /**
1855 * If the given Item is null, then a new Text item is created with the
1856 * current date If the given Item is not null, then the current date is
1857 * prepended to the Item's text
1858 *
1859 * @param toAdd
1860 * The Item to prepend the date to, or null
1861 */
1862 private static void AddDate(Item toAdd) {
1863 String date1 = Logger.EasyDateFormat(LONG_DATE_FORMAT);
1864 String date2 = Logger.EasyDateFormat(SHORT_DATE_FORMAT);
1865 final String leftSeparator = " :";
1866 final String rightSeparator = ": ";
1867 String dateToAdd = date1 + rightSeparator;
1868 boolean prepend = false;
1869 boolean append = false;
1870
1871 // if the user is pointing at an item, add the date where ever the
1872 // cursor is pointing
1873 if (toAdd != null) {
1874 if (toAdd instanceof Text) {
1875 // permission check
1876 if (!toAdd.hasPermission(Permission.full)) {
1877 MessageBay
1878 .displayMessage("Insufficicent permission to add the date to that item");
1879 return;
1880 }
1881
1882 Text textItem = (Text) toAdd;
1883
1884 String text = textItem.getText();
1885
1886 // check if the default date has already been put on this item
1887 if (text.startsWith(date1 + rightSeparator)) {
1888 textItem.removeText(date1 + rightSeparator);
1889 dateToAdd = date2 + rightSeparator;
1890 prepend = true;
1891 } else if (text.startsWith(date2 + rightSeparator)) {
1892 textItem.removeText(date2 + rightSeparator);
1893 dateToAdd = leftSeparator + date2;
1894 append = true;
1895 } else if (text.endsWith(leftSeparator + date2)) {
1896 textItem.removeEndText(leftSeparator + date2);
1897 append = true;
1898 dateToAdd = leftSeparator + date1;
1899 } else if (text.endsWith(leftSeparator + date1)) {
1900 textItem.removeEndText(leftSeparator + date1);
1901 if (textItem.getLength() > 0) {
1902 dateToAdd = "";
1903 prepend = true;
1904 } else {
1905 // use the default date format
1906 prepend = true;
1907 }
1908 }
1909
1910 if (prepend) {
1911 // add the date to the text item
1912 textItem.prependText(dateToAdd);
1913 if (dateToAdd.length() == textItem.getLength())
1914 DisplayIO.setCursorPosition(textItem
1915 .getParagraphEndPosition());
1916 } else if (append) {
1917 textItem.appendText(dateToAdd);
1918 if (dateToAdd.length() == textItem.getLength())
1919 DisplayIO.setCursorPosition(textItem.getPosition());
1920 } else {
1921 for (int i = 0; i < date1.length(); i++) {
1922 processChar(date1.charAt(i), false);
1923 }
1924 }
1925
1926 textItem.getParent().setChanged(true);
1927 FrameGraphics.Repaint();
1928 } else {
1929 MessageBay
1930 .displayMessage("Only text items can have the date prepended to them");
1931 }
1932 // otherwise, create a new text item
1933 } else {
1934 Text newText = createText();
1935 newText.setText(dateToAdd);
1936 DisplayIO.getCurrentFrame().addItem(newText);
1937 DisplayIO.getCurrentFrame().setChanged(true);
1938 FrameGraphics.Repaint();
1939
1940 DisplayIO.setCursorPosition(newText.getParagraphEndPosition());
1941 }
1942
1943 }
1944
1945 /**
1946 * Creates a new Frameset with the name given by the Item
1947 *
1948 * @param name
1949 */
1950 private static void CreateFrameset(Item item) {
1951 if (item == null) {
1952 MessageBay
1953 .displayMessage("There is no selected item to use for the frameset name");
1954 return;
1955 }
1956
1957 if (!(item instanceof Text)) {
1958 MessageBay
1959 .displayMessage("Framesets can only be created from text items");
1960 return;
1961 }
1962
1963 // dont create frameset if the item is linked
1964 if (item.getLink() != null) {
1965 MessageBay
1966 .displayMessage("A frameset can not be created from a linked item");
1967 return;
1968 }
1969
1970 // check permissions
1971 if (!item.hasPermission(Permission.full)) {
1972 MessageBay
1973 .displayMessage("Insufficient permission to create a frameset from this item");
1974 return;
1975 }
1976
1977 Text text = (Text) item;
1978 try {
1979 // create the new frameset
1980 Frame linkTo = FrameIO.CreateNewFrameset(text.getFirstLine());
1981 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1982 text.setLink(linkTo.getName());
1983 text.getParent().setChanged(true);
1984 FrameUtils.DisplayFrame(linkTo, true);
1985 linkTo.moveMouseToDefaultLocation();
1986 // this needs to be done if the user doesnt move the mouse before
1987 // doing Tdfc while the cursor is set to the text cursor
1988 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1989 } catch (Exception e) {
1990 MessageBay.errorMessage(e.getMessage());
1991 }
1992 }
1993
1994 /**
1995 * Forces a re-parse and repaint of the current Frame.
1996 */
1997 public static void Refresh() {
1998 Frame currentFrame = DisplayIO.getCurrentFrame();
1999 if (FrameIO.isProfileFrame(currentFrame)) {
2000 // TODO ensure that users can not delete the first frame in a
2001 // frameset...
2002 // TODO handle the case when users manually delete the first frame
2003 // in a frameset from the filesystem
2004 Frame profile = FrameIO.LoadFrame(currentFrame.getFramesetName()
2005 + "1");
2006 assert (profile != null);
2007 FrameUtils.Parse(currentFrame);
2008 FrameUtils.ParseProfile(profile);
2009 } else {
2010 FrameUtils.Parse(currentFrame);
2011 }
2012 // Need to update the cursor for when text items change to @b pictures
2013 // etc and the text cursor is showing
2014 FrameMouseActions.updateCursor();
2015 FrameMouseActions.getInstance().refreshHighlights();
2016 FrameGraphics.ForceRepaint();
2017 }
2018}
Note: See TracBrowser for help on using the repository browser.