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

Last change on this file since 1413 was 1413, checked in by bln4, 5 years ago

Changed surrogates to work the way discussed with David. EncryptedExpReader/Writer updated to work with this.

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