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

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

org.expeditee.gio.gesture.ExpediteeKBMGestureTranslator ->
org.expeditee.gio.gesture.StandardGestureActions ->
org.expeditee.gui.Frame ->
org.expeditee.gui.ItemsList ->

Added debug gesutre that prints out body, primary and surrogates before and after a reparse. Ran with command Ctrl + Shift + R

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