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

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

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

Beginnings of IndirectKeyboardActions...

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