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

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

The remaining of the implemention of Indirect keyboard and mouse commands

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