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

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

If there is no current item, it can not accept enter key.

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