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

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

You now have the ability to anchor Items to the center of the frame. AnchorCenterX: 0 will anchor a item directly to the vertical center of the frame. AnchorCenterY: 0 to the horizontal center of the frame. Negative numbers go left/up from the center, positive numbers right/down.

More work to come to deal with connected items such as rectangles made up on connected dots.

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