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

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

Implementation of IndirectMouseActions. Programmers are now able to intercept actions relating to mouse input events and make them act as they please.

Beginnings of IndirectKeyboardActions...

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