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
RevLine 
[919]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
[4]19package org.expeditee.gui;
20
21import java.awt.Color;
[147]22import java.awt.Rectangle;
[4]23import java.awt.Toolkit;
24import java.awt.datatransfer.StringSelection;
25import java.awt.event.KeyEvent;
26import java.awt.event.KeyListener;
[108]27import java.awt.geom.Point2D;
[286]28import java.text.NumberFormat;
[4]29import java.util.ArrayList;
[72]30import java.util.Collection;
[156]31import java.util.Collections;
[108]32import java.util.HashSet;
[4]33import java.util.LinkedList;
34import java.util.List;
35
36import org.expeditee.actions.Actions;
[306]37import org.expeditee.actions.Navigation;
[4]38import org.expeditee.actions.Simple;
[943]39import org.expeditee.gui.indirect.keyboard.IndirectKeyboardActions;
40import org.expeditee.gui.indirect.keyboard.KeyboardAction;
41import org.expeditee.gui.indirect.keyboard.KeyboardAction.KeyboardInfo;
[504]42import org.expeditee.io.ItemSelection;
[108]43import org.expeditee.items.Circle;
[4]44import org.expeditee.items.Dot;
45import org.expeditee.items.Item;
46import org.expeditee.items.ItemUtils;
47import org.expeditee.items.Line;
[778]48import org.expeditee.items.Text;
[454]49import org.expeditee.items.UserAppliedPermission;
[108]50import org.expeditee.items.XRayable;
[928]51import org.expeditee.items.MagneticConstraint.MagneticConstraints;
[626]52import org.expeditee.items.widgets.InteractiveWidget;
[229]53import org.expeditee.items.widgets.WidgetCorner;
[333]54import org.expeditee.items.widgets.WidgetEdge;
[778]55import org.expeditee.settings.templates.TemplateSettings;
[419]56import org.expeditee.stats.Formatter;
57import org.expeditee.stats.Logger;
[4]58import org.expeditee.stats.SessionStats;
59
60public class FrameKeyboardActions implements KeyListener {
[362]61
[345]62 private static FrameKeyboardActions _instance = new FrameKeyboardActions();
[362]63
[764]64 protected FrameKeyboardActions() {
[944]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;
[943]96 }
[944]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;
[943]105 }
[944]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 });
[345]236 }
[362]237
[345]238 public static FrameKeyboardActions getInstance() {
239 return _instance;
240 }
[4]241
[7]242 private static Text _toRemove = null;
243
[282]244 private static Collection<Item> _enclosedItems = null;
[286]245
[282]246 public static void resetEnclosedItems() {
247 _enclosedItems = null;
248 }
249
[4]250 public synchronized void keyTyped(KeyEvent e) {
[376]251
[86]252 if (Simple.isProgramRunning()) {
[108]253 if (e.isControlDown()
254 && (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.getKeyChar() == KeyEvent.VK_C)) {
[86]255 Simple.stop();
[108]256 return;
[87]257 } else if (e.isControlDown() && e.getKeyChar() == KeyEvent.VK_SPACE) {
[86]258 Simple.nextStatement();
[108]259 return;
260 } else {
261 Simple.KeyStroke(e.getKeyChar());
[86]262 }
263 if (Simple.consumeKeyboardInput())
264 return;
[4]265 }
266
267 // ignore escape character and control characters
268 if (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.isControlDown()) {
269 return;
270 }
271
[108]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 // }
[4]281 e.consume();
282 char ch = e.getKeyChar();
[78]283 // System.out.println(ch);
284
[4]285 if (e.isAltDown()) {
[87]286
[4]287 } else {
[80]288 processChar(ch, e.isShiftDown());
[4]289 }
290 // FrameGraphics.Repaint();
291 }
292
[80]293 public static void processChar(char ch, boolean isShiftDown) {
[306]294 Navigation.ResetLastAddToBack();
[7]295 Item on = FrameUtils.getCurrentItem();
[4]296
297 // permission check
[454]298 if (on != null && !on.hasPermission(UserAppliedPermission.full)) {
[121]299 MessageBay
300 .displayMessage("Insufficient permission to edit this item");
[4]301 return;
302 }
303
[944]304 if (isShiftDown && MagneticConstraints.getInstance().keyHit(-ch, on))
305 return;
306 else if (MagneticConstraints.getInstance().keyHit(ch, on))
307 return;
308
[7]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;
[4]317
318 // ignore delete and backspace if in free space
[108]319 if ((on == null || !(on instanceof Text))
[4]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
[229]326 if (on != null && on instanceof Dot && !(on instanceof WidgetCorner)) {
[50]327 if (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE) {
328 return;
329 }
[72]330 replaceDot((Item) on, ch);
[4]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
[7]340 Text text = (Text) on;
[4]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
[376]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() */) {
[4]360 // DisplayIO.UpdateTitle();
361 text = createText(ch);
[400]362 text.justify(false);
[154]363
[115]364 FrameUtils.setLastEdited(text);
[105]365 DisplayIO.setTextCursor(text, Text.NONE);
[4]366 return;
[376]367 } else {
368 FrameUtils.setLastEdited(text);
[4]369 }
370
[105]371 DisplayIO.setTextCursor(text, Text.NONE);
[944]372 IndirectKeyboardActions.getInstance().getInsertCharacterAction()
373 .exec(new KeyboardInfo(null, ch, isShiftDown, isShiftDown,
374 _enclosedItems, text, _enclosedItems, _enclosedItems), ch);
[400]375
[108]376 // This repaint is needed for WINDOWS only?!?!? Mike is not sure why!
377 if (ch == KeyEvent.VK_DELETE)
[147]378 FrameGraphics.requestRefresh(true);
[108]379
[4]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()) {
[7]386 _toRemove = text;
387
[133]388 if (text.hasAction())
[4]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 {
[7]395 // DisplayIO.getCurrentFrame().removeItem(text);
[4]396 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
397 }
398 }
399 }
400
[108]401 public static Text replaceDot(Item dot, char ch) {
402 Text text = createText(ch);
[72]403 Item.DuplicateItem(dot, text);
[115]404 FrameUtils.setLastEdited(text);
[4]405
[72]406 // Copy the lines list so it can be modified
[50]407 List<Line> lines = new LinkedList<Line>(dot.getLines());
[4]408 for (Line line : lines)
[97]409 line.replaceLineEnd(dot, text);
[74]410 Frame current = dot.getParentOrCurrentFrame();
[72]411 current.removeItem(dot);
412 ItemUtils.EnclosedCheck(current.getItems());
[50]413 return text;
[4]414 }
415
[72]416 /**
417 * Replaces the given text item with a dot
418 */
[78]419 public static Item replaceText(Item text) {
[72]420 Item dot = new Dot(text.getX(), text.getY(), text.getID());
[4]421 Item.DuplicateItem(text, dot);
422
423 List<Line> lines = new LinkedList<Line>();
424 lines.addAll(text.getLines());
[50]425 if (lines.size() > 0)
426 dot.setColor(lines.get(0).getColor());
[72]427 for (Line line : lines) {
[97]428 line.replaceLineEnd(text, dot);
[50]429 }
430 text.delete();
[74]431 Frame current = text.getParentOrCurrentFrame();
[72]432 current.addItem(dot);
[4]433 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
[72]434 ItemUtils.EnclosedCheck(current.getItems());
435 return dot;
[4]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) {
[944]448 return IndirectKeyboardActions.getInstance().getCreateNewTextAction()
449 .exec(new KeyboardInfo(null, start, false, false, _enclosedItems, _toRemove, _enclosedItems, _enclosedItems), start);
[4]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
[376]463 private void move(int direction, boolean isShiftDown, boolean isCtrlDown) {
[105]464 Item on = FrameUtils.getCurrentItem();
[4]465
[105]466 if (on == null) {
[13]467 navigateFrame(direction);
[4]468 return;
469 }
470
471 if (on instanceof Text) {
[176]472 Text text = (Text) on;
[4]473 // When the user hits the left and right button with mouse
474 // positions over the the frame name navigation occurs
[176]475 if (text.isFrameName()) {
[13]476 navigateFrame(direction);
[4]477 return;
478 } else {
[176]479 FrameUtils.setLastEdited(text);
[376]480 DisplayIO.setTextCursor(text, direction, false, isShiftDown,
481 isCtrlDown, true);
[4]482 }
483 }
484 }
485
[13]486 private void navigateFrame(int direction) {
[19]487 switch (direction) {
[13]488 case Text.RIGHT:
489 case Text.PAGE_UP:
[306]490 Navigation.NextFrame(false);
[13]491 break;
492 case Text.LEFT:
493 case Text.PAGE_DOWN:
[306]494 Navigation.PreviousFrame(false);
[13]495 break;
496 case Text.HOME:
[154]497 case Text.LINE_HOME:
[306]498 Navigation.ZeroFrame();
[13]499 break;
500 case Text.END:
[154]501 case Text.LINE_END:
[306]502 Navigation.LastFrame();
[13]503 break;
504 }
505 }
506
[4]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) {
[145]514 int keyCode = e.getKeyCode();
[286]515
516 if (keyCode != KeyEvent.VK_F1 && keyCode != KeyEvent.VK_F2) {
[282]517 resetEnclosedItems();
518 }
[286]519
[145]520 SessionStats.AddFrameEvent("k" + KeyEvent.getKeyText(keyCode));
[4]521
522 FrameUtils.ResponseTimer.restart();
[147]523 // e.consume();
[4]524
525 if (Actions.isAgentRunning()) {
[145]526 if (keyCode == KeyEvent.VK_ESCAPE)
[4]527 Actions.stopAgent();
528 else
529 Actions.interruptAgent();
530 return;
[87]531 } else if (Simple.consumeKeyboardInput()) {
[4]532 return;
533 }
534
[145]535 if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12) {
[944]536 functionKey(FunctionKey.values()[keyCode - KeyEvent.VK_F1 + 1],
537 e.isShiftDown(), e.isControlDown());
[4]538 return;
[145]539 } else if (e.isAltDown()) {
[176]540 int distance = e.isShiftDown() ? 1 : 20;
[145]541 switch (keyCode) {
542 case KeyEvent.VK_1:
[147]543 FrameMouseActions.leftButton();
544 // DisplayIO.clickMouse(InputEvent.BUTTON1_MASK);
[145]545 break;
546 case KeyEvent.VK_2:
[147]547 // DisplayIO.clickMouse(InputEvent.BUTTON2_MASK);
548 FrameMouseActions.middleButton();
[145]549 break;
550 case KeyEvent.VK_3:
[147]551 // DisplayIO.clickMouse(InputEvent.BUTTON3_MASK);
552 FrameMouseActions.rightButton();
[145]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 }
[108]567 return;
[427]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());
[145]580 return;
[108]581 }
[4]582
[145]583 switch (keyCode) {
584 case KeyEvent.VK_ESCAPE:
585 // Do escape after control so Ctl+Escape does not perform DropDown
[944]586 functionKey(FunctionKey.DropDown, e.isShiftDown(),
587 e.isControlDown());
[108]588 SessionStats.Escape();
[97]589 break;
[4]590 case KeyEvent.VK_LEFT:
[376]591 move(Text.LEFT, e.isShiftDown(), e.isControlDown());
[4]592 break;
593 case KeyEvent.VK_RIGHT:
[376]594 move(Text.RIGHT, e.isShiftDown(), e.isControlDown());
[4]595 break;
[13]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;
[4]602 case KeyEvent.VK_UP:
[176]603 if (e.isControlDown()) {
[72]604 NextTextItem(FrameUtils.getCurrentItem(), false);
[4]605 } else {
[376]606 move(Text.UP, e.isShiftDown(), e.isControlDown());
[4]607 }
608 break;
609 case KeyEvent.VK_DOWN:
[176]610 if (e.isControlDown()) {
[72]611 NextTextItem(FrameUtils.getCurrentItem(), true);
612 } else {
[376]613 move(Text.DOWN, e.isShiftDown(), e.isControlDown());
[72]614 }
[4]615 break;
616 case KeyEvent.VK_END:
[176]617 if (e.isControlDown())
[376]618 move(Text.END, e.isShiftDown(), e.isControlDown());
[147]619 else
[376]620 move(Text.LINE_END, e.isShiftDown(), e.isControlDown());
[4]621 break;
622 case KeyEvent.VK_HOME:
[176]623 if (e.isControlDown())
[376]624 move(Text.HOME, e.isShiftDown(), e.isControlDown());
[147]625 else
[376]626 move(Text.LINE_HOME, e.isShiftDown(), e.isControlDown());
[4]627 break;
[147]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;
[4]637 }
638 }
639
640 /**
[72]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();
[376]651 Text title = current.getTitleItem();
[156]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 {
[376]659 if (title != null)
660 textItems.add(title);
[156]661 textItems.addAll(current.getBodyTextItems(true));
662 }
663
664 Collections.sort(textItems);
665
[72]666 if (textItems.size() == 0) {
[115]667 // If there are no text items on the frame its a NoOp
668 if (title == null)
[108]669 return;
[376]670 if (title != null)
671 DisplayIO.MoveCursorToEndOfItem(title);
[72]672 FrameGraphics.Repaint();
673 return;
674 }
675
[80]676 // If the user is mouse wheeling in free space...
[72]677 if (currentItem == null) {
678 // find the nearest item in the correct direction
[105]679 int currY = FrameMouseActions.getY();
[72]680 for (int i = 0; i < textItems.size(); i++) {
[78]681 Item t = textItems.get(i);
[72]682 if (currY < t.getY()) {
683 if (down) {
[86]684 DisplayIO.MoveCursorToEndOfItem(t);
[72]685 } else {
[80]686 if (i == 0) {
[86]687 DisplayIO.MoveCursorToEndOfItem(current
688 .getTitleItem());
[80]689 } else {
[86]690 DisplayIO.MoveCursorToEndOfItem(textItems
691 .get(i - 1));
[80]692 }
[72]693 }
694 FrameGraphics.Repaint();
695 return;
696 }
697 }
[80]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) {
[944]701 DisplayIO
702 .MoveCursorToEndOfItem(textItems.get(textItems.size() - 1));
[80]703 }
[72]704 return;
705 }
[108]706
[72]707 // Find the current item... then move to the next item
[130]708 int i = textItems.indexOf(currentItem);
709
710 int nextIndex = i + (down ? 1 : -1);
[156]711 if (nextIndex >= 0 && nextIndex < textItems.size()) {
712 DisplayIO.MoveCursorToEndOfItem(textItems.get(nextIndex));
713 } else {
714 DisplayIO.MoveCursorToEndOfItem(currentItem);
[72]715 }
[130]716 return;
[72]717
718 }
719
[4]720 public void keyReleased(KeyEvent e) {
[97]721 if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
[108]722 FrameMouseActions.control(e);
[427]723 } else if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
724 FrameMouseActions.shift(e);
[147]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 // }
[97]737 }
[4]738 }
739
[108]740 private static void copyItemToClipboard(Item on) {
[105]741 if (on == null || !(on instanceof Text))
742 return;
743
[145]744 Text text = (Text) on;
[131]745 String string = text.copySelectedText();
[145]746
747 if (string == null || string.length() == 0)
[131]748 string = text.getText();
[145]749
[105]750 // add the text of the item to the clipboard
[131]751 StringSelection selection = new StringSelection(string);
[944]752 Toolkit.getDefaultToolkit().getSystemClipboard()
753 .setContents(selection, null);
[105]754 }
[108]755
[7]756 /**
[4]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 */
[176]763 private void controlChar(int key, boolean isShiftDown) {
[87]764 Logger.Log(Logger.USER, Logger.CONTROL_CHAR, "User pressing: Ctrl+"
[105]765 + KeyEvent.getKeyText(key));
[87]766 //
767 // if (FrameUtils.getCurrentItem() == null
768 // && !Frame.itemAttachedToCursor()) {
769 // Item t = DisplayIO.getCurrentFrame().createNewText(ch + ": ");
770 // FrameMouseActions.pickup(t);
771 // } else {
[376]772 // remove the link for ctrl+l
[87]773 Item current = FrameUtils.getCurrentItem();
774 Frame currentFrame = DisplayIO.getCurrentFrame();
[176]775 int distance = isShiftDown ? 1 : 20;
[145]776 switch (key) {
[176]777 case KeyEvent.VK_HOME:
778 if (current != null && current instanceof Text) {
[376]779 move(Text.HOME, isShiftDown, true);
[176]780 } else {
781 while (DisplayIO.Back())
782 ;
783 }
784 break;
785 case KeyEvent.VK_END:
786 if (current != null && current instanceof Text) {
[376]787 move(Text.END, isShiftDown, true);
[176]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;
[161]799 case KeyEvent.VK_TAB:
[294]800 calculateItem(current);
[161]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;
[145]807 case KeyEvent.VK_1:
[147]808 FrameMouseActions.leftButton();
809 // DisplayIO.clickMouse(InputEvent.BUTTON1_MASK);
[145]810 break;
811 case KeyEvent.VK_2:
[147]812 FrameMouseActions.middleButton();
813 // DisplayIO.clickMouse(InputEvent.BUTTON2_MASK);
[145]814 break;
815 case KeyEvent.VK_3:
[147]816 FrameMouseActions.rightButton();
817 // DisplayIO.clickMouse(InputEvent.BUTTON3_MASK);
[145]818 break;
819 case KeyEvent.VK_LEFT:
[176]820 if (current instanceof Text) {
[376]821 DisplayIO.setTextCursor((Text) current, Text.LEFT, false,
822 isShiftDown, true, true);
[176]823 } else {
[376]824 DisplayIO.translateCursor(-distance, 0);
[176]825 }
[145]826 break;
[376]827 case KeyEvent.VK_RIGHT:
[176]828 if (current instanceof Text) {
[376]829 DisplayIO.setTextCursor((Text) current, Text.RIGHT, false,
830 isShiftDown, true, true);
[176]831 } else {
[376]832 DisplayIO.translateCursor(distance, 0);
[176]833 }
[145]834 break;
[376]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;
[145]849 case KeyEvent.VK_L:
[87]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();
[145]853 // Ignore the annotation if there is one
854 if (text.charAt(0) == '@')
[130]855 text = text.substring(1);
[145]856
[87]857 if (FrameIO.isValidFrameName(text)) {
858 current.setLink(text);
859 } else if (FrameIO.isValidFramesetName(text)) {
860 current.setLink(text + '1');
861 }
[297]862 } else if (current != null) {
[87]863 // If its linked remove the link
864 current.setLink(null);
865 }
[145]866 break;
867 case KeyEvent.VK_G:
[87]868 // If its not linked then link it to its self
869 if (current instanceof Text) {
870 String text = ((Text) current).getText();
[130]871 if (text.charAt(0) == '@')
872 text = text.substring(1);
873
[87]874 if (FrameIO.isValidFrameName(text)) {
875 current.setLink(text);
876 } else if (FrameIO.isValidFramesetName(text)) {
877 current.setLink(text + '1');
878 }
879 }
[314]880 if (current != null && current.getLink() != null) {
[306]881 Navigation.Goto(current.getAbsoluteLink());
[87]882 return;
883 }
[145]884 break;
885 case KeyEvent.VK_A:
[87]886 // If its not linked then link it to its self
[115]887 if (current instanceof Text) {
[133]888 if (!current.hasAction()) {
[115]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();
[95]900
[115]901 }
902 current.setAction(text);
903 } else {
904 // If its linked remove the link
905 current.setActions(null);
[88]906 }
[87]907 }
[145]908 break;
[362]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;
[145]919 case KeyEvent.VK_V:
[504]920 ItemSelection.paste();
921 return;
[145]922 case KeyEvent.VK_C:
[944]923 if (FreeItems.itemsAttachedToCursor()) {
[740]924 ItemSelection.copyClone();
[108]925 return;
[504]926 }
[944]927 if (current instanceof Text) {
[108]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 }
[293]943 if (item != null) {
944 item.setText("@c");
945 DisplayIO.setCursorPosition(item.getX(), item.getY());
946 FrameUtils.setLastEdited(null);
947 Refresh();
948 }
[105]949 return;
[145]950 case KeyEvent.VK_X:
[740]951 ItemSelection.cut();
[4]952 return;
[145]953 case KeyEvent.VK_M:
[108]954 if (current == null)
955 return;
[944]956 if (current != null
957 && !current.hasPermission(UserAppliedPermission.full)) {
[121]958 MessageBay
959 .displayMessage("Insufficient permission toggle the items mark");
[108]960 return;
961 }
962 boolean newValue = !(current.getLinkMark() || current
963 .getActionMark());
964 current.setLinkMark(newValue);
965 current.setActionMark(newValue);
[145]966 break;
967 case KeyEvent.VK_Z:
[944]968 if (isShiftDown) {
[744]969 DisplayIO.getCurrentFrame().redo();
970 } else {
971 DisplayIO.getCurrentFrame().undo();
972 }
[108]973 return;
[145]974 case KeyEvent.VK_D:
[4]975 // perform a delete operation
[108]976 processChar((char) KeyEvent.VK_DELETE, isShiftDown);
[145]977 break;
978 case KeyEvent.VK_DELETE:
[105]979 // perform a delete operation
[87]980 FrameMouseActions.delete(current);
[145]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:
[415]990 // perform a format operation
[427]991 if (isShiftDown) {
992 Actions.PerformActionCatchErrors(currentFrame, null, "HFormat");
993 } else {
994 Actions.PerformActionCatchErrors(currentFrame, null, "Format");
995 }
[87]996 return;
[145]997 case KeyEvent.VK_J:
[4]998 Text text = getCurrentTextItem();
[247]999 if (text == null) {
[376]1000 for (Text t : currentFrame.getBodyTextItems(false)) {
[400]1001 t.justify(true);
[242]1002 }
[247]1003
[4]1004 return;
[242]1005 }
[4]1006
[154]1007 // if (text.getWidth() < 0)
1008 // text.setWidth(text.getBoundsWidth() - Item.MARGIN_RIGHT
1009 // - UserSettings.Gravity);
[400]1010 text.justify(true);
[145]1011 break;
[410]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;
[145]1026 case KeyEvent.VK_S:
[348]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 */
[362]1033 if (!isShiftDown) {
[348]1034 Save();
1035 return;
1036 }
[145]1037 Text text2 = getCurrentTextItem();
[4]1038 // split the current text item
[145]1039 if (text2 == null)
[4]1040 return;
[145]1041 List<String> textLines = text2.getTextList();
[4]1042 if (textLines.size() <= 1)
1043 return;
1044 // remove all except the first line of text from the item being
1045 // split
[145]1046 text2.setText(textLines.get(0));
1047 int y = text2.getY();
[4]1048 for (int i = 1; i < textLines.size(); i++) {
[145]1049 Text newText = text2.copy();
[4]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
[87]1055 newText.setID(currentFrame.getNextItemID());
1056 currentFrame.addItem(newText);
[4]1057 }
[145]1058 break;
1059 case KeyEvent.VK_ENTER:
1060 FrameMouseActions.leftButton();
1061 break;
1062 case KeyEvent.VK_BACK_SPACE:
[108]1063 DisplayIO.Back();
[145]1064 break;
[87]1065 }
[95]1066 FrameGraphics.Repaint();
[4]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() {
[108]1076 Item item = FrameUtils.getCurrentItem();
[4]1077
[454]1078 if (item != null && !item.hasPermission(UserAppliedPermission.full)) {
[121]1079 MessageBay
1080 .displayMessage("Insufficient permission to copy that item");
[4]1081 return null;
1082 }
1083
1084 Item on = null;
[108]1085 if (item != null)
1086 on = item;
[4]1087
1088 if (on == null || !(on instanceof Text))
1089 return null;
1090
1091 return (Text) on;
1092 }
1093
[115]1094 public static void functionKey(FunctionKey key, boolean isShiftDown,
1095 boolean isControlDown) {
1096 functionKey(key, 1, isShiftDown, isControlDown);
[21]1097 }
[22]1098
[4]1099 /**
1100 * Called when a Function key has been pressed, and performs the specific
1101 * action based on the key.
1102 */
[108]1103 public static void functionKey(FunctionKey key, int repeat,
[115]1104 boolean isShiftDown, boolean isControlDown) {
[4]1105 // get whatever the user is pointing at
[7]1106 Item on = FrameUtils.getCurrentItem();
[289]1107 String displayMessage = "F" + key.ordinal() + ": " + key.toString();
[4]1108 // check for enclosed mode
[80]1109 if (on == null && key.ordinal() < FunctionKey.AudienceMode.ordinal()) {
[86]1110 Collection<Item> enclosed = FrameUtils.getCurrentItems(on);
[161]1111
[4]1112 if (enclosed != null && enclosed.size() > 0) {
1113 // ensure only one dot\line is present in the list
[108]1114 Collection<Item> lineEnds = FrameUtils.getEnclosingLineEnds();
1115 Item firstConnected = lineEnds.iterator().next();
[72]1116 Collection<Item> connected = firstConnected.getAllConnected();
[4]1117
[72]1118 switch (key) {
[176]1119 case DropDown:
[944]1120 IndirectKeyboardActions
1121 .getInstance()
1122 .getDropDownAction()
1123 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1124 isControlDown, enclosed, firstConnected,
1125 connected, lineEnds), (char) 0);
[176]1126 break;
[80]1127 case SizeUp:
[944]1128 IndirectKeyboardActions
1129 .getInstance()
1130 .getSizeUpAction()
1131 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1132 isControlDown, enclosed, firstConnected,
1133 connected, lineEnds), (char) 0);
[72]1134 break;
[80]1135 case SizeDown:
[944]1136 IndirectKeyboardActions
1137 .getInstance()
1138 .getSizeDownAction()
1139 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1140 isControlDown, enclosed, firstConnected,
1141 connected, lineEnds), (char) 0);
[72]1142 break;
[80]1143 case ChangeColor:
[944]1144 IndirectKeyboardActions
1145 .getInstance()
1146 .getChangeColorAction()
1147 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1148 isControlDown, enclosed, firstConnected,
1149 connected, lineEnds), (char) 0);
[80]1150 break;
1151 case ToggleAnnotation:
[944]1152 IndirectKeyboardActions
1153 .getInstance()
1154 .getToggleAnnotationAction()
1155 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1156 isControlDown, enclosed, firstConnected,
1157 connected, lineEnds), (char) 0);
[72]1158 break;
[80]1159 case ChangeFontStyle:
[944]1160 IndirectKeyboardActions
1161 .getInstance()
1162 .getChangeFontStyleAction()
1163 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1164 isControlDown, enclosed, firstConnected,
1165 connected, lineEnds), (char) 0);
[72]1166 break;
[80]1167 case ChangeFontFamily:
[944]1168 IndirectKeyboardActions
1169 .getInstance()
1170 .getChangeFontFamilyAction()
1171 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1172 isControlDown, enclosed, firstConnected,
1173 connected, lineEnds), (char) 0);
[72]1174 break;
[80]1175 case InsertDate:
[944]1176 IndirectKeyboardActions
1177 .getInstance()
1178 .getInsertDateAction()
1179 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1180 isControlDown, enclosed, firstConnected,
1181 connected, lineEnds), (char) 0);
[72]1182 break;
[289]1183 case Save:
[944]1184 IndirectKeyboardActions
1185 .getInstance()
1186 .getSaveAction()
1187 .exec(new KeyboardInfo(key, repeat, isShiftDown,
1188 isControlDown, enclosed, firstConnected,
1189 connected, lineEnds), (char) 0);
[289]1190 MessageBay.displayMessage(displayMessage);
1191 break;
[4]1192 }
1193 return;
1194 }
1195 }
[80]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) {
[86]1200 // These function keys still work in free space
[80]1201 case DropDown:
1202 case InsertDate:
1203 case XRayMode:
1204 case AudienceMode:
1205 case Refresh:
[289]1206 case Save:
[80]1207 break;
[289]1208 case SizeDown:
[851]1209 zoomFrame(DisplayIO.getCurrentFrame(), 0.909090909f);
[850]1210 DisplayIO.getCurrentFrame().refreshSize();
1211 FrameKeyboardActions.Refresh();
1212 return;
[944]1213 // if (isControlDown) {
1214 // UserSettings.ScaleFactor.set(UserSettings.ScaleFactor.get() -
1215 // 0.05f);
1216 // Misc.repaint();
1217 // return;
1218 // }
[289]1219 case SizeUp:
[851]1220 zoomFrame(DisplayIO.getCurrentFrame(), 1.1f);
[850]1221 DisplayIO.getCurrentFrame().refreshSize();
1222 FrameKeyboardActions.Refresh();
1223 return;
[944]1224 // if (isControlDown) {
1225 // UserSettings.ScaleFactor.set(UserSettings.ScaleFactor.get() +
1226 // 0.05f);
1227 // Misc.repaint();
1228 // return;
1229 // }
[80]1230 default:
[121]1231 MessageBay.displayMessageOnce(displayMessage);
[80]1232 return;
1233 }
1234 }
[4]1235
1236 switch (key) {
[80]1237 case DropDown:
[161]1238 if (isShiftDown || isControlDown) {
[235]1239 if (on != null) {
[294]1240 calculateItem(on);
[235]1241 }
[161]1242 }
[247]1243 Drop(on, false);
[80]1244 return;
1245 case SizeUp:
[410]1246 SetSize(on, repeat, true, false, isControlDown);
[105]1247 if (on instanceof Text) {
[376]1248 DisplayIO.setTextCursor((Text) on, Text.NONE, true, false,
1249 false, true);
[105]1250 }
[4]1251 break;
[80]1252 case SizeDown:
[410]1253 SetSize(on, -repeat, true, false, isControlDown);
[105]1254 if (on instanceof Text) {
[376]1255 DisplayIO.setTextCursor((Text) on, Text.NONE, true, false,
1256 false, true);
[105]1257 }
[4]1258 break;
[80]1259 case ChangeColor:
[115]1260 SetColor(on, isShiftDown, isControlDown);
[4]1261 break;
[80]1262 case ToggleAnnotation:
[4]1263 ToggleAnnotation(on);
1264 break;
[80]1265 case ChangeFontStyle:
[4]1266 ToggleFontStyle(on);
1267 break;
[80]1268 case ChangeFontFamily:
[4]1269 ToggleFontFamily(on);
1270 break;
[80]1271 case InsertDate:
[4]1272 AddDate(on);
[80]1273 return;
1274 case NewFrameset:
[4]1275 CreateFrameset(on);
1276 break;
[80]1277 case XRayMode:
[108]1278 FrameGraphics.ToggleXRayMode();
[4]1279 break;
[80]1280 case AudienceMode:
[108]1281 FrameGraphics.ToggleAudienceMode();
[4]1282 break;
[80]1283 case Refresh:
[108]1284 Refresh();
[4]1285 break;
[289]1286 case Save:
1287 Save();
1288 break;
[4]1289 }
[108]1290 on = FrameUtils.getCurrentItem();
1291 Collection<Item> enclosed = FrameUtils.getCurrentItems(on);
1292 if (on == null && (enclosed == null || enclosed.size() == 0))
[121]1293 MessageBay.displayMessage(displayMessage);
[4]1294 }
1295
[294]1296 private static void calculateItem(Item toCalculate) {
[376]1297 if (toCalculate == null)
1298 return;
1299
[294]1300 if (!toCalculate.update()) {
1301 toCalculate.setFormula(null);
1302 MessageBay.errorMessage("Can not calculate formula ["
1303 + toCalculate.getText() + ']');
1304 }
1305 }
1306
[289]1307 private static void Save() {
1308 Frame current = DisplayIO.getCurrentFrame();
1309 current.change();
[294]1310 FrameIO.SaveFrame(current, true, true);
[289]1311 }
1312
[4]1313 public static final String DEFAULT_NEW_ITEM_TEXT = "";
[235]1314
[4]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 */
[284]1326 public static boolean Drop(Item toDropFrom, boolean bPasting) {
[72]1327 try {
[108]1328 FrameUtils.setLastEdited(null);
1329
[72]1330 String newItemText = DEFAULT_NEW_ITEM_TEXT;
[4]1331
[72]1332 // if a line is being rubber-banded, this is a no-op
1333 if (Frame.rubberbandingLine())
[284]1334 return false; // No-op
[4]1335
[72]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 }
[4]1342
[72]1343 // if no item was found, return
1344 if (toDropFrom == null) {
[121]1345 MessageBay.errorMessage("No item could be found to drop from");
[284]1346 return false;
[72]1347 }
[4]1348
[72]1349 if (!(toDropFrom instanceof Text)) {
[121]1350 MessageBay
1351 .displayMessage("Only text items can be dropped from");
[284]1352 return false;
[72]1353 }
[4]1354
[72]1355 // Get the list of items that must be dropped
[156]1356 List<Text> column = DisplayIO.getCurrentFrame().getColumn(
[72]1357 toDropFrom);
[4]1358
[72]1359 if (column == null) {
[121]1360 MessageBay.errorMessage("No column found to align items to");
[284]1361 return false;
[72]1362 }
[4]1363
[80]1364 Item title = DisplayIO.getCurrentFrame().getTitleItem();
[4]1365
[72]1366 // We wont do auto bulleting when dropping from titles
[247]1367 if (!bPasting && toDropFrom != title) {
[72]1368 newItemText = getAutoBullet(((Text) toDropFrom).getFirstLine());
1369 }
[4]1370
[72]1371 Text dummyItem = null;
[247]1372 if (!bPasting && FreeItems.textOnlyAttachedToCursor()) {
[156]1373 dummyItem = (Text) FreeItems.getItemAttachedToCursor();
[80]1374 String autoBullet = getAutoBullet(dummyItem.getText());
[4]1375
[72]1376 if (autoBullet.length() > 0)
1377 newItemText = "";
[80]1378 dummyItem.setText(newItemText + dummyItem.getText());
[22]1379 }
[286]1380
[72]1381 dummyItem = createText();
[242]1382 if (FreeItems.textOnlyAttachedToCursor()) {
[156]1383 Text t = (Text) FreeItems.getItemAttachedToCursor();
[72]1384 dummyItem.setSize(t.getSize());
[130]1385 int lines = t.getTextList().size();
1386 for (int i = 0; i < lines; i++) {
[72]1387 newItemText += '\n';
1388 }
1389 }
[22]1390
[72]1391 dummyItem.setText(newItemText);
[22]1392
[72]1393 // If the only item on the frame is the title and the frame name
[176]1394 // goto the zero frame and drop to the @start if there is one
1395 // or a fixed amount if there is not
[72]1396 if (column.size() == 0) {
[176]1397 Frame current = DisplayIO.getCurrentFrame();
[286]1398 // Item itemTemplate = current.getItemTemplate();
[284]1399 int xPos = title.getX() + FrameCreator.INDENT_FROM_TITLE;
1400 int yPos = FrameCreator.getYStart(title);
[176]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
[72]1409 dummyItem.setPosition(xPos, yPos);
[348]1410 // DisplayIO.setCursorPosition(xPos, yPos);
1411
1412 checkMovingCursor(dummyItem);
[72]1413 } else {
1414 int yPos = column.get(0).getY() + 1;
[348]1415 int xPos = column.get(0).getX();
[72]1416 // Either position the new item below the title or just above
1417 // the first item below the title
[348]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);
[72]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()) {
[282]1438 // Check for the 'next' tag!
[247]1439 Frame current = DisplayIO.getCurrentFrame();
[282]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);
[247]1451
[282]1452 // Create the new frame linked to the next tag
1453 boolean mouseMoved = FrameMouseActions.tdfc(next);
[72]1454 Frame moreFrame = DisplayIO.getCurrentFrame();
[282]1455
1456 // Add previous button to the new frame
[944]1457 frameCreator.addPreviousButton(moreFrame,
1458 firstFrame.getName());
[282]1459 Item first = current.getAnnotation("first");
1460 if (first != null) {
[944]1461 frameCreator.addFirstButton(moreFrame,
1462 first.getLink());
[282]1463 } else {
[944]1464 frameCreator.addFirstButton(moreFrame,
1465 firstFrame.getName());
[247]1466 }
[282]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 // }
[247]1473
[80]1474 moreFrame.setTitle(firstFrame.getTitleItem().getText());
[72]1475 // need to move the mouse to the top of the frame if
[282]1476 // there wasnt an @start on it
[72]1477 if (!mouseMoved) {
[80]1478 Item moreTitle = moreFrame.getTitleItem();
[944]1479 moreTitle
1480 .setOverlayPermission(UserAppliedPermission.full);
[247]1481 Drop(moreTitle, bPasting);
[72]1482 }
1483 // Add the bullet text to the item
[105]1484 dummyItem.setPosition(DisplayIO.getMouseX(),
1485 FrameMouseActions.getY());
[72]1486 } else {
[121]1487 MessageBay
1488 .warningMessage("Can not create items outside the frame area");
[72]1489 // ensures correct repainting when items don't move
1490 DisplayIO.setCursorPosition(DisplayIO.getMouseX(),
[105]1491 FrameMouseActions.getY());
[284]1492 return false;
[4]1493 }
1494 }
[247]1495 if (!FreeItems.textOnlyAttachedToCursor()
1496 && !dummyItem.isEmpty()) {
[72]1497 DisplayIO.getCurrentFrame().addItem(dummyItem);
1498 }
[4]1499
[348]1500 checkMovingCursor(dummyItem);
[4]1501 }
[130]1502 if (dummyItem.getText().length() == 0
[247]1503 || FreeItems.itemsAttachedToCursor()) {
[108]1504 dummyItem.getParentOrCurrentFrame().removeItem(dummyItem);
[400]1505 dummyItem.setRightMargin(FrameGraphics.getMaxFrameSize().width,
1506 false);
[247]1507 } else {
[242]1508 dummyItem.setWidth(toDropFrom.getWidth());
1509 }
[247]1510
[72]1511 DisplayIO.resetCursorOffset();
1512 FrameGraphics.Repaint();
1513 } catch (RuntimeException e) {
[121]1514 // MessageBay.errorMessage(e.getMessage());
[80]1515 e.printStackTrace();
[284]1516 return false;
[4]1517 }
[284]1518 return true;
[4]1519 }
1520
1521 /**
[348]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) {
[410]1537 int oldX = FrameMouseActions.getX();
1538 int oldY = FrameMouseActions.getY();
[427]1539
1540 if (oldX == x && oldY == y)
[410]1541 return;
[427]1542
[348]1543 DisplayIO.setCursorPosition(x, y);
1544 Item firstItem = FreeItems.getItemAttachedToCursor();
[362]1545
1546 if (firstItem == null) {
1547 firstItem = null;
[376]1548 return;
[362]1549 }
1550
[348]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 /**
[4]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
[108]1588 if (s == null)
1589 return newItemText;
[4]1590 /*
1591 * Item i = ItemUtils.FindTag(DisplayIO.getCurrentFrame().getItems(),
1592 * "@NoAutoBullets"); if (i != null) return newItemText;
1593 */
[80]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 }
[4]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))) {
[115]1611 if (Text.isBulletChar(s.charAt(0))) {
[4]1612 int nonSpaceIndex = 1;
[80]1613 // Find the end of the bullet and space after the bullet
[4]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 }
[80]1632 return preceedingSpace + newItemText;
[4]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 }
[944]1711
[814]1712 private static boolean refreshAnchors(Collection<Item> items) {
1713 boolean bReparse = false;
[944]1714
1715 for (Item i : items) {
1716 Float anchorLeft = i.getAnchorLeft();
1717 Float anchorRight = i.getAnchorRight();
1718 Float anchorTop = i.getAnchorTop();
[814]1719 Float anchorBottom = i.getAnchorBottom();
[944]1720
[814]1721 if (anchorLeft != null) {
1722 i.setAnchorLeft(anchorLeft);
1723 if (i.hasVector()) {
1724 bReparse = true;
1725 }
1726 }
[944]1727
[814]1728 if (anchorRight != null) {
1729 i.setAnchorRight(anchorRight);
1730 if (i.hasVector()) {
1731 bReparse = true;
1732 }
1733 }
[944]1734
[814]1735 if (anchorTop != null) {
1736 i.setAnchorTop(anchorTop);
1737 if (i.hasVector()) {
1738 bReparse = true;
1739 }
1740 }
[944]1741
[814]1742 if (anchorBottom != null) {
1743 i.setAnchorBottom(anchorBottom);
1744 if (i.hasVector()) {
1745 bReparse = true;
1746 }
1747 }
1748 }
1749 return bReparse;
1750 }
[944]1751
[941]1752 public static void zoomFrame(Frame frame, double scaleFactor) {
[944]1753
[851]1754 int x = FrameMouseActions.getX(), y = FrameMouseActions.getY();
[944]1755
[851]1756 if (frame == null) {
1757 return;
1758 }
[944]1759
[851]1760 Collection<Item> items = frame.getVisibleItems();
[944]1761
1762 for (Item item : items) {
1763 if (item instanceof Text
1764 && item.getSize() <= Text.MINIMUM_FONT_SIZE
1765 && scaleFactor < 1) {
[851]1766 return;
1767 }
1768 }
[944]1769
[851]1770 for (Vector v : frame.getVectors()) {
1771 v.Source.scale((float) scaleFactor, x, y);
1772 }
[944]1773
[851]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 }
[4]1786
[851]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
[4]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 */
[214]1813 public static void SetSize(Item item, int diff, boolean moveCursor,
[410]1814 boolean insideEnclosure, boolean isControlDown) {
[108]1815 Collection<Item> toSize = new HashSet<Item>();
[626]1816 Collection<InteractiveWidget> widgets = new HashSet<InteractiveWidget>();
[4]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
[72]1822 if (item == null) {
[242]1823 if (FreeItems.itemsAttachedToCursor())
[121]1824 toSize.addAll(FreeItems.getInstance());
[4]1825 else {
[121]1826 MessageBay
1827 .displayMessage("There are no Items selected on the Frame or on the Cursor");
[4]1828 return;
1829 }
1830 } else {
[80]1831 if (item.isFrameName()) {
[512]1832 // scale the entire frame
[944]1833 if (diff != 0) {
1834 zoomFrame(DisplayIO.getCurrentFrame(), diff > 0 ? 1.1
1835 : 0.909090909);
[850]1836 DisplayIO.getCurrentFrame().refreshSize();
1837 FrameKeyboardActions.Refresh();
1838 }
[512]1839 // MessageBay.displayMessage("Can not resize the frame name");
[80]1840 return;
1841 }
[4]1842 // check permissions
[454]1843 if (!item.hasPermission(UserAppliedPermission.full)) {
[286]1844 Item editTarget = item.getEditTarget();
1845 if (editTarget != item
[454]1846 && editTarget.hasPermission(UserAppliedPermission.full)) {
[286]1847 item = editTarget;
1848 } else {
1849 MessageBay
1850 .displayMessage("Insufficient permission to change the size of that item");
1851 return;
1852 }
[4]1853 }
[72]1854 toSet = item;
[108]1855 // For resizing enclosures pick up everything that is attached to
1856 // items partly in the enclosure
[115]1857 // TODO make this only pick up stuff COMPLETELY enclosed... if we
1858 // change copying to copy only the stuff completely enclosed
[108]1859 if (insideEnclosure) {
[282]1860 if (_enclosedItems == null) {
1861 for (Item i : FrameUtils.getCurrentItems(toSet)) {
[454]1862 if (i.hasPermission(UserAppliedPermission.full)
[282]1863 && !toSize.contains(i))
1864 toSize.addAll(i.getAllConnected());
1865 }
1866 _enclosedItems = toSize;
1867 } else {
1868 toSize = _enclosedItems;
[108]1869 }
[282]1870
[115]1871 }// Enclosed circle centers are resized with the center as origin
[944]1872 // Just add the circle center to the list of items to size
[115]1873 else if (!toSet.hasEnclosures() && !(toSet instanceof Text)
1874 && toSet.isLineEnd()) {
[72]1875 toSize.addAll(toSet.getLines());
1876 } else if (toSet instanceof Line) {
[348]1877
[72]1878 Line line = (Line) toSet;
[333]1879
[348]1880 if (!(toSet instanceof WidgetEdge)
1881 || ((WidgetEdge) toSet).getWidgetSource()
1882 .isWidgetEdgeThicknessAdjustable()) {
1883
[333]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;
[348]1889
[333]1890 }
[348]1891
[50]1892 } else {
[72]1893 toSize.add(toSet);
[50]1894 }
[4]1895 }
[944]1896
[626]1897 // add widgets to notify
[944]1898 for (Item i : toSize) {
1899 if (i instanceof WidgetEdge) {
[626]1900 widgets.add(((WidgetEdge) i).getWidgetSource());
[944]1901 } else if (i instanceof WidgetCorner) {
[626]1902 widgets.add(((WidgetCorner) i).getWidgetSource());
1903 }
1904 }
[4]1905
[108]1906 Point2D origin = new Point2D.Float(FrameMouseActions.MouseX,
1907 FrameMouseActions.MouseY);
1908 // Inside enclosures increase the size of the enclosure
[115]1909 double ratio = (100.0 + diff * 2) / 100.0;
[108]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;
[115]1916
[108]1917 if (i.isLineEnd()) {
[348]1918
1919 if (!(i instanceof WidgetCorner)
1920 || !((WidgetCorner) i).getWidgetSource()
1921 .isFixedSize()) { // don't size fixed
1922 // widgets
1923
[333]1924 Collection<Item> allConnected = i.getAllConnected();
1925 done.addAll(allConnected);
1926 for (Item it : allConnected) {
1927 it.translate(origin, ratio);
[348]1928 it.setArrowheadLength((float) (it
1929 .getArrowheadLength() * ratio));
[333]1930 }
1931 i.setThickness((float) (i.getThickness() * ratio));
[108]1932 }
1933 } else if (i instanceof XRayable) {
[286]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)) {
[348]1938 scaleText(insideEnclosure, origin, ratio, done, source);
[286]1939 }
1940
[108]1941 i.translate(origin, ratio);
1942 i.setThickness((float) (i.getThickness() * ratio));
1943 done.add(i);
[286]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;
[944]1949 AttributeValuePair avp = new AttributeValuePair(
1950 text.getText());
[286]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();
[108]1961 } else if (i instanceof Text) {
[348]1962 scaleText(insideEnclosure, origin, ratio, done, (Text) i);
[108]1963 }
1964 }
[814]1965 // refresh anchored items
[944]1966 if (refreshAnchors(toSize)) {
1967 FrameUtils.Parse(DisplayIO.getCurrentFrame(), false);
1968 }
[626]1969 // notify widgets they were resized
[944]1970 for (InteractiveWidget iw : widgets) {
[626]1971 iw.onResized();
1972 }
[278]1973 FrameGraphics.refresh(true);
[108]1974 return;
[4]1975 }
1976
1977 // adjust the size of all the items
1978 for (Item i : toSize) {
1979 // Lines and dots use thickness, not size
[115]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) {
[108]1985 float current = Math.abs(i.getThickness());
[176]1986 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
[108]1987 i.setThickness(current);
[4]1988 } else if (i instanceof Dot) {
[72]1989 Item dot = (Item) i;
[4]1990 float current = Math.abs(dot.getThickness());
[176]1991 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
[4]1992 dot.setThickness(current);
[286]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();
[4]2007 } else {
[108]2008 float oldSize = Math.abs(i.getSize());
[176]2009 float newSize = Math
2010 .max(oldSize + diff, Item.MINIMUM_THICKNESS);
[108]2011 float resizeRatio = newSize / oldSize;
2012 // Set size for Picture also translates
[105]2013 i.setSize(newSize);
[108]2014 if (i instanceof Text && i.getSize() != oldSize) {
[427]2015 if (toSize.size() == 1 && !isControlDown) {
[410]2016 moveCursorAndFreeItems(i.getX(), i.getY());
2017 } else {
2018 i.translate(origin, resizeRatio);
2019 if (i.isLineEnd()) {
2020 i.setPosition(i.getPosition());
2021 }
[282]2022 }
[105]2023 }
[4]2024 }
2025 }
2026
2027 if (toSet != null)
2028 toSet.getParent().setChanged(true);
[944]2029
[814]2030 // refresh anchored items
[944]2031 if (refreshAnchors(toSize)) {
[814]2032 FrameUtils.Parse(DisplayIO.getCurrentFrame(), false);
2033 }
[4]2034
[626]2035 // notify widgets they were resized
[944]2036 for (InteractiveWidget iw : widgets) {
[626]2037 iw.onResized();
2038 }
[278]2039 FrameGraphics.refresh(true);
[4]2040 }
2041
[286]2042 /**
2043 * @param origin
2044 * @param ratio
2045 * @param done
2046 * @param source
2047 */
[362]2048 private static void scaleText(boolean insideEnclosure, Point2D origin,
2049 double ratio, Collection<Item> done, Text source) {
2050 if (insideEnclosure)
[348]2051 source.setWidth(Math.round((float) (source.getWidth() * ratio)));
[286]2052 source.translate(origin, ratio);
2053 source.setSize((float) (source.getSize() * ratio));
2054 done.add(source);
2055 }
2056
[108]2057 private static void SetFillColor(Item item, boolean setTransparent) {
[4]2058 if (item == null)
2059 return;
[145]2060
[454]2061 if (!item.hasPermission(UserAppliedPermission.full)) {
[133]2062 MessageBay
[145]2063 .displayMessage("Insufficient permission to change fill color");
[133]2064 return;
2065 }
[4]2066
[108]2067 Item toSet = item;
[4]2068 Color color = toSet.getFillColor();
[108]2069 if (setTransparent)
2070 color = null;
2071 else
[944]2072 color = ColorUtils.getNextColor(color,
2073 TemplateSettings.FillColorWheel.get(),
2074 toSet.getGradientColor());
[4]2075
[108]2076 // if (color == null) {
[121]2077 // MessageBay.displayMessage("FillColor is now transparent");
[108]2078 // }
[4]2079
2080 toSet.setFillColor(color);
2081 toSet.getParent().setChanged(true);
2082
2083 FrameGraphics.Repaint();
2084 }
2085
[145]2086 private static void SetGradientColor(Item item, boolean setTransparent) {
2087 if (item == null)
2088 return;
2089
[454]2090 if (!item.hasPermission(UserAppliedPermission.full)) {
[145]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
[944]2101 color = ColorUtils.getNextColor(color,
2102 TemplateSettings.ColorWheel.get(), toSet.getFillColor());
[145]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
[4]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 */
[115]2121 private static void SetColor(Item item, boolean setTransparent,
2122 boolean setBackgroundColor) {
[4]2123 // first determine the next color
2124 Color color = null;
[80]2125 Frame currentFrame = DisplayIO.getCurrentFrame();
[115]2126 if (item == null) {
[242]2127 if (FreeItems.itemsAttachedToCursor()) {
[121]2128 color = FreeItems.getInstance().get(0).getColor();
[4]2129 } else {
2130 return;
2131 }
2132 // change the background color if the user is pointing on the
2133 // frame name
[115]2134 } else if (item == currentFrame.getNameItem()) {
[108]2135 // check permissions
[454]2136 if (!item.hasPermission(UserAppliedPermission.full)) {
[121]2137 MessageBay
2138 .displayMessage("Insufficient permission to the frame's background color");
[108]2139 return;
2140 }
2141 if (setTransparent)
2142 currentFrame.setBackgroundColor(null);
2143 else
2144 currentFrame.toggleBackgroundColor();
[80]2145 // Display a message if the color has changed to transparent
[108]2146 // if (currentFrame.getBackgroundColor() == null)
2147 // FrameGraphics
[121]2148 // .displayMessage("Background color is now transparent");
[80]2149 FrameGraphics.Repaint();
[4]2150 return;
2151 } else {
2152 // check permissions
[454]2153 if (!item.hasPermission(UserAppliedPermission.full)) {
[286]2154 Item editTarget = item.getEditTarget();
2155 if (editTarget != item
[454]2156 && editTarget.hasPermission(UserAppliedPermission.full)) {
[286]2157 item = editTarget;
2158 } else {
2159 MessageBay
2160 .displayMessage("Insufficient permission to change color");
2161 return;
2162 }
[4]2163 }
[115]2164 // Toggling color of circle center changes the circle fill color
2165 if (item.hasEnclosures()) {
[145]2166 if (setBackgroundColor) {
2167 SetGradientColor(item.getEnclosures().iterator().next(),
2168 setTransparent);
2169 } else {
2170 SetFillColor(item.getEnclosures().iterator().next(),
2171 setTransparent);
2172 }
[115]2173 } else if (setBackgroundColor) {
2174 color = item.getPaintBackgroundColor();
[130]2175 } else {
[115]2176 color = item.getPaintColor();
2177 }
[4]2178 }
[108]2179 if (setTransparent)
2180 color = null;
[115]2181 else if (setBackgroundColor) {
[944]2182 color = ColorUtils
2183 .getNextColor(color, TemplateSettings.FillColorWheel.get(),
2184 item.getPaintColor());
[115]2185 } else {
[944]2186 color = ColorUtils.getNextColor(color,
2187 TemplateSettings.ColorWheel.get(),
[108]2188 currentFrame.getPaintBackgroundColor());
[115]2189 }
[108]2190 // if (currentFrame.getPaintForegroundColor().equals(color))
2191 // color = null;
[4]2192
[108]2193 // if color is being set to default display a message to indicate that
2194 // if (color == null) {
[121]2195 // MessageBay.displayMessage("Color is set to default");
[108]2196 // }
[4]2197
[115]2198 if (setBackgroundColor) {
[242]2199 if (item == null && FreeItems.itemsAttachedToCursor()) {
[121]2200 for (Item i : FreeItems.getInstance())
[115]2201 i.setBackgroundColor(color);
2202 } else {
2203 item.setBackgroundColor(color);
2204 item.getParent().setChanged(true);
2205 }
[4]2206 } else {
[242]2207 if (item == null && FreeItems.itemsAttachedToCursor()) {
[121]2208 for (Item i : FreeItems.getInstance())
[115]2209 i.setColor(color);
2210 } else {
2211 item.setColor(color);
2212 item.getParent().setChanged(true);
2213 }
[4]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 */
[7]2224 private static void ToggleAnnotation(Item toToggle) {
[4]2225 if (toToggle == null) {
[121]2226 MessageBay.displayMessage("There is no Item selected to toggle");
[4]2227 return;
2228 }
2229
2230 // check permissions
[454]2231 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
[121]2232 MessageBay
2233 .displayMessage("Insufficient permission to toggle that item's annotation");
[4]2234 return;
2235 }
[80]2236 toToggle.setAnnotation(!toToggle.isAnnotation());
[4]2237
[7]2238 toToggle.getParent().setChanged(true);
[4]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 */
[7]2248 private static void ToggleFontStyle(Item toToggle) {
[4]2249 if (toToggle == null) {
[121]2250 MessageBay.displayMessage("There is no Item selected to toggle");
[4]2251 return;
2252 }
2253
2254 // check permissions
[454]2255 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
[121]2256 MessageBay
2257 .displayMessage("Insufficient permission to toggle that item's annotation");
[4]2258 return;
2259 }
2260
[7]2261 if (toToggle instanceof Text) {
2262 Text text = (Text) toToggle;
[4]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 */
[7]2276 private static void ToggleFontFamily(Item toToggle) {
[4]2277 if (toToggle == null) {
[121]2278 MessageBay.displayMessage("There is no Item selected to toggle");
[4]2279 return;
2280 }
2281
2282 // check permissions
[454]2283 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
[130]2284 MessageBay
2285 .displayMessage("Insufficient permission to toggle that item's annotation");
[4]2286 return;
2287 }
2288
[7]2289 if (toToggle instanceof Text) {
2290 Text text = (Text) toToggle;
[4]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 */
[885]2306 public static void AddDate(Item toAdd) {
[419]2307 String date1 = Formatter.getDateTime();
2308 String date2 = Formatter.getDate();
[4]2309 final String leftSeparator = " :";
2310 final String rightSeparator = ": ";
2311 String dateToAdd = date1 + rightSeparator;
2312 boolean prepend = false;
2313 boolean append = false;
2314
[7]2315 // if the user is pointing at an item, add the date where ever the
2316 // cursor is pointing
[424]2317 if (toAdd != null && toAdd instanceof Text) {
[427]2318 // permission check
[454]2319 if (!toAdd.hasPermission(UserAppliedPermission.full)) {
[427]2320 MessageBay
2321 .displayMessage("Insufficicent permission to add the date to that item");
2322 return;
2323 }
[7]2324
[427]2325 Text textItem = (Text) toAdd;
[7]2326
[427]2327 String text = textItem.getText();
[4]2328
[427]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 = "";
[4]2346 prepend = true;
[427]2347 } else {
2348 // use the default date format
2349 prepend = true;
[4]2350 }
[427]2351 }
[7]2352
[427]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);
[4]2366 }
[427]2367 }
[4]2368
[427]2369 textItem.getParent().setChanged(true);
2370 FrameGraphics.Repaint();
[424]2371 // } else {
2372 // MessageBay
2373 // .displayMessage("Only text items can have the date prepended to
2374 // them");
[427]2375 // }
[4]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
[147]2384 DisplayIO.setCursorPosition(newText.getParagraphEndPosition());
[4]2385 }
2386
2387 }
2388
2389 /**
2390 * Creates a new Frameset with the name given by the Item
2391 *
2392 * @param name
2393 */
[70]2394 private static void CreateFrameset(Item item) {
2395 if (item == null) {
[121]2396 MessageBay
2397 .displayMessage("There is no selected item to use for the frameset name");
[4]2398 return;
2399 }
[72]2400
[70]2401 if (!(item instanceof Text)) {
[121]2402 MessageBay
2403 .displayMessage("Framesets can only be created from text items");
[70]2404 return;
2405 }
[72]2406
2407 // dont create frameset if the item is linked
[70]2408 if (item.getLink() != null) {
[121]2409 MessageBay
2410 .displayMessage("A frameset can not be created from a linked item");
[70]2411 return;
2412 }
[72]2413
[4]2414 // check permissions
[454]2415 if (!item.hasPermission(UserAppliedPermission.full)) {
[121]2416 MessageBay
2417 .displayMessage("Insufficient permission to create a frameset from this item");
[4]2418 return;
2419 }
2420
[70]2421 Text text = (Text) item;
[7]2422 try {
2423 // create the new frameset
2424 Frame linkTo = FrameIO.CreateNewFrameset(text.getFirstLine());
2425 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
[80]2426 text.setLink(linkTo.getName());
[4]2427 text.getParent().setChanged(true);
[408]2428 FrameUtils.DisplayFrame(linkTo, true, true);
[7]2429 linkTo.moveMouseToDefaultLocation();
2430 // this needs to be done if the user doesnt move the mouse before
[8]2431 // doing Tdfc while the cursor is set to the text cursor
[7]2432 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2433 } catch (Exception e) {
[121]2434 MessageBay.errorMessage(e.getMessage());
[4]2435 }
2436 }
2437
2438 /**
2439 * Forces a re-parse and repaint of the current Frame.
2440 */
[108]2441 public static void Refresh() {
[95]2442 Frame currentFrame = DisplayIO.getCurrentFrame();
[944]2443
2444 if (FrameMouseActions.isShiftDown()) {
[814]2445 currentFrame.refreshSize();
2446 }
[348]2447
2448 // Refresh widgets that use its self as a data source
[376]2449 currentFrame.notifyObservers(true);
[348]2450
[95]2451 if (FrameIO.isProfileFrame(currentFrame)) {
[97]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
[95]2456 Frame profile = FrameIO.LoadFrame(currentFrame.getFramesetName()
2457 + "1");
2458 assert (profile != null);
[121]2459 FrameUtils.Parse(currentFrame);
[95]2460 FrameUtils.ParseProfile(profile);
2461 } else {
2462 FrameUtils.Parse(currentFrame);
2463 }
[78]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();
[108]2467 FrameMouseActions.getInstance().refreshHighlights();
[4]2468 FrameGraphics.ForceRepaint();
2469 }
2470}
Note: See TracBrowser for help on using the repository browser.