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

Last change on this file since 1532 was 1532, checked in by bnemhaus, 4 years ago

New feature: when creating a frameset, if the item being used is linked and you hold shift, it will use the items on the linked frame to populate the zero frame of the new frameset

File size: 149.2 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 * @param toExtract
4719 * Item containing the Item to extract the attributes from.
4720 */
4721 private static void extractAttributes(Item toExtract) {
4722 if (toExtract == null || toExtract == null) {
4723 return;
4724 }
4725
4726 if (FreeItems.hasItemsAttachedToCursor()) {
4727 return;
4728 }
4729
4730 Item attribs;
4731 Item item = toExtract;
4732 // Extract the frames attributes when the user clicks on the frame name
4733 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
4734 if (item.isFrameName()) {
4735 attribs = AttributeUtils.extractAttributes(item.getParent());
4736 } else {
4737 attribs = AttributeUtils.extractAttributes(item);
4738 }
4739
4740 if (attribs == null) {
4741 MessageBay.displayMessage("All attributes of that item are default values.");
4742 } else {
4743 // Give the attribute text item the color of the item for which
4744 // attributes are being extracted.
4745 // attribs.setColor(item.getColor());
4746 pickup(attribs);
4747 }
4748 }
4749
4750 private void pickupRange(Text text, Point position, boolean copy, boolean inheritAttributes) {
4751 Text ranged;
4752 if (inheritAttributes) {
4753 // If shift is down, copy everything (size, color, etc.) except actions, links
4754 // and data
4755 ranged = text.copy();
4756 ranged.setActions(null);
4757 ranged.setData((List<String>) null);
4758 ranged.setLink(null);
4759 } else {
4760 // If shift isn't down, don't copy any attributes, but base the new text item on
4761 // the appropriate template
4762 final String copySelectedText = text.copySelectedText();
4763 if (copySelectedText.length() < 1) {
4764 return;
4765 }
4766 ranged = DisplayController.getCurrentFrame().getItemTemplate(copySelectedText.charAt(0));
4767 }
4768
4769 // if the user is cutting text from the item
4770 if (!copy) {
4771 // Check if the user is trying to range an item for which they
4772 // do not have permission to do so... or it is the frame name
4773 if (!text.hasPermission(UserAppliedPermission.full) || text.isFrameName()) {
4774 MessageBay.displayMessage("Insufficient permission to cut text");
4775 text.clearSelection();
4776 DisplayController.requestRefresh(true);
4777 return;
4778 }
4779 // if the entire text is selected and its not a line end then pickup the item
4780 boolean entireText = text.getSelectionSize() == text.getLength();
4781 if (entireText && !text.isLineEnd()) {
4782 text.clearSelection();
4783 ranged.delete();
4784 handlePickup(text, position, false, false);
4785 return;
4786 } else {
4787 ranged.setText(text.cutSelectedText());
4788 ranged.setWidth(text.getWidth());
4789 // If its the whole text then replace last ranged with a dot
4790 if (entireText) {
4791 Item dot = Item.replaceText(text);
4792 dot.setHighlightMode(HighlightMode.None);
4793 }
4794 }
4795 // if the user is copying text from the item
4796 } else {
4797 // Check if the user is trying to range an item for which they
4798 // do not have permission to do so... or it is the frame name
4799 if (!text.hasPermission(UserAppliedPermission.copy)) {
4800 MessageBay.displayMessage("Insufficient permission to copy text");
4801 text.clearSelection();
4802 DisplayController.requestRefresh(true);
4803 return;
4804 }
4805 ranged.setText(text.copySelectedText());
4806 }
4807
4808 ranged.setParent(null);
4809 ranged.setPosition(position);
4810 pickup(ranged);
4811 text.clearSelection();
4812 text.setHighlightMode(HighlightMode.None);
4813 refreshHighlights();
4814 DisplayController.requestRefresh(false);
4815 return;
4816 }
4817}
Note: See TracBrowser for help on using the repository browser.