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

Last change on this file was 1548, checked in by bnemhaus, 3 years ago

With a Item under your cursor, pressing left and right mouse buttons at the same time, extracts the properties of that item. (No change)
However, if an Item is attached to the users cursor when doing this, nothing happens. Now, instead of nothing happening, all attached Items are checked to see if they are in the format of "Property:". If they are then those specified properties are extracted.

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