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

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

Support for new regime in the form of new fields and conditional setting of all paths fields.

Settings are now able to generate their own representation. This allows for the user to explicitly inspect the default values.

When profiles are created, an optional parameter may now be provided. If not null, the new map parameter can contain default values for settings to apply to this profile. This allows for the creation of profiles to that have (for example), their username set to an explicit value. Multiuser mode uses this functionality for usernames and key values among other things.

Frames can now be asked were they are located on the file system. Furthermore, frame indirection is now a thing. Rather than containing data to display, an exp file can contain a line in the format of "REDIRECT:<path>" to go looking for that data. Frames are able to return both their logical (their exp file) and real (the file actually containing the data) paths.

Frames can now store data.

Further fixes to how edits from other users are loaded in.

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