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

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

Added HTML styles

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