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

Last change on this file since 941 was 941, checked in by bln4, 9 years ago

Altered zoomFrame to be public

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