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

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

Minor refactor.

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