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

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

org.expeditee.gui.managment.ResourceUtil.newImageWithName(Image, String) now handles the generation of new images when copy/pasting images that do not have a file (for example, from a web page or paint program)

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