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

Last change on this file since 928 was 928, checked in by bln4, 10 years ago

Altered method processChar to pass responsibility off to MagneticConstraints.keyHit if the character in question is registered by MagneticConstraints

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