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

Last change on this file since 1450 was 1450, checked in by bnemhaus, 5 years ago

Fixed bug I introduced in the delete image file on delete expeditee picture. Bug was causing issues with non-shift delete of pictures, this is fixed now.

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 boolean isSameOwner = item.getOwner().equals(UserSettings.UserName.get());
3792 boolean isOwnerPermissionChange =
3793 isSameOwner && merger.getText().startsWith("Permission:");
3794 if (!item.hasPermission(UserAppliedPermission.full) && !isOwnerPermissionChange) {
3795 // Items on the message box have parent == null
3796 if (item.getParent() != null) {
3797 if (!item.isFrameName()) {
3798 Item editTarget = item.getEditTarget();
3799 if (editTarget != item && editTarget.hasPermission(UserAppliedPermission.full)) {
3800 item = editTarget;
3801 } else {
3802 MessageBay.displayMessage("Insufficient permission");
3803 return items;
3804 }
3805 }
3806
3807 } else /* Its in the message area */ {
3808 MessageBay.displayMessage("Insufficient permission");
3809 return items;
3810 }
3811 }
3812
3813 Collection<Item> left = null;
3814 // when anchoring a line end onto a text line end, holding shift
3815 // prevents the line ends from being merged
3816 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
3817 left = FreeItems.getInstance();
3818 } else {
3819 left = merge(FreeItems.getInstance(), item);
3820 }
3821 Collection<Item> toDelete = new LinkedList<Item>();
3822 toDelete.addAll(FreeItems.getInstance());
3823 toDelete.removeAll(left);
3824 anchor(left);
3825 FreeItems.getInstance().clear();
3826 DisplayController.getCurrentFrame().removeAllItems(toDelete);
3827 updateCursor();
3828 // Make sure the dot goes away when anchoring a line end behind
3829 // a text line end
3830 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
3831 refreshHighlights();
3832 }
3833 DisplayController.requestRefresh(true);
3834 return items;
3835 // otherwise, anchor the items
3836 }
3837
3838 private static final int RECTANGLE_TO_POINT_THRESHOLD = 20;
3839
3840 public static Collection<Item> merge(List<Item> merger, Item mergee) {
3841 assert (mergee != null);
3842 if (mergee.isFrameName()) {
3843 return mergee.getParent().merge(merger);
3844 }
3845
3846 // if(mergee instanceof XRayable)
3847 // return merger;
3848
3849 // check for rectangle merging
3850 if (merger.size() == 3 && mergee.getLines().size() == 2) {
3851 Item corner = getShapeCorner(merger);
3852 // if this is a corner of a shape
3853 if (corner != null) {
3854 Collection<Item> allConnected = corner.getAllConnected();
3855 // Check if we are collapsing a rectangle
3856 if (allConnected.size() == 8 && allConnected.contains(mergee)) {
3857 DisplayController.setCursorPosition(mergee.getPosition());
3858 DisplayController.getCurrentFrame().removeAllItems(allConnected);
3859
3860 // find the point opposite corner...
3861 Item opposite = null;
3862 List<Line> lines = corner.getLines();
3863 for (Line l : lines) {
3864 allConnected.remove(l.getOppositeEnd(corner));
3865 }
3866 allConnected.remove(corner);
3867 for (Item i : allConnected) {
3868 if (i.isLineEnd()) {
3869 opposite = i;
3870 break;
3871 }
3872 }
3873 assert (opposite != null);
3874
3875 // check if the rectangle is small enough that it should be
3876 // collapsed to a single point
3877 int x1 = Math.abs(opposite.getX() - mergee.getX());
3878 int x2 = Math.abs(opposite.getY() - mergee.getY());
3879 int distance = (int) Math.sqrt(Math.pow(x1, 2) + Math.pow(x2, 2));
3880
3881 if (distance < RECTANGLE_TO_POINT_THRESHOLD) {
3882 mergee.removeAllConstraints();
3883 mergee.removeAllLines();
3884 mergee.setThickness(4 * mergee.getThickness());
3885 return mergee.getAllConnected();
3886 } else {
3887 removeAllLinesExcept(mergee, opposite);
3888 removeAllLinesExcept(opposite, mergee);
3889
3890 return mergee.getAllConnected();
3891 }
3892 }
3893 }
3894 }
3895
3896 List<Item> remain = new ArrayList<Item>();
3897 Item res = null;
3898
3899 for (Item i : merger) {
3900 if (!i.isVisible()) {
3901 continue;
3902 }
3903 // check for link merging
3904 if (i instanceof Text && FrameIO.isValidFrameName((((Text) i).getFirstLine()))
3905 && FrameIO.canAccessFrame((((Text) i).getFirstLine()))) {
3906 // check that we can actually access the frame this link
3907 // corresponds to
3908 mergee.setLink(((Text) i).getFirstLine());
3909 } else {
3910 // check for attribute merging
3911 if (i instanceof Text && !i.isLineEnd()) {
3912 Text txt = (Text) i;
3913
3914 // if this is not an attribute merge
3915 if (!AttributeUtils.setAttribute(mergee, txt)) {
3916 // set mouse position for text merges
3917 if (mergee instanceof Text) {
3918 ((Text) mergee).insertText(txt.getText(), DisplayController.getMouseX(),
3919 DisplayController.getMouseY());
3920 // Delete the item which had its text merged
3921 txt.delete();
3922 return remain;
3923 } else if (mergee instanceof WidgetCorner) {
3924 if (merger.size() == 1 && txt.getLink() != null) {
3925 // If the text item is linked then use that
3926 ((WidgetCorner) mergee).setLink(txt.getAbsoluteLink(), txt);
3927 } else {
3928 remain.addAll(merger);
3929 }
3930 return remain;
3931 } else if (mergee instanceof WidgetEdge) {
3932 if (merger.size() == 1 && txt.getLink() != null) {
3933 // If the text item is linked then use that
3934 ((WidgetEdge) mergee).setLink(txt.getAbsoluteLink(), txt);
3935 } else {
3936 remain.addAll(merger);
3937 }
3938 return remain;
3939 } else if (mergee instanceof Dot) {
3940 DisplayController.setCursorPosition(mergee.getPosition());
3941 txt.setPosition(mergee.getPosition());
3942 txt.setThickness(mergee.getThickness());
3943 Frame parent = mergee.getParent();
3944 parent.removeItem(mergee);
3945 anchor(txt);
3946 // change the end points of the lines to the text
3947 // item
3948 while (mergee.getLines().size() > 0) {
3949 Line l = mergee.getLines().get(0);
3950 l.replaceLineEnd(mergee, txt);
3951 }
3952 break;
3953 }
3954
3955 // TODO tidy this up...
3956 // Dot override doesnt use the x and y coords
3957 // Text does... but could be removed
3958 res = mergee.merge(i, DisplayController.getMouseX(), DisplayController.getMouseY());
3959 if (res != null) {
3960 remain.add(res);
3961 }
3962 }
3963 } else {
3964 if (mergee.isLineEnd()) {
3965 DisplayController.setCursorPosition(mergee.getPosition());
3966 }
3967 // Moving the cursor ensures shapes are anchored correctly
3968 res = mergee.merge(i, DisplayController.getMouseX(), DisplayController.getMouseY());
3969 if (res != null) {
3970 remain.addAll(res.getConnected());
3971 }
3972
3973 }
3974 }
3975 }
3976 updateCursor();
3977
3978 mergee.getParent().setChanged(true);
3979
3980 ItemUtils.EnclosedCheck(mergee.getParent().getSortedItems());
3981 // Mike: Why does parse frame have to be called?!?
3982 FrameUtils.Parse(mergee.getParent());
3983
3984 return remain;
3985 }
3986
3987 private static void removeAllLinesExcept(Item from, Item but) {
3988 List<Line> lines = new LinkedList<Line>();
3989 lines.addAll(from.getLines());
3990 for (Line line : lines) {
3991 if (line.getOppositeEnd(from) != but) {
3992 from.removeLine(line);
3993 }
3994 }
3995
3996 List<Constraint> consts = new LinkedList<Constraint>();
3997 consts.addAll(from.getConstraints());
3998 for (Constraint c : consts) {
3999 if (c.getOppositeEnd(from) != but) {
4000 from.removeConstraint(c);
4001 }
4002 }
4003 }
4004
4005 public void anchorFreeItemsAction(Collection<Item> items) {
4006 if (items != null && FreeItems.getInstance().size() == 1) {
4007 Item item = FreeItems.getItemAttachedToCursor();
4008 if (item instanceof Text) {
4009 Text text = (Text) item;
4010 if (AttributeUtils.setAttribute(text, text, 2)) {
4011 items.removeAll(FrameUtils.getEnclosingLineEnds().iterator().next().getAllConnected());
4012 for (Item i : items) {
4013 AttributeUtils.setAttribute(i, text);
4014 }
4015 FreeItems.getInstance().clear();
4016 }
4017 }
4018 }
4019
4020 // if a line is being rubber-banded, check for auto
4021 // straightening
4022 anchor(FreeItems.getInstance());
4023 // FreeItems.getInstance().clear();
4024 updateCursor();
4025 _offX = _offY = 0;
4026 }
4027
4028 public void copyPlace(Item item) {
4029 List<Item> copies = null;
4030 if (FreeItems.getInstance().size() == 1 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
4031 // Dont stamp if the user is painting... because we dont want to
4032 // save any of the items created!
4033 return;
4034 // if the user is clicking on something, merge the items unless
4035 // it is a point onto something other than a lineEnd or a dot
4036 } else if (item != null
4037 && (!(FreeItems.getItemAttachedToCursor() instanceof Dot) || item instanceof Dot || item.isLineEnd())) {
4038 if(item instanceof WidgetCorner) {
4039 final WidgetCorner wc = (WidgetCorner) item;
4040 wc.getWidgetSource().ItemsRightClickDropped();
4041 return;
4042 }
4043
4044 // TODO Change the items merge methods so the logic is simplified
4045 // check permissions
4046 if (!item.hasPermission(UserAppliedPermission.full) && item.getParent().getNameItem() != item) {
4047 MessageBay.displayMessage("Insufficient permission to merge items");
4048 return;
4049 }
4050 if (item instanceof Text || item instanceof Dot || item instanceof XRayable) {
4051 if (isRubberBandingCorner()) {
4052 copies = mergeGroupAction(item);
4053 // line onto something
4054 } else if (FreeItems.getInstance().size() == 2 /* && clicked instanceof XRayable */) {
4055 copies = mergeTwoItemsAction(item);
4056 } else if (FreeItems.getInstance().size() == 1) {
4057 copies = mergeSingleItemAction(item);
4058 } else {
4059 copies = stampAction();
4060 }
4061 } else {
4062 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4063 if (copies == null) {
4064 copies = copy(FreeItems.getInstance());
4065 }
4066 for (Item i : copies) {
4067 i.setOffset(0, 0);
4068 }
4069 anchor(FreeItems.getInstance());
4070 FreeItems.getInstance().clear();
4071 pickup(copies);
4072 }
4073 // otherwise, anchor the items
4074 } else {
4075 // check if this is anchoring a rectangle
4076 if (isRubberBandingCorner()) {
4077 copies = rubberBandingCornerAction();
4078 } else {
4079 if (FreeItems.rubberBanding()) {
4080 copies = rubberBandingCopyAction(item);
4081 } else {
4082 stampItemsOnCursor(true);
4083 copies = FreeItems.getInstance();
4084 }
4085 }
4086 }
4087 }
4088
4089 /**
4090 * Returns a list of copies of the list passed in
4091 *
4092 * @param toCopy
4093 * The list of items to copy
4094 * @return A List of copied Items
4095 */
4096 private static List<Item> copy(Collection<Item> toCopy) {
4097 return ItemUtils.CopyItems(toCopy);
4098 }
4099
4100 public List<Item> mergeGroupAction(Item item) {
4101 List<Item> copies = new ArrayList<Item>();
4102 // Move the cursor so that the copy is exactly the
4103 // same as the shape that was anchored
4104 DisplayController.setCursorPosition(item.getPosition());
4105 Item d = getFirstFreeLineEnd();
4106 // get a copy of all enclosed items before merging
4107 // lineEnds
4108 Collection<Item> items = FrameUtils.getItemsEnclosedBy(DisplayController.getCurrentFrame(),
4109 d.getEnclosedShape());
4110 // If its not an enclosed shape then pick up the
4111 // connected shape
4112 if (items == null || items.size() == 0) {
4113 items = d.getAllConnected();
4114 } else {
4115 // For some reason the item that was clicked ends up
4116 // in the enclosure and needs to be removed
4117 items.removeAll(item.getConnected());
4118 // the item that was the origin of the enclosed
4119 // shape used to create the enclosure does not get
4120 // returned from getItemsEnclosedBy to the enclosure
4121 // so it must be added
4122 items.addAll(d.getConnected());
4123 }
4124
4125 Collection<Item> toCopy = new LinkedHashSet<Item>();
4126
4127 for (Item ip : items) {
4128 if (ip.hasPermission(UserAppliedPermission.copy)) {
4129 toCopy.add(ip);
4130 }
4131 }
4132 copies = copy(toCopy);
4133 // Now do the merging
4134 Collection<Item> remain = merge(FreeItems.getInstance(), item);
4135 // anchor the points
4136 anchor(remain);
4137 FreeItems.getInstance().clear();
4138 pickup(copies);
4139 return copies;
4140 }
4141
4142 private static Item getFirstFreeLineEnd() {
4143 for (Item i : FreeItems.getInstance()) {
4144 if (i.isLineEnd()) {
4145 return i;
4146 }
4147 }
4148 return null;
4149 }
4150
4151 public List<Item> mergeTwoItemsAction(Item item) {
4152 List<Item> copies = new ArrayList<Item>();
4153 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4154 Collection<Item> leftOver = merge(FreeItems.getInstance(), item);
4155 anchor(leftOver);
4156 if (copies == null) {
4157 copies = copy(FreeItems.getInstance());
4158 }
4159 FreeItems.getInstance().clear();
4160 for (Item i : copies) {
4161 i.setOffset(0, 0);
4162 }
4163 // need to move to prevent cursor dislocation
4164 move(copies, EcosystemManager.getInputManager().getCursorPosition());
4165 pickup(copies);
4166 // point onto point
4167 return copies;
4168 }
4169
4170 public List<Item> mergeSingleItemAction(Item item) {
4171 List<Item> copies = new ArrayList<Item>();
4172 copies = copy(FreeItems.getInstance());
4173 Collection<Item> remain = merge(copies, item);
4174
4175 // ignore items that could not be merged.
4176 anchor(remain);
4177 return copies;
4178 }
4179
4180 public List<Item> stampAction() {
4181 List<Item> copies = new ArrayList<Item>();
4182 stampItemsOnCursor(true);
4183 copies = FreeItems.getInstance();
4184 return copies;
4185 }
4186
4187 /**
4188 *
4189 */
4190 private static void stampItemsOnCursor(boolean save) {
4191 List<Item> copies = copy(FreeItems.getInstance());
4192 // MIKE: what does the below 2 lines do?
4193 for (Item i : copies) {
4194 i.setOffset(0, 0);
4195 i.setSave(save);
4196 }
4197 // The below code has a little problem withflicker when stamp and dragging
4198 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4199 for (Item i : copies) {
4200 i.setHighlightMode(HighlightMode.None);
4201 }
4202 anchor(copies);
4203 }
4204
4205 public List<Item> rubberBandingCornerAction() {
4206 List<Item> copies = new ArrayList<Item>();
4207 Item d = getFirstFreeLineEnd();
4208 // anchor the points
4209 anchor(FreeItems.getInstance());
4210 FreeItems.getInstance().clear();
4211 updateCursor();
4212 // pick up a copy of all enclosed items
4213 Collection<Item> enclosedItems = FrameUtils.getItemsEnclosedBy(DisplayController.getCurrentFrame(),
4214 d.getEnclosedShape());
4215 if (enclosedItems != null) {
4216 enclosedItems.removeAll(d.getAllConnected());
4217 Collection<Item> toCopy = getFullyEnclosedItems(enclosedItems);
4218
4219 if (toCopy.size() > 0) {
4220 // Find the closest item to the mouse cursor
4221 double currentX = DisplayController.getMouseX();
4222 double currentY = DisplayController.getMouseY();
4223 Item closest = null;
4224 double shortestDistance = Double.MAX_VALUE;
4225 for (Item next : toCopy) {
4226 if (next instanceof Line) {
4227 continue;
4228 }
4229 double distance = Point.distanceBetween((int) currentX, (int) currentY, next.getX(), next.getY());
4230 if (distance < shortestDistance) {
4231 shortestDistance = distance;
4232 closest = next;
4233 }
4234 }
4235 // Move the cursor to closest item
4236 DisplayController.setCursorPosition(closest.getPosition());
4237 // Pickup copy of the stuff inside the rectangle
4238 copies = copy(toCopy);
4239 pickup(copies);
4240 // Remove the rectangle
4241 d.getParentOrCurrentFrame().removeAllItems(d.getAllConnected());
4242 } else {
4243 // Pick up a copy of the rectangle
4244 copies = copy(d.getAllConnected());
4245 pickup(copies);
4246 }
4247 }
4248 return copies;
4249 }
4250
4251 /**
4252 * @param enclosedItems
4253 * @return
4254 */
4255 private static Collection<Item> getFullyEnclosedItems(Collection<Item> enclosure) {
4256 // copy the enclosedItems because the list will be modified
4257 Collection<Item> enclosedItems = new LinkedHashSet<Item>(enclosure);
4258 Collection<Item> toCopy = new LinkedHashSet<Item>(enclosedItems.size());
4259
4260 while (enclosedItems.size() > 0) {
4261 Item i = enclosedItems.iterator().next();
4262 if (i.hasPermission(UserAppliedPermission.copy)) {
4263 Collection<Item> items = i.getAllConnected();
4264 // Only copy if the entire shape is enclosed
4265 if (enclosedItems.containsAll(items)) {
4266 toCopy.addAll(items);
4267 }
4268 enclosedItems.removeAll(items);
4269 } else {
4270 enclosedItems.remove(i);
4271 }
4272 }
4273 return toCopy;
4274 }
4275
4276 public List<Item> rubberBandingCopyAction(Item item) {
4277 List<Item> copies = new ArrayList<Item>();
4278 if (item != null) {
4279 Collection<Item> leftOver = merge(FreeItems.getInstance(), item);
4280 anchor(leftOver);
4281 }
4282 // This is executed when the user is putting down a line
4283 // endpoint and unreeling. ie. Normal unreeling
4284 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4285
4286 if (copies == null) {
4287 copies = copy(FreeItems.getInstance());
4288 }
4289
4290 anchor(FreeItems.getInstance());
4291 for (Item i : copies) {
4292 i.setOffset(0, 0);
4293 }
4294 // need to move to prevent cursor dislocation
4295 move(copies, EcosystemManager.getInputManager().getCursorPosition());
4296 pickup(copies);
4297 return copies;
4298 }
4299
4300 public List<Item> newLineAction(Point position, Item item) {
4301 List<Item> copies = new ArrayList<Item>();
4302 // If we have permission to copy this item then pick it up
4303 if (item != null && item.isLineEnd() && item.hasPermission(UserAppliedPermission.full)) {
4304 item.removeAllConstraints();
4305 pickup(item);
4306 return copies;
4307 }
4308
4309 if (item instanceof WidgetEdge) {
4310 // Don't allow the user to break widget edges.
4311 // Note: had to return here because random dots would
4312 // appear otherwise... cannot understand code below
4313 // with create line.
4314 return copies;
4315 }
4316
4317 // if its on a line then split the line and put a point on it and
4318 // pick that point up. Only if it is not a widget line
4319 if (item instanceof Line && item.hasPermission(UserAppliedPermission.full)) {
4320 Frame current = DisplayController.getCurrentFrame();
4321 // create the two endpoints
4322 Line oldLine = (Line) item;
4323 Item newPoint = oldLine.getStartItem().copy();
4324 newPoint.setPosition(position);
4325
4326 Item end = oldLine.getEndItem();
4327 // create the Line
4328 Line newLine = new Line(newPoint, end, current.getNextItemID());
4329 oldLine.replaceLineEnd(end, newPoint);
4330 newPoint.removeAllConstraints();
4331 pickup(newPoint);
4332 // Update the stats
4333 Collection<Item> created = new LinkedList<Item>();
4334 created.add(newPoint);
4335 created.add(newLine);
4336 SessionStats.CreatedItems(newLine.getAllConnected());
4337 return copies;
4338 }
4339 Line newLine = createLine();
4340 SessionStats.CreatedItems(newLine.getAllConnected());
4341 return copies;
4342 }
4343
4344 private static Line createLine() {
4345 Frame current = DisplayController.getCurrentFrame();
4346 // create the two endpoints
4347 Item end = DisplayController.getCurrentFrame().createDot();
4348 Item start = DisplayController.getCurrentFrame().createDot();
4349
4350 // create the Line
4351 Line line = new Line(start, end, current.getNextItemID());
4352 line.autoArrowheadLength();
4353
4354 // anchor the start
4355 anchor(start);
4356
4357 // attach the line to the cursor
4358 pickup(end);
4359 _lastHighlightedItem = null;
4360
4361 // TODO figure out how to get the end to highlight
4362 // end.setSelectedMode(SelectedMode.Normal);
4363 // end.setSelectedMode(SelectedMode.None);
4364
4365 return line;
4366 }
4367
4368 public List<Item> createRectangleAction() {
4369 final List<Item> copies = new ArrayList<Item>();
4370 Item[] d = new Item[4];
4371 // create dots
4372 Frame current = DisplayController.getCurrentFrame();
4373 for (int i = 0; i < d.length; i++) {
4374 d[i] = current.createDot();
4375 copies.add(d[i]);
4376 }
4377
4378 current.nextDot();
4379
4380 // create lines
4381 copies.add(new Line(d[0], d[1], current.getNextItemID()));
4382 copies.add(new Line(d[1], d[2], current.getNextItemID()));
4383 copies.add(new Line(d[2], d[3], current.getNextItemID()));
4384 copies.add(new Line(d[3], d[0], current.getNextItemID()));
4385
4386 new Constraint(d[0], d[1], current.getNextItemID(), Constraint.HORIZONTAL);
4387 new Constraint(d[2], d[3], current.getNextItemID(), Constraint.HORIZONTAL);
4388 new Constraint(d[1], d[2], current.getNextItemID(), Constraint.VERTICAL);
4389 new Constraint(d[3], d[0], current.getNextItemID(), Constraint.VERTICAL);
4390
4391 anchor(new ArrayList<Item>(copies));
4392 pickup(d[3]);
4393 d[3].setHighlightMode(HighlightMode.Normal);
4394
4395 SessionStats.CreatedItems(copies);
4396 copies.clear();
4397 return copies;
4398 }
4399
4400 /**
4401 * This method handles all left-click actions
4402 */
4403 private void click(Item clicked, Collection<Item> clickedIn, Point position) {
4404
4405 // Gets the current frame
4406 Frame f = DisplayController.getCurrentFrame();
4407
4408 // Checks if the current frame is an overlay
4409 if (f.getOverlays() != null && FrameUtils.getCurrentItem() != null) {
4410 Item i = FrameUtils.getCurrentItem();
4411
4412 // Checks if the item clicked in the overlay is a Rubbish Bin. If it is, delete
4413 // the item attached to the cursor and return.
4414 if (i instanceof WidgetCorner) {
4415
4416 try {
4417 WidgetCorner wc = (WidgetCorner) i;
4418 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
4419
4420 // Should call a button widgets 'itemheldwhileclicked' method, and process
4421 // depending on the widget type - else will return false.
4422 if (bw.itemHeldWhileClicked(bw) == true) {
4423
4424 return;
4425 }
4426 } catch (Exception e) {
4427
4428 e.printStackTrace();
4429 }
4430 }
4431 }
4432
4433 // if the user is pointing at something then either follow the link or
4434 // do TDFC
4435 if (clicked == null) {
4436 // Check if the user is nearby another item...
4437 int mouseX = position.getX();
4438 int mouseY = position.getY();
4439 // System.out.println(mouseX + "," + mouseY);
4440 for (Item i : DisplayController.getCurrentFrame().getSortedItems()) {
4441 // System.out.println(i.getName().toString());
4442 if (i instanceof Text) {
4443 if (i.isNear(mouseX, mouseY)) {
4444 clicked = i;
4445 break;
4446 }
4447 }
4448 }
4449 }
4450
4451 if (clicked instanceof Text) {
4452 Text text = (Text) clicked;
4453 /* Don't follow link when just highlighting text with the left button */
4454 if (text.getText().length() == 0) {
4455 clicked = null;
4456 } else if (text.getSelectionSize() > 0) {
4457 return;
4458 }
4459 }
4460
4461 // If the user clicked into a widgets free space...
4462 if (clicked == null && clickedIn != null && clickedIn.size() >= 4) {
4463
4464 // Check to see if the user clicked into a widgets empty space
4465 Widget iw = null;
4466
4467 for (Item i : clickedIn) {
4468
4469 if (i instanceof WidgetCorner) {
4470 iw = ((WidgetCorner) i).getWidgetSource();
4471 break;
4472 } else if (i instanceof WidgetEdge) {
4473 iw = ((WidgetEdge) i).getWidgetSource();
4474 break;
4475 }
4476 }
4477
4478 if (iw != null) {
4479
4480 // Handle dropping items on widgets
4481 if (iw.ItemsLeftClickDropped()) {
4482 return;
4483 }
4484
4485 // Note: mustn't directly use source for handling the link
4486 // because all link operations will by-pass the widgets special
4487 // handling with links...
4488 Item widgetLink = iw.getItems().get(0);
4489 assert (widgetLink != null);
4490 clicked = widgetLink;
4491 } else {
4492 for (Item i : clickedIn) {
4493 /*
4494 * Find the first linked item or the first unlinked Dot This code assumes that
4495 * items are are ordered from top to bottom. TODO make sure the list will always
4496 * be ordered correctly!!
4497 */
4498 if (i.hasLink() || i instanceof Dot) {
4499 clicked = i;
4500 break;
4501 }
4502 }
4503 }
4504
4505 }
4506
4507 if (clicked instanceof Picture) {
4508 int mouseX = position.getX();
4509 int mouseY = position.getY();
4510 Picture clickedOnPicture = (Picture) clicked;
4511 Frame current_frame = DisplayController.getCurrentFrame();
4512 Colour bg_col = current_frame.getBackgroundColor();
4513 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
4514 // Make 'clicked' null, effectively causing a back() operation
4515 clicked = null;
4516 }
4517 }
4518
4519 // This makes it so clicking repeatedly on the frameName doesn't add the
4520 // frames to the backup stack. Only the first frame is added to the
4521 // backup stack.
4522 if (!(clicked != null && clicked.isFrameName())) {
4523 Navigation.ResetLastAddToBack();
4524 }
4525
4526 if (clicked != null) {
4527 // check item permissions
4528 boolean hasLinkOrAction = clicked.hasLink() || clicked.hasAction();
4529
4530 if ((hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.followLinks))
4531 || (!hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.createFrames))) {
4532 Item editTarget = clicked.getEditTarget();
4533 if (editTarget != clicked) {
4534 if (editTarget.hasPermission(UserAppliedPermission.followLinks)) {
4535 clicked = editTarget;
4536 } else {
4537 MessageBay.displayMessage("Insufficient permission to perform action on item");
4538 return;
4539 }
4540 }
4541 }
4542
4543 Item clickedOn = clicked;
4544
4545 // actions take priority
4546 if (clickedOn.hasAction()) {
4547 executeActionAction(clicked);
4548 } else if (clickedOn.getLink() != null) {
4549 followLinkAction(clicked);
4550 // no link is found, perform TDFC
4551 } else {
4552 /*
4553 * if the user is clicking on the frame name then move to the next or previous
4554 * frame regardless of whether or not the frame is protected
4555 */
4556 if (clickedOn.isFrameName()) {
4557 respondToFrameNameClickAction();
4558 }
4559
4560 TDFCAction(clicked);
4561 }
4562
4563 } else {
4564 backAction();
4565 }
4566 }
4567
4568 public void executeActionAction(Item item) {
4569 item.performActions();
4570 item.setHighlightMode(HighlightMode.None);
4571 refreshHighlights();
4572 }
4573
4574 public void followLinkAction(Item item) {
4575 /*
4576 * Dont save the frame if we are moving to an old version of this frame because
4577 * everytime we save with the old tag... the frame is backed up
4578 */
4579 if (!item.isOldTag()) {
4580 FrameIO.SaveFrame(DisplayController.getCurrentFrame());
4581 }
4582
4583 Navigation.setLastNavigationItem(item);
4584 load(item.getAbsoluteLink(), item.getLinkHistory());
4585 // DisplayIO.UpdateTitle();
4586 }
4587
4588 private static void load(String toLoad, boolean addToHistory) {
4589 if (FrameIO.isValidFrameName(toLoad)) {
4590 DisplayController.clearBackedUpFrames();
4591 FrameUtils.DisplayFrame(toLoad, addToHistory, true);
4592 } else {
4593 MessageBay.errorMessage(toLoad + " is not a valid frame name.");
4594 }
4595 }
4596
4597 public void respondToFrameNameClickAction() {
4598 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)) {
4599 Navigation.PreviousFrame(false);
4600 } else {
4601 Navigation.NextFrame(false);
4602 }
4603 }
4604
4605 public List<Item> TDFCAction(Item item) {
4606 // check for TDFC permission
4607 if (!item.hasPermission(UserAppliedPermission.createFrames)) {
4608 MessageBay.displayMessage("Insufficient permission to TDFC (Top Down Frame Creation) from that item");
4609 return null;
4610 }
4611
4612 if (item.isOldTag()) {
4613 return null;
4614 }
4615
4616 try {
4617 tdfc(item);
4618 } catch (RuntimeException e) {
4619 e.printStackTrace();
4620 MessageBay.errorMessage("Top Down Frame Creation (TDFC) error: " + e.getMessage());
4621 }
4622 return null;
4623 }
4624
4625 public void backAction() {
4626 // if user is not pointing at something, this is a back
4627 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)
4628 || StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
4629 forward();
4630 } else {
4631 back();
4632 }
4633 }
4634
4635 private static void forward() {
4636 DisplayController.Forward();
4637
4638 // repaint things if necessary
4639 if (FreeItems.hasItemsAttachedToCursor()) {
4640 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4641 }
4642
4643 if (FreeItems.hasCursor()) {
4644 move(FreeItems.getCursor(), EcosystemManager.getInputManager().getCursorPosition(), true);
4645 }
4646 }
4647
4648 private static void back() {
4649 DisplayController.Back();
4650
4651 // repaint things if necessary
4652 if (FreeItems.hasItemsAttachedToCursor()) {
4653 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4654 }
4655
4656 if (FreeItems.hasCursor()) {
4657 move(FreeItems.getCursor(), EcosystemManager.getInputManager().getCursorPosition(), true);
4658 }
4659 }
4660
4661 /**
4662 * Creates a new Text item and fills it with particular attributes extracted
4663 * from the given Item. Note: Users always have permission to extract
4664 * attributes, so it is not checked.
4665 *
4666 * @param toExtract
4667 * Item containing the Item to extract the attributes from.
4668 */
4669 private static void extractAttributes(Item toExtract) {
4670 if (toExtract == null || toExtract == null) {
4671 return;
4672 }
4673
4674 if (FreeItems.hasItemsAttachedToCursor()) {
4675 return;
4676 }
4677
4678 Item attribs;
4679 Item item = toExtract;
4680 // Extract the frames attributes when the user clicks on the frame name
4681 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
4682 if (item.isFrameName()) {
4683 attribs = AttributeUtils.extractAttributes(item.getParent());
4684 } else {
4685 attribs = AttributeUtils.extractAttributes(item);
4686 }
4687
4688 if (attribs == null) {
4689 MessageBay.displayMessage("All attributes of that item are default values.");
4690 } else {
4691 // Give the attribute text item the color of the item for which
4692 // attributes are being extracted.
4693 // attribs.setColor(item.getColor());
4694 pickup(attribs);
4695 }
4696 }
4697
4698 private void pickupRange(Text text, Point position, boolean copy, boolean inheritAttributes) {
4699 Text ranged;
4700 if (inheritAttributes) {
4701 // If shift is down, copy everything (size, color, etc.) except actions, links
4702 // and data
4703 ranged = text.copy();
4704 ranged.setActions(null);
4705 ranged.setData((List<String>) null);
4706 ranged.setLink(null);
4707 } else {
4708 // If shift isn't down, don't copy any attributes, but base the new text item on
4709 // the appropriate template
4710 final String copySelectedText = text.copySelectedText();
4711 if (copySelectedText.length() < 1) {
4712 return;
4713 }
4714 ranged = DisplayController.getCurrentFrame().getItemTemplate(copySelectedText.charAt(0));
4715 }
4716
4717 // if the user is cutting text from the item
4718 if (!copy) {
4719 // Check if the user is trying to range an item for which they
4720 // do not have permission to do so... or it is the frame name
4721 if (!text.hasPermission(UserAppliedPermission.full) || text.isFrameName()) {
4722 MessageBay.displayMessage("Insufficient permission to cut text");
4723 text.clearSelection();
4724 DisplayController.requestRefresh(true);
4725 return;
4726 }
4727 // if the entire text is selected and its not a line end then pickup the item
4728 boolean entireText = text.getSelectionSize() == text.getLength();
4729 if (entireText && !text.isLineEnd()) {
4730 text.clearSelection();
4731 ranged.delete();
4732 handlePickup(text, position, false, false);
4733 return;
4734 } else {
4735 ranged.setText(text.cutSelectedText());
4736 ranged.setWidth(text.getWidth());
4737 // If its the whole text then replace last ranged with a dot
4738 if (entireText) {
4739 Item dot = Item.replaceText(text);
4740 dot.setHighlightMode(HighlightMode.None);
4741 }
4742 }
4743 // if the user is copying text from the item
4744 } else {
4745 // Check if the user is trying to range an item for which they
4746 // do not have permission to do so... or it is the frame name
4747 if (!text.hasPermission(UserAppliedPermission.copy)) {
4748 MessageBay.displayMessage("Insufficient permission to copy text");
4749 text.clearSelection();
4750 DisplayController.requestRefresh(true);
4751 return;
4752 }
4753 ranged.setText(text.copySelectedText());
4754 }
4755
4756 ranged.setParent(null);
4757 ranged.setPosition(position);
4758 pickup(ranged);
4759 text.clearSelection();
4760 text.setHighlightMode(HighlightMode.None);
4761 refreshHighlights();
4762 DisplayController.requestRefresh(false);
4763 return;
4764 }
4765}
Note: See TracBrowser for help on using the repository browser.