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

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