source: trunk/src/org/expeditee/gio/gesture/StandardGestureActions.java@ 1511

Last change on this file since 1511 was 1511, checked in by bnemhaus, 4 years ago

Frame::Parse has been updated to include a new boolean parameter. When true, widgets that are created as a result of the parse send not only notify the widget framework that they have been added, but are also visible. When false, they only notify they have been added.

The widget framework now distinguishes between added and visible widgets, this fixes a bug. Bug: when programmatically adding a widget to not the current frame, it never gets properly removed and therefore still catches click events from users. By distinguishing between adding and making visible this is avoided.


Another bug has been fixed. Bug: When setting a text item to have a right anchor, and then subsequently reducing the size of the window, this text item would get a width of zero assigned. This was caused by some issues with the logic of how right margins for items were calculated.

File size: 148.1 KB
Line 
1package org.expeditee.gio.gesture;
2
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6import java.nio.file.Paths;
7import java.nio.file.StandardCopyOption;
8import java.text.NumberFormat;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.LinkedHashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.Set;
19
20import org.expeditee.actions.Actions;
21import org.expeditee.actions.Misc;
22import org.expeditee.actions.Navigation;
23import org.expeditee.core.Colour;
24import org.expeditee.core.Point;
25import org.expeditee.core.bounds.AxisAlignedBoxBounds;
26import org.expeditee.gio.ClipboardManager.ClipboardData;
27import org.expeditee.gio.EcosystemManager;
28import org.expeditee.gio.gesture.Gesture.GestureType;
29import org.expeditee.gio.gesture.data.ChangeColourGestureData;
30import org.expeditee.gio.gesture.data.ClickGestureData;
31import org.expeditee.gio.gesture.data.CreateItemGestureData;
32import org.expeditee.gio.gesture.data.CreateItemGestureData.ItemType;
33import org.expeditee.gio.gesture.data.DeleteGestureData;
34import org.expeditee.gio.gesture.data.FormatGestureData;
35import org.expeditee.gio.gesture.data.InsertStringGestureData;
36import org.expeditee.gio.gesture.data.ItemSpecificGestureData;
37import org.expeditee.gio.gesture.data.JustifyGestureData;
38import org.expeditee.gio.gesture.data.LinkGestureData;
39import org.expeditee.gio.gesture.data.NavigateFrameGestureData;
40import org.expeditee.gio.gesture.data.NavigateTextGestureData;
41import org.expeditee.gio.gesture.data.PanGestureData;
42import org.expeditee.gio.gesture.data.PickUpGestureData;
43import org.expeditee.gio.gesture.data.RefreshGestureData;
44import org.expeditee.gio.gesture.data.ScaleGestureData;
45import org.expeditee.gio.gesture.data.SelectAreaGestureData;
46import org.expeditee.gio.gesture.data.UndoGestureData;
47import org.expeditee.gio.gesture.data.ZoomGestureData;
48import org.expeditee.gio.input.KBMInputEvent.Key;
49import org.expeditee.gio.input.StandardInputEventListeners;
50import org.expeditee.gui.AttributeUtils;
51import org.expeditee.gui.AttributeValuePair;
52import org.expeditee.gui.Browser;
53import org.expeditee.gui.ColorUtils;
54import org.expeditee.gui.DisplayController;
55import org.expeditee.gui.Frame;
56import org.expeditee.gui.FrameCreator;
57import org.expeditee.gui.FrameGraphics;
58import org.expeditee.gui.FrameIO;
59import org.expeditee.gui.FrameUtils;
60import org.expeditee.gui.FreeItems;
61import org.expeditee.gui.ItemsList;
62import org.expeditee.gui.MessageBay;
63import org.expeditee.gui.Vector;
64import org.expeditee.io.ExpClipReader;
65import org.expeditee.io.ItemSelection;
66import org.expeditee.io.ItemSelection.ExpDataHandler;
67import org.expeditee.items.Circle;
68import org.expeditee.items.Constraint;
69import org.expeditee.items.Dot;
70import org.expeditee.items.Item;
71import org.expeditee.items.Item.HighlightMode;
72import org.expeditee.items.ItemUtils;
73import org.expeditee.items.Line;
74import org.expeditee.items.Picture;
75import org.expeditee.items.Text;
76import org.expeditee.items.UserAppliedPermission;
77import org.expeditee.items.XRayable;
78import org.expeditee.items.MagneticConstraint.MagneticConstraints;
79import org.expeditee.items.widgets.ButtonWidget;
80import org.expeditee.items.widgets.Widget;
81import org.expeditee.items.widgets.WidgetCorner;
82import org.expeditee.items.widgets.WidgetEdge;
83import org.expeditee.settings.UserSettings;
84import org.expeditee.settings.experimental.ExperimentalFeatures;
85import org.expeditee.settings.templates.TemplateSettings;
86import org.expeditee.stats.SessionStats;
87
88public class StandardGestureActions implements GestureListener {
89 /** The gesture types offered by core Expeditee. */
90 //@formatter:off
91 public enum StandardGestureType {
92 ACTION, // Make action, remove action
93 CALCULATE,
94 CHANGE_COLOUR, // F3
95 CLICK,
96 COPY,
97 CREATE_ITEM,
98 CUT,
99 DELETE,
100 DROP_DOWN, // F0 (ESC) (Positions the cursor below the current text item. Similar to enter in traditional editors.)
101 EXTRACT_ATTRIBUTES,
102 EXTRUDE,
103 FORMAT,
104 INSERT_DATE, // F5
105 INSERT_STRING,
106 JUSTIFY,
107 LINK, // Create link, remove link, follow link
108 MAKE_CIRCLE,
109 MOVE_CURSOR,
110 NAVIGATE_FRAME, // Forward/back/next/previous frame
111 NAVIGATE_TEXT,
112 NEW_FRAMESET, // F6
113 NEXT_ITEM,
114 PAN,
115 PASTE,
116 PICK_UP,
117 PLACE,
118 REFRESH, // F12
119 ROTATE_DISPLAY_MODE, // F9
120 ROTATE_FONT_FAMILY, // F8
121 ROTATE_FONT_STYLE, // F7
122 SAVE, // F11
123 SCALE, // F1 & F2
124 SELECT_AREA,
125 SPLIT_TEXT,
126 TOGGLE_ANNOTATION, // F4
127 TOGGLE_ARROWHEAD,
128 TOGGLE_BOLD,
129 TOGGLE_ITALICS,
130 TOGGLE_ITEMS_MARK, // Show/hide the little circle indicating the item has a link and/or action
131 TOGGLE_XRAY_MODE, // F10
132 CYCLE_SURROGATE_MODE, // Shift + F10
133 UNDO,
134 ZOOM,
135 ACTIVATE_BUTTON, // Enter while over Item with _acceptsKeyboardEnter set to true
136 REPARSE // Ctrl + Shift + R
137 }
138 //@formatter:on
139
140 private static StandardGestureActions _instance = null;
141
142 public static StandardGestureActions getInstance() {
143 if (_instance == null) {
144 _instance = new StandardGestureActions();
145 }
146
147 return _instance;
148 }
149
150 private HashMap<StandardGestureType, GestureType> _gestureTypes;
151
152 private HashMap<GestureType, GestureAction> _actions;
153
154 private StandardGestureActions() {
155 _gestureTypes = new HashMap<StandardGestureType, GestureType>();
156 initialiseGestureTypes();
157 _actions = new HashMap<GestureType, GestureAction>();
158 initialiseActions();
159 }
160
161 @Override
162 public void preGesture(final Gesture gesture) {
163 final GestureAction action = getGestureAction(gesture.getType());
164 if (action == null) {
165 return;
166 }
167 action.prepare(gesture);
168 }
169
170 @Override
171 public void onGesture(final Gesture gesture) {
172 final GestureAction action = getGestureAction(gesture.getType());
173 if (action == null) {
174 return;
175 }
176 action.exec(gesture);
177 }
178
179 @Override
180 public void postGesture(final Gesture gesture) {
181 final GestureAction action = getGestureAction(gesture.getType());
182 if (action == null) {
183 return;
184 }
185 action.finalise(gesture);
186 }
187
188 private void setGestureAction(GestureType type, GestureAction action) {
189 if (type == null) {
190 return;
191 }
192
193 _actions.put(type, action);
194 }
195
196 private GestureAction getGestureAction(GestureType type) {
197 if (type == null) {
198 return null;
199 }
200
201 return _actions.get(type);
202 }
203
204 /**
205 * Sets up the standard gesture actions.
206 */
207 private void initialiseActions() {
208 // Set the ACTION action
209 setGestureAction(gestureType(StandardGestureType.ACTION), new GestureAction() {
210 @Override
211 public void exec(Gesture gesture) {
212 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
213 Item current = data.getCurrentItem();
214 // If its not linked then link it to its self
215 if (current instanceof Text) {
216 if (!current.hasAction()) {
217 String text = ((Text) current).getText().trim();
218 // first trim the annotation
219 if (text.startsWith("@")) {
220 text = text.substring(1).trim();
221 }
222 // then trim the action
223 String lowerCaseText = text.toLowerCase();
224 if (lowerCaseText.startsWith("a:")) {
225 text = text.substring("a:".length()).trim();
226 } else if (lowerCaseText.startsWith("action:")) {
227 text = text.substring("action:".length()).trim();
228
229 }
230 current.setAction(text);
231 } else {
232 // If its linked remove the link
233 current.setActions(null);
234 }
235 }
236 }
237 });
238
239 // Set the CALCULATE action
240 setGestureAction(gestureType(StandardGestureType.CALCULATE), new GestureAction() {
241 @Override
242 public void exec(Gesture gesture) {
243 Item on = ((ItemSpecificGestureData) gesture.getData()).getCurrentItem();
244 if (on != null) {
245 calculateItem(on);
246 DisplayController.requestRefresh(true);
247 }
248 }
249 });
250
251 // Set the CHANGE_COLOUR action
252 setGestureAction(gestureType(StandardGestureType.CHANGE_COLOUR), new GestureAction() {
253 @Override
254 public void exec(Gesture gesture) {
255 ChangeColourGestureData data = (ChangeColourGestureData) gesture.getData();
256 Item currentItem = data.getCurrentItem();
257 Collection<Item> currentItems = data.getCurrentItems();
258 Collection<Item> enclosure = data.getEnclosure();
259 boolean shouldSetTransparent = data.shouldSetTransparent();
260 boolean shouldSetSecondaryColour = data.shouldSetSecondaryColour();
261 if (currentItem == null && currentItems != null && currentItems.size() > 0) {
262 Collection<Item> connected = data.getEnclosure().iterator().next().getAllConnected();
263 if (connected.size() > 0) {
264 for (Item d : enclosure) {
265 if (shouldSetSecondaryColour) {
266 SetGradientColor(d, shouldSetTransparent);
267 } else {
268 SetFillColor(d, shouldSetTransparent);
269 }
270 break;
271 }
272 }
273 } else if (currentItem != null) {
274 SetColor(currentItem, shouldSetTransparent, shouldSetSecondaryColour);
275 }
276 }
277 });
278
279 // Set the CLICK action
280 setGestureAction(gestureType(StandardGestureType.CLICK), new GestureAction() {
281 @Override
282 public void exec(Gesture gesture) {
283 ClickGestureData data = (ClickGestureData) gesture.getData();
284 click(data.getCurrentItem(), data.getCurrentItems(), data.getPosition());
285 }
286 });
287
288 // Set the COPY action
289 setGestureAction(gestureType(StandardGestureType.COPY), new GestureAction() {
290 @Override
291 public void exec(Gesture gesture) {
292 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
293 Item current = data.getCurrentItem();
294 if (FreeItems.hasItemsAttachedToCursor()) {
295 ItemSelection.copyClone();
296 } else if (current instanceof Text) {
297 copyItemToClipboard(current);
298 }
299 }
300 });
301
302 // Set the CREATE_ITEM action
303 setGestureAction(gestureType(StandardGestureType.CREATE_ITEM), new GestureAction() {
304 @Override
305 public void exec(Gesture gesture) {
306 CreateItemGestureData data = (CreateItemGestureData) gesture.getData();
307 if (data.getItemType() == ItemType.LINE) {
308 newLineAction(data.getPosition(), data.getCurrentItem());
309 } else if (data.getItemType() == ItemType.BOX) {
310 createRectangleAction();
311 }
312 // TODO: Complete. cts16
313 }
314 });
315
316 // Set the CUT action
317 setGestureAction(gestureType(StandardGestureType.CUT), new GestureAction() {
318 @Override
319 public void exec(Gesture gesture) {
320 ItemSelection.cut();
321 }
322 });
323
324 // Set the DELETE action
325 setGestureAction(gestureType(StandardGestureType.DELETE), new GestureAction() {
326 @Override
327 public void exec(Gesture gesture) {
328 DeleteGestureData data = (DeleteGestureData) gesture.getData();
329 if (FreeItems.getInstance().isEmpty() && data.getCurrentItem() == null && data.getCurrentItems() == null
330 && data.getEnclosure() == null) {
331 final Gesture undoGesture = data.getAlternateMode()
332 ? new Gesture(StandardGestureActions.getInstance().gestureType(StandardGestureType.UNDO),
333 new UndoGestureData(true))
334 : new Gesture(StandardGestureActions.getInstance().gestureType(StandardGestureType.UNDO),
335 new UndoGestureData(false));
336 getGestureAction(gestureType(StandardGestureType.UNDO)).exec(undoGesture);
337 } else {
338 delete(data.getCurrentItem(), data.getCurrentItems(), data.getEnclosure(), data.getAlternateMode());
339 }
340 }
341 });
342
343 // Set the DROP_DOWN action
344 setGestureAction(gestureType(StandardGestureType.DROP_DOWN), new GestureAction() {
345 @Override
346 public void exec(Gesture gesture) {
347
348 // Get the enclosed items
349 Item on = FrameUtils.getCurrentItem();
350 if (on == null) {
351 Collection<Item> enclosed = FrameUtils.getCurrentItems(on);
352
353 Collection<Item> lineEnds;
354 Item firstConnected;
355 if (enclosed != null && enclosed.size() > 0) {
356 // ensure only one dot\line is present in the list
357 lineEnds = FrameUtils.getEnclosingLineEnds();
358 firstConnected = lineEnds.iterator().next();
359
360 // Get the last text item and drop from in
361 Item lastText = null;
362 for (Item i : enclosed) {
363 if (i instanceof Text) {
364 lastText = i;
365 }
366 }
367
368 // Drop from the item if there was a text item in the box
369 if (lastText != null) {
370 Drop(lastText, false);
371 } else {
372 // Move to the top of the box
373 AxisAlignedBoxBounds rect = firstConnected.getBoundingBox();
374 int newX = rect.getMinX() + Text.MARGIN_LEFT;
375 int newY = Text.MARGIN_LEFT + rect.getMinY()
376 + DisplayController.getCurrentFrame().getItemTemplate().getBoundsHeight();
377 moveCursorAndFreeItems(newX, newY);
378 // TODO can resetOffset be put inside moveCursorAndFreeItems
379 resetOffset();
380 }
381 return;
382 }
383 }
384
385 Drop(on, false);
386 }
387 });
388
389 // Set the EXTRACT_ATTRIBUTES action
390 setGestureAction(gestureType(StandardGestureType.EXTRACT_ATTRIBUTES), new GestureAction() {
391 @Override
392 public void exec(Gesture gesture) {
393 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
394 extractAttributes(data.getCurrentItem());
395 }
396 });
397
398 // Set the FORMAT action
399 setGestureAction(gestureType(StandardGestureType.FORMAT), new GestureAction() {
400 @Override
401 public void exec(Gesture gesture) {
402 FormatGestureData data = (FormatGestureData) gesture.getData();
403 if (data.getHorizontal()) {
404 Actions.LegacyPerformActionCatchErrors(data.getFrame(), null, "HFormat");
405 } else {
406 Actions.LegacyPerformActionCatchErrors(data.getFrame(), null, "Format");
407 }
408 }
409 });
410
411 // Set the INSERT_DATE action
412 setGestureAction(gestureType(StandardGestureType.INSERT_DATE), new GestureAction() {
413 @Override
414 public void exec(Gesture gesture) {
415 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
416 Item currentItem = data.getCurrentItem();
417 Collection<Item> currentItems = data.getCurrentItems();
418 if (currentItem == null && currentItems != null && currentItems.size() > 0) {
419 Text.AddDate(data.getEnclosure().iterator().next());
420 } else {
421 Text.AddDate(currentItem);
422 }
423 }
424 });
425
426 // Set the INSERT_STRING action
427 setGestureAction(gestureType(StandardGestureType.INSERT_STRING), new GestureAction() {
428 @Override
429 public void exec(Gesture gesture) {
430 InsertStringGestureData data = (InsertStringGestureData) gesture.getData();
431
432 Item currentItem = data.getCurrentItem();
433 char[] charArray = data.getString().toCharArray();
434
435 if (currentItem != null) {
436 // If we are over a item taht accepts keyboard enter and the only
437 // character being entered is the enter key then we do not insert it.
438 boolean keyboardEnter = data.getCurrentItem().acceptsKeyboardEnter();
439 if (keyboardEnter && charArray.length == 1 && charArray[0] == (char) 10) {
440 return;
441 }
442 }
443
444 for (char c : charArray) {
445 processChar(c, data.isShiftDown());
446 }
447 }
448 });
449
450 // Set the JUSTIFY action
451 setGestureAction(gestureType(StandardGestureType.JUSTIFY), new GestureAction() {
452 @Override
453 public void exec(Gesture gesture) {
454 JustifyGestureData data = (JustifyGestureData) gesture.getData();
455 Text textCurrent = data.getCurrentTextItem();
456 if (textCurrent == null) {
457 for (Text t : data.getFrame().getBodyTextItems(false)) {
458 if (data.getResetWidth()) {
459 t.setWidth(null);
460 }
461 t.justify(true);
462 }
463 } else {
464 if (data.getResetWidth()) {
465 textCurrent.setWidth(null);
466 }
467 textCurrent.justify(true);
468 }
469 }
470 });
471
472 // Set the REPARSE FRAME BODY action
473 setGestureAction(gestureType(StandardGestureType.REPARSE), new GestureAction() {
474 @Override
475 public void exec(Gesture gesture) {
476 Frame currentFrame = DisplayController.getCurrentFrame();
477 if (Browser.DEBUG) {
478 ItemsList body = currentFrame.getBody(false);
479 ItemsList primaryBody = currentFrame.getPrimaryBody();
480 ItemsList surrogateBody = currentFrame.getSurrogateBody();
481 MessageBay.displayMessage("Body:" + body.toString());
482 MessageBay.displayMessage("Primaries:" + primaryBody.toString());
483 MessageBay.displayMessage("Surrogates:" + surrogateBody.toString());
484 }
485 currentFrame.parse();
486 if (Browser.DEBUG) {
487 ItemsList body = currentFrame.getBody(false);
488 ItemsList primaryBody = currentFrame.getPrimaryBody();
489 ItemsList surrogateBody = currentFrame.getSurrogateBody();
490 MessageBay.displayMessage("Body:" + body.toString());
491 MessageBay.displayMessage("Primaries:" + primaryBody.toString());
492 MessageBay.displayMessage("Surrogates:" + surrogateBody.toString());
493 }
494 }
495
496 });
497
498 // Set the LINK action
499 setGestureAction(gestureType(StandardGestureType.LINK), new GestureAction() {
500 @Override
501 public void exec(Gesture gesture) {
502 LinkGestureData data = (LinkGestureData) gesture.getData();
503 Item current = data.getCurrentItem();
504 boolean follow = data.getFollow();
505 // If its not linked then link it to itself
506 if (current instanceof Text && current.getLink() == null) {
507 String text = ((Text) current).getText();
508 // Ignore the annotation if there is one
509 if (text.charAt(0) == '@') {
510 text = text.substring(1);
511 }
512
513 if (FrameIO.isValidFrameName(text)) {
514 current.setLink(text);
515 } else if (FrameIO.isValidFramesetName(text)) {
516 current.setLink(text + '1');
517 }
518 } else if (current != null && !follow) {
519 // If its linked remove the link
520 current.setLink(null);
521 }
522 if (current != null && current.getLink() != null && follow) {
523 Navigation.Goto(current.getAbsoluteLink());
524 return;
525 }
526 }
527 });
528
529 // Set the MAKE_CIRCLE action
530 setGestureAction(gestureType(StandardGestureType.MAKE_CIRCLE), new GestureAction() {
531 @Override
532 public void exec(Gesture gesture) {
533 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
534 Item current = data.getCurrentItem();
535 Text item = null;
536 // Check if its a line to be turned into a circle
537 if (current instanceof Dot && current.getLines().size() == 1) {
538 item = Item.replaceDot(current, '@');
539 } else if (current instanceof Line && current.getAllConnected().size() == 3) {
540 Item end = ((Line) current).getEndItem();
541 if (end instanceof Dot) {
542 item = Item.replaceDot(end, '@');
543 } else if (end instanceof Text) {
544 item = (Text) end;
545 }
546 }
547 if (item != null) {
548 item.setText("@c");
549 DisplayController.setCursorPosition(item.getX(), item.getY());
550 FrameUtils.setLastEdited(null);
551 Refresh();
552 }
553 }
554 });
555
556 // Set the MOVE_CURSOR action
557 setGestureAction(gestureType(StandardGestureType.MOVE_CURSOR), new GestureAction() {
558 @Override
559 public void exec(Gesture gesture) {
560 if (gesture.isRobotic()) {
561 EcosystemManager.getInputManager().setCursorPosition(gesture.getData().getPosition());
562 } else {
563 mouseMoved(gesture.getData().getPosition());
564 }
565 }
566 });
567
568 // Set the NAVIGATE_FRAME action
569 setGestureAction(gestureType(StandardGestureType.NAVIGATE_FRAME), new GestureAction() {
570 @Override
571 public void exec(Gesture gesture) {
572 NavigateFrameGestureData data = (NavigateFrameGestureData) gesture.getData();
573 switch (data.getNavigateTo()) {
574 case BACK_FRAME:
575 DisplayController.Back();
576 break;
577 case EARLIEST_FRAME:
578 while (DisplayController.Back()) {
579 ;
580 }
581 break;
582 case FIRST_FRAME:
583 Navigation.ZeroFrame();
584 Navigation.NextFrame();
585 break;
586 case FORWARD_FRAME:
587 DisplayController.Forward();
588 break;
589 case LAST_FRAME:
590 Navigation.LastFrame();
591 break;
592 case LATEST_FRAME:
593 while (DisplayController.Forward()) {
594 ;
595 }
596 break;
597 case NEXT_FRAME:
598 Navigation.NextFrame(false);
599 break;
600 case PREVIOUS_FRAME:
601 Navigation.PreviousFrame(false);
602 break;
603 case SPECIFIC_FRAME:
604 Navigation.Goto(data.getCurrentFrameset() + data.getCurrentFrameNumber());
605 break;
606 case ZERO_FRAME:
607 Navigation.ZeroFrame();
608 break;
609 default:
610 break;
611 }
612 }
613 });
614
615 // Set the NAVIGATE_TEXT action
616 setGestureAction(gestureType(StandardGestureType.NAVIGATE_TEXT), new GestureAction() {
617 @Override
618 public void exec(Gesture gesture) {
619 NavigateTextGestureData data = (NavigateTextGestureData) gesture.getData();
620 switch (data.getNavigateTo()) {
621 case ABOVE_LINE:
622 move(Text.UP, data.getSelecting(), false);
623 break;
624 case BELOW_LINE:
625 move(Text.DOWN, data.getSelecting(), false);
626 break;
627 case LINE_END:
628 move(Text.LINE_END, data.getSelecting(), false);
629 break;
630 case LINE_HOME:
631 move(Text.LINE_HOME, data.getSelecting(), false);
632 break;
633 case NEXT_CHARACTER:
634 move(Text.RIGHT, data.getSelecting(), false);
635 break;
636 case NEXT_TEXT_ITEM:
637 NextTextItem(data.getCurrentItem(), true);
638 break;
639 case NEXT_WORD:
640 DisplayController.setTextCursor((Text) data.getCurrentItem(), Text.RIGHT, false,
641 data.getSelecting(), true, true);
642 break;
643 case PARAGRAPH_END:
644 move(Text.END, data.getSelecting(), true);
645 break;
646 case PARAGRAPH_HOME:
647 move(Text.HOME, data.getSelecting(), true);
648 break;
649 case PREVIOUS_CHARACTER:
650 move(Text.LEFT, data.getSelecting(), false);
651 break;
652 case PREVIOUS_TEXT_ITEM:
653 NextTextItem(data.getCurrentItem(), false);
654 break;
655 case PREVIOUS_WORD:
656 DisplayController.setTextCursor((Text) data.getCurrentItem(), Text.LEFT, false, data.getSelecting(),
657 true, true);
658 break;
659 default:
660 break;
661 }
662 }
663 });
664
665 // Set the NEW_FRAMESET action
666 setGestureAction(gestureType(StandardGestureType.NEW_FRAMESET), new GestureAction() {
667 @Override
668 public void exec(Gesture gesture) {
669 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
670 if (data.getCurrentItem() != null) {
671 CreateFrameset(data.getCurrentItem());
672 }
673 }
674 });
675
676 // Set the NEXT_ITEM action
677 setGestureAction(gestureType(StandardGestureType.NEXT_ITEM), new GestureAction() {
678 @Override
679 public void exec(Gesture gesture) {
680 // GestureData data = (GestureData) gesture.getData();
681 // TODO: Complete. cts16
682 }
683 });
684
685 // Set the PAN action
686 setGestureAction(gestureType(StandardGestureType.PAN), new GestureAction() {
687 @Override
688 public void exec(Gesture gesture) {
689 PanGestureData data = (PanGestureData) gesture.getData();
690 Misc.pan(DisplayController.getCurrentFrame(), data.getPanDelta().getX(), data.getPanDelta().getY());
691 }
692 });
693
694 // Set the PASTE action
695 setGestureAction(gestureType(StandardGestureType.PASTE), new GestureAction() {
696 @Override
697 public void exec(Gesture gesture) {
698 ItemSelection.paste();
699 }
700 });
701
702 // Set the PICK_UP action
703 setGestureAction(gestureType(StandardGestureType.PICK_UP), new GestureAction() {
704 @Override
705 public void exec(Gesture gesture) {
706 PickUpGestureData data = (PickUpGestureData) gesture.getData();
707 if (!data.wasDragged()) {
708 if (data.getCurrentItem() != null) {
709 handlePickup(data.getCurrentItem(), data.getPosition(), data.getCopy(), data.getExtrude());
710 } else if (data.getCurrentItems() != null) {
711 handlePickup(data.getCurrentItems(), data.getEnclosure(), data.getPosition(), data.getCopy());
712 }
713 } else {
714 Item item = data.getCurrentItem();
715 if (item instanceof Text) {
716 Text text = (Text) item;
717 text.setSelectionStart(data.getDraggedFrom());
718 text.setSelectionEnd(data.getPosition());
719 text.setSelectionColour(data.getCopy() ? Text.RANGE_COPY_COLOUR : Text.RANGE_CUT_COLOUR);
720 DisplayController.setTextCursor(text, Text.NONE, false, false, false, false);
721 if (data.getFinishedDragging()) {
722 pickupRange(text, data.getPosition(), data.getCopy(), data.getInheritAttributes());
723 } else {
724 DisplayController.requestRefresh(true);
725 }
726 } else if (item instanceof Picture && data.getCopy()) {
727 Picture picture = (Picture) item;
728 picture.setStartCrop(data.getDraggedFrom());
729 picture.setEndCrop(data.getPosition());
730 picture.setShowCrop(true);
731 picture.setHighlightMode(Item.HighlightMode.None);
732 picture.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
733 if (data.getFinishedDragging()) {
734 if (picture.isCropTooSmall()) {
735 return;
736 }
737 Picture cropped = picture.copy();
738 cropped.setParent(null);
739 // move the cropped image to the cursor
740 int width = cropped.getWidth();
741 int height = cropped.getHeight();
742 if (cropped.getSource().getX() + width < data.getPosition().getX()) {
743 cropped.getSource().setX(data.getPosition().getX() - width);
744 }
745 if (cropped.getSource().getY() + height < data.getPosition().getY()) {
746 cropped.getSource().setY(data.getPosition().getY() - height);
747 }
748 pickup(cropped);
749 // MIKE put the code below up here
750 picture.clearCropping();
751 FrameGraphics.changeHighlightMode(picture, HighlightMode.None);
752 }
753 DisplayController.requestRefresh(true);
754 }
755 }
756 }
757 });
758
759 // Set the PLACE action
760 setGestureAction(gestureType(StandardGestureType.PLACE), new GestureAction() {
761
762 private boolean shouldClearFreeItems = false;
763
764 @Override
765 public void exec(final Gesture gesture) {
766 final PickUpGestureData data = (PickUpGestureData) gesture.getData();
767 if (data.getCopy()) {
768 shouldClearFreeItems = false;
769 copyPlace(data.getCurrentItem());
770 } else {
771 if (doMerging(data.getCurrentItem())) {
772 shouldClearFreeItems = true;
773 deleteItemsAction(data.getCurrentItem());
774 } else {
775 shouldClearFreeItems = true;
776 anchorFreeItemsAction(data.getCurrentItems());
777 }
778 }
779 }
780
781 @Override
782 public void finalise(final Gesture gesture) {
783 if (shouldClearFreeItems) {
784 FreeItems.getInstance().clear();
785 }
786 }
787 });
788
789 // Set the REFRESH action
790 setGestureAction(gestureType(StandardGestureType.REFRESH), new GestureAction() {
791 @Override
792 public void exec(Gesture gesture) {
793 RefreshGestureData data = (RefreshGestureData) gesture.getData();
794 if (data.shouldReloadFrameFirst()) {
795 MessageBay.displayMessage("Loaded in data from external edit made my another user.");
796 DisplayController.Reload(DisplayController.TwinFramesSide.LEFT);
797 }
798 Refresh(data.shouldRefreshFrameSize());
799 }
800 });
801
802 // Set the ROTATE_DISPLAY_MODE action
803 setGestureAction(gestureType(StandardGestureType.ROTATE_DISPLAY_MODE), new GestureAction() {
804 @Override
805 public void exec(Gesture gesture) {
806 DisplayController.rotateAudienceModes();
807 }
808 });
809
810 // Set the ROTATE_FONT_FAMILY action
811 setGestureAction(gestureType(StandardGestureType.ROTATE_FONT_FAMILY), new GestureAction() {
812 @Override
813 public void exec(Gesture gesture) {
814 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
815 Item currentItem = data.getCurrentItem();
816 Collection<Item> currentItems = data.getCurrentItems();
817 if (currentItem == null && currentItems != null && currentItems.size() > 0) {
818 ToggleFontFamily(data.getEnclosure().iterator().next());
819 } else if (currentItem != null) {
820 ToggleFontFamily(currentItem);
821 }
822 }
823 });
824
825 // Set the ROTATE_FONT_STYLE action
826 setGestureAction(gestureType(StandardGestureType.ROTATE_FONT_STYLE), new GestureAction() {
827 @Override
828 public void exec(Gesture gesture) {
829 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
830 Item currentItem = data.getCurrentItem();
831 Collection<Item> currentItems = data.getCurrentItems();
832 if (currentItem == null && currentItems != null && currentItems.size() > 0) {
833 ToggleFontStyle(data.getEnclosure().iterator().next());
834 } else if (currentItem != null) {
835 ToggleFontStyle(currentItem);
836 }
837 }
838 });
839
840 // Set the SAVE action
841 setGestureAction(gestureType(StandardGestureType.SAVE), new GestureAction() {
842 @Override
843 public void exec(Gesture gesture) {
844 Save();
845 }
846 });
847
848 // Set the SCALE action
849 setGestureAction(gestureType(StandardGestureType.SCALE), new GestureAction() {
850 @Override
851 public void exec(Gesture gesture) {
852 ScaleGestureData data = (ScaleGestureData) gesture.getData();
853 boolean scaleAroundCursor = data.getPosition() != null;
854 int scaleFactor = data.getScaleFactor();
855 Item currentItem = data.getCurrentItem();
856 Collection<Item> currentItems = data.getCurrentItems();
857 if (currentItem == null && currentItems != null && currentItems.size() > 0) {
858 SetSize(data.getEnclosure().iterator().next(), scaleFactor, false, true, scaleAroundCursor);
859 } else if (currentItem != null) {
860 SetSize(currentItem, scaleFactor, true, false, scaleAroundCursor);
861 if (currentItem instanceof Text) {
862 DisplayController.setTextCursor((Text) currentItem, Text.NONE, true, false, false, true);
863 }
864 }
865 }
866 });
867
868 // Set the SELECT_AREA action
869 setGestureAction(gestureType(StandardGestureType.SELECT_AREA), new GestureAction() {
870 @Override
871 public void exec(Gesture gesture) {
872 SelectAreaGestureData data = (SelectAreaGestureData) gesture.getData();
873 Item item = data.getCurrentItem();
874 if (item != null && item instanceof Text) {
875 Text text = (Text) item;
876 text.setSelectionStart(data.getDraggedFrom());
877 text.setSelectionEnd(data.getPosition());
878 text.setSelectionColour(Text.RANGE_SELECT_COLOUR);
879 DisplayController.setTextCursor(text, Text.NONE, false, false, false, false);
880 DisplayController.requestRefresh(true);
881 }
882 }
883 });
884
885 // Set the SPLIT_TEXT action
886 setGestureAction(gestureType(StandardGestureType.SPLIT_TEXT), new GestureAction() {
887 @Override
888 public void exec(Gesture gesture) {
889 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
890 Text text = data.getCurrentTextItem();
891 if (text == null) {
892 return;
893 }
894 List<String> textLines = text.getTextList();
895 if (textLines.size() <= 1) {
896 return;
897 }
898 // remove all except the first line of text from the item being split
899 text.setText(textLines.get(0));
900 int y = text.getY();
901 Frame parent = text.getParent();
902 for (int i = 1; i < textLines.size(); i++) {
903 Text newText = text.copy();
904 newText.setText(textLines.get(i));
905 y += newText.getBoundsHeight();
906 newText.setY(y);
907 // update the items ID to prevent conflicts with the current frame
908 if (parent != null) {
909 newText.setID(parent.getNextItemID());
910 parent.addItem(newText);
911 }
912 }
913 }
914 });
915
916 // Set the TOGGLE_ANNOTATION action
917 setGestureAction(gestureType(StandardGestureType.TOGGLE_ANNOTATION), new GestureAction() {
918 @Override
919 public void exec(Gesture gesture) {
920 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
921 Item currentItem = data.getCurrentItem();
922 Collection<Item> currentItems = data.getCurrentItems();
923 if (currentItem == null && currentItems != null && currentItems.size() > 0) {
924 ToggleAnnotation(data.getEnclosure().iterator().next());
925 } else if (currentItem != null) {
926 ToggleAnnotation(currentItem);
927 }
928 }
929 });
930
931 // Set the TOGGLE_ARROWHEAD action
932 setGestureAction(gestureType(StandardGestureType.TOGGLE_ARROWHEAD), new GestureAction() {
933 @Override
934 public void exec(Gesture gesture) {
935 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
936 Item item = data.getCurrentItem();
937 if (item instanceof Line) {
938 ((Line) item).toggleArrow();
939 DisplayController.requestRefresh(true);
940 }
941 }
942 });
943
944 // Set the TOGGLE_BOLD action
945 setGestureAction(gestureType(StandardGestureType.TOGGLE_BOLD), new GestureAction() {
946 @Override
947 public void exec(Gesture gesture) {
948 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
949 Item current = data.getCurrentItem();
950 if (current instanceof Text) {
951 ((Text) current).toggleBold();
952 }
953 }
954 });
955
956 // Set the TOGGLE_ITALICS action
957 setGestureAction(gestureType(StandardGestureType.TOGGLE_ITALICS), new GestureAction() {
958 @Override
959 public void exec(Gesture gesture) {
960 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
961 Item current = data.getCurrentItem();
962 if (current instanceof Text) {
963 ((Text) current).toggleItalics();
964 }
965 }
966 });
967
968 // Set the TOGGLE_ITEMS_MARK action
969 setGestureAction(gestureType(StandardGestureType.TOGGLE_ITEMS_MARK), new GestureAction() {
970 @Override
971 public void exec(Gesture gesture) {
972 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
973 Item current = data.getCurrentItem();
974 if (current == null) {
975 return;
976 }
977 if (current != null && !current.hasPermission(UserAppliedPermission.full)) {
978 MessageBay.displayMessage("Insufficient permission toggle the items mark");
979 return;
980 }
981 boolean newValue = !(current.getLinkMark() || current.getActionMark());
982 current.setLinkMark(newValue);
983 current.setActionMark(newValue);
984 }
985 });
986
987 // Set the TOGGLE_XRAY_MODE action
988 setGestureAction(gestureType(StandardGestureType.TOGGLE_XRAY_MODE), new GestureAction() {
989 @Override
990 public void exec(Gesture gesture) {
991 DisplayController.ToggleXRayMode();
992 }
993 });
994
995 // Set the UNDO action
996 setGestureAction(gestureType(StandardGestureType.UNDO), new GestureAction() {
997 @Override
998 public void exec(Gesture gesture) {
999 UndoGestureData data = (UndoGestureData) gesture.getData();
1000 if (data.getRedo()) {
1001 DisplayController.getCurrentFrame().redo();
1002 } else {
1003 DisplayController.getCurrentFrame().undo();
1004 }
1005 }
1006 });
1007
1008 // Set the ZOOM action
1009 setGestureAction(gestureType(StandardGestureType.ZOOM), new GestureAction() {
1010 @Override
1011 public void exec(Gesture gesture) {
1012 ZoomGestureData data = (ZoomGestureData) gesture.getData();
1013 zoomFrameIfEnabled(DisplayController.getCurrentFrame(), data.getScaleFactor(), data.getPosition());
1014 }
1015 });
1016
1017 // Set the ACTIVATE BUTTON action
1018 setGestureAction(gestureType(StandardGestureType.ACTIVATE_BUTTON), new GestureAction() {
1019 @Override
1020 public void exec(Gesture gesture) {
1021 ItemSpecificGestureData data = (ItemSpecificGestureData) gesture.getData();
1022 Item currentItem = data.getCurrentItem();
1023 if (currentItem != null && currentItem.acceptsKeyboardEnter()) {
1024 getGestureAction(Gesture.GestureType.get("CLICK")).exec(gesture);
1025 }
1026 }
1027 });
1028
1029 setGestureAction(gestureType(StandardGestureType.CYCLE_SURROGATE_MODE), new GestureAction() {
1030 @Override
1031 public void exec(Gesture gesture) {
1032 if (((UndoGestureData) gesture.getData()).getRedo()) {
1033 DisplayController.ResetSurrogateMode();
1034 } else {
1035 DisplayController.ToggleSurrogateMode();
1036 }
1037 }
1038 });
1039
1040 }
1041
1042 /** Initialises the set of gesture types. */
1043 private void initialiseGestureTypes() {
1044 for (StandardGestureType type : StandardGestureType.values()) {
1045 GestureType gestureType;
1046 if ((gestureType = GestureType.register(type.toString())) == null) {
1047 gestureType = GestureType.get(type.toString());
1048 }
1049 _gestureTypes.put(type, gestureType);
1050 }
1051 }
1052
1053 /** Gets the gesture type associated with the given standard type. */
1054 public GestureType gestureType(StandardGestureType type) {
1055 if (type == null) {
1056 return null;
1057 }
1058
1059 return _gestureTypes.get(type);
1060 }
1061
1062 /*
1063 * TODO: EVERYTHING BELOW HERE IS ONLY TEMPORARILY SITUATED IN THIS CLASS
1064 *
1065 * The following methods were mostly copy-pasted here from the old
1066 * FrameKeyboardActions and FrameMouseActions classes (to enable quick
1067 * change-over to the new input system). Really they should be relocated to more
1068 * suitable homes based on their function.
1069 */
1070
1071 /**
1072 * Picks up an item on a frame.
1073 *
1074 * @param toGrab
1075 * item to be picked up
1076 * @param removeItem
1077 * true if the item should be removed from the frame
1078 */
1079 public static void pickup(Item toGrab) {
1080 if (toGrab.isFrameName()) {
1081 return;
1082 }
1083
1084 if (!toGrab.hasPermission(UserAppliedPermission.full)) {
1085 if (toGrab.getEditTarget() != toGrab) {
1086 pickup(toGrab.getEditTarget());
1087 } else {
1088 MessageBay.displayMessage("Insufficient permission pickup the item");
1089 }
1090 return;
1091 }
1092
1093 if (toGrab instanceof Circle) {
1094 toGrab.setHighlightMode(HighlightMode.Connected);
1095 } else if (toGrab.isVisible()) {
1096 toGrab.setHighlightMode(HighlightMode.Normal);
1097 }
1098
1099 // Brook: If the widget corner is being picked up. Instead refer to
1100 // picking up the edge for fixed-sized widgets so it is not so confusing
1101 if (toGrab instanceof WidgetCorner) {
1102 WidgetCorner wc = (WidgetCorner) toGrab;
1103 if (wc.getWidgetSource().isFixedSize()) {
1104 for (Item i : toGrab.getConnected()) {
1105 if (i instanceof WidgetEdge) {
1106 toGrab = i;
1107 break;
1108 }
1109 }
1110 }
1111 }
1112 pickup(toGrab.getConnected());
1113 }
1114
1115 // TODO: Review if these are needed as they ugly up the code. cts16
1116 static int _offX, _offY;
1117
1118 /**
1119 * Picks up a group of items on a frame.
1120 *
1121 * @param toGrab
1122 * The items to pick up.
1123 */
1124 public static void pickup(Collection<Item> toGrab) {
1125 if (toGrab == null || toGrab.size() == 0) {
1126 return;
1127 }
1128
1129 boolean bReparse = false;
1130 boolean bRecalculate = false;
1131
1132 Frame currentFrame = DisplayController.getCurrentFrame();
1133 String currentFrameName = currentFrame.getName();
1134 Iterator<Item> iter = toGrab.iterator();
1135 while (iter.hasNext()) {
1136 Item i = iter.next();
1137 if (!i.hasPermission(UserAppliedPermission.full)) {
1138 iter.remove();
1139 continue;
1140 }
1141 if (i.equals(_lastHighlightedItem)) {
1142 _lastHighlightedItem = null;
1143 }
1144
1145 bRecalculate |= i.recalculateWhenChanged();
1146 // i.setSelectedMode(SelectedMode.None);
1147 // Check if it has a relative link if so make it absolute
1148 i.setAbsoluteLink();
1149 // parent may be null
1150 if (i.getParent() != null) {
1151 i.getParent().removeItem(i);
1152 if (currentFrameName.equals(i.getParent().getName())) {
1153 i.setParent(null);
1154 }
1155 }
1156 FreeItems.getInstance().add(i);
1157 i.setFloating(true);
1158 // If its a vector pick up a copy of the stuff on the vector frame
1159 if (i.hasVector()) {
1160 bReparse = true;
1161
1162 Frame overlayFrame = FrameIO.LoadFrame(i.getAbsoluteLink());
1163 Collection<Item> copies = ItemUtils.CopyItems(overlayFrame.getNonAnnotationItems(false), i.getVector());
1164 for (Item copy : copies) {
1165 FreeItems.getInstance().add(copy);
1166 copy.setEditTarget(i);
1167 copy.setFloating(true);
1168 copy.setParent(null);
1169 // copy.setHighlightMode(HighlightMode.Connected);
1170 }
1171 }
1172 }
1173 currentFrame.change();
1174
1175 _lastHighlightedItem = null;
1176 updateCursor();
1177
1178 // if there are multiple items in the list, determine which to use for
1179 // offset calculations
1180 if (toGrab.size() > 1) {
1181 for (Item i : toGrab) {
1182 // MIKE: Movement goes haywire if these are removed because Line
1183 // class returns 0 for getX
1184 if (!(i instanceof Line) && !(i instanceof XRayable)) {
1185 _offX = DisplayController.getMouseX() - i.getX() + i.getOffset().getX();
1186 _offY = DisplayController.getMouseY() - i.getY() + i.getOffset().getY();
1187
1188 // make the offset item the first item in the list (so
1189 // move method knows which item to use)
1190 FreeItems.getInstance().set(FreeItems.getInstance().indexOf(i), FreeItems.getInstance().get(0));
1191 FreeItems.getInstance().set(0, i);
1192 break;
1193 }
1194 }
1195
1196 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
1197 ItemUtils.EnclosedCheck(toGrab);
1198 // otherwise, just use the first item
1199 } else if (toGrab.size() == 1) {
1200 Item soleItem = toGrab.iterator().next();
1201 _offX = DisplayController.getMouseX() - soleItem.getX() + soleItem.getOffset().getX();
1202 _offY = DisplayController.getMouseY() - soleItem.getY() + soleItem.getOffset().getY();
1203 // Now call move so that if we are on a message in the message box
1204 // It doesn't appear up the top of the screen!!
1205 move(toGrab, EcosystemManager.getInputManager().getCursorPosition());
1206 } else {
1207 MessageBay.displayMessage("Insufficient permission to pickup the items");
1208 }
1209 if (bReparse) {
1210 FrameUtils.Parse(currentFrame, false, false, false);
1211 } else {
1212 currentFrame.notifyObservers(bRecalculate);
1213 }
1214
1215 DisplayController.requestRefresh(true);
1216 }
1217
1218 /**
1219 * Updates the current mouse cursor to whatever it should be. i.e. Hidden when
1220 * rubber-banding lines, otherwise default (arrow)
1221 */
1222 public static void updateCursor() {
1223 if (FreeItems.rubberBanding()) {
1224 DisplayController.setCursor(Item.HIDDEN_CURSOR);
1225 return;
1226 }
1227 // This is to make sure the TEXT_CURSOR doesn't get inadvertently turned off!
1228 Item on = FrameUtils.getCurrentItem();
1229 if (on != null && on instanceof Text) {
1230 return;
1231 }
1232 DisplayController.setCursor(Item.DEFAULT_CURSOR);
1233 }
1234
1235 private static Text _toRemove = null;
1236
1237 public static void processChar(char ch, boolean isShiftDown) {
1238 Navigation.ResetLastAddToBack();
1239 Item on = FrameUtils.getCurrentItem();
1240
1241 // permission check
1242 if (on != null && !on.hasPermission(UserAppliedPermission.full)) {
1243 boolean canTabToNext = !isShiftDown && ch == '\t' && on instanceof Text && ((Text) on).getTabNext() != null;
1244 boolean canTabToPrevious = isShiftDown && ch == '\t' && on instanceof Text && ((Text) on).getTabPrevious() != null;
1245 if (!canTabToNext && !canTabToPrevious) {
1246 MessageBay.displayMessage("Insufficient permission to edit this item");
1247 return;
1248 }
1249 }
1250
1251 // Certain keys are handled by MagneticConstraints.
1252 // If the shift key is down the 'inverted' version of that key is used.
1253 if (isShiftDown && MagneticConstraints.getInstance().keyHit(-ch, on)) {
1254 return;
1255 } else if (MagneticConstraints.getInstance().keyHit(ch, on)) {
1256 return;
1257 }
1258
1259 if (_toRemove != null && on != _toRemove) {
1260 assert (_toRemove.getLength() == 0);
1261 // This line is to protect mistaken removal of items if there is a bug...
1262 if (_toRemove.getLength() == 0) {
1263 DisplayController.getCurrentFrame().removeItem(_toRemove);
1264 }
1265 }
1266 _toRemove = null;
1267
1268 // ignore delete and backspace if in free space
1269 if ((on == null || !(on instanceof Text)) && (ch == '\b' || ch == '\t' || ch == Text.DELETE_CHARACTER)) {
1270 return;
1271 }
1272
1273 SessionStats.TypedChar(ch);
1274
1275 // check for dot's being replaced with text
1276 if (on != null && on instanceof Dot && !(on instanceof WidgetCorner)) {
1277 if (ch == '\b' || ch == Text.DELETE_CHARACTER) {
1278 return;
1279 }
1280 Item.replaceDot(on, ch);
1281 return;
1282 }
1283
1284 // only text can interact with keyboard events
1285 if (on != null && !(on instanceof Text)) {
1286 on = null;
1287 }
1288
1289 // DisplayIO.UpdateTitle();
1290
1291 Text text = (Text) on;
1292 // if this text is empty but has not been removed (such as from ESC-pushdown)
1293 if (text != null && text.isEmpty() && (ch == '\b' || ch == Text.DELETE_CHARACTER)) {
1294 if (text.getLines().size() > 0) {
1295 Text.replaceText(text);
1296 } else {
1297 DisplayController.setCursor(Item.DEFAULT_CURSOR);
1298 }
1299 return;
1300 }
1301
1302 // if the user is in free space, create a new text item
1303 // MikeSays: Why do we have to check is highlighted... doing so causes
1304 // problems if you type characters too fast, they turn into multiple text
1305 // items. ie. JK together on the Linux laptop.
1306 if (on == null /* || !on.isHighlighted() */) {
1307 // DisplayIO.UpdateTitle();
1308 text = Text.createText(ch);
1309 text.justify(false);
1310
1311 FrameUtils.setLastEdited(text);
1312 DisplayController.setTextCursor(text, Text.NONE);
1313 DisplayController.requestRefresh(true);
1314 return;
1315 } else {
1316 FrameUtils.setLastEdited(text);
1317 }
1318
1319 DisplayController.setTextCursor(text, Text.NONE);
1320 insertCharacterAction(text, isShiftDown, ch);
1321
1322 // This repaint is needed for WINDOWS only?!?!? Mike is not sure why!
1323 // if (ch == Text.DELETE_CHARACTER)
1324 DisplayController.requestRefresh(true);
1325
1326 // a change has occured to the Frame
1327 text.getParent().setChanged(true);
1328
1329 // check that the Text item still exists (hasn't been deleted\backspaced
1330 // away)
1331 if (text.isEmpty() && text.getMinWidth() == null) {
1332 _toRemove = text;
1333
1334 if (text.hasAction()) {
1335 text.setActionMark(true);
1336 } else if (text.getLink() != null) {
1337 text.setLinkMark(true);
1338 } else if (text.getLines().size() > 0) {
1339 Item.replaceText(text);
1340 } else {
1341 // DisplayIO.getCurrentFrame().removeItem(text);
1342 DisplayController.setCursor(Item.DEFAULT_CURSOR);
1343 }
1344 }
1345 }
1346
1347 public static Text insertCharacterAction(Text text, boolean isShiftDown, final char c) {
1348 final float oldY = DisplayController.getFloatMouseY();
1349 final float oldX = DisplayController.getFloatMouseX();
1350 // System.err.println("insertCharacterAction: Prior to inserting character mouse
1351 // at: " + oldX + "," + oldY);
1352 Point newMouse = null;
1353 if (c == '\t') {
1354 if (isShiftDown) {
1355 Text tabPrevious = text.getTabPrevious();
1356 if (tabPrevious == null) {
1357 newMouse = text.removeTab(c, oldX, oldY);
1358 } else {
1359 StandardGestureActions.moveCursorAndFreeItems(tabPrevious.getX(), tabPrevious.getY());
1360 return text;
1361 }
1362 } else {
1363 Text tabNext = text.getTabNext();
1364 if (tabNext == null) {
1365 newMouse = text.insertTab(c, oldX, oldY);
1366 } else {
1367 StandardGestureActions.moveCursorAndFreeItems(tabNext.getX(), tabNext.getY());
1368 return text;
1369 }
1370 }
1371 } else {
1372 newMouse = text.insertChar(c, oldX, DisplayController.getFloatMouseY());
1373 }
1374
1375 // check if the user hit enter and the cursor is now on another text item
1376 if (oldY < newMouse.getY()) {
1377 AxisAlignedBoxBounds rect = text.getBoundingBox();
1378
1379 Item justBelow = FrameUtils.onItem(DisplayController.getCurrentFrame(), text.getX() + 10,
1380 rect.getMinY() + rect.getHeight() + 1, false);
1381
1382 // Dont drop unless
1383 if (justBelow != null && justBelow instanceof Text && justBelow != text) {
1384 // Drop all the items below it down!
1385 // Get the list of items that must be dropped
1386 List<Text> column = DisplayController.getCurrentFrame().getColumn(text);
1387 FrameUtils.Align(column, false, 0);
1388 }
1389 }
1390
1391 DisplayController.setCursorPosition(newMouse.getX(), newMouse.getY(), false);
1392 return text;
1393 }
1394
1395 /**
1396 * Moves the items to the current mouse position (plus the current offset)
1397 *
1398 * @param toMove
1399 */
1400 public static void move(Collection<Item> toMove, Point to) {
1401 move(toMove, to, false);
1402 }
1403
1404 static void move(Collection<Item> toMove, Point to, boolean cursor) {
1405 Item firstDot = toMove.iterator().next();
1406
1407 int deltax = (to.getX() - (cursor ? 0 : _offX)) - firstDot.getX();
1408 int deltay = (to.getY() - (cursor ? 0 : _offY)) - firstDot.getY();
1409
1410 // Cache the position of all items before moving any as constraints
1411 // may move the items before we get to them, causing jumping
1412 HashMap<Item, Point> preMovePositions = new HashMap<Item, Point>();
1413 for (Item move : toMove) {
1414 preMovePositions.put(move, move.getPosition());
1415 }
1416
1417 for (Item move : toMove) {
1418 Point pos = preMovePositions.get(move);
1419 move.setPosition(pos.getX() + deltax, pos.getY() + deltay);
1420
1421 if (!cursor && move instanceof Text) {
1422 ((Text) move).setAlpha(FrameUtils.hasCurrentItem() && FreeItems.getInstance().size() > 0 ? 60 : -1);
1423 }
1424 }
1425
1426 DisplayController.requestRefresh(true);
1427 }
1428
1429 /**
1430 * Returns true if the mouse moved during TDFC. This will happen if there is a
1431 * start annotation item on the frame.
1432 *
1433 * @param linker
1434 * @return
1435 */
1436 public static boolean tdfc(Item linker) throws RuntimeException {
1437 // if this is a non-usable item
1438 if (linker.getID() < 0) {
1439 return false;
1440 }
1441
1442 // Check if its an image that can be resized to fit a box around it
1443 String text = linker.getText();
1444 boolean isVector = text.equals("@v") || text.equals("@av");
1445 boolean isFrameImage = text.equals("@f");
1446 boolean isBitmap = false; // text.equals("@b");
1447
1448 if (isVector || isFrameImage || isBitmap) {
1449 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds(linker.getPosition());
1450 if (enclosure != null) {
1451 for (Item i : enclosure) {
1452 if (i.isLineEnd() && i.isEnclosed()) {
1453 if (!isVector) {
1454 DisplayController.getCurrentFrame().removeAllItems(enclosure);
1455 }
1456 AxisAlignedBoxBounds rect = i.getEnclosedBox();
1457 long width = Math.round(rect.getWidth());
1458 if (isVector) {
1459 NumberFormat nf = Vector.getNumberFormatter();
1460 linker.setText(linker.getText() + ": "
1461 + nf.format((width / DisplayController.getFramePaintAreaWidth())));
1462 } else {
1463 linker.setText(linker.getText() + ": " + width);
1464 }
1465
1466 linker.setPosition(new Point(rect.getMinX(), rect.getMinY()));
1467 linker.setThickness(i.getThickness());
1468 linker.setBorderColor(i.getColor());
1469 break;
1470 }
1471 }
1472 if (!isVector) {
1473 deleteItems(enclosure, false);
1474 }
1475 }
1476 }
1477
1478 boolean mouseMoved;
1479
1480 linker.getParent().setChanged(true);
1481
1482 Frame next = FrameIO.CreateNewFrame(linker, null);
1483
1484 linker.setLink("" + next.getNumber());
1485
1486 for (Item i : next.getTextItems()) {
1487 // Set the link for @Parent annotation item if one
1488 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT) && i.getLink() == null) {
1489 Frame parent = linker.getParentOrCurrentFrame();
1490 i.setLink(parent.getName());
1491 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_BACKUP, false)) {
1492 // Delink backup tag if it is on the frame
1493 i.setLink(null);
1494 }
1495 }
1496
1497 FrameUtils.DisplayFrame(next, true, true);
1498 FrameUtils.setTdfcItem(linker);
1499
1500 mouseMoved = next.moveMouseToDefaultLocation();
1501 // this needs to be done if the user doesnt move the mouse before doing
1502 // tdfc while the cursor is set to the text cursor
1503 DisplayController.setCursor(Item.DEFAULT_CURSOR);
1504 // This needs to be done in case there was a @start on the frame which
1505 // triggers changed to be set to true when it should stay as false
1506 next.setChanged(false);
1507 return mouseMoved;
1508 }
1509
1510 public static void resetOffset() {
1511 if (FreeItems.hasItemsAttachedToCursor()) {
1512 _offX = DisplayController.getMouseX() - FreeItems.getInstance().get(0).getX()
1513 + FreeItems.getInstance().get(0).getOffset().getX();
1514 _offY = DisplayController.getMouseY() - FreeItems.getInstance().get(0).getY()
1515 + FreeItems.getInstance().get(0).getOffset().getY();
1516 }
1517 }
1518
1519 /**
1520 * Forces a re-parse and repaint of the current Frame.
1521 */
1522 public static void Refresh() {
1523 Refresh(false);
1524 }
1525
1526 /**
1527 * Forces a re-parse and repaint of the current Frame.
1528 */
1529 public static void Refresh(boolean refreshFrameSize) {
1530 Frame currentFrame = DisplayController.getCurrentFrame();
1531
1532 if (refreshFrameSize) {
1533 currentFrame.refreshSize();
1534 }
1535
1536 // Refresh widgets that use its self as a data source
1537 currentFrame.notifyObservers(true);
1538
1539 if (FrameIO.isProfileFrame(currentFrame)) {
1540 // TODO ensure that users can not delete the first frame in a frameset...
1541 // TODO handle the case when users manually delete the first frame in a frameset
1542 // from the filesystem
1543 Frame profile = FrameIO.LoadFrame(currentFrame.getFramesetName() + "1");
1544 assert (profile != null);
1545 FrameUtils.Parse(currentFrame);
1546 FrameUtils.ParseProfile(profile);
1547 } else {
1548 FrameUtils.Parse(currentFrame);
1549 }
1550 // Need to update the cursor for when text items change to @b pictures
1551 // etc and the text cursor is showing
1552 updateCursor();
1553 refreshHighlights();
1554 DisplayController.requestRefresh(false);
1555 }
1556
1557 /**
1558 * Saves the current frame.
1559 */
1560 public static void Save() {
1561 Frame current = DisplayController.getCurrentFrame();
1562 current.change();
1563 FrameIO.SaveFrame(current, true, true);
1564 MessageBay.displayMessage("Current frame has been saved.");
1565 }
1566
1567 public static final String DEFAULT_NEW_ITEM_TEXT = "";
1568
1569 public static String getAutoBullet(String s) {
1570 return getBullet(s, true);
1571 }
1572
1573 private static String getBullet(String s, boolean nextBullet) {
1574 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1575
1576 if (s == null) {
1577 return newItemText;
1578 }
1579
1580 /*
1581 * Item i = ItemUtils.FindTag(DisplayIO.getCurrentFrame().getItems(),
1582 * "@NoAutoBullets"); if (i != null) return newItemText;
1583 */
1584 // Separate the space at the start of the text item
1585 String preceedingSpace = "";
1586 for (int i = 0; i < s.length(); i++) {
1587 if (!Character.isSpaceChar(s.charAt(i))) {
1588 preceedingSpace = s.substring(0, i);
1589 s = s.substring(i);
1590 break;
1591 }
1592 }
1593
1594 // figure out the type of the text item
1595 // This allows us to do auto bulleting
1596 if (s != null && s.length() > 1) {
1597 // First check for text beginning with * @ # etc
1598 // These are simple auto bullets
1599 if (!Character.isLetterOrDigit(s.charAt(0)) && !Character.isSpaceChar(s.charAt(0))) {
1600 if (Text.isBulletChar(s.charAt(0))) {
1601 int nonSpaceIndex = 1;
1602 // Find the end of the bullet and space after the bullet
1603 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1604 nonSpaceIndex++;
1605 }
1606 // we must have a special char followed by >= 1 space
1607 if (nonSpaceIndex > 1) {
1608 newItemText = s.substring(0, nonSpaceIndex);
1609 }
1610 }
1611 // Auto numbering and lettering
1612 } else {
1613 if (Character.isDigit(s.charAt(0))) {
1614 newItemText = getAutoNumber(s, nextBullet);
1615 // Auto lettering
1616 } else if (Character.isLetter(s.charAt(0))) {
1617 newItemText = getAutoLetter(s, nextBullet);
1618 }
1619 }
1620 }
1621 return preceedingSpace + newItemText;
1622 }
1623
1624 /**
1625 * Gets the string to be used to start the next auto lettered text item.
1626 *
1627 * @param s
1628 * the previous text items
1629 * @return the initial text for the new text item
1630 */
1631 private static String getAutoLetter(String s, boolean nextBullet) {
1632 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1633
1634 int nonLetterIndex = 1;
1635
1636 if (isAutoNumberOrLetterChar(s.charAt(nonLetterIndex))) {
1637
1638 // Now search for the next non space character
1639 int nonSpaceIndex = nonLetterIndex + 1;
1640 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1641 nonSpaceIndex++;
1642 }
1643
1644 // If there was a space then we have reached the end of our auto
1645 // text
1646 if (nonSpaceIndex > nonLetterIndex + 1) {
1647 if (nextBullet) {
1648 newItemText = nextLetterSequence(s.substring(0, nonLetterIndex))
1649 + s.substring(nonLetterIndex, nonSpaceIndex);
1650 } else {
1651 newItemText = s.substring(0, nonSpaceIndex);
1652 }
1653 }
1654 }
1655 return newItemText;
1656 }
1657
1658 /**
1659 * Gets the string to be used to start the next auto numbered text item.
1660 *
1661 * @param s
1662 * the previous text item
1663 * @return the beginning of the next auto numbered text item
1664 */
1665 private static String getAutoNumber(String s, boolean nextBullet) {
1666 String newItemText = DEFAULT_NEW_ITEM_TEXT;
1667
1668 int nonDigitIndex = 1;
1669 while (Character.isDigit(s.charAt(nonDigitIndex))) {
1670 nonDigitIndex++;
1671
1672 if (nonDigitIndex + 1 >= s.length()) {
1673 return DEFAULT_NEW_ITEM_TEXT;
1674 }
1675 }
1676
1677 if (isAutoNumberOrLetterChar(s.charAt(nonDigitIndex))) {
1678
1679 // we must have a number followed one non letter
1680 // then one or more spaces
1681 int nonSpaceIndex = nonDigitIndex + 1;
1682 while (nonSpaceIndex < s.length() && s.charAt(nonSpaceIndex) == ' ') {
1683 nonSpaceIndex++;
1684 }
1685
1686 if (nonSpaceIndex > nonDigitIndex + 1) {
1687 if (nextBullet) {
1688 newItemText = (Integer.parseInt(s.substring(0, nonDigitIndex)) + 1)
1689 + s.substring(nonDigitIndex, nonSpaceIndex);
1690 } else {
1691 newItemText = s.substring(0, nonSpaceIndex);
1692 }
1693 }
1694 }
1695 return newItemText;
1696 }
1697
1698 private static boolean isAutoNumberOrLetterChar(char c) {
1699 return c == ':' || c == '-' || c == '.' || c == ')' || c == '>';
1700 }
1701
1702 /**
1703 * Gets the next letter sequence for a given string to be used in auto
1704 * lettering.
1705 *
1706 * @param s
1707 * a sequence of letters
1708 * @return the next sequence of letters
1709 */
1710 static private String nextLetterSequence(String s) {
1711 if (s.length() > 1) {
1712 return s;
1713 }
1714
1715 if (s.equals("z")) {
1716 return "a";
1717 }
1718
1719 return (char) (s.charAt(0) + 1) + "";
1720 }
1721
1722 public static String getBullet(String s) {
1723 return getBullet(s, false);
1724 }
1725
1726 /**
1727 * true if lastItem only has highlighting removed when a new item is
1728 * highlighted.
1729 */
1730 private static boolean _lastHoldsHighlight = false;
1731
1732 public static void setHighlightHold(boolean hold) {
1733 _lastHoldsHighlight = hold;
1734 }
1735
1736 public static final int CONTEXT_FREESPACE = 0;
1737
1738 public static final int CONTEXT_AT_TEXT = 1;
1739
1740 public static final int CONTEXT_AT_LINE = 2;
1741
1742 public static final int CONTEXT_AT_DOT = 3;
1743
1744 public static final int CONTEXT_AT_ENCLOSURE = 4;
1745
1746 /** the current context of the cursor. */
1747 private static int _context = 0;
1748
1749 /** keeps track of the last highlighted Item. */
1750 private static Item _lastHighlightedItem = null;
1751
1752 private static boolean _forceArrowCursor = true;
1753
1754 public static void refreshHighlights() {
1755 // ByMike: Get the item the mouse is hovering over
1756 Item click = FrameUtils.getCurrentItem();
1757 Item on = null;
1758
1759 // System.out.println(click);
1760 if (click != null) {
1761 on = click;
1762 // set the context
1763 if (on instanceof Line) {
1764 _context = CONTEXT_AT_LINE;
1765 } else if (on instanceof Dot) {
1766 _context = CONTEXT_AT_DOT;
1767 } else if (on instanceof Text) {
1768 _context = CONTEXT_AT_TEXT;
1769 }
1770 } else {
1771 _context = CONTEXT_FREESPACE;
1772 }
1773
1774 // if the user is pointing at an item, highlight it
1775 if (on != null && !FreeItems.getInstance().contains(on)) {
1776 // if the user can spot-weld, show the virtual spot
1777 if (FreeItems.getInstance().size() == 2 && on instanceof Line) {
1778 Line line = (Line) on;
1779 Item freeItem0 = FreeItems.getInstance().get(0);
1780 Item freeItem1 = FreeItems.getInstance().get(1);
1781 Item lineEnd = freeItem0.isLineEnd() ? freeItem0 : (freeItem1.isLineEnd() ? freeItem1 : null);
1782 if (lineEnd != null) {
1783 line.showVirtualSpot(lineEnd, DisplayController.getMouseX(), DisplayController.getMouseY());
1784 } else {
1785 // The user is pointing at another point or text item etc
1786 FrameGraphics.changeHighlightMode(on, Item.HighlightMode.Normal);
1787 }
1788 } else {
1789 // FrameGraphics.ChangeSelectionMode(on,
1790 // Item.SelectedMode.Connected);
1791 // TODO: The method below is for the most part redundant
1792
1793 // Stops interfering with the highlighting while cropping a picture
1794 if (on instanceof Picture) {
1795 Picture p = (Picture) on;
1796 if (!p.isBeingCropped()) {
1797 on = FrameGraphics.Highlight(on.getEditTarget());
1798 }
1799 } else {
1800 on = FrameGraphics.Highlight(on.getEditTarget());
1801 }
1802
1803 }
1804 // if the last item highlighted is still highlighted, clear it
1805 if (_lastHoldsHighlight) {
1806 _lastHoldsHighlight = false;
1807 for (Item i : DisplayController.getCurrentFrame().getSortedItems()) {
1808 if (i.isHighlighted() && i != on) {
1809 FrameGraphics.changeHighlightMode(i, Item.HighlightMode.None);
1810 }
1811 }
1812 }
1813
1814 // if the user is not pointing at an item, check for enclosure highlighting
1815 } else if (on == null) {
1816 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds();
1817 if (enclosure != null && enclosure.size() > 0) {
1818 Item firstLineEnd = enclosure.iterator().next();
1819 HighlightMode hm;
1820 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
1821 hm = HighlightMode.Connected;
1822 } else {
1823 hm = HighlightMode.Enclosed;
1824 }
1825 if (firstLineEnd.getLines().size() > 1 &&
1826 // check that the enclosure is not part of a point being dragged in space
1827 !ContainsOneOf(enclosure, FreeItems.getInstance())) {
1828 on = firstLineEnd.getLines().get(0);
1829 // System.out.println(on == null ? "Null" :
1830 // on.toString());
1831 FrameGraphics.changeHighlightMode(on, hm);
1832 } else if (firstLineEnd instanceof XRayable) {
1833 on = firstLineEnd;
1834 FrameGraphics.changeHighlightMode(firstLineEnd, hm);
1835 }
1836 _context = CONTEXT_AT_ENCLOSURE;
1837 } else if (_lastHighlightedItem != null) {
1838 // System.out.println("LastHighlightedItem");
1839 _lastHoldsHighlight = false;
1840 }
1841 }
1842
1843 // disable cursor changes when the cursor has items attached
1844 if (FreeItems.hasItemsAttachedToCursor() && DisplayController.getCursor() != Item.TEXT_CURSOR) {
1845 _forceArrowCursor = false;
1846 }
1847
1848 // setLastHighlightedItem(on);
1849
1850 if (_lastHighlightedItem != null && _lastHighlightedItem != on && !_lastHoldsHighlight) {
1851 // Turn off the highlighting only if the last highlighted item
1852 // is not connected to the currentItem. Otherwise we get flickering
1853 // in transition from connected to normal mode while moving the cursor along a
1854 // line.
1855 if (on == null || (!on.getAllConnected().contains(_lastHighlightedItem))) {
1856 FrameGraphics.changeHighlightMode(_lastHighlightedItem, Item.HighlightMode.None);
1857 }
1858 }
1859
1860 _lastHighlightedItem = on;
1861
1862 }
1863
1864 private static boolean ContainsOneOf(Collection<Item> enclosure, Collection<Item> freeItems) {
1865 if (freeItems == null) {
1866 return false;
1867 }
1868 for (Item i : freeItems) {
1869 if (enclosure.contains(i)) {
1870 return true;
1871 }
1872 }
1873 return false;
1874 }
1875
1876 public static void anchor(Item toAnchor, boolean checkEnclosure) {
1877 // Only anchor items we have full permission over... i.e. don't anchor vector
1878 // items
1879 if (!toAnchor.hasPermission(UserAppliedPermission.full)) {
1880 return;
1881 }
1882
1883 toAnchor.anchor();
1884
1885 if (checkEnclosure) {
1886 ItemUtils.EnclosedCheck(toAnchor.getParentOrCurrentFrame().getSortedItems());
1887 DisplayController.requestRefresh(true);
1888 }
1889 }
1890
1891 public static void anchor(Item toAnchor) {
1892 anchor(toAnchor, true);
1893 }
1894
1895 public static void anchor(Collection<Item> toAnchor) {
1896 boolean bReparse = false;
1897 boolean bRecalculate = false;
1898 // Need to make sure we check enclosure for overlays etc
1899 Set<Frame> checkEnclosure = new HashSet<Frame>();
1900
1901 // Create a clone of toAnchor since in the proccess of anchoring items
1902 // they can change the state of the toAnchor collection and thus create
1903 // concurrent modification exceptions.
1904 // This is especially needed for widgets being removed when anchored:
1905 // since they
1906 // currently are composed of 8 items this is vital. In the new revision
1907 // of
1908 // widgets being implemented as a single item this this can be
1909 // depreciated
1910 // however it may be useful for other applications.
1911 Collection<Item> toAnchorCopy = new ArrayList<Item>(toAnchor);
1912
1913 for (Item i : toAnchorCopy) {
1914 // Widget components in this loop.
1915 if (toAnchor.contains(i)) { // since to anchor could change while
1916 // anchoring
1917 // if (!i.hasVector())
1918 anchor(i, false);
1919 checkEnclosure.add(i.getParentOrCurrentFrame());
1920 bReparse |= i.hasOverlay();
1921 bRecalculate |= i.recalculateWhenChanged();
1922 // toAnchor.remove(i);
1923 }
1924 }
1925
1926 toAnchor.clear();
1927 // Check enclosure for all the frames of the items that were anchored
1928 for (Frame f : checkEnclosure) {
1929 // ItemUtils.EnclosedCheck(f.getItems());
1930 ItemUtils.Justify(f);
1931 }
1932
1933 Frame currentFrame = DisplayController.getCurrentFrame();
1934 if (bReparse) {
1935 FrameUtils.Parse(currentFrame, false, false, false);
1936 } else {
1937 currentFrame.notifyObservers(bRecalculate);
1938 }
1939 DisplayController.requestRefresh(true);
1940 }
1941
1942 public static void deleteItems(Collection<Item> itemList) {
1943 deleteItems(itemList, true);
1944 }
1945
1946 public static void deleteItems(Collection<Item> itemList, boolean addToUndo) {
1947 boolean bReparse = false;
1948 boolean bRecalculate = false;
1949
1950 SessionStats.DeletedItems(itemList);
1951 List<Frame> modifiedFrames = new LinkedList<Frame>();
1952 // Get a list of all the modified frames
1953 for (Item i : itemList) {
1954 Frame parent = i.getParent();
1955 if (parent != null) {
1956 modifiedFrames.add(parent);
1957 }
1958 i.setHighlightMode(HighlightMode.None);
1959 bReparse |= i.hasOverlay();
1960 bRecalculate |= i.recalculateWhenChanged();
1961 }
1962 // If they are all free items then add the current frame
1963 if (modifiedFrames.size() == 0) {
1964 modifiedFrames.add(DisplayController.getCurrentFrame());
1965 }
1966
1967 Collection<Item> toUndo = new LinkedHashSet<Item>();
1968 // disconnect any connected items
1969 for (Item i : itemList) {
1970
1971 // Only delete the current item if have not already deleted.
1972 // This is especially important for heavy duty widgets - so they
1973 // do not have to expire several times per delete.
1974 if (toUndo.contains(i)) {
1975 continue;
1976 }
1977
1978 // Make sure text items attached to cursor are reset back to the
1979 // transparency they should have.
1980 if (i instanceof Text) {
1981 ((Text) i).setAlpha(-1);
1982 }
1983
1984 if (i.getLines().size() > 0) {
1985
1986 Collection<Item> toDelete = deleteLineEnd(i);
1987 if (addToUndo) {
1988 // add the copied items to the undo stack
1989 for (Item itemToUndo : toDelete) {
1990
1991 if (!toUndo.contains(itemToUndo)) {
1992 toUndo.add(itemToUndo);
1993 }
1994
1995 }
1996 }
1997 } else if (!toUndo.contains(i)) {
1998 if (addToUndo) {
1999 toUndo.add(i); // Why was is this a copy
2000 }
2001 }
2002 }
2003
2004 for (Frame f : modifiedFrames) {
2005 f.removeAllItems(itemList);
2006 // ItemUtils.EnclosedCheck(f.getItems());
2007 ItemUtils.Justify(f);
2008 }
2009 // TODO: How should undelete deal with undo when items are removed from
2010 // the current frame as well as the overlay frame
2011 Frame currentFrame = DisplayController.getCurrentFrame();
2012 currentFrame.addToUndoDelete(new ItemsList(itemList), false);
2013 itemList.clear();
2014 if (bReparse) {
2015 FrameUtils.Parse(currentFrame, false, false, false);
2016 /*
2017 * TODO check if I need to recalculate even if reparse occurs, here and in
2018 * anchor, pickup etc
2019 */
2020 } else {
2021 currentFrame.notifyObservers(bRecalculate);
2022 }
2023
2024 }
2025
2026 private static Collection<Item> deleteLineEnd(Item lineEnd) {
2027
2028 if (lineEnd instanceof WidgetCorner) { // Brook
2029
2030 WidgetCorner wc = (WidgetCorner) lineEnd;
2031 Frame parent = wc.getWidgetSource().getParentFrame();
2032
2033 // Remove from the parent frame
2034 if (parent != null) {
2035 parent.removeAllItems(wc.getWidgetSource().getItems());
2036 }
2037
2038 wc.getWidgetSource().onDelete(); // Changes the widgets
2039 // corner/edges ID's...
2040
2041 return wc.getWidgetSource().getItems();
2042
2043 } else {
2044
2045 // // create a backup copy of the dot and its lines
2046 // List<Item> copy = copy(lineEnd.getConnected());
2047 //
2048 // // Remove lines from their anchored dots
2049 // // note: the line is kept so that it can be properly restored
2050 // for (Item ic : copy) {
2051 // if (ic instanceof Line) {
2052 // Line line = (Line) ic;
2053 // // Remove the line from the item that is not the copy of the
2054 // // line end being deletedF
2055 // if (!copy.contains(line.getStartItem()))
2056 // line.getStartItem().removeLine(line);
2057 // if (!copy.contains(line.getEndItem()))
2058 // line.getEndItem().removeLine(line);
2059 // }
2060 // }
2061
2062 Collection<Item> copy = lineEnd.getConnected();
2063
2064 // remove all lines being deleted
2065 for (Item ic : lineEnd.getConnected()) {
2066 if (ic instanceof Line && ((Line) ic).getOppositeEnd(lineEnd) != null) {
2067 Line line = (Line) ic;
2068
2069 // Invalidate the line to make sure we dont get any ghost
2070 // arrowheads.
2071 ic.invalidateAll();
2072
2073 Item d = line.getOppositeEnd(lineEnd);
2074 d.removeLine(line);
2075
2076 // if the dot was only part of one line, it can be
2077 // removed
2078 if (d.getLines().size() == 0) {
2079 if (d.getParent() != null) {
2080 d.getParent().removeItem(d);
2081 }
2082 if (!copy.contains(d)) {
2083 copy.add(d);
2084 }
2085 }
2086
2087 if (lineEnd.getParent() != null) {
2088 lineEnd.getParent().removeItem(ic);
2089 }
2090 }
2091 }
2092 return copy;
2093 }
2094 }
2095
2096 public static Item getlastHighlightedItem() {
2097 return _lastHighlightedItem;
2098 }
2099
2100 /**
2101 * event called when mouse exits window (can't use MouseListener callback since
2102 * that callback doesn't correctly receive all mouse exit events) *** Above
2103 * comment no longer applies! *** TODO: Rename. cts16
2104 */
2105 public static void mouseExitedWindow() {
2106 if (FreeItems.hasItemsAttachedToCursor()) {
2107 boolean cut = true;
2108 for (Item i : FreeItems.getInstance()) {
2109 for (Item j : i.getAllConnected()) {
2110 if (!FreeItems.getInstance().contains(j)) {
2111 cut = false;
2112 break;
2113 }
2114 }
2115 }
2116 if (cut) {
2117 ItemSelection.cut();
2118 }
2119 }
2120 }
2121
2122 public static void setForceArrow(boolean val) {
2123 _forceArrowCursor = val;
2124 }
2125
2126 protected static void zoomFrame(Frame frame, double scaleFactor, int x, int y) {
2127
2128 if (frame == null) {
2129 return;
2130 }
2131
2132 Collection<Item> items = frame.getVisibleItems();
2133
2134 for (Item item : items) {
2135 if (item instanceof Text && item.getSize() <= Text.MINIMUM_FONT_SIZE && scaleFactor < 1) {
2136 return;
2137 }
2138 }
2139
2140 for (Vector v : frame.getVectors()) {
2141 v.Source.scale((float) scaleFactor, x, y);
2142 }
2143
2144 for (Item item : items) {
2145 // This line is only needed for circles!!
2146 // Need to really fix up the way this works!!
2147 if (item.hasEnclosures()) {
2148 continue;
2149 }
2150 if (!item.hasPermission(UserAppliedPermission.full)) {
2151 continue;
2152 }
2153 item.invalidateAll();
2154 if (!(item instanceof Line)) {
2155 item.scale((float) scaleFactor, x, y);
2156 }
2157 }
2158
2159 for (Item item : items) {
2160 if (!item.hasPermission(UserAppliedPermission.full)) {
2161 continue;
2162 }
2163 // if (!(item instanceof Line))
2164 item.invalidateBounds();
2165
2166 if (item instanceof Line) {
2167 ((Line) item).refreshStroke(item.getThickness());
2168 }
2169
2170 item.invalidateAll();
2171 }
2172 }
2173
2174 public static boolean zoomFrameIfEnabled(Frame frame, double scaleFactor, Point centreOfZoom) {
2175 boolean zoom_active = ExperimentalFeatures.FrameZoom.get();
2176
2177 if (zoom_active) {
2178
2179 zoomFrame(DisplayController.getCurrentFrame(), scaleFactor, centreOfZoom.getX(), centreOfZoom.getY());
2180 DisplayController.getCurrentFrame().refreshSize();
2181 Refresh();
2182 } else {
2183 String frameZoomingDisabledMessage = "Frame Zooming currently disabled. "
2184 + "Access Settings->Experimental->FrameZoom and set to 'true' to enable this";
2185 MessageBay.displayMessageOnce(frameZoomingDisabledMessage);
2186 }
2187
2188 return zoom_active;
2189 }
2190
2191 /**
2192 * Performs the dropping action: If the cursor is in free space then: the cursor
2193 * is repositioned below the last non-annotation text item. If the cursor is on
2194 * an item, and has items attached then: the cursor is positioned below the
2195 * pointed to item, and the items below are 'pushed down' to make room.
2196 *
2197 * @param toDropFrom
2198 * The Item being pointed at by the mouse, may be null to indicate
2199 * the cursor is in free space.
2200 */
2201 public static boolean Drop(Item toDropFrom, boolean bPasting) {
2202 try {
2203 FrameUtils.setLastEdited(null);
2204
2205 String newItemText = DEFAULT_NEW_ITEM_TEXT;
2206
2207 // if a line is being rubber-banded, this is a no-op
2208 if (Frame.rubberbandingLine()) {
2209 return false;
2210 }
2211
2212 // if the cursor is in free space then the drop will happen from the
2213 // last non annotation text item on the frame
2214 if (toDropFrom == null) {
2215 toDropFrom = DisplayController.getCurrentFrame().getLastNonAnnotationTextItem();
2216 }
2217
2218 // if no item was found, return
2219 if (toDropFrom == null) {
2220 MessageBay.errorMessage("No item could be found to drop from");
2221 return false;
2222 }
2223
2224 if (!(toDropFrom instanceof Text)) {
2225 MessageBay.displayMessage("Only text items can be dropped from");
2226 return false;
2227 }
2228
2229 // Get the list of items that must be dropped
2230 List<Text> column = DisplayController.getCurrentFrame().getColumn(toDropFrom);
2231
2232 if (column == null) {
2233 MessageBay.errorMessage("No column found to align items to");
2234 return false;
2235 }
2236
2237 Item title = DisplayController.getCurrentFrame().getTitleItem();
2238
2239 // We won't do auto-bulleting when dropping from titles
2240 if (!bPasting && toDropFrom != title) {
2241 newItemText = getAutoBullet(((Text) toDropFrom).getFirstLine());
2242 }
2243
2244 Text dummyItem = null;
2245 if (!bPasting && FreeItems.textOnlyAttachedToCursor()) {
2246 dummyItem = (Text) FreeItems.getItemAttachedToCursor();
2247 String autoBullet = getAutoBullet(dummyItem.getText());
2248
2249 if (autoBullet.length() > 0) {
2250 newItemText = "";
2251 }
2252 dummyItem.setText(newItemText + dummyItem.getText());
2253 }
2254
2255 dummyItem = Text.createText();
2256 if (FreeItems.textOnlyAttachedToCursor()) {
2257 Text t = (Text) FreeItems.getItemAttachedToCursor();
2258 dummyItem.setSize(t.getSize());
2259 int lines = t.getTextList().size();
2260 for (int i = 0; i < lines; i++) {
2261 newItemText += '\n';
2262 }
2263 }
2264
2265 dummyItem.setText(newItemText);
2266
2267 // If the only item on the frame is the title and the frame name
2268 // goto the zero frame and drop to the @start if there is one
2269 // or a fixed amount if there is not
2270 if (column.size() == 0) {
2271 Frame current = DisplayController.getCurrentFrame();
2272 // Item itemTemplate = current.getItemTemplate();
2273 int xPos = title.getX() + FrameCreator.INDENT_FROM_TITLE;
2274 int yPos = FrameCreator.getYStart(title);
2275 // Check for @start on the zero frame
2276 Frame zero = FrameIO.LoadFrame(current.getFramesetName() + '0');
2277 Text start = zero.getAnnotation("start");
2278 if (start != null) {
2279 xPos = start.getX();
2280 yPos = start.getY();
2281 }
2282
2283 dummyItem.setPosition(xPos, yPos);
2284 // DisplayIO.setCursorPosition(xPos, yPos);
2285
2286 checkMovingCursor(dummyItem);
2287 } else {
2288 int yPos = column.get(0).getY() + 1;
2289 int xPos = column.get(0).getX();
2290 // Either position the new item below the title or just above
2291 // the first item below the title
2292 if (toDropFrom == title && column.get(0) != title) {
2293 // If dropping from the title position just above top item
2294 yPos = column.get(0).getY() - 1;
2295
2296 Frame current = DisplayController.getCurrentFrame();
2297 // Check for @start on the zero frame
2298 Frame zero = FrameIO.LoadFrame(current.getFramesetName() + '0');
2299 Text start = zero.getAnnotation("start");
2300 if (start != null) {
2301 yPos = Math.min(yPos, start.getY());
2302 }
2303 }
2304 dummyItem.setPosition(xPos, yPos);
2305 column.add(dummyItem);
2306 FrameUtils.Align(column, false, 0);
2307 // Check if it will be outside the frame area
2308 if (dummyItem.getY() < 0 || dummyItem.getY() > DisplayController.getFramePaintAreaHeight()) {
2309 // Check for the 'next' tag!
2310 Frame current = DisplayController.getCurrentFrame();
2311 Item next = current.getAnnotation("next");
2312 Item prev = current.getAnnotation("previous");
2313 // Check for an unlinked next tag
2314 if ((next != null && !next.hasLink()) || (prev != null && prev.hasLink())) {
2315 Frame firstFrame = current;
2316 if (next != null) {
2317 next.delete();
2318 }
2319 FrameCreator frameCreator = new FrameCreator(null);
2320 // Add the next button
2321 next = frameCreator.addNextButton(current, null);
2322
2323 // Create the new frame linked to the next tag
2324 boolean mouseMoved = tdfc(next);
2325 Frame moreFrame = DisplayController.getCurrentFrame();
2326
2327 // Add previous button to the new frame
2328 frameCreator.addPreviousButton(moreFrame, firstFrame.getName());
2329 Item first = current.getAnnotation("first");
2330 if (first != null) {
2331 frameCreator.addFirstButton(moreFrame, first.getLink());
2332 } else {
2333 frameCreator.addFirstButton(moreFrame, firstFrame.getName());
2334 }
2335 // Add the @next if we are pasting
2336 // if (bPasting) {
2337 // Item copy = next.copy();
2338 // copy.setLink(null);
2339 // moreFrame.addItem(copy);
2340 // }
2341
2342 moreFrame.setTitle(firstFrame.getTitleItem().getText());
2343 // need to move the mouse to the top of the frame if
2344 // there wasnt an @start on it
2345 if (!mouseMoved) {
2346 Item moreTitle = moreFrame.getTitleItem();
2347 moreTitle.setOverlayPermission(UserAppliedPermission.full);
2348 Drop(moreTitle, bPasting);
2349 }
2350 // Add the bullet text to the item
2351 dummyItem.setPosition(DisplayController.getMouseX(), DisplayController.getMouseY());
2352 } else {
2353 MessageBay.warningMessage("Can not create items outside the frame area");
2354 // ensures correct repainting when items don't move
2355 DisplayController.setCursorPosition(DisplayController.getMouseX(),
2356 DisplayController.getMouseY());
2357 return false;
2358 }
2359 }
2360 if (!FreeItems.textOnlyAttachedToCursor() && !dummyItem.isEmpty()) {
2361 DisplayController.getCurrentFrame().addItem(dummyItem);
2362 }
2363
2364 checkMovingCursor(dummyItem);
2365 }
2366 if (dummyItem.getText().length() == 0 || FreeItems.hasItemsAttachedToCursor()) {
2367 dummyItem.getParentOrCurrentFrame().removeItem(dummyItem);
2368 dummyItem.setRightMargin(DisplayController.getFramePaintAreaWidth(), false);
2369 } else {
2370 dummyItem.setWidth(toDropFrom.getWidth());
2371 }
2372
2373 DisplayController.resetCursorOffset();
2374 DisplayController.requestRefresh(true);
2375 } catch (RuntimeException e) {
2376 // MessageBay.errorMessage(e.getMessage());
2377 e.printStackTrace();
2378 return false;
2379 }
2380 return true;
2381 }
2382
2383 /**
2384 * @param dummyItem
2385 */
2386 public static void moveCursorAndFreeItems(int x, int y) {
2387 Point oldMousePosition = DisplayController.getMousePosition();
2388
2389 if (oldMousePosition.getX() == x && oldMousePosition.getY() == y) {
2390 return;
2391 }
2392
2393 DisplayController.setCursorPosition(x, y);
2394 Item firstItem = FreeItems.getItemAttachedToCursor();
2395
2396 if (firstItem == null) {
2397 return;
2398 }
2399
2400 int deltaX = firstItem.getX() - x;
2401 int deltaY = firstItem.getY() - y;
2402
2403 for (Item i : FreeItems.getInstance()) {
2404 i.setPosition(i.getX() - deltaX, i.getY() - deltaY);
2405 }
2406 }
2407
2408 /**
2409 * @param dummyItem
2410 */
2411 private static void checkMovingCursor(Text dummyItem) {
2412 // Move the item to the cursor position
2413 if (FreeItems.hasItemsAttachedToCursor()) {
2414 moveCursorAndFreeItems(dummyItem.getX(), dummyItem.getY());
2415 } else {
2416 DisplayController.MoveCursorToEndOfItem(dummyItem);
2417 }
2418 }
2419
2420 private static void calculateItem(Item toCalculate) {
2421 if (toCalculate == null) {
2422 return;
2423 }
2424
2425 if (!toCalculate.update()) {
2426 toCalculate.setFormula(null);
2427 MessageBay.errorMessage("Can not calculate formula [" + toCalculate.getText() + ']');
2428 }
2429 }
2430
2431 /**
2432 * Adjusts the size of the given Item, by the given amount. Note: The amount is
2433 * relative and can be positive or negative.
2434 *
2435 * @param toSet
2436 * The Item whose size is to be adjusted
2437 * @param diff
2438 * The amount to adjust the Item's size by
2439 * @param moveCursor
2440 * true if the cursor position should be automatically adjusted with
2441 * resizing
2442 */
2443 public static void SetSize(Item item, int diff, boolean moveCursor, boolean insideEnclosure,
2444 boolean isControlDown) {
2445 Collection<Item> toSize = new HashSet<Item>();
2446 Collection<Widget> widgets = new HashSet<Widget>();
2447 // the mouse is only moved when the Item is on the frame, not free
2448 // boolean moveMouse = false;
2449 Item toSet = null;
2450
2451 // if the user is not pointing to any item
2452 if (item == null) {
2453 if (FreeItems.hasItemsAttachedToCursor()) {
2454 toSize.addAll(FreeItems.getInstance());
2455 } else {
2456 MessageBay.displayMessage("There are no Items selected on the Frame or on the Cursor");
2457 return;
2458 }
2459 } else {
2460 if (item.isFrameName()) {
2461 // scale the entire frame
2462 if (diff != 0) {
2463 double scaleFactor = diff > 0 ? 1.1 : 0.909090909;
2464 zoomFrameIfEnabled(DisplayController.getCurrentFrame(), scaleFactor, new Point(0, 0));
2465 }
2466 // MessageBay.displayMessage("Can not resize the frame name");
2467 return;
2468 }
2469 // check permissions
2470 if (!item.hasPermission(UserAppliedPermission.full)) {
2471 Item editTarget = item.getEditTarget();
2472 if (editTarget != item && editTarget.hasPermission(UserAppliedPermission.full)) {
2473 item = editTarget;
2474 } else {
2475 MessageBay.displayMessage("Insufficient permission to change the size of that item");
2476 return;
2477 }
2478 }
2479 toSet = item;
2480 // For resizing enclosures pick up everything that is attached to
2481 // items partly in the enclosure
2482 // TODO make this only pick up stuff COMPLETELY enclosed... if we
2483 // change copying to copy only the stuff completely enclosed
2484 if (insideEnclosure) {
2485 for (Item i : FrameUtils.getCurrentItems(toSet)) {
2486 if (i.hasPermission(UserAppliedPermission.full) && !toSize.contains(i)) {
2487 toSize.addAll(i.getAllConnected());
2488 }
2489 }
2490
2491 } // Enclosed circle centers are resized with the center as origin
2492 // Just add the circle center to the list of items to size
2493 else if (!toSet.hasEnclosures() && !(toSet instanceof Text) && toSet.isLineEnd()) {
2494 toSize.addAll(toSet.getLines());
2495 } else if (toSet instanceof Line) {
2496
2497 Line line = (Line) toSet;
2498
2499 if (!(toSet instanceof WidgetEdge)
2500 || ((WidgetEdge) toSet).getWidgetSource().isWidgetEdgeThicknessAdjustable()) {
2501
2502 float current = Math.abs(line.getThickness());
2503 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
2504 line.setThickness(current);
2505 DisplayController.requestRefresh(true);
2506 return;
2507
2508 }
2509
2510 } else {
2511 toSize.add(toSet);
2512 }
2513 }
2514
2515 // add widgets to notify
2516 for (Item i : toSize) {
2517 if (i instanceof WidgetEdge) {
2518 widgets.add(((WidgetEdge) i).getWidgetSource());
2519 } else if (i instanceof WidgetCorner) {
2520 widgets.add(((WidgetCorner) i).getWidgetSource());
2521 }
2522 }
2523
2524 Point origin = EcosystemManager.getInputManager().getCursorPosition();
2525 // Inside enclosures increase the size of the enclosure
2526 double ratio = (100.0 + diff * 2) / 100.0;
2527 if (insideEnclosure) {
2528 Collection<Item> done = new HashSet<Item>();
2529 // adjust the size of all the items
2530 for (Item i : toSize) {
2531 if (done.contains(i)) {
2532 continue;
2533 }
2534
2535 if (i.isLineEnd()) {
2536 if (!(i instanceof WidgetCorner) || !((WidgetCorner) i).getWidgetSource().isFixedSize()) // don't
2537 // size
2538 // fixed
2539 // widgets
2540 {
2541 Collection<Item> allConnected = i.getAllConnected();
2542 done.addAll(allConnected);
2543 for (Item it : allConnected) {
2544 it.translate(origin, ratio);
2545 it.setArrowheadLength((float) (it.getArrowheadLength() * ratio));
2546 }
2547 i.setThickness((float) (i.getThickness() * ratio));
2548 }
2549 } else if (i instanceof XRayable) {
2550 XRayable xRay = (XRayable) i;
2551 Text source = xRay.getSource();
2552 // Ensure that the source is done before the XRayable
2553 if (!done.contains(source)) {
2554 scaleText(insideEnclosure, origin, ratio, done, source);
2555 }
2556 i.translate(origin, ratio);
2557 i.setThickness((float) (i.getThickness() * ratio));
2558 done.add(i);
2559 } else if (i.hasVector()) {
2560 // TODO Improve the effiency of resizing vectors... ie...
2561 // dont want to have to reparse all the time
2562 assert (i instanceof Text);
2563 Text text = (Text) i;
2564 AttributeValuePair avp = new AttributeValuePair(text.getText());
2565 double scale = 1F;
2566 try {
2567 scale = avp.getDoubleValue();
2568 } catch (Exception e) {
2569 }
2570 scale *= ratio;
2571 NumberFormat nf = Vector.getNumberFormatter();
2572 text.setAttributeValue(nf.format(scale));
2573 text.translate(origin, ratio);
2574 item.getParent().parse();
2575 } else if (i instanceof Text) {
2576 scaleText(insideEnclosure, origin, ratio, done, (Text) i);
2577 }
2578 }
2579 // refresh anchored items
2580 if (refreshAnchors(toSize)) {
2581 FrameUtils.Parse(DisplayController.getCurrentFrame(), false);
2582 }
2583 // notify widgets they were resized
2584 for (Widget iw : widgets) {
2585 iw.onResized();
2586 }
2587 DisplayController.requestRefresh(true);
2588 return;
2589 }
2590
2591 // adjust the size of all the items
2592 for (Item i : toSize) {
2593 // Lines and dots use thickness, not size
2594 if (i.hasEnclosures()) {
2595 Circle c = (Circle) i.getEnclosures().iterator().next();
2596 c.setSize(c.getSize() * (float) ratio);
2597 } else if (i instanceof Line || i instanceof Circle && !insideEnclosure) {
2598 float current = Math.abs(i.getThickness());
2599 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
2600 i.setThickness(current);
2601 } else if (i instanceof Dot) {
2602 Item dot = i;
2603 float current = Math.abs(dot.getThickness());
2604 current = Math.max(current + diff, Item.MINIMUM_THICKNESS);
2605 dot.setThickness(current);
2606 } else if (i.hasVector()) {
2607 assert (item instanceof Text);
2608 Text text = (Text) item;
2609 AttributeValuePair avp = new AttributeValuePair(text.getText());
2610 double scale = 1F;
2611 try {
2612 scale = avp.getDoubleValue();
2613 } catch (Exception e) {
2614 }
2615 scale *= ratio;
2616 NumberFormat nf = Vector.getNumberFormatter();
2617 text.setAttributeValue(nf.format(scale));
2618 text.translate(origin, ratio);
2619 item.getParent().parse();
2620 } else {
2621 float oldSize = Math.abs(i.getSize());
2622 float newSize = Math.max(oldSize + diff, Item.MINIMUM_THICKNESS);
2623 float resizeRatio = newSize / oldSize;
2624 // Set size for Picture also translates
2625 i.setSize(newSize);
2626 if (i instanceof Text && i.getSize() != oldSize) {
2627 if (toSize.size() == 1 && !isControlDown) {
2628 moveCursorAndFreeItems(i.getX(), i.getY());
2629 } else {
2630 i.translate(origin, resizeRatio);
2631 if (i.isLineEnd()) {
2632 i.setPosition(i.getPosition());
2633 }
2634 }
2635 }
2636 }
2637 }
2638
2639 if (toSet != null) {
2640 toSet.getParent().setChanged(true);
2641 }
2642
2643 // refresh anchored items
2644 if (refreshAnchors(toSize)) {
2645 FrameUtils.Parse(DisplayController.getCurrentFrame(), false);
2646 }
2647
2648 DisplayController.requestRefresh(true);
2649 }
2650
2651 /**
2652 * @param origin
2653 * @param ratio
2654 * @param done
2655 * @param source
2656 */
2657 private static void scaleText(boolean insideEnclosure, Point origin, double ratio, Collection<Item> done,
2658 Text source) {
2659 if (insideEnclosure) {
2660 source.setWidth(Math.round((float) (source.getAbsoluteWidth() * ratio)));
2661 }
2662 source.translate(origin, ratio);
2663 source.setSize((float) (source.getSize() * ratio));
2664 done.add(source);
2665 }
2666
2667 private static boolean refreshAnchors(Collection<Item> items) {
2668 boolean bReparse = false;
2669
2670 for (Item i : items) {
2671 Integer anchorLeft = i.getAnchorLeft();
2672 Integer anchorRight = i.getAnchorRight();
2673 Integer anchorTop = i.getAnchorTop();
2674 Integer anchorBottom = i.getAnchorBottom();
2675
2676 if (anchorLeft != null) {
2677 i.setAnchorLeft(anchorLeft);
2678 if (i.hasVector()) {
2679 bReparse = true;
2680 }
2681 }
2682
2683 if (anchorRight != null) {
2684 i.setAnchorRight(anchorRight);
2685 if (i.hasVector()) {
2686 bReparse = true;
2687 }
2688 }
2689
2690 if (anchorTop != null) {
2691 i.setAnchorTop(anchorTop);
2692 if (i.hasVector()) {
2693 bReparse = true;
2694 }
2695 }
2696
2697 if (anchorBottom != null) {
2698 i.setAnchorBottom(anchorBottom);
2699 if (i.hasVector()) {
2700 bReparse = true;
2701 }
2702 }
2703 }
2704 return bReparse;
2705 }
2706
2707 /**
2708 * Sets the colour of the current Item based on its current colour. The colours
2709 * proceed in the order stored in COLOR_WHEEL.
2710 *
2711 * @param toSet
2712 * The Item whose colour is to be changed
2713 */
2714 private static void SetColor(Item item, boolean setTransparent, boolean setBackgroundColor) {
2715 // first determine the next color
2716 Colour color = null;
2717 Frame currentFrame = DisplayController.getCurrentFrame();
2718 if (item == null) {
2719 if (FreeItems.hasItemsAttachedToCursor()) {
2720 color = FreeItems.getInstance().get(0).getColor();
2721 } else {
2722 return;
2723 }
2724 // change the background color if the user is pointing on the frame name
2725 } else if (item == currentFrame.getNameItem()) {
2726 // check permissions
2727 if (!item.hasPermission(UserAppliedPermission.full)) {
2728 MessageBay.displayMessage("Insufficient permission to the frame's background color");
2729 return;
2730 }
2731
2732 if (setTransparent) {
2733 currentFrame.setBackgroundColor(null);
2734 } else {
2735 currentFrame.toggleBackgroundColor();
2736 }
2737
2738 DisplayController.requestRefresh(true);
2739 return;
2740 } else {
2741 // check permissions
2742 if (!item.hasPermission(UserAppliedPermission.full)) {
2743 Item editTarget = item.getEditTarget();
2744 if (editTarget != item && editTarget.hasPermission(UserAppliedPermission.full)) {
2745 item = editTarget;
2746 } else {
2747 MessageBay.displayMessage("Insufficient permission to change color");
2748 return;
2749 }
2750 }
2751
2752 // Toggling color of circle center changes the circle fill color
2753 if (item.hasEnclosures()) {
2754 if (setBackgroundColor) {
2755 SetGradientColor(item.getEnclosures().iterator().next(), setTransparent);
2756 } else {
2757 SetFillColor(item.getEnclosures().iterator().next(), setTransparent);
2758 }
2759 } else if (setBackgroundColor) {
2760 color = item.getPaintBackgroundColor();
2761 } else {
2762 color = item.getPaintColor();
2763 }
2764 }
2765
2766 if (setTransparent) {
2767 color = null;
2768 } else if (setBackgroundColor) {
2769 color = ColorUtils.getNextColor(color, TemplateSettings.FillColorWheel.get(), item.getPaintColor());
2770 } else {
2771 color = ColorUtils.getNextColor(color, TemplateSettings.ColorWheel.get(),
2772 currentFrame.getPaintBackgroundColor());
2773 }
2774
2775 if (setBackgroundColor) {
2776 if (item == null && FreeItems.hasItemsAttachedToCursor()) {
2777 for (Item i : FreeItems.getInstance()) {
2778 i.setBackgroundColor(color);
2779 }
2780 } else {
2781 item.setBackgroundColor(color);
2782 item.getParent().setChanged(true);
2783 }
2784 } else {
2785 if (item == null && FreeItems.hasItemsAttachedToCursor()) {
2786 for (Item i : FreeItems.getInstance()) {
2787 i.setColor(color);
2788 }
2789 } else {
2790 item.setColor(color);
2791 item.getParent().setChanged(true);
2792 }
2793 }
2794
2795 DisplayController.requestRefresh(true);
2796 }
2797
2798 private static void SetFillColor(Item item, boolean setTransparent) {
2799 if (item == null) {
2800 return;
2801 }
2802
2803 if (!item.hasPermission(UserAppliedPermission.full)) {
2804 MessageBay.displayMessage("Insufficient permission to change fill color");
2805 return;
2806 }
2807
2808 Item toSet = item;
2809 Colour color = toSet.getFillColor();
2810 if (setTransparent) {
2811 color = null;
2812 } else {
2813 color = ColorUtils.getNextColor(color, TemplateSettings.FillColorWheel.get(), toSet.getGradientColor());
2814 }
2815
2816 // if (color == null) {
2817 // MessageBay.displayMessage("FillColor is now transparent");
2818 // }
2819
2820 toSet.setFillColor(color);
2821 toSet.getParent().setChanged(true);
2822
2823 DisplayController.requestRefresh(true);
2824 }
2825
2826 private static void SetGradientColor(Item item, boolean setTransparent) {
2827 if (item == null) {
2828 return;
2829 }
2830
2831 if (!item.hasPermission(UserAppliedPermission.full)) {
2832 MessageBay.displayMessage("Insufficient permission to change gradient color");
2833 return;
2834 }
2835
2836 Item toSet = item;
2837 Colour color = toSet.getGradientColor();
2838 if (setTransparent) {
2839 color = null;
2840 } else {
2841 color = ColorUtils.getNextColor(color, TemplateSettings.ColorWheel.get(), toSet.getFillColor());
2842 }
2843
2844 // if (color == null) {
2845 // MessageBay.displayMessage("FillColor is now transparent");
2846 // }
2847
2848 toSet.setGradientColor(color);
2849 toSet.getParent().setChanged(true);
2850
2851 DisplayController.requestRefresh(true);
2852 }
2853
2854 /**
2855 * Toggles the given Item's annotation status on\off.
2856 *
2857 * @param toToggle
2858 * The Item to toggle
2859 */
2860 private static void ToggleAnnotation(Item toToggle) {
2861 if (toToggle == null) {
2862 MessageBay.displayMessage("There is no Item selected to toggle");
2863 return;
2864 }
2865
2866 // check permissions
2867 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2868 MessageBay.displayMessage("Insufficient permission to toggle that item's annotation");
2869 return;
2870 }
2871 toToggle.setAnnotation(!toToggle.isAnnotation());
2872
2873 toToggle.getParent().setChanged(true);
2874 DisplayController.requestRefresh(true);
2875 }
2876
2877 /**
2878 * Toggles the face style of a text item
2879 *
2880 * @param toToggle
2881 * The Item to toggle
2882 */
2883 private static void ToggleFontStyle(Item toToggle) {
2884 if (toToggle == null) {
2885 MessageBay.displayMessage("There is no Item selected to toggle");
2886 return;
2887 }
2888
2889 // check permissions
2890 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2891 MessageBay.displayMessage("Insufficient permission to toggle that item's annotation");
2892 return;
2893 }
2894
2895 if (toToggle instanceof Text) {
2896 Text text = (Text) toToggle;
2897 text.toggleFontStyle();
2898
2899 text.getParent().setChanged(true);
2900 DisplayController.requestRefresh(true);
2901 }
2902 }
2903
2904 /**
2905 * Toggles the face style of a text item
2906 *
2907 * @param toToggle
2908 * The Item to toggle
2909 */
2910 private static void ToggleFontFamily(Item toToggle) {
2911 if (toToggle == null) {
2912 MessageBay.displayMessage("There is no Item selected to toggle");
2913 return;
2914 }
2915
2916 // check permissions
2917 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2918 MessageBay.displayMessage("Insufficient permission to toggle that item's annotation");
2919 return;
2920 }
2921
2922 if (toToggle instanceof Text) {
2923 Text text = (Text) toToggle;
2924 text.toggleFontFamily();
2925
2926 text.getParent().setChanged(true);
2927 DisplayController.requestRefresh(true);
2928 }
2929 }
2930
2931 /**
2932 * Creates a new Frameset with the name given by the Item
2933 *
2934 * @param name
2935 */
2936 private static void CreateFrameset(Item item) {
2937 if (item == null) {
2938 MessageBay.displayMessage("There is no selected item to use for the frameset name");
2939 return;
2940 }
2941
2942 if (!(item instanceof Text)) {
2943 MessageBay.displayMessage("Framesets can only be created from text items");
2944 return;
2945 }
2946
2947 // Don't create frameset if the item is linked
2948 if (item.getLink() != null) {
2949 MessageBay.displayMessage("A frameset can not be created from a linked item");
2950 return;
2951 }
2952
2953 // check permissions
2954 if (!item.hasPermission(UserAppliedPermission.full)) {
2955 MessageBay.displayMessage("Insufficient permission to create a frameset from this item");
2956 return;
2957 }
2958
2959 Text text = (Text) item;
2960 try {
2961 String name = text.getFirstLine();
2962 Frame linkTo = null;
2963 if (name.toLowerCase().startsWith("group: ")) {
2964 String groupName = name.substring(7);
2965 // create the new group
2966 linkTo = FrameIO.CreateNewGroup(groupName);
2967 } else {
2968 // create the new frameset
2969 linkTo = FrameIO.CreateNewFrameset(name);
2970 }
2971 DisplayController.setCursor(Item.DEFAULT_CURSOR);
2972 text.setLink(linkTo.getName());
2973 text.getParent().setChanged(true);
2974 FrameUtils.DisplayFrame(linkTo, true, true);
2975 linkTo.moveMouseToDefaultLocation();
2976 // this needs to be done if the user doesn't move the mouse before
2977 // doing Tdfc while the cursor is set to the text cursor
2978 DisplayController.setCursor(Item.DEFAULT_CURSOR);
2979 } catch (Exception e) {
2980 MessageBay.errorMessage(e.getMessage());
2981 }
2982 }
2983
2984 private void move(int direction, boolean isShiftDown, boolean isCtrlDown) {
2985 Item on = FrameUtils.getCurrentItem();
2986
2987 if ((on == null) || (on instanceof Picture)) {
2988 navigateFrame(direction);
2989 return;
2990 }
2991
2992 if (on instanceof Text) {
2993 Text text = (Text) on;
2994 // When the user hits the left and right button with mouse
2995 // positions over the the frame name navigation occurs
2996 if (text.isFrameName()) {
2997 navigateFrame(direction);
2998 return;
2999 } else {
3000 FrameUtils.setLastEdited(text);
3001 DisplayController.setTextCursor(text, direction, false, isShiftDown, isCtrlDown, true);
3002 }
3003 }
3004 }
3005
3006 private void navigateFrame(int direction) {
3007 switch (direction) {
3008 case Text.RIGHT:
3009 case Text.PAGE_UP:
3010 Navigation.NextFrame(false);
3011 break;
3012 case Text.LEFT:
3013 case Text.PAGE_DOWN:
3014 Navigation.PreviousFrame(false);
3015 break;
3016 case Text.HOME:
3017 case Text.LINE_HOME:
3018 Navigation.ZeroFrame();
3019 break;
3020 case Text.END:
3021 case Text.LINE_END:
3022 Navigation.LastFrame();
3023 break;
3024 }
3025 }
3026
3027 /**
3028 * Moves the cursor to the next text item on the frame
3029 *
3030 * @param currentItem
3031 * @param direction
3032 * move up if direction is negative, down if direction is positive
3033 */
3034 public static void NextTextItem(Item currentItem, boolean down) {
3035 // Move the cursor to the next text item
3036 Frame current = DisplayController.getCurrentFrame();
3037 Text title = current.getTitleItem();
3038
3039 Collection<Text> currentItems = FrameUtils.getCurrentTextItems();
3040 List<Text> textItems = new ArrayList<Text>();
3041 // Move to the next text item in the box if
3042 if (currentItems.contains(currentItem)) {
3043 textItems.addAll(currentItems);
3044 } else {
3045 if (title != null) {
3046 textItems.add(title);
3047 }
3048 textItems.addAll(current.getBodyTextItems(true));
3049 }
3050
3051 Collections.sort(textItems);
3052
3053 if (textItems.size() == 0) {
3054 // If there are no text items on the frame its a NoOp
3055 if (title == null) {
3056 return;
3057 }
3058 if (title != null) {
3059 DisplayController.MoveCursorToEndOfItem(title);
3060 }
3061 DisplayController.requestRefresh(true);
3062 return;
3063 }
3064
3065 // If the user is mouse wheeling in free space...
3066 if (currentItem == null) {
3067 // find the nearest item in the correct direction
3068 int currY = DisplayController.getMouseY();
3069 for (int i = 0; i < textItems.size(); i++) {
3070 Item t = textItems.get(i);
3071 if (currY < t.getY()) {
3072 if (down) {
3073 DisplayController.MoveCursorToEndOfItem(t);
3074 } else {
3075 if (i == 0) {
3076 DisplayController.MoveCursorToEndOfItem(current.getTitleItem());
3077 } else {
3078 DisplayController.MoveCursorToEndOfItem(textItems.get(i - 1));
3079 }
3080 }
3081 DisplayController.requestRefresh(true);
3082 return;
3083 }
3084 }
3085 // If we are at the bottom of the screen and the user scrolls down
3086 // then scroll backup to the title
3087 if (textItems.size() > 0) {
3088 DisplayController.MoveCursorToEndOfItem(textItems.get(textItems.size() - 1));
3089 }
3090 return;
3091 }
3092
3093 // Find the current item... then move to the next item
3094 int i = textItems.indexOf(currentItem);
3095
3096 int nextIndex = i + (down ? 1 : -1);
3097 if (nextIndex >= 0 && nextIndex < textItems.size()) {
3098 DisplayController.MoveCursorToEndOfItem(textItems.get(nextIndex));
3099 } else {
3100 DisplayController.MoveCursorToEndOfItem(currentItem);
3101 }
3102 return;
3103 }
3104
3105 private static void copyItemToClipboard(Item on) {
3106 if (on == null || !(on instanceof Text)) {
3107 return;
3108 }
3109
3110 Text text = (Text) on;
3111 String string = text.copySelectedText();
3112
3113 if (string == null || string.length() == 0) {
3114 string = text.getText();
3115 }
3116
3117 // add the text of the item to the clipboard
3118 ClipboardData clipboardData = ClipboardData.fromString(string);
3119 EcosystemManager.getClipboardManager().set(clipboardData);
3120 }
3121
3122 public static void delete(Item toDelete, Collection<Item> deleteItems, Collection<Item> deleteEnclosure,
3123 boolean alternateMode) {
3124 boolean bRecalculate = false;
3125
3126 FrameUtils.setLastEdited(null);
3127 _offX = _offY = 0;
3128
3129 Frame currentFrame = DisplayController.getCurrentFrame();
3130 // check if the user is pointing at the frame's framename
3131 if (toDelete != null && toDelete == currentFrame.getNameItem()) {
3132 currentFrame.clear(false);
3133 DisplayController.requestRefresh(true);
3134 return;
3135 }
3136
3137 // if the user is deleting items attached to the cursor
3138 if (FreeItems.hasItemsAttachedToCursor()) {
3139 // if this is an item-swap
3140 if (FreeItems.getInstance().size() == 1 && FreeItems.getInstance().get(0) instanceof Text
3141 && toDelete != null && toDelete instanceof Text) {
3142
3143 // check permissions
3144 if (!toDelete.hasPermission(UserAppliedPermission.full)) {
3145 MessageBay.displayMessage("Insufficient permission to swap Item text");
3146 return;
3147 }
3148 Text anchored = (Text) toDelete;
3149 Text free = (Text) FreeItems.getInstance().get(0);
3150 SessionStats.DeletedItem(free);
3151 // List<String> temp = anchored.getText();
3152 anchored.setText(free.getText());
3153 anchored.setFormula(free.getFormula());
3154
3155 // free.setTextList(temp);
3156 FreeItems.getInstance().clear();
3157
3158 anchored.getParent().setChanged(true);
3159
3160 bRecalculate |= free.recalculateWhenChanged();
3161 bRecalculate |= anchored.recalculateWhenChanged();
3162
3163 // update the offset since the text has changed
3164 _offX = DisplayController.getMouseX() - anchored.getX() + anchored.getOffset().getX();
3165 _offY = DisplayController.getMouseY() - anchored.getY() + anchored.getOffset().getY();
3166 } else {
3167 // if shift is pressed delete the entire shape attached to the dot
3168 if (alternateMode) {
3169 List<Item> tmp = new ArrayList<Item>(FreeItems.getInstance());
3170 for (Item i : tmp) {
3171 // remove entire rectangles instead of just the corner
3172 if (i instanceof Dot) {
3173 FreeItems.getInstance().addAll(i.getAllConnected());
3174 for (Item j : i.getAllConnected()) {
3175 if (j instanceof Dot) {
3176 FreeItems.getInstance().addAll(j.getLines());
3177 }
3178 }
3179 }
3180 }
3181 }
3182 deleteItems(FreeItems.getInstance());
3183 }
3184 // reset the mouse cursor
3185 updateCursor();
3186 // the user is not pointing at an item
3187 } else if (toDelete == null) {
3188
3189 // if the user is pointing inside a closed shape, delete it
3190
3191 Collection<Item> items = null;
3192 // if shift is down, only delete the enclosing shape (ignore the items inside)
3193 if (alternateMode) {
3194 Collection<Item> tmp = deleteEnclosure;
3195 if (tmp != null) {
3196 items = new ArrayList<Item>();
3197 items.addAll(tmp);
3198 for (Item i : tmp) {
3199 if (i instanceof Dot) {
3200 items.addAll(((Dot) i).getLines());
3201 }
3202 }
3203 }
3204 } else {
3205 items = deleteItems;
3206 }
3207
3208 if (items != null) {
3209 Collection<Item> toRemove = new LinkedHashSet<Item>(items.size());
3210 for (Item ip : items) {
3211 if (ip.hasPermission(UserAppliedPermission.full)) {
3212 // Only include lines if one of their enpoints are also
3213 // being removed
3214 if (ip instanceof Line) {
3215 Line l = (Line) ip;
3216 Item end = l.getEndItem();
3217 Item start = l.getStartItem();
3218
3219 // If one end of a line is being deleted, remove the
3220 // other end if all its connecting lines are being
3221 // deleted
3222 if (items.contains(end)) {
3223 if (!items.contains(start) && items.containsAll(start.getLines())) {
3224 toRemove.add(start);
3225 }
3226 } else if (items.contains(start)) {
3227 if (items.containsAll(end.getLines())) {
3228 toRemove.add(end);
3229 }
3230 } else {
3231 continue;
3232 }
3233 }
3234 toRemove.add(ip);
3235 }
3236 }
3237
3238 deleteItems(toRemove);
3239
3240 // reset the mouse cursor
3241 updateCursor();
3242 DisplayController.requestRefresh(true);
3243
3244 // otherwise this is an undo command
3245 }
3246 return;
3247 // this is a delete command
3248 } else {
3249
3250 // Special case if toDelete item is an image: only want to delete if over
3251 // non-background pixel color
3252 if (toDelete instanceof Picture) {
3253 int mouseX = DisplayController.getMouseX();
3254 int mouseY = DisplayController.getMouseY();
3255 Picture clickedOnPicture = (Picture) toDelete;
3256 Frame current_frame = DisplayController.getCurrentFrame();
3257 Colour bg_col = current_frame.getBackgroundColor();
3258 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
3259 return;
3260 }
3261 // If get to here, then user clicked on an image (non-trival pixel color),
3262 // so go ahead and let it be deleted in the usual way
3263 }
3264
3265 // check permissions
3266 if (!toDelete.hasPermission(UserAppliedPermission.full)) {
3267 Item editTarget = toDelete.getEditTarget();
3268 if (editTarget != toDelete && editTarget.hasPermission(UserAppliedPermission.full)) {
3269 toDelete = editTarget;
3270 } else {
3271 MessageBay.displayMessage("Insufficient permission to delete item");
3272 return;
3273 }
3274 }
3275
3276 Frame parent = toDelete.getParent();
3277 if (parent != null) {
3278 parent.setChanged(true);
3279 }
3280 Collection<Item> toUndo = null;
3281 List<Path> canditateFilesToDelete = new ArrayList<Path>();
3282 if (toDelete.isLineEnd()) {
3283 // delete the entire connected shape if shift is down
3284 if (alternateMode) {
3285 List<Item> tmp = new ArrayList<Item>();
3286 tmp.add(toDelete);
3287 // remove entire shape instead of just the corner
3288 tmp.addAll(toDelete.getAllConnected());
3289 for (Item j : toDelete.getAllConnected()) {
3290 if (j instanceof Dot) {
3291 tmp.addAll(j.getLines());
3292 }
3293 }
3294 deleteItems(tmp);
3295 return;
3296 } else {
3297 toUndo = deleteLineEnd(toDelete);
3298 }
3299 // delete the entire connected shape if shift is down, unless we're hovering the
3300 // end of the line
3301 } else if (toDelete instanceof WidgetEdge) { // must notify
3302 // widgets that they
3303 // are being deleted
3304 ((WidgetEdge) toDelete).getWidgetSource().onDelete();
3305 toUndo = toDelete.getConnected();
3306 } else if (toDelete instanceof Line && alternateMode
3307 || toDelete.getHighlightMode() == Item.HighlightMode.Disconnect) {
3308 Line line = (Line) toDelete;
3309 Item start = line.getStartItem();
3310 Item end = line.getEndItem();
3311 Collection<Item> delete = new LinkedList<Item>();
3312 delete.add(toDelete);
3313 if (end.getLines().size() == 1) {
3314 delete.add(end);
3315 } else {
3316 end.removeLine(line);
3317 }
3318 if (start.getLines().size() == 1) {
3319 delete.add(start);
3320 } else {
3321 start.removeLine(line);
3322 }
3323 toUndo = delete;
3324 } else {
3325 bRecalculate |= toDelete.recalculateWhenChanged();
3326 toUndo = toDelete.getConnected(); // copy(toDelete.getConnected());
3327 if (toDelete instanceof Picture && alternateMode) {
3328 String pathStr = ((Picture) toDelete).getPath();
3329 Path path = Paths.get(pathStr);
3330 canditateFilesToDelete.add(path);
3331 }
3332 }
3333 SessionStats.DeletedItems(toUndo);
3334 if (parent != null) {
3335 parent.addToUndoDelete(new ItemsList(toUndo), !canditateFilesToDelete.isEmpty());
3336 parent.removeAllItems(toUndo); // toDelete.getConnected()
3337 deleteUnusedFiles(canditateFilesToDelete);
3338 }
3339 // reset the mouse cursor
3340 updateCursor();
3341 if (parent != null) {
3342 // ItemUtils.EnclosedCheck(parent.getItems());
3343 ItemUtils.Justify(parent);
3344 }
3345 if (toDelete.hasOverlay()) {
3346 FrameUtils.Parse(parent, false, false, false);
3347 DisplayController.requestRefresh(false);
3348 }
3349
3350 DisplayController.setCursor(Item.DEFAULT_CURSOR);
3351
3352 }
3353
3354 currentFrame.notifyObservers(bRecalculate);
3355
3356 DisplayController.requestRefresh(true);
3357 }
3358
3359 private static void deleteUnusedFiles(List<Path> canditateFilesToDelete) {
3360 for (Path p: canditateFilesToDelete) {
3361 try {
3362 Path trashPath = Paths.get(FrameIO.TRASH_PATH);
3363 trashPath.toFile().mkdirs();
3364 Path destination = trashPath.resolve(p.getFileName());
3365 Files.move(p, destination, StandardCopyOption.ATOMIC_MOVE);
3366 } catch (IOException e) {
3367 MessageBay.displayMessage("There was a problem moving a file to the trash. File: " + p.getFileName());
3368 }
3369 }
3370 }
3371
3372 public static void mouseEnteredWindow() {
3373 // if there is expeditee data on the clipboard that has not yet been autoPasted,
3374 // autoPaste it
3375 // Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
3376 // Transferable content = c.getContents(null);
3377 ClipboardData clipboardData = EcosystemManager.getClipboardManager().get();
3378 try {
3379 if (clipboardData.data instanceof ExpDataHandler) { // Expeditee data
3380 ExpDataHandler expData = (ExpDataHandler) clipboardData.data;
3381 if (expData.autoPaste) {
3382 List<Item> items = new ExpClipReader(DisplayController.getMouseX(), DisplayController.getMouseY())
3383 .read(expData.items);
3384 // generate new IDs and pickup
3385 pickup(ItemUtils.CopyItems(items));
3386 // update the clipboard contents so they won't be autoPasted again
3387 expData.autoPaste = false;
3388 /*
3389 * String stringData = ""; Image imageData = null;
3390 * if(content.isDataFlavorSupported(DataFlavor.stringFlavor)) { stringData =
3391 * (String) content.getTransferData(DataFlavor.stringFlavor); }
3392 * if(content.isDataFlavorSupported(DataFlavor.imageFlavor)) { imageData =
3393 * (Image) content.getTransferData(DataFlavor.imageFlavor); } c.setContents(new
3394 * ItemSelection(stringData, imageData, expData), null);
3395 */
3396 EcosystemManager.getClipboardManager().set(clipboardData);
3397 }
3398 }
3399 } catch (Exception ex) {
3400 ex.printStackTrace();
3401 }
3402 }
3403
3404 private static boolean oldControlDown = false;
3405
3406 private void mouseMoved(Point to) {
3407
3408 boolean shiftStateChanged = false;
3409 if (oldControlDown != StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)) {
3410 shiftStateChanged = true;
3411 oldControlDown = StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL);
3412 }
3413
3414 float MouseX = to.getX();
3415 float MouseY = to.getY();
3416
3417 // Moving the mouse a certain distance removes the last edited text if it is
3418 // empty
3419 Text lastEdited = FrameUtils.getLastEdited();
3420 if (lastEdited != null && lastEdited.getText().length() == 0 && lastEdited.getPosition().getDistanceTo(to) > 20) // TODO:
3421 // Remove
3422 // magic
3423 // constant.
3424 // cts16
3425 {
3426 FrameUtils.setLastEdited(null);
3427 }
3428
3429 // If shift is down then the movement is constrained
3430 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL) && FreeItems.getInstance().size() > 0) {
3431 // Check if we are rubber banding a line
3432 if (shiftStateChanged && FreeItems.rubberBanding()) {
3433 // Get the line end that is being rubber banded
3434 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems.getInstance().get(0)
3435 : FreeItems.getInstance().get(1);
3436 Line line = (Line) (FreeItems.getInstance().get(0).isLineEnd() ? FreeItems.getInstance().get(1)
3437 : FreeItems.getInstance().get(0));
3438 Item otherEnd = line.getOppositeEnd(thisEnd);
3439 int deltaX = Math.abs(to.getX() - otherEnd.getX());
3440 int deltaY = Math.abs(to.getY() - otherEnd.getY());
3441 // Check if its a vertical line
3442 if (deltaX < deltaY / 2) {
3443 new Constraint(thisEnd, otherEnd, thisEnd.getParentOrCurrentFrame().getNextItemID(),
3444 Constraint.VERTICAL);
3445 }
3446 // Check if its horizontal
3447 else if (deltaY <= deltaX / 2) {
3448 new Constraint(thisEnd, otherEnd, thisEnd.getParentOrCurrentFrame().getNextItemID(),
3449 Constraint.HORIZONTAL);
3450 } else {
3451 // Add DIAGONAL constraints
3452 int constraint = Constraint.DIAGONAL_NEG;
3453 // Check if the slope is positive
3454 if ((thisEnd.getY() - otherEnd.getY()) / (double) (thisEnd.getX() - otherEnd.getX()) > 0.0) {
3455 constraint = Constraint.DIAGONAL_POS;
3456 }
3457
3458 new Constraint(thisEnd, otherEnd, thisEnd.getParentOrCurrentFrame().getNextItemID(), constraint);
3459 }
3460 // If it's a line-end attached to two lines lengthen the
3461 // shorter so it is the same length as the longer line
3462 } else if (FreeItems.getInstance().size() == 3) {
3463 // check if we are rubber banding the corner of a shape
3464 Item thisEnd = getShapeCorner(FreeItems.getInstance());
3465 if (thisEnd != null) {
3466 Line line1 = thisEnd.getLines().get(0);
3467 Line line2 = thisEnd.getLines().get(1);
3468
3469 // Check if the two lines are constrained and hence it is a rectangle
3470 Integer c1 = line1.getPossibleConstraint();
3471 Integer c2 = line2.getPossibleConstraint();
3472
3473 if (c1 != null && c2 != null) {
3474 // This is the case of a constrained rectangle
3475 if ((c1 == Constraint.VERTICAL && c2 == Constraint.HORIZONTAL)
3476 || (c1 == Constraint.HORIZONTAL && c2 == Constraint.VERTICAL)) {
3477 Line vLine = line2;
3478 Line hLine = line1;
3479 if (c1 == Constraint.VERTICAL) {
3480 vLine = line1;
3481 hLine = line2;
3482 }
3483 Item hOtherEnd = hLine.getOppositeEnd(thisEnd);
3484 Item vOtherEnd = vLine.getOppositeEnd(thisEnd);
3485
3486 double vLength = Math.abs(vOtherEnd.getY() - MouseY);
3487 double hLength = Math.abs(hOtherEnd.getX() - MouseX);
3488
3489 if (vLength > hLength) {
3490 MouseX = Math.round(hOtherEnd.getX() + vLength * (MouseX > hOtherEnd.getX() ? 1 : -1));
3491 } else /* if (hLength > vLength) */ {
3492 MouseY = Math.round(vOtherEnd.getY() + hLength * (MouseY > vOtherEnd.getY() ? 1 : -1));
3493 }
3494 }
3495 // Other wise it is a not constrained shape so constrain
3496 // the two lines lengths to be equal
3497 } else {
3498 Item lineEnd1 = line1.getOppositeEnd(thisEnd);
3499 Item lineEnd2 = line2.getOppositeEnd(thisEnd);
3500 double l1 = Point.distanceBetween(lineEnd1.getPosition(), to);
3501 double l2 = Point.distanceBetween(lineEnd2.getPosition(), to);
3502 double l3 = Point.distanceBetween(lineEnd1.getPosition(), lineEnd2.getPosition());
3503 // l1 needs to be the shorter end
3504 if (l1 > l2) {
3505 Item temp = lineEnd1;
3506 lineEnd1 = lineEnd2;
3507 lineEnd2 = temp;
3508 double tempL = l1;
3509 l1 = l2;
3510 l2 = tempL;
3511 }
3512 // Now use the cosine rule to calculate the angle between l1 and l3
3513 double cosTheta = (l1 * l1 + l3 * l3 - l2 * l2) / (2 * l1 * l3);
3514 // now calculate the new length for the lines using cos rule
3515 double l_new = l3 / (2 * cosTheta);
3516 double ratio = l_new / l1;
3517 MouseX = Math.round((to.getX() - lineEnd1.getX()) * ratio) + lineEnd1.getX();
3518 MouseY = Math.round((to.getY() - lineEnd1.getY()) * ratio) + lineEnd1.getY();
3519 }
3520 }
3521 }
3522 } else if (shiftStateChanged && !StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)
3523 && FreeItems.rubberBanding()) {
3524 // Get the line end that is being rubber banded
3525 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems.getInstance().get(0)
3526 : FreeItems.getInstance().get(1);
3527 thisEnd.removeAllConstraints();
3528 }
3529
3530 refreshHighlights();
3531
3532 if (FreeItems.hasCursor()) {
3533 move(FreeItems.getCursor(), to, true);
3534 }
3535
3536 if (FreeItems.hasItemsAttachedToCursor()) {
3537 move(FreeItems.getInstance(), to);
3538 }
3539
3540 if (_forceArrowCursor) {
3541 updateCursor();
3542 }
3543
3544 _forceArrowCursor = true;
3545 }
3546
3547 /**
3548 * Gets the rectangle corner from the list of items that are part of a
3549 * rectangle.
3550 *
3551 * @param partialRectangle
3552 * a corner and its two connecting lines.
3553 * @return the rectangle corner or null if the list of items is not part of a
3554 * rectangle.
3555 */
3556 private static Item getShapeCorner(List<Item> partialRectangle) {
3557 if (partialRectangle.size() < 3) {
3558 return null;
3559 }
3560 Item lineEnd = null;
3561 // only one lineEnd will be present for rectangles
3562 // All other items must be lines
3563 for (Item i : partialRectangle) {
3564 if (i.isLineEnd()) {
3565 if (lineEnd == null) {
3566 lineEnd = i;
3567 } else {
3568 return null;
3569 }
3570 } else if (!(i instanceof Line)) {
3571 return null;
3572 }
3573 }
3574 // if this is at least the corner of two connected lines
3575 if (lineEnd != null && lineEnd.getAllConnected().size() >= 5) {
3576 return lineEnd;
3577 }
3578
3579 return null;
3580 }
3581
3582 /**
3583 * Performs picking up of a single item, with special handling for lines.
3584 */
3585 private void handlePickup(Item item, Point atPosition, boolean copy, boolean extrude) {
3586 // check permissions
3587 if (copy && item.isLineEnd()) {
3588 if (!item.hasPermission(UserAppliedPermission.full)) {
3589 MessageBay.displayMessage("Insufficient permission to unreel");
3590 return;
3591 }
3592 } else if (!item.hasPermission(UserAppliedPermission.full)) {
3593 Item editTarget = item.getEditTarget();
3594 if ((editTarget != item && editTarget.hasPermission(UserAppliedPermission.full))
3595 || (copy && editTarget.hasPermission(UserAppliedPermission.copy))) {
3596 item = editTarget;
3597 } else {
3598 MessageBay.displayMessage("Insufficient permission to pick up item");
3599 return;
3600 }
3601 }
3602
3603 // Make a copy of the picked-up item
3604 if (copy) {
3605 List<Item> copies = ItemUtils.UnreelLine(item, false);
3606 // Copies will NOT be null if the user right clicked on a point
3607 if (copies == null) {
3608 Collection<Item> originals = item.getConnected();
3609 copies = ItemUtils.CopyItems(originals, extrude);
3610 // if this is the title of the frame, link it to the frame
3611 if (originals.size() == 1 && copies.size() == 1) {
3612 Item firstCopy = copies.get(0);
3613 Item original = originals.iterator().next();
3614 if (original.getLink() == null && original.isFrameTitle()) {
3615 // save the frame after copying
3616 // i.getParent().setChanged(true);
3617 firstCopy.setLink(original.getParentOrCurrentFrame().getName());
3618 }
3619 }
3620
3621 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
3622
3623 if (!extrude) {
3624 clearParent(copies);
3625 }
3626 }
3627
3628 ItemUtils.Justify(copies);
3629
3630 pickup(copies);
3631
3632 // Just pick up the item without copying
3633 } else {
3634
3635 // BROOK: WIDGET RECTANGLES DONT ALLOW DISCONNECTION
3636 if (item instanceof Line && !(item instanceof WidgetEdge)) {
3637 // Check if within 20% of the end of the line
3638 Line l = (Line) item;
3639 Item toDisconnect = l.getEndPointToDisconnect(atPosition.getX(), atPosition.getY());
3640
3641 if (toDisconnect == null) {
3642 pickup(item);
3643 } else {
3644 if (toDisconnect.getHighlightMode() == Item.HighlightMode.Normal) {
3645 DisplayController.setCursorPosition(toDisconnect.getPosition(), false);
3646 pickup(toDisconnect);
3647 } else {
3648 List<Line> lines = toDisconnect.getLines();
3649 // This is to remove constraints from single lines
3650 // with constraints...
3651 // ie. partially deleted rectangles
3652 if (lines.size() == 1) {
3653 toDisconnect.removeAllConstraints();
3654
3655 DisplayController.setCursorPosition(toDisconnect.getPosition(), false);
3656 // This is to ensure the selected mode will be set
3657 // to Normal rather than disconnect when the line is
3658 // anchored
3659 toDisconnect.setHighlightMode(Item.HighlightMode.Normal);
3660 pickup(toDisconnect);
3661 } else {
3662 // If we are then detatch the line and pick up its
3663 // end point...
3664 Frame currentFrame = DisplayController.getCurrentFrame();
3665 Item newPoint = null;
3666
3667 // If the point we are disconnecting is text...
3668 // Then we want to leave the text behind
3669 // And disconnect a point
3670 if (toDisconnect instanceof Text) {
3671 newPoint = new Dot(toDisconnect.getX(), toDisconnect.getY(), -1);
3672 Item.DuplicateItem(toDisconnect, newPoint);
3673 } else {
3674 newPoint = toDisconnect.copy();
3675 }
3676
3677 currentFrame.addItem(newPoint);
3678 // remove the current item from the connected
3679 // list for this item
3680 l.replaceLineEnd(toDisconnect, newPoint);
3681 // remove unneeded constrains
3682 newPoint.removeAllConstraints();
3683
3684 // Set the new points mode to normal before picking
3685 // it up so it will be restored correctly when
3686 // anchored
3687 newPoint.setHighlightMode(Item.HighlightMode.Normal);
3688 toDisconnect.setHighlightMode(Item.HighlightMode.None);
3689 DisplayController.setCursorPosition(toDisconnect.getPosition(), false);
3690 pickup(newPoint);
3691 ItemUtils.EnclosedCheck(toDisconnect.getParentOrCurrentFrame().getSortedItems());
3692 }
3693 }
3694 }
3695 } else {
3696 if (item.isLineEnd()) {
3697 DisplayController.setCursorPosition(item.getPosition(), false);
3698 }
3699 pickup(item);
3700 }
3701 }
3702 }
3703
3704 /**
3705 * Performs picking up of a single item, with special handling for lines.
3706 */
3707 private void handlePickup(Collection<Item> items, Collection<Item> enclosure, Point atPosition, boolean copy) {
3708 List<Item> toPickup = new ArrayList<Item>(items.size());
3709
3710 if (copy) {
3711 List<Item> copies = new ArrayList<Item>();
3712 // Set the selection mode for the items that were clicked in
3713 Collection<Item> enclosed = getFullyEnclosedItems(items);
3714 if (enclosed.size() == 0) {
3715 MessageBay.displayMessage("Insufficient permission to copy items");
3716 } else {
3717 copies = copy(enclosed);
3718 clearParent(copies);
3719 ItemUtils.Justify(copies);
3720 pickup(copies);
3721 for (Item i : items) {
3722 i.setHighlightMode(HighlightMode.None);
3723 }
3724 }
3725 toPickup = copies;
3726 } else {
3727 for (Item ip : items) {
3728 if (ip.hasPermission(UserAppliedPermission.full)) {
3729 toPickup.add(ip);
3730 }
3731 }
3732 }
3733 pickup(toPickup);
3734 }
3735
3736 /**
3737 * Marks the items as not belonging to any specific frame. When picking up items
3738 * the parent will be automatically cleared for items on the current frame but
3739 * not for overlay items. This method ensures that overlay items will also be
3740 * cleared. This is useful when picking up copies of items from an overlay (with
3741 * the right mouse button) to ensure that the copy will be anchored on the
3742 * current frame rather than the overlay. When items are picked up with the
3743 * middle button clearParent should NOT be called.
3744 *
3745 * @param items
3746 * to have their parent cleared
3747 */
3748 private static void clearParent(List<Item> items) {
3749 for (Item i : items) {
3750 // The next line is only necessary for circles...
3751 // Need to clean up/refactory some of this stuff
3752 i.getParentOrCurrentFrame().removeItem(i);
3753 i.setParent(null);
3754 }
3755 }
3756
3757 private static boolean doMerging(Item clicked) {
3758 if (clicked == null) {
3759 return false;
3760 }
3761
3762 if(clicked instanceof WidgetCorner) {
3763 final WidgetCorner wc = (WidgetCorner) clicked;
3764 if(wc.getWidgetSource().ItemsMiddleClickDropped()) {
3765 return true;
3766 }
3767 }
3768
3769 // // Brook: widgets do not merge
3770 // if (clicked instanceof WidgetCorner)
3771 // return false;
3772 //
3773 // // Brook: widgets do not merge
3774 // if (clicked instanceof WidgetEdge)
3775 // return false;
3776
3777 // System.out.println(FreeItems.getInstance().size());
3778 if (isRubberBandingCorner()) {
3779 if (clicked.isLineEnd() || clicked.getAllConnected().contains(FreeItems.getItemAttachedToCursor())) {
3780 return true;
3781 }
3782 }
3783
3784 if (FreeItems.getInstance().size() > 2) {
3785 return false;
3786 }
3787
3788 Item attachedToCursor = FreeItems.getItemAttachedToCursor();
3789
3790 if (clicked instanceof Text && !(attachedToCursor instanceof Text || attachedToCursor.isLineEnd())) {
3791 return false;
3792 }
3793
3794 if (clicked instanceof Picture) {
3795 int mouseX = DisplayController.getMouseX();
3796 int mouseY = DisplayController.getMouseY();
3797 Picture clickedOnPicture = (Picture) clicked;
3798 Frame current_frame = DisplayController.getCurrentFrame();
3799 Colour bg_col = current_frame.getBackgroundColor();
3800 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
3801 // Make 'clicked' null, effectively causing a back() operation
3802 return false;
3803 }
3804 }
3805
3806 return true;
3807 }
3808
3809 private static boolean isRubberBandingCorner() {
3810 return getShapeCorner(FreeItems.getInstance()) != null;
3811 }
3812
3813 public List<Item> deleteItemsAction(Item item) {
3814 List<Item> items = new ArrayList<Item>();
3815 Item merger = FreeItems.getItemAttachedToCursor();
3816 assert (merger != null);
3817
3818 // check permissions
3819 String toDeleteOwner = item.getOwner();
3820 boolean isSameOwner = toDeleteOwner.equals(UserSettings.UserName.get());
3821 boolean isOwnerPermissionChange =
3822 isSameOwner && merger.getText().startsWith("Permission:");
3823 if (!item.hasPermission(UserAppliedPermission.full) && !isOwnerPermissionChange) {
3824 // Items on the message box have parent == null
3825 if (item.getParent() != null) {
3826 if (!item.isFrameName()) {
3827 Item editTarget = item.getEditTarget();
3828 if (editTarget != item && editTarget.hasPermission(UserAppliedPermission.full)) {
3829 item = editTarget;
3830 } else {
3831 MessageBay.displayMessage("Insufficient permission");
3832 return items;
3833 }
3834 }
3835
3836 } else /* Its in the message area */ {
3837 MessageBay.displayMessage("Insufficient permission");
3838 return items;
3839 }
3840 }
3841
3842 Collection<Item> left = null;
3843 // when anchoring a line end onto a text line end, holding shift
3844 // prevents the line ends from being merged
3845 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
3846 left = FreeItems.getInstance();
3847 } else {
3848 left = merge(FreeItems.getInstance(), item);
3849 }
3850 Collection<Item> toDelete = new LinkedList<Item>();
3851 toDelete.addAll(FreeItems.getInstance());
3852 toDelete.removeAll(left);
3853 anchor(left);
3854 FreeItems.getInstance().clear();
3855 DisplayController.getCurrentFrame().removeAllItems(toDelete);
3856 updateCursor();
3857 // Make sure the dot goes away when anchoring a line end behind
3858 // a text line end
3859 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
3860 refreshHighlights();
3861 }
3862 DisplayController.requestRefresh(true);
3863 return items;
3864 // otherwise, anchor the items
3865 }
3866
3867 private static final int RECTANGLE_TO_POINT_THRESHOLD = 20;
3868
3869 public static Collection<Item> merge(List<Item> merger, Item mergee) {
3870 assert (mergee != null);
3871 if (mergee.isFrameName()) {
3872 return mergee.getParent().merge(merger);
3873 }
3874
3875 // if(mergee instanceof XRayable)
3876 // return merger;
3877
3878 // check for rectangle merging
3879 if (merger.size() == 3 && mergee.getLines().size() == 2) {
3880 Item corner = getShapeCorner(merger);
3881 // if this is a corner of a shape
3882 if (corner != null) {
3883 Collection<Item> allConnected = corner.getAllConnected();
3884 // Check if we are collapsing a rectangle
3885 if (allConnected.size() == 8 && allConnected.contains(mergee)) {
3886 DisplayController.setCursorPosition(mergee.getPosition());
3887 DisplayController.getCurrentFrame().removeAllItems(allConnected);
3888
3889 // find the point opposite corner...
3890 Item opposite = null;
3891 List<Line> lines = corner.getLines();
3892 for (Line l : lines) {
3893 allConnected.remove(l.getOppositeEnd(corner));
3894 }
3895 allConnected.remove(corner);
3896 for (Item i : allConnected) {
3897 if (i.isLineEnd()) {
3898 opposite = i;
3899 break;
3900 }
3901 }
3902 assert (opposite != null);
3903
3904 // check if the rectangle is small enough that it should be
3905 // collapsed to a single point
3906 int x1 = Math.abs(opposite.getX() - mergee.getX());
3907 int x2 = Math.abs(opposite.getY() - mergee.getY());
3908 int distance = (int) Math.sqrt(Math.pow(x1, 2) + Math.pow(x2, 2));
3909
3910 if (distance < RECTANGLE_TO_POINT_THRESHOLD) {
3911 mergee.removeAllConstraints();
3912 mergee.removeAllLines();
3913 mergee.setThickness(4 * mergee.getThickness());
3914 return mergee.getAllConnected();
3915 } else {
3916 removeAllLinesExcept(mergee, opposite);
3917 removeAllLinesExcept(opposite, mergee);
3918
3919 return mergee.getAllConnected();
3920 }
3921 }
3922 }
3923 }
3924
3925 List<Item> remain = new ArrayList<Item>();
3926 Item res = null;
3927
3928 for (Item i : merger) {
3929 if (!i.isVisible()) {
3930 continue;
3931 }
3932 // check for link merging
3933 if (i instanceof Text && FrameIO.isValidFrameName((((Text) i).getFirstLine()))
3934 && FrameIO.canAccessFrame((((Text) i).getFirstLine()))) {
3935 // check that we can actually access the frame this link
3936 // corresponds to
3937 mergee.setLink(((Text) i).getFirstLine());
3938 } else {
3939 // check for attribute merging
3940 if (i instanceof Text && !i.isLineEnd()) {
3941 Text txt = (Text) i;
3942
3943 // if this is not an attribute merge
3944 if (!AttributeUtils.setAttribute(mergee, txt)) {
3945 // set mouse position for text merges
3946 if (mergee instanceof Text) {
3947 ((Text) mergee).insertText(txt.getText(), DisplayController.getMouseX(),
3948 DisplayController.getMouseY());
3949 // Delete the item which had its text merged
3950 txt.delete();
3951 return remain;
3952 } else if (mergee instanceof WidgetCorner) {
3953 if (merger.size() == 1 && txt.getLink() != null) {
3954 // If the text item is linked then use that
3955 ((WidgetCorner) mergee).setLink(txt.getAbsoluteLink(), txt);
3956 } else {
3957 remain.addAll(merger);
3958 }
3959 return remain;
3960 } else if (mergee instanceof WidgetEdge) {
3961 if (merger.size() == 1 && txt.getLink() != null) {
3962 // If the text item is linked then use that
3963 ((WidgetEdge) mergee).setLink(txt.getAbsoluteLink(), txt);
3964 } else {
3965 remain.addAll(merger);
3966 }
3967 return remain;
3968 } else if (mergee instanceof Dot) {
3969 DisplayController.setCursorPosition(mergee.getPosition());
3970 txt.setPosition(mergee.getPosition());
3971 txt.setThickness(mergee.getThickness());
3972 Frame parent = mergee.getParent();
3973 parent.removeItem(mergee);
3974 anchor(txt);
3975 // change the end points of the lines to the text
3976 // item
3977 while (mergee.getLines().size() > 0) {
3978 Line l = mergee.getLines().get(0);
3979 l.replaceLineEnd(mergee, txt);
3980 }
3981 break;
3982 }
3983
3984 // TODO tidy this up...
3985 // Dot override doesnt use the x and y coords
3986 // Text does... but could be removed
3987 res = mergee.merge(i, DisplayController.getMouseX(), DisplayController.getMouseY());
3988 if (res != null) {
3989 remain.add(res);
3990 }
3991 }
3992 } else {
3993 if (mergee.isLineEnd()) {
3994 DisplayController.setCursorPosition(mergee.getPosition());
3995 }
3996 // Moving the cursor ensures shapes are anchored correctly
3997 res = mergee.merge(i, DisplayController.getMouseX(), DisplayController.getMouseY());
3998 if (res != null) {
3999 remain.addAll(res.getConnected());
4000 }
4001
4002 }
4003 }
4004 }
4005 updateCursor();
4006
4007 mergee.getParent().setChanged(true);
4008
4009 ItemUtils.EnclosedCheck(mergee.getParent().getSortedItems());
4010 // Mike: Why does parse frame have to be called?!?
4011 FrameUtils.Parse(mergee.getParent());
4012
4013 return remain;
4014 }
4015
4016 private static void removeAllLinesExcept(Item from, Item but) {
4017 List<Line> lines = new LinkedList<Line>();
4018 lines.addAll(from.getLines());
4019 for (Line line : lines) {
4020 if (line.getOppositeEnd(from) != but) {
4021 from.removeLine(line);
4022 }
4023 }
4024
4025 List<Constraint> consts = new LinkedList<Constraint>();
4026 consts.addAll(from.getConstraints());
4027 for (Constraint c : consts) {
4028 if (c.getOppositeEnd(from) != but) {
4029 from.removeConstraint(c);
4030 }
4031 }
4032 }
4033
4034 public void anchorFreeItemsAction(Collection<Item> items) {
4035 if (items != null && FreeItems.getInstance().size() == 1) {
4036 Item item = FreeItems.getItemAttachedToCursor();
4037 if (item instanceof Text) {
4038 Text text = (Text) item;
4039 if (AttributeUtils.setAttribute(text, text, 2)) {
4040 items.removeAll(FrameUtils.getEnclosingLineEnds().iterator().next().getAllConnected());
4041 for (Item i : items) {
4042 AttributeUtils.setAttribute(i, text);
4043 }
4044 FreeItems.getInstance().clear();
4045 }
4046 }
4047 }
4048
4049 // if a line is being rubber-banded, check for auto
4050 // straightening
4051 anchor(FreeItems.getInstance());
4052 // FreeItems.getInstance().clear();
4053 updateCursor();
4054 _offX = _offY = 0;
4055 }
4056
4057 public void copyPlace(Item item) {
4058 List<Item> copies = null;
4059 if (FreeItems.getInstance().size() == 1 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
4060 // Dont stamp if the user is painting... because we dont want to
4061 // save any of the items created!
4062 return;
4063 // if the user is clicking on something, merge the items unless
4064 // it is a point onto something other than a lineEnd or a dot
4065 } else if (item != null
4066 && (!(FreeItems.getItemAttachedToCursor() instanceof Dot) || item instanceof Dot || item.isLineEnd())) {
4067 if(item instanceof WidgetCorner) {
4068 final WidgetCorner wc = (WidgetCorner) item;
4069 wc.getWidgetSource().ItemsRightClickDropped();
4070 return;
4071 }
4072
4073 // TODO Change the items merge methods so the logic is simplified
4074 // check permissions
4075 if (!item.hasPermission(UserAppliedPermission.full) && item.getParent().getNameItem() != item) {
4076 MessageBay.displayMessage("Insufficient permission to merge items");
4077 return;
4078 }
4079 if (item instanceof Text || item instanceof Dot || item instanceof XRayable) {
4080 if (isRubberBandingCorner()) {
4081 copies = mergeGroupAction(item);
4082 // line onto something
4083 } else if (FreeItems.getInstance().size() == 2 /* && clicked instanceof XRayable */) {
4084 copies = mergeTwoItemsAction(item);
4085 } else if (FreeItems.getInstance().size() == 1) {
4086 copies = mergeSingleItemAction(item);
4087 } else {
4088 copies = stampAction();
4089 }
4090 } else {
4091 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4092 if (copies == null) {
4093 copies = copy(FreeItems.getInstance());
4094 }
4095 for (Item i : copies) {
4096 i.setOffset(0, 0);
4097 }
4098 anchor(FreeItems.getInstance());
4099 FreeItems.getInstance().clear();
4100 pickup(copies);
4101 }
4102 // otherwise, anchor the items
4103 } else {
4104 // check if this is anchoring a rectangle
4105 if (isRubberBandingCorner()) {
4106 copies = rubberBandingCornerAction();
4107 } else {
4108 if (FreeItems.rubberBanding()) {
4109 copies = rubberBandingCopyAction(item);
4110 } else {
4111 stampItemsOnCursor(true);
4112 copies = FreeItems.getInstance();
4113 }
4114 }
4115 }
4116 }
4117
4118 /**
4119 * Returns a list of copies of the list passed in
4120 *
4121 * @param toCopy
4122 * The list of items to copy
4123 * @return A List of copied Items
4124 */
4125 private static List<Item> copy(Collection<Item> toCopy) {
4126 return ItemUtils.CopyItems(toCopy);
4127 }
4128
4129 public List<Item> mergeGroupAction(Item item) {
4130 List<Item> copies = new ArrayList<Item>();
4131 // Move the cursor so that the copy is exactly the
4132 // same as the shape that was anchored
4133 DisplayController.setCursorPosition(item.getPosition());
4134 Item d = getFirstFreeLineEnd();
4135 // get a copy of all enclosed items before merging
4136 // lineEnds
4137 Collection<Item> items = FrameUtils.getItemsEnclosedBy(DisplayController.getCurrentFrame(),
4138 d.getEnclosedShape());
4139 // If its not an enclosed shape then pick up the
4140 // connected shape
4141 if (items == null || items.size() == 0) {
4142 items = d.getAllConnected();
4143 } else {
4144 // For some reason the item that was clicked ends up
4145 // in the enclosure and needs to be removed
4146 items.removeAll(item.getConnected());
4147 // the item that was the origin of the enclosed
4148 // shape used to create the enclosure does not get
4149 // returned from getItemsEnclosedBy to the enclosure
4150 // so it must be added
4151 items.addAll(d.getConnected());
4152 }
4153
4154 Collection<Item> toCopy = new LinkedHashSet<Item>();
4155
4156 for (Item ip : items) {
4157 if (ip.hasPermission(UserAppliedPermission.copy)) {
4158 toCopy.add(ip);
4159 }
4160 }
4161 copies = copy(toCopy);
4162 // Now do the merging
4163 Collection<Item> remain = merge(FreeItems.getInstance(), item);
4164 // anchor the points
4165 anchor(remain);
4166 FreeItems.getInstance().clear();
4167 pickup(copies);
4168 return copies;
4169 }
4170
4171 private static Item getFirstFreeLineEnd() {
4172 for (Item i : FreeItems.getInstance()) {
4173 if (i.isLineEnd()) {
4174 return i;
4175 }
4176 }
4177 return null;
4178 }
4179
4180 public List<Item> mergeTwoItemsAction(Item item) {
4181 List<Item> copies = new ArrayList<Item>();
4182 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4183 Collection<Item> leftOver = merge(FreeItems.getInstance(), item);
4184 anchor(leftOver);
4185 if (copies == null) {
4186 copies = copy(FreeItems.getInstance());
4187 }
4188 FreeItems.getInstance().clear();
4189 for (Item i : copies) {
4190 i.setOffset(0, 0);
4191 }
4192 // need to move to prevent cursor dislocation
4193 move(copies, EcosystemManager.getInputManager().getCursorPosition());
4194 pickup(copies);
4195 // point onto point
4196 return copies;
4197 }
4198
4199 public List<Item> mergeSingleItemAction(Item item) {
4200 List<Item> copies = new ArrayList<Item>();
4201 copies = copy(FreeItems.getInstance());
4202 Collection<Item> remain = merge(copies, item);
4203
4204 // ignore items that could not be merged.
4205 anchor(remain);
4206 return copies;
4207 }
4208
4209 public List<Item> stampAction() {
4210 List<Item> copies = new ArrayList<Item>();
4211 stampItemsOnCursor(true);
4212 copies = FreeItems.getInstance();
4213 return copies;
4214 }
4215
4216 /**
4217 *
4218 */
4219 private static void stampItemsOnCursor(boolean save) {
4220 List<Item> copies = copy(FreeItems.getInstance());
4221 // MIKE: what does the below 2 lines do?
4222 for (Item i : copies) {
4223 i.setOffset(0, 0);
4224 i.setSave(save);
4225 }
4226 // The below code has a little problem withflicker when stamp and dragging
4227 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4228 for (Item i : copies) {
4229 i.setHighlightMode(HighlightMode.None);
4230 }
4231 anchor(copies);
4232 }
4233
4234 public List<Item> rubberBandingCornerAction() {
4235 List<Item> copies = new ArrayList<Item>();
4236 Item d = getFirstFreeLineEnd();
4237 // anchor the points
4238 anchor(FreeItems.getInstance());
4239 FreeItems.getInstance().clear();
4240 updateCursor();
4241 // pick up a copy of all enclosed items
4242 Collection<Item> enclosedItems = FrameUtils.getItemsEnclosedBy(DisplayController.getCurrentFrame(),
4243 d.getEnclosedShape());
4244 if (enclosedItems != null) {
4245 enclosedItems.removeAll(d.getAllConnected());
4246 Collection<Item> toCopy = getFullyEnclosedItems(enclosedItems);
4247
4248 if (toCopy.size() > 0) {
4249 // Find the closest item to the mouse cursor
4250 double currentX = DisplayController.getMouseX();
4251 double currentY = DisplayController.getMouseY();
4252 Item closest = null;
4253 double shortestDistance = Double.MAX_VALUE;
4254 for (Item next : toCopy) {
4255 if (next instanceof Line) {
4256 continue;
4257 }
4258 double distance = Point.distanceBetween((int) currentX, (int) currentY, next.getX(), next.getY());
4259 if (distance < shortestDistance) {
4260 shortestDistance = distance;
4261 closest = next;
4262 }
4263 }
4264 // Move the cursor to closest item
4265 DisplayController.setCursorPosition(closest.getPosition());
4266 // Pickup copy of the stuff inside the rectangle
4267 copies = copy(toCopy);
4268 pickup(copies);
4269 // Remove the rectangle
4270 d.getParentOrCurrentFrame().removeAllItems(d.getAllConnected());
4271 } else {
4272 // Pick up a copy of the rectangle
4273 copies = copy(d.getAllConnected());
4274 pickup(copies);
4275 }
4276 }
4277 return copies;
4278 }
4279
4280 /**
4281 * @param enclosedItems
4282 * @return
4283 */
4284 private static Collection<Item> getFullyEnclosedItems(Collection<Item> enclosure) {
4285 // copy the enclosedItems because the list will be modified
4286 Collection<Item> enclosedItems = new LinkedHashSet<Item>(enclosure);
4287 Collection<Item> toCopy = new LinkedHashSet<Item>(enclosedItems.size());
4288
4289 while (enclosedItems.size() > 0) {
4290 Item i = enclosedItems.iterator().next();
4291 if (i.hasPermission(UserAppliedPermission.copy)) {
4292 Collection<Item> items = i.getAllConnected();
4293 // Only copy if the entire shape is enclosed
4294 if (enclosedItems.containsAll(items)) {
4295 toCopy.addAll(items);
4296 }
4297 enclosedItems.removeAll(items);
4298 } else {
4299 enclosedItems.remove(i);
4300 }
4301 }
4302 return toCopy;
4303 }
4304
4305 public List<Item> rubberBandingCopyAction(Item item) {
4306 List<Item> copies = new ArrayList<Item>();
4307 if (item != null) {
4308 Collection<Item> leftOver = merge(FreeItems.getInstance(), item);
4309 anchor(leftOver);
4310 }
4311 // This is executed when the user is putting down a line
4312 // endpoint and unreeling. ie. Normal unreeling
4313 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4314
4315 if (copies == null) {
4316 copies = copy(FreeItems.getInstance());
4317 }
4318
4319 anchor(FreeItems.getInstance());
4320 for (Item i : copies) {
4321 i.setOffset(0, 0);
4322 }
4323 // need to move to prevent cursor dislocation
4324 move(copies, EcosystemManager.getInputManager().getCursorPosition());
4325 pickup(copies);
4326 return copies;
4327 }
4328
4329 public List<Item> newLineAction(Point position, Item item) {
4330 List<Item> copies = new ArrayList<Item>();
4331 // If we have permission to copy this item then pick it up
4332 if (item != null && item.isLineEnd() && item.hasPermission(UserAppliedPermission.full)) {
4333 item.removeAllConstraints();
4334 pickup(item);
4335 return copies;
4336 }
4337
4338 if (item instanceof WidgetEdge) {
4339 // Don't allow the user to break widget edges.
4340 // Note: had to return here because random dots would
4341 // appear otherwise... cannot understand code below
4342 // with create line.
4343 return copies;
4344 }
4345
4346 // if its on a line then split the line and put a point on it and
4347 // pick that point up. Only if it is not a widget line
4348 if (item instanceof Line && item.hasPermission(UserAppliedPermission.full)) {
4349 Frame current = DisplayController.getCurrentFrame();
4350 // create the two endpoints
4351 Line oldLine = (Line) item;
4352 Item newPoint = oldLine.getStartItem().copy();
4353 newPoint.setPosition(position);
4354
4355 Item end = oldLine.getEndItem();
4356 // create the Line
4357 Line newLine = new Line(newPoint, end, current.getNextItemID());
4358 oldLine.replaceLineEnd(end, newPoint);
4359 newPoint.removeAllConstraints();
4360 pickup(newPoint);
4361 // Update the stats
4362 Collection<Item> created = new LinkedList<Item>();
4363 created.add(newPoint);
4364 created.add(newLine);
4365 SessionStats.CreatedItems(newLine.getAllConnected());
4366 return copies;
4367 }
4368 Line newLine = createLine();
4369 SessionStats.CreatedItems(newLine.getAllConnected());
4370 return copies;
4371 }
4372
4373 private static Line createLine() {
4374 Frame current = DisplayController.getCurrentFrame();
4375 // create the two endpoints
4376 Item end = DisplayController.getCurrentFrame().createDot();
4377 Item start = DisplayController.getCurrentFrame().createDot();
4378
4379 // create the Line
4380 Line line = new Line(start, end, current.getNextItemID());
4381 line.autoArrowheadLength();
4382
4383 // anchor the start
4384 anchor(start);
4385
4386 // attach the line to the cursor
4387 pickup(end);
4388 _lastHighlightedItem = null;
4389
4390 // TODO figure out how to get the end to highlight
4391 // end.setSelectedMode(SelectedMode.Normal);
4392 // end.setSelectedMode(SelectedMode.None);
4393
4394 return line;
4395 }
4396
4397 public List<Item> createRectangleAction() {
4398 final List<Item> copies = new ArrayList<Item>();
4399 Item[] d = new Item[4];
4400 // create dots
4401 Frame current = DisplayController.getCurrentFrame();
4402 for (int i = 0; i < d.length; i++) {
4403 d[i] = current.createDot();
4404 copies.add(d[i]);
4405 }
4406
4407 current.nextDot();
4408
4409 // create lines
4410 copies.add(new Line(d[0], d[1], current.getNextItemID()));
4411 copies.add(new Line(d[1], d[2], current.getNextItemID()));
4412 copies.add(new Line(d[2], d[3], current.getNextItemID()));
4413 copies.add(new Line(d[3], d[0], current.getNextItemID()));
4414
4415 new Constraint(d[0], d[1], current.getNextItemID(), Constraint.HORIZONTAL);
4416 new Constraint(d[2], d[3], current.getNextItemID(), Constraint.HORIZONTAL);
4417 new Constraint(d[1], d[2], current.getNextItemID(), Constraint.VERTICAL);
4418 new Constraint(d[3], d[0], current.getNextItemID(), Constraint.VERTICAL);
4419
4420 anchor(new ArrayList<Item>(copies));
4421 pickup(d[3]);
4422 d[3].setHighlightMode(HighlightMode.Normal);
4423
4424 SessionStats.CreatedItems(copies);
4425 copies.clear();
4426 return copies;
4427 }
4428
4429 /**
4430 * This method handles all left-click actions
4431 */
4432 private void click(Item clicked, Collection<Item> clickedIn, Point position) {
4433
4434 // Gets the current frame
4435 Frame f = DisplayController.getCurrentFrame();
4436
4437 // Checks if the current frame is an overlay
4438 if (f.getOverlays() != null && FrameUtils.getCurrentItem() != null) {
4439 Item i = FrameUtils.getCurrentItem();
4440
4441 // Checks if the item clicked in the overlay is a Rubbish Bin. If it is, delete
4442 // the item attached to the cursor and return.
4443 if (i instanceof WidgetCorner) {
4444
4445 try {
4446 WidgetCorner wc = (WidgetCorner) i;
4447 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
4448
4449 // Should call a button widgets 'itemheldwhileclicked' method, and process
4450 // depending on the widget type - else will return false.
4451 if (bw.itemHeldWhileClicked(bw) == true) {
4452
4453 return;
4454 }
4455 } catch (Exception e) {
4456
4457 e.printStackTrace();
4458 }
4459 }
4460 }
4461
4462 // if the user is pointing at something then either follow the link or
4463 // do TDFC
4464 if (clicked == null) {
4465 // Check if the user is nearby another item...
4466 int mouseX = position.getX();
4467 int mouseY = position.getY();
4468 // System.out.println(mouseX + "," + mouseY);
4469 for (Item i : DisplayController.getCurrentFrame().getSortedItems()) {
4470 // System.out.println(i.getName().toString());
4471 if (i instanceof Text) {
4472 if (i.isNear(mouseX, mouseY)) {
4473 clicked = i;
4474 break;
4475 }
4476 }
4477 }
4478 }
4479
4480 if (clicked instanceof Text) {
4481 Text text = (Text) clicked;
4482 /* Don't follow link when just highlighting text with the left button */
4483 if (text.getText().length() == 0) {
4484 clicked = null;
4485 } else if (text.getSelectionSize() > 0) {
4486 return;
4487 }
4488 }
4489
4490 // If the user clicked into a widgets free space...
4491 if (clicked == null && clickedIn != null && clickedIn.size() >= 4) {
4492
4493 // Check to see if the user clicked into a widgets empty space
4494 Widget iw = null;
4495
4496 for (Item i : clickedIn) {
4497
4498 if (i instanceof WidgetCorner) {
4499 iw = ((WidgetCorner) i).getWidgetSource();
4500 break;
4501 } else if (i instanceof WidgetEdge) {
4502 iw = ((WidgetEdge) i).getWidgetSource();
4503 break;
4504 }
4505 }
4506
4507 if (iw != null) {
4508
4509 // Handle dropping items on widgets
4510 if (iw.ItemsLeftClickDropped()) {
4511 return;
4512 }
4513
4514 // Note: mustn't directly use source for handling the link
4515 // because all link operations will by-pass the widgets special
4516 // handling with links...
4517 Item widgetLink = iw.getItems().get(0);
4518 assert (widgetLink != null);
4519 clicked = widgetLink;
4520 } else {
4521 for (Item i : clickedIn) {
4522 /*
4523 * Find the first linked item or the first unlinked Dot This code assumes that
4524 * items are are ordered from top to bottom. TODO make sure the list will always
4525 * be ordered correctly!!
4526 */
4527 if (i.hasLink() || i instanceof Dot) {
4528 clicked = i;
4529 break;
4530 }
4531 }
4532 }
4533
4534 }
4535
4536 if (clicked instanceof Picture) {
4537 int mouseX = position.getX();
4538 int mouseY = position.getY();
4539 Picture clickedOnPicture = (Picture) clicked;
4540 Frame current_frame = DisplayController.getCurrentFrame();
4541 Colour bg_col = current_frame.getBackgroundColor();
4542 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
4543 // Make 'clicked' null, effectively causing a back() operation
4544 clicked = null;
4545 }
4546 }
4547
4548 // This makes it so clicking repeatedly on the frameName doesn't add the
4549 // frames to the backup stack. Only the first frame is added to the
4550 // backup stack.
4551 if (!(clicked != null && clicked.isFrameName())) {
4552 Navigation.ResetLastAddToBack();
4553 }
4554
4555 if (clicked != null) {
4556 // check item permissions
4557 boolean hasLinkOrAction = clicked.hasLink() || clicked.hasAction();
4558
4559 if ((hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.followLinks))
4560 || (!hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.createFrames))) {
4561 Item editTarget = clicked.getEditTarget();
4562 if (editTarget != clicked) {
4563 if (editTarget.hasPermission(UserAppliedPermission.followLinks)) {
4564 clicked = editTarget;
4565 } else {
4566 MessageBay.displayMessage("Insufficient permission to perform action on item");
4567 return;
4568 }
4569 }
4570 }
4571
4572 Item clickedOn = clicked;
4573
4574 // actions take priority
4575 if (clickedOn.hasAction()) {
4576 executeActionAction(clicked);
4577 } else if (clickedOn.getLink() != null) {
4578 followLinkAction(clicked);
4579 // no link is found, perform TDFC
4580 } else {
4581 /*
4582 * if the user is clicking on the frame name then move to the next or previous
4583 * frame regardless of whether or not the frame is protected
4584 */
4585 if (clickedOn.isFrameName()) {
4586 respondToFrameNameClickAction();
4587 }
4588
4589 TDFCAction(clicked);
4590 }
4591
4592 } else {
4593 backAction();
4594 }
4595 }
4596
4597 public void executeActionAction(Item item) {
4598 item.performActions();
4599 item.setHighlightMode(HighlightMode.None);
4600 refreshHighlights();
4601 }
4602
4603 public void followLinkAction(Item item) {
4604 /*
4605 * Dont save the frame if we are moving to an old version of this frame because
4606 * everytime we save with the old tag... the frame is backed up
4607 */
4608 if (!item.isOldTag()) {
4609 FrameIO.SaveFrame(DisplayController.getCurrentFrame());
4610 }
4611
4612 Navigation.setLastNavigationItem(item);
4613 load(item.getAbsoluteLink(), item.getLinkHistory());
4614 // DisplayIO.UpdateTitle();
4615 }
4616
4617 private static void load(String toLoad, boolean addToHistory) {
4618 if (FrameIO.isValidFrameName(toLoad)) {
4619 DisplayController.clearBackedUpFrames();
4620 FrameUtils.DisplayFrame(toLoad, addToHistory, true);
4621 } else {
4622 MessageBay.errorMessage(toLoad + " is not a valid frame name.");
4623 }
4624 }
4625
4626 public void respondToFrameNameClickAction() {
4627 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)) {
4628 Navigation.PreviousFrame(false);
4629 } else {
4630 Navigation.NextFrame(false);
4631 }
4632 }
4633
4634 public List<Item> TDFCAction(Item item) {
4635 // check for TDFC permission
4636 if (!item.hasPermission(UserAppliedPermission.createFrames)) {
4637 MessageBay.displayMessage("Insufficient permission to TDFC (Top Down Frame Creation) from that item");
4638 return null;
4639 }
4640
4641 if (item.isOldTag()) {
4642 return null;
4643 }
4644
4645 try {
4646 tdfc(item);
4647 } catch (RuntimeException e) {
4648 e.printStackTrace();
4649 MessageBay.errorMessage("Top Down Frame Creation (TDFC) error: " + e.getMessage());
4650 }
4651 return null;
4652 }
4653
4654 public void backAction() {
4655 // if user is not pointing at something, this is a back
4656 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)
4657 || StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
4658 forward();
4659 } else {
4660 back();
4661 }
4662 }
4663
4664 private static void forward() {
4665 DisplayController.Forward();
4666
4667 // repaint things if necessary
4668 if (FreeItems.hasItemsAttachedToCursor()) {
4669 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4670 }
4671
4672 if (FreeItems.hasCursor()) {
4673 move(FreeItems.getCursor(), EcosystemManager.getInputManager().getCursorPosition(), true);
4674 }
4675 }
4676
4677 private static void back() {
4678 DisplayController.Back();
4679
4680 // repaint things if necessary
4681 if (FreeItems.hasItemsAttachedToCursor()) {
4682 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4683 }
4684
4685 if (FreeItems.hasCursor()) {
4686 move(FreeItems.getCursor(), EcosystemManager.getInputManager().getCursorPosition(), true);
4687 }
4688 }
4689
4690 /**
4691 * Creates a new Text item and fills it with particular attributes extracted
4692 * from the given Item. Note: Users always have permission to extract
4693 * attributes, so it is not checked.
4694 *
4695 * @param toExtract
4696 * Item containing the Item to extract the attributes from.
4697 */
4698 private static void extractAttributes(Item toExtract) {
4699 if (toExtract == null || toExtract == null) {
4700 return;
4701 }
4702
4703 if (FreeItems.hasItemsAttachedToCursor()) {
4704 return;
4705 }
4706
4707 Item attribs;
4708 Item item = toExtract;
4709 // Extract the frames attributes when the user clicks on the frame name
4710 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
4711 if (item.isFrameName()) {
4712 attribs = AttributeUtils.extractAttributes(item.getParent());
4713 } else {
4714 attribs = AttributeUtils.extractAttributes(item);
4715 }
4716
4717 if (attribs == null) {
4718 MessageBay.displayMessage("All attributes of that item are default values.");
4719 } else {
4720 // Give the attribute text item the color of the item for which
4721 // attributes are being extracted.
4722 // attribs.setColor(item.getColor());
4723 pickup(attribs);
4724 }
4725 }
4726
4727 private void pickupRange(Text text, Point position, boolean copy, boolean inheritAttributes) {
4728 Text ranged;
4729 if (inheritAttributes) {
4730 // If shift is down, copy everything (size, color, etc.) except actions, links
4731 // and data
4732 ranged = text.copy();
4733 ranged.setActions(null);
4734 ranged.setData((List<String>) null);
4735 ranged.setLink(null);
4736 } else {
4737 // If shift isn't down, don't copy any attributes, but base the new text item on
4738 // the appropriate template
4739 final String copySelectedText = text.copySelectedText();
4740 if (copySelectedText.length() < 1) {
4741 return;
4742 }
4743 ranged = DisplayController.getCurrentFrame().getItemTemplate(copySelectedText.charAt(0));
4744 }
4745
4746 // if the user is cutting text from the item
4747 if (!copy) {
4748 // Check if the user is trying to range an item for which they
4749 // do not have permission to do so... or it is the frame name
4750 if (!text.hasPermission(UserAppliedPermission.full) || text.isFrameName()) {
4751 MessageBay.displayMessage("Insufficient permission to cut text");
4752 text.clearSelection();
4753 DisplayController.requestRefresh(true);
4754 return;
4755 }
4756 // if the entire text is selected and its not a line end then pickup the item
4757 boolean entireText = text.getSelectionSize() == text.getLength();
4758 if (entireText && !text.isLineEnd()) {
4759 text.clearSelection();
4760 ranged.delete();
4761 handlePickup(text, position, false, false);
4762 return;
4763 } else {
4764 ranged.setText(text.cutSelectedText());
4765 ranged.setWidth(text.getWidth());
4766 // If its the whole text then replace last ranged with a dot
4767 if (entireText) {
4768 Item dot = Item.replaceText(text);
4769 dot.setHighlightMode(HighlightMode.None);
4770 }
4771 }
4772 // if the user is copying text from the item
4773 } else {
4774 // Check if the user is trying to range an item for which they
4775 // do not have permission to do so... or it is the frame name
4776 if (!text.hasPermission(UserAppliedPermission.copy)) {
4777 MessageBay.displayMessage("Insufficient permission to copy text");
4778 text.clearSelection();
4779 DisplayController.requestRefresh(true);
4780 return;
4781 }
4782 ranged.setText(text.copySelectedText());
4783 }
4784
4785 ranged.setParent(null);
4786 ranged.setPosition(position);
4787 pickup(ranged);
4788 text.clearSelection();
4789 text.setHighlightMode(HighlightMode.None);
4790 refreshHighlights();
4791 DisplayController.requestRefresh(false);
4792 return;
4793 }
4794}
Note: See TracBrowser for help on using the repository browser.