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

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

Added @autoFormat tag

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