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

Last change on this file since 1077 was 1077, checked in by davidb, 8 years ago

Force reload added to F12 when control is down

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