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

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

org.expeditee.gio.gesture.StandardGestureActions ->

Reparse action only prints debug information if debug flag is set to true.

File size: 148.1 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);
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);
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);
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 anchorRight = i.getAnchorRight();
2673 Integer anchorTop = i.getAnchorTop();
2674 Integer anchorBottom = i.getAnchorBottom();
2675
2676 if (anchorLeft != null) {
2677 i.setAnchorLeft(anchorLeft);
2678 if (i.hasVector()) {
2679 bReparse = true;
2680 }
2681 }
2682
2683 if (anchorRight != null) {
2684 i.setAnchorRight(anchorRight);
2685 if (i.hasVector()) {
2686 bReparse = true;
2687 }
2688 }
2689
2690 if (anchorTop != null) {
2691 i.setAnchorTop(anchorTop);
2692 if (i.hasVector()) {
2693 bReparse = true;
2694 }
2695 }
2696
2697 if (anchorBottom != null) {
2698 i.setAnchorBottom(anchorBottom);
2699 if (i.hasVector()) {
2700 bReparse = true;
2701 }
2702 }
2703 }
2704 return bReparse;
2705 }
2706
2707 /**
2708 * Sets the colour of the current Item based on its current colour. The colours
2709 * proceed in the order stored in COLOR_WHEEL.
2710 *
2711 * @param toSet
2712 * The Item whose colour is to be changed
2713 */
2714 private static void SetColor(Item item, boolean setTransparent, boolean setBackgroundColor) {
2715 // first determine the next color
2716 Colour color = null;
2717 Frame currentFrame = DisplayController.getCurrentFrame();
2718 if (item == null) {
2719 if (FreeItems.hasItemsAttachedToCursor()) {
2720 color = FreeItems.getInstance().get(0).getColor();
2721 } else {
2722 return;
2723 }
2724 // change the background color if the user is pointing on the frame name
2725 } else if (item == currentFrame.getNameItem()) {
2726 // check permissions
2727 if (!item.hasPermission(UserAppliedPermission.full)) {
2728 MessageBay.displayMessage("Insufficient permission to the frame's background color");
2729 return;
2730 }
2731
2732 if (setTransparent) {
2733 currentFrame.setBackgroundColor(null);
2734 } else {
2735 currentFrame.toggleBackgroundColor();
2736 }
2737
2738 DisplayController.requestRefresh(true);
2739 return;
2740 } else {
2741 // check permissions
2742 if (!item.hasPermission(UserAppliedPermission.full)) {
2743 Item editTarget = item.getEditTarget();
2744 if (editTarget != item && editTarget.hasPermission(UserAppliedPermission.full)) {
2745 item = editTarget;
2746 } else {
2747 MessageBay.displayMessage("Insufficient permission to change color");
2748 return;
2749 }
2750 }
2751
2752 // Toggling color of circle center changes the circle fill color
2753 if (item.hasEnclosures()) {
2754 if (setBackgroundColor) {
2755 SetGradientColor(item.getEnclosures().iterator().next(), setTransparent);
2756 } else {
2757 SetFillColor(item.getEnclosures().iterator().next(), setTransparent);
2758 }
2759 } else if (setBackgroundColor) {
2760 color = item.getPaintBackgroundColor();
2761 } else {
2762 color = item.getPaintColor();
2763 }
2764 }
2765
2766 if (setTransparent) {
2767 color = null;
2768 } else if (setBackgroundColor) {
2769 color = ColorUtils.getNextColor(color, TemplateSettings.FillColorWheel.get(), item.getPaintColor());
2770 } else {
2771 color = ColorUtils.getNextColor(color, TemplateSettings.ColorWheel.get(),
2772 currentFrame.getPaintBackgroundColor());
2773 }
2774
2775 if (setBackgroundColor) {
2776 if (item == null && FreeItems.hasItemsAttachedToCursor()) {
2777 for (Item i : FreeItems.getInstance()) {
2778 i.setBackgroundColor(color);
2779 }
2780 } else {
2781 item.setBackgroundColor(color);
2782 item.getParent().setChanged(true);
2783 }
2784 } else {
2785 if (item == null && FreeItems.hasItemsAttachedToCursor()) {
2786 for (Item i : FreeItems.getInstance()) {
2787 i.setColor(color);
2788 }
2789 } else {
2790 item.setColor(color);
2791 item.getParent().setChanged(true);
2792 }
2793 }
2794
2795 DisplayController.requestRefresh(true);
2796 }
2797
2798 private static void SetFillColor(Item item, boolean setTransparent) {
2799 if (item == null) {
2800 return;
2801 }
2802
2803 if (!item.hasPermission(UserAppliedPermission.full)) {
2804 MessageBay.displayMessage("Insufficient permission to change fill color");
2805 return;
2806 }
2807
2808 Item toSet = item;
2809 Colour color = toSet.getFillColor();
2810 if (setTransparent) {
2811 color = null;
2812 } else {
2813 color = ColorUtils.getNextColor(color, TemplateSettings.FillColorWheel.get(), toSet.getGradientColor());
2814 }
2815
2816 // if (color == null) {
2817 // MessageBay.displayMessage("FillColor is now transparent");
2818 // }
2819
2820 toSet.setFillColor(color);
2821 toSet.getParent().setChanged(true);
2822
2823 DisplayController.requestRefresh(true);
2824 }
2825
2826 private static void SetGradientColor(Item item, boolean setTransparent) {
2827 if (item == null) {
2828 return;
2829 }
2830
2831 if (!item.hasPermission(UserAppliedPermission.full)) {
2832 MessageBay.displayMessage("Insufficient permission to change gradient color");
2833 return;
2834 }
2835
2836 Item toSet = item;
2837 Colour color = toSet.getGradientColor();
2838 if (setTransparent) {
2839 color = null;
2840 } else {
2841 color = ColorUtils.getNextColor(color, TemplateSettings.ColorWheel.get(), toSet.getFillColor());
2842 }
2843
2844 // if (color == null) {
2845 // MessageBay.displayMessage("FillColor is now transparent");
2846 // }
2847
2848 toSet.setGradientColor(color);
2849 toSet.getParent().setChanged(true);
2850
2851 DisplayController.requestRefresh(true);
2852 }
2853
2854 /**
2855 * Toggles the given Item's annotation status on\off.
2856 *
2857 * @param toToggle
2858 * The Item to toggle
2859 */
2860 private static void ToggleAnnotation(Item toToggle) {
2861 if (toToggle == null) {
2862 MessageBay.displayMessage("There is no Item selected to toggle");
2863 return;
2864 }
2865
2866 // check permissions
2867 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2868 MessageBay.displayMessage("Insufficient permission to toggle that item's annotation");
2869 return;
2870 }
2871 toToggle.setAnnotation(!toToggle.isAnnotation());
2872
2873 toToggle.getParent().setChanged(true);
2874 DisplayController.requestRefresh(true);
2875 }
2876
2877 /**
2878 * Toggles the face style of a text item
2879 *
2880 * @param toToggle
2881 * The Item to toggle
2882 */
2883 private static void ToggleFontStyle(Item toToggle) {
2884 if (toToggle == null) {
2885 MessageBay.displayMessage("There is no Item selected to toggle");
2886 return;
2887 }
2888
2889 // check permissions
2890 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2891 MessageBay.displayMessage("Insufficient permission to toggle that item's annotation");
2892 return;
2893 }
2894
2895 if (toToggle instanceof Text) {
2896 Text text = (Text) toToggle;
2897 text.toggleFontStyle();
2898
2899 text.getParent().setChanged(true);
2900 DisplayController.requestRefresh(true);
2901 }
2902 }
2903
2904 /**
2905 * Toggles the face style of a text item
2906 *
2907 * @param toToggle
2908 * The Item to toggle
2909 */
2910 private static void ToggleFontFamily(Item toToggle) {
2911 if (toToggle == null) {
2912 MessageBay.displayMessage("There is no Item selected to toggle");
2913 return;
2914 }
2915
2916 // check permissions
2917 if (!toToggle.hasPermission(UserAppliedPermission.full)) {
2918 MessageBay.displayMessage("Insufficient permission to toggle that item's annotation");
2919 return;
2920 }
2921
2922 if (toToggle instanceof Text) {
2923 Text text = (Text) toToggle;
2924 text.toggleFontFamily();
2925
2926 text.getParent().setChanged(true);
2927 DisplayController.requestRefresh(true);
2928 }
2929 }
2930
2931 /**
2932 * Creates a new Frameset with the name given by the Item
2933 *
2934 * @param name
2935 */
2936 private static void CreateFrameset(Item item) {
2937 if (item == null) {
2938 MessageBay.displayMessage("There is no selected item to use for the frameset name");
2939 return;
2940 }
2941
2942 if (!(item instanceof Text)) {
2943 MessageBay.displayMessage("Framesets can only be created from text items");
2944 return;
2945 }
2946
2947 // Don't create frameset if the item is linked
2948 if (item.getLink() != null) {
2949 MessageBay.displayMessage("A frameset can not be created from a linked item");
2950 return;
2951 }
2952
2953 // check permissions
2954 if (!item.hasPermission(UserAppliedPermission.full)) {
2955 MessageBay.displayMessage("Insufficient permission to create a frameset from this item");
2956 return;
2957 }
2958
2959 Text text = (Text) item;
2960 try {
2961 String name = text.getFirstLine();
2962 Frame linkTo = null;
2963 if (name.toLowerCase().startsWith("group: ")) {
2964 String groupName = name.substring(7);
2965 // create the new group
2966 linkTo = FrameIO.CreateNewGroup(groupName);
2967 } else {
2968 // create the new frameset
2969 linkTo = FrameIO.CreateNewFrameset(name);
2970 }
2971 DisplayController.setCursor(Item.DEFAULT_CURSOR);
2972 text.setLink(linkTo.getName());
2973 text.getParent().setChanged(true);
2974 FrameUtils.DisplayFrame(linkTo, true, true);
2975 linkTo.moveMouseToDefaultLocation();
2976 // this needs to be done if the user doesn't move the mouse before
2977 // doing Tdfc while the cursor is set to the text cursor
2978 DisplayController.setCursor(Item.DEFAULT_CURSOR);
2979 } catch (Exception e) {
2980 MessageBay.errorMessage(e.getMessage());
2981 }
2982 }
2983
2984 private void move(int direction, boolean isShiftDown, boolean isCtrlDown) {
2985 Item on = FrameUtils.getCurrentItem();
2986
2987 if ((on == null) || (on instanceof Picture)) {
2988 navigateFrame(direction);
2989 return;
2990 }
2991
2992 if (on instanceof Text) {
2993 Text text = (Text) on;
2994 // When the user hits the left and right button with mouse
2995 // positions over the the frame name navigation occurs
2996 if (text.isFrameName()) {
2997 navigateFrame(direction);
2998 return;
2999 } else {
3000 FrameUtils.setLastEdited(text);
3001 DisplayController.setTextCursor(text, direction, false, isShiftDown, isCtrlDown, true);
3002 }
3003 }
3004 }
3005
3006 private void navigateFrame(int direction) {
3007 switch (direction) {
3008 case Text.RIGHT:
3009 case Text.PAGE_UP:
3010 Navigation.NextFrame(false);
3011 break;
3012 case Text.LEFT:
3013 case Text.PAGE_DOWN:
3014 Navigation.PreviousFrame(false);
3015 break;
3016 case Text.HOME:
3017 case Text.LINE_HOME:
3018 Navigation.ZeroFrame();
3019 break;
3020 case Text.END:
3021 case Text.LINE_END:
3022 Navigation.LastFrame();
3023 break;
3024 }
3025 }
3026
3027 /**
3028 * Moves the cursor to the next text item on the frame
3029 *
3030 * @param currentItem
3031 * @param direction
3032 * move up if direction is negative, down if direction is positive
3033 */
3034 public static void NextTextItem(Item currentItem, boolean down) {
3035 // Move the cursor to the next text item
3036 Frame current = DisplayController.getCurrentFrame();
3037 Text title = current.getTitleItem();
3038
3039 Collection<Text> currentItems = FrameUtils.getCurrentTextItems();
3040 List<Text> textItems = new ArrayList<Text>();
3041 // Move to the next text item in the box if
3042 if (currentItems.contains(currentItem)) {
3043 textItems.addAll(currentItems);
3044 } else {
3045 if (title != null) {
3046 textItems.add(title);
3047 }
3048 textItems.addAll(current.getBodyTextItems(true));
3049 }
3050
3051 Collections.sort(textItems);
3052
3053 if (textItems.size() == 0) {
3054 // If there are no text items on the frame its a NoOp
3055 if (title == null) {
3056 return;
3057 }
3058 if (title != null) {
3059 DisplayController.MoveCursorToEndOfItem(title);
3060 }
3061 DisplayController.requestRefresh(true);
3062 return;
3063 }
3064
3065 // If the user is mouse wheeling in free space...
3066 if (currentItem == null) {
3067 // find the nearest item in the correct direction
3068 int currY = DisplayController.getMouseY();
3069 for (int i = 0; i < textItems.size(); i++) {
3070 Item t = textItems.get(i);
3071 if (currY < t.getY()) {
3072 if (down) {
3073 DisplayController.MoveCursorToEndOfItem(t);
3074 } else {
3075 if (i == 0) {
3076 DisplayController.MoveCursorToEndOfItem(current.getTitleItem());
3077 } else {
3078 DisplayController.MoveCursorToEndOfItem(textItems.get(i - 1));
3079 }
3080 }
3081 DisplayController.requestRefresh(true);
3082 return;
3083 }
3084 }
3085 // If we are at the bottom of the screen and the user scrolls down
3086 // then scroll backup to the title
3087 if (textItems.size() > 0) {
3088 DisplayController.MoveCursorToEndOfItem(textItems.get(textItems.size() - 1));
3089 }
3090 return;
3091 }
3092
3093 // Find the current item... then move to the next item
3094 int i = textItems.indexOf(currentItem);
3095
3096 int nextIndex = i + (down ? 1 : -1);
3097 if (nextIndex >= 0 && nextIndex < textItems.size()) {
3098 DisplayController.MoveCursorToEndOfItem(textItems.get(nextIndex));
3099 } else {
3100 DisplayController.MoveCursorToEndOfItem(currentItem);
3101 }
3102 return;
3103 }
3104
3105 private static void copyItemToClipboard(Item on) {
3106 if (on == null || !(on instanceof Text)) {
3107 return;
3108 }
3109
3110 Text text = (Text) on;
3111 String string = text.copySelectedText();
3112
3113 if (string == null || string.length() == 0) {
3114 string = text.getText();
3115 }
3116
3117 // add the text of the item to the clipboard
3118 ClipboardData clipboardData = ClipboardData.fromString(string);
3119 EcosystemManager.getClipboardManager().set(clipboardData);
3120 }
3121
3122 public static void delete(Item toDelete, Collection<Item> deleteItems, Collection<Item> deleteEnclosure,
3123 boolean alternateMode) {
3124 boolean bRecalculate = false;
3125
3126 FrameUtils.setLastEdited(null);
3127 _offX = _offY = 0;
3128
3129 Frame currentFrame = DisplayController.getCurrentFrame();
3130 // check if the user is pointing at the frame's framename
3131 if (toDelete != null && toDelete == currentFrame.getNameItem()) {
3132 currentFrame.clear(false);
3133 DisplayController.requestRefresh(true);
3134 return;
3135 }
3136
3137 // if the user is deleting items attached to the cursor
3138 if (FreeItems.hasItemsAttachedToCursor()) {
3139 // if this is an item-swap
3140 if (FreeItems.getInstance().size() == 1 && FreeItems.getInstance().get(0) instanceof Text
3141 && toDelete != null && toDelete instanceof Text) {
3142
3143 // check permissions
3144 if (!toDelete.hasPermission(UserAppliedPermission.full)) {
3145 MessageBay.displayMessage("Insufficient permission to swap Item text");
3146 return;
3147 }
3148 Text anchored = (Text) toDelete;
3149 Text free = (Text) FreeItems.getInstance().get(0);
3150 SessionStats.DeletedItem(free);
3151 // List<String> temp = anchored.getText();
3152 anchored.setText(free.getText());
3153 anchored.setFormula(free.getFormula());
3154
3155 // free.setTextList(temp);
3156 FreeItems.getInstance().clear();
3157
3158 anchored.getParent().setChanged(true);
3159
3160 bRecalculate |= free.recalculateWhenChanged();
3161 bRecalculate |= anchored.recalculateWhenChanged();
3162
3163 // update the offset since the text has changed
3164 _offX = DisplayController.getMouseX() - anchored.getX() + anchored.getOffset().getX();
3165 _offY = DisplayController.getMouseY() - anchored.getY() + anchored.getOffset().getY();
3166 } else {
3167 // if shift is pressed delete the entire shape attached to the dot
3168 if (alternateMode) {
3169 List<Item> tmp = new ArrayList<Item>(FreeItems.getInstance());
3170 for (Item i : tmp) {
3171 // remove entire rectangles instead of just the corner
3172 if (i instanceof Dot) {
3173 FreeItems.getInstance().addAll(i.getAllConnected());
3174 for (Item j : i.getAllConnected()) {
3175 if (j instanceof Dot) {
3176 FreeItems.getInstance().addAll(j.getLines());
3177 }
3178 }
3179 }
3180 }
3181 }
3182 deleteItems(FreeItems.getInstance());
3183 }
3184 // reset the mouse cursor
3185 updateCursor();
3186 // the user is not pointing at an item
3187 } else if (toDelete == null) {
3188
3189 // if the user is pointing inside a closed shape, delete it
3190
3191 Collection<Item> items = null;
3192 // if shift is down, only delete the enclosing shape (ignore the items inside)
3193 if (alternateMode) {
3194 Collection<Item> tmp = deleteEnclosure;
3195 if (tmp != null) {
3196 items = new ArrayList<Item>();
3197 items.addAll(tmp);
3198 for (Item i : tmp) {
3199 if (i instanceof Dot) {
3200 items.addAll(((Dot) i).getLines());
3201 }
3202 }
3203 }
3204 } else {
3205 items = deleteItems;
3206 }
3207
3208 if (items != null) {
3209 Collection<Item> toRemove = new LinkedHashSet<Item>(items.size());
3210 for (Item ip : items) {
3211 if (ip.hasPermission(UserAppliedPermission.full)) {
3212 // Only include lines if one of their enpoints are also
3213 // being removed
3214 if (ip instanceof Line) {
3215 Line l = (Line) ip;
3216 Item end = l.getEndItem();
3217 Item start = l.getStartItem();
3218
3219 // If one end of a line is being deleted, remove the
3220 // other end if all its connecting lines are being
3221 // deleted
3222 if (items.contains(end)) {
3223 if (!items.contains(start) && items.containsAll(start.getLines())) {
3224 toRemove.add(start);
3225 }
3226 } else if (items.contains(start)) {
3227 if (items.containsAll(end.getLines())) {
3228 toRemove.add(end);
3229 }
3230 } else {
3231 continue;
3232 }
3233 }
3234 toRemove.add(ip);
3235 }
3236 }
3237
3238 deleteItems(toRemove);
3239
3240 // reset the mouse cursor
3241 updateCursor();
3242 DisplayController.requestRefresh(true);
3243
3244 // otherwise this is an undo command
3245 }
3246 return;
3247 // this is a delete command
3248 } else {
3249
3250 // Special case if toDelete item is an image: only want to delete if over
3251 // non-background pixel color
3252 if (toDelete instanceof Picture) {
3253 int mouseX = DisplayController.getMouseX();
3254 int mouseY = DisplayController.getMouseY();
3255 Picture clickedOnPicture = (Picture) toDelete;
3256 Frame current_frame = DisplayController.getCurrentFrame();
3257 Colour bg_col = current_frame.getBackgroundColor();
3258 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
3259 return;
3260 }
3261 // If get to here, then user clicked on an image (non-trival pixel color),
3262 // so go ahead and let it be deleted in the usual way
3263 }
3264
3265 // check permissions
3266 if (!toDelete.hasPermission(UserAppliedPermission.full)) {
3267 Item editTarget = toDelete.getEditTarget();
3268 if (editTarget != toDelete && editTarget.hasPermission(UserAppliedPermission.full)) {
3269 toDelete = editTarget;
3270 } else {
3271 MessageBay.displayMessage("Insufficient permission to delete item");
3272 return;
3273 }
3274 }
3275
3276 Frame parent = toDelete.getParent();
3277 if (parent != null) {
3278 parent.setChanged(true);
3279 }
3280 Collection<Item> toUndo = null;
3281 List<Path> canditateFilesToDelete = new ArrayList<Path>();
3282 if (toDelete.isLineEnd()) {
3283 // delete the entire connected shape if shift is down
3284 if (alternateMode) {
3285 List<Item> tmp = new ArrayList<Item>();
3286 tmp.add(toDelete);
3287 // remove entire shape instead of just the corner
3288 tmp.addAll(toDelete.getAllConnected());
3289 for (Item j : toDelete.getAllConnected()) {
3290 if (j instanceof Dot) {
3291 tmp.addAll(j.getLines());
3292 }
3293 }
3294 deleteItems(tmp);
3295 return;
3296 } else {
3297 toUndo = deleteLineEnd(toDelete);
3298 }
3299 // delete the entire connected shape if shift is down, unless we're hovering the
3300 // end of the line
3301 } else if (toDelete instanceof WidgetEdge) { // must notify
3302 // widgets that they
3303 // are being deleted
3304 ((WidgetEdge) toDelete).getWidgetSource().onDelete();
3305 toUndo = toDelete.getConnected();
3306 } else if (toDelete instanceof Line && alternateMode
3307 || toDelete.getHighlightMode() == Item.HighlightMode.Disconnect) {
3308 Line line = (Line) toDelete;
3309 Item start = line.getStartItem();
3310 Item end = line.getEndItem();
3311 Collection<Item> delete = new LinkedList<Item>();
3312 delete.add(toDelete);
3313 if (end.getLines().size() == 1) {
3314 delete.add(end);
3315 } else {
3316 end.removeLine(line);
3317 }
3318 if (start.getLines().size() == 1) {
3319 delete.add(start);
3320 } else {
3321 start.removeLine(line);
3322 }
3323 toUndo = delete;
3324 } else {
3325 bRecalculate |= toDelete.recalculateWhenChanged();
3326 toUndo = toDelete.getConnected(); // copy(toDelete.getConnected());
3327 if (toDelete instanceof Picture && alternateMode) {
3328 String pathStr = ((Picture) toDelete).getPath();
3329 Path path = Paths.get(pathStr);
3330 canditateFilesToDelete.add(path);
3331 }
3332 }
3333 SessionStats.DeletedItems(toUndo);
3334 if (parent != null) {
3335 parent.addToUndoDelete(new ItemsList(toUndo), !canditateFilesToDelete.isEmpty());
3336 parent.removeAllItems(toUndo); // toDelete.getConnected()
3337 deleteUnusedFiles(canditateFilesToDelete);
3338 }
3339 // reset the mouse cursor
3340 updateCursor();
3341 if (parent != null) {
3342 // ItemUtils.EnclosedCheck(parent.getItems());
3343 ItemUtils.Justify(parent);
3344 }
3345 if (toDelete.hasOverlay()) {
3346 FrameUtils.Parse(parent, false, false);
3347 DisplayController.requestRefresh(false);
3348 }
3349
3350 DisplayController.setCursor(Item.DEFAULT_CURSOR);
3351
3352 }
3353
3354 currentFrame.notifyObservers(bRecalculate);
3355
3356 DisplayController.requestRefresh(true);
3357 }
3358
3359 private static void deleteUnusedFiles(List<Path> canditateFilesToDelete) {
3360 for (Path p: canditateFilesToDelete) {
3361 try {
3362 Path trashPath = Paths.get(FrameIO.TRASH_PATH);
3363 trashPath.toFile().mkdirs();
3364 Path destination = trashPath.resolve(p.getFileName());
3365 Files.move(p, destination, StandardCopyOption.ATOMIC_MOVE);
3366 } catch (IOException e) {
3367 MessageBay.displayMessage("There was a problem moving a file to the trash. File: " + p.getFileName());
3368 }
3369 }
3370 }
3371
3372 public static void mouseEnteredWindow() {
3373 // if there is expeditee data on the clipboard that has not yet been autoPasted,
3374 // autoPaste it
3375 // Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
3376 // Transferable content = c.getContents(null);
3377 ClipboardData clipboardData = EcosystemManager.getClipboardManager().get();
3378 try {
3379 if (clipboardData.data instanceof ExpDataHandler) { // Expeditee data
3380 ExpDataHandler expData = (ExpDataHandler) clipboardData.data;
3381 if (expData.autoPaste) {
3382 List<Item> items = new ExpClipReader(DisplayController.getMouseX(), DisplayController.getMouseY())
3383 .read(expData.items);
3384 // generate new IDs and pickup
3385 pickup(ItemUtils.CopyItems(items));
3386 // update the clipboard contents so they won't be autoPasted again
3387 expData.autoPaste = false;
3388 /*
3389 * String stringData = ""; Image imageData = null;
3390 * if(content.isDataFlavorSupported(DataFlavor.stringFlavor)) { stringData =
3391 * (String) content.getTransferData(DataFlavor.stringFlavor); }
3392 * if(content.isDataFlavorSupported(DataFlavor.imageFlavor)) { imageData =
3393 * (Image) content.getTransferData(DataFlavor.imageFlavor); } c.setContents(new
3394 * ItemSelection(stringData, imageData, expData), null);
3395 */
3396 EcosystemManager.getClipboardManager().set(clipboardData);
3397 }
3398 }
3399 } catch (Exception ex) {
3400 ex.printStackTrace();
3401 }
3402 }
3403
3404 private static boolean oldControlDown = false;
3405
3406 private void mouseMoved(Point to) {
3407
3408 boolean shiftStateChanged = false;
3409 if (oldControlDown != StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)) {
3410 shiftStateChanged = true;
3411 oldControlDown = StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL);
3412 }
3413
3414 float MouseX = to.getX();
3415 float MouseY = to.getY();
3416
3417 // Moving the mouse a certain distance removes the last edited text if it is
3418 // empty
3419 Text lastEdited = FrameUtils.getLastEdited();
3420 if (lastEdited != null && lastEdited.getText().length() == 0 && lastEdited.getPosition().getDistanceTo(to) > 20) // TODO:
3421 // Remove
3422 // magic
3423 // constant.
3424 // cts16
3425 {
3426 FrameUtils.setLastEdited(null);
3427 }
3428
3429 // If shift is down then the movement is constrained
3430 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL) && FreeItems.getInstance().size() > 0) {
3431 // Check if we are rubber banding a line
3432 if (shiftStateChanged && FreeItems.rubberBanding()) {
3433 // Get the line end that is being rubber banded
3434 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems.getInstance().get(0)
3435 : FreeItems.getInstance().get(1);
3436 Line line = (Line) (FreeItems.getInstance().get(0).isLineEnd() ? FreeItems.getInstance().get(1)
3437 : FreeItems.getInstance().get(0));
3438 Item otherEnd = line.getOppositeEnd(thisEnd);
3439 int deltaX = Math.abs(to.getX() - otherEnd.getX());
3440 int deltaY = Math.abs(to.getY() - otherEnd.getY());
3441 // Check if its a vertical line
3442 if (deltaX < deltaY / 2) {
3443 new Constraint(thisEnd, otherEnd, thisEnd.getParentOrCurrentFrame().getNextItemID(),
3444 Constraint.VERTICAL);
3445 }
3446 // Check if its horizontal
3447 else if (deltaY <= deltaX / 2) {
3448 new Constraint(thisEnd, otherEnd, thisEnd.getParentOrCurrentFrame().getNextItemID(),
3449 Constraint.HORIZONTAL);
3450 } else {
3451 // Add DIAGONAL constraints
3452 int constraint = Constraint.DIAGONAL_NEG;
3453 // Check if the slope is positive
3454 if ((thisEnd.getY() - otherEnd.getY()) / (double) (thisEnd.getX() - otherEnd.getX()) > 0.0) {
3455 constraint = Constraint.DIAGONAL_POS;
3456 }
3457
3458 new Constraint(thisEnd, otherEnd, thisEnd.getParentOrCurrentFrame().getNextItemID(), constraint);
3459 }
3460 // If it's a line-end attached to two lines lengthen the
3461 // shorter so it is the same length as the longer line
3462 } else if (FreeItems.getInstance().size() == 3) {
3463 // check if we are rubber banding the corner of a shape
3464 Item thisEnd = getShapeCorner(FreeItems.getInstance());
3465 if (thisEnd != null) {
3466 Line line1 = thisEnd.getLines().get(0);
3467 Line line2 = thisEnd.getLines().get(1);
3468
3469 // Check if the two lines are constrained and hence it is a rectangle
3470 Integer c1 = line1.getPossibleConstraint();
3471 Integer c2 = line2.getPossibleConstraint();
3472
3473 if (c1 != null && c2 != null) {
3474 // This is the case of a constrained rectangle
3475 if ((c1 == Constraint.VERTICAL && c2 == Constraint.HORIZONTAL)
3476 || (c1 == Constraint.HORIZONTAL && c2 == Constraint.VERTICAL)) {
3477 Line vLine = line2;
3478 Line hLine = line1;
3479 if (c1 == Constraint.VERTICAL) {
3480 vLine = line1;
3481 hLine = line2;
3482 }
3483 Item hOtherEnd = hLine.getOppositeEnd(thisEnd);
3484 Item vOtherEnd = vLine.getOppositeEnd(thisEnd);
3485
3486 double vLength = Math.abs(vOtherEnd.getY() - MouseY);
3487 double hLength = Math.abs(hOtherEnd.getX() - MouseX);
3488
3489 if (vLength > hLength) {
3490 MouseX = Math.round(hOtherEnd.getX() + vLength * (MouseX > hOtherEnd.getX() ? 1 : -1));
3491 } else /* if (hLength > vLength) */ {
3492 MouseY = Math.round(vOtherEnd.getY() + hLength * (MouseY > vOtherEnd.getY() ? 1 : -1));
3493 }
3494 }
3495 // Other wise it is a not constrained shape so constrain
3496 // the two lines lengths to be equal
3497 } else {
3498 Item lineEnd1 = line1.getOppositeEnd(thisEnd);
3499 Item lineEnd2 = line2.getOppositeEnd(thisEnd);
3500 double l1 = Point.distanceBetween(lineEnd1.getPosition(), to);
3501 double l2 = Point.distanceBetween(lineEnd2.getPosition(), to);
3502 double l3 = Point.distanceBetween(lineEnd1.getPosition(), lineEnd2.getPosition());
3503 // l1 needs to be the shorter end
3504 if (l1 > l2) {
3505 Item temp = lineEnd1;
3506 lineEnd1 = lineEnd2;
3507 lineEnd2 = temp;
3508 double tempL = l1;
3509 l1 = l2;
3510 l2 = tempL;
3511 }
3512 // Now use the cosine rule to calculate the angle between l1 and l3
3513 double cosTheta = (l1 * l1 + l3 * l3 - l2 * l2) / (2 * l1 * l3);
3514 // now calculate the new length for the lines using cos rule
3515 double l_new = l3 / (2 * cosTheta);
3516 double ratio = l_new / l1;
3517 MouseX = Math.round((to.getX() - lineEnd1.getX()) * ratio) + lineEnd1.getX();
3518 MouseY = Math.round((to.getY() - lineEnd1.getY()) * ratio) + lineEnd1.getY();
3519 }
3520 }
3521 }
3522 } else if (shiftStateChanged && !StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)
3523 && FreeItems.rubberBanding()) {
3524 // Get the line end that is being rubber banded
3525 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems.getInstance().get(0)
3526 : FreeItems.getInstance().get(1);
3527 thisEnd.removeAllConstraints();
3528 }
3529
3530 refreshHighlights();
3531
3532 if (FreeItems.hasCursor()) {
3533 move(FreeItems.getCursor(), to, true);
3534 }
3535
3536 if (FreeItems.hasItemsAttachedToCursor()) {
3537 move(FreeItems.getInstance(), to);
3538 }
3539
3540 if (_forceArrowCursor) {
3541 updateCursor();
3542 }
3543
3544 _forceArrowCursor = true;
3545 }
3546
3547 /**
3548 * Gets the rectangle corner from the list of items that are part of a
3549 * rectangle.
3550 *
3551 * @param partialRectangle
3552 * a corner and its two connecting lines.
3553 * @return the rectangle corner or null if the list of items is not part of a
3554 * rectangle.
3555 */
3556 private static Item getShapeCorner(List<Item> partialRectangle) {
3557 if (partialRectangle.size() < 3) {
3558 return null;
3559 }
3560 Item lineEnd = null;
3561 // only one lineEnd will be present for rectangles
3562 // All other items must be lines
3563 for (Item i : partialRectangle) {
3564 if (i.isLineEnd()) {
3565 if (lineEnd == null) {
3566 lineEnd = i;
3567 } else {
3568 return null;
3569 }
3570 } else if (!(i instanceof Line)) {
3571 return null;
3572 }
3573 }
3574 // if this is at least the corner of two connected lines
3575 if (lineEnd != null && lineEnd.getAllConnected().size() >= 5) {
3576 return lineEnd;
3577 }
3578
3579 return null;
3580 }
3581
3582 /**
3583 * Performs picking up of a single item, with special handling for lines.
3584 */
3585 private void handlePickup(Item item, Point atPosition, boolean copy, boolean extrude) {
3586 // check permissions
3587 if (copy && item.isLineEnd()) {
3588 if (!item.hasPermission(UserAppliedPermission.full)) {
3589 MessageBay.displayMessage("Insufficient permission to unreel");
3590 return;
3591 }
3592 } else if (!item.hasPermission(UserAppliedPermission.full)) {
3593 Item editTarget = item.getEditTarget();
3594 if ((editTarget != item && editTarget.hasPermission(UserAppliedPermission.full))
3595 || (copy && editTarget.hasPermission(UserAppliedPermission.copy))) {
3596 item = editTarget;
3597 } else {
3598 MessageBay.displayMessage("Insufficient permission to pick up item");
3599 return;
3600 }
3601 }
3602
3603 // Make a copy of the picked-up item
3604 if (copy) {
3605 List<Item> copies = ItemUtils.UnreelLine(item, false);
3606 // Copies will NOT be null if the user right clicked on a point
3607 if (copies == null) {
3608 Collection<Item> originals = item.getConnected();
3609 copies = ItemUtils.CopyItems(originals, extrude);
3610 // if this is the title of the frame, link it to the frame
3611 if (originals.size() == 1 && copies.size() == 1) {
3612 Item firstCopy = copies.get(0);
3613 Item original = originals.iterator().next();
3614 if (original.getLink() == null && original.isFrameTitle()) {
3615 // save the frame after copying
3616 // i.getParent().setChanged(true);
3617 firstCopy.setLink(original.getParentOrCurrentFrame().getName());
3618 }
3619 }
3620
3621 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
3622
3623 if (!extrude) {
3624 clearParent(copies);
3625 }
3626 }
3627
3628 ItemUtils.Justify(copies);
3629
3630 pickup(copies);
3631
3632 // Just pick up the item without copying
3633 } else {
3634
3635 // BROOK: WIDGET RECTANGLES DONT ALLOW DISCONNECTION
3636 if (item instanceof Line && !(item instanceof WidgetEdge)) {
3637 // Check if within 20% of the end of the line
3638 Line l = (Line) item;
3639 Item toDisconnect = l.getEndPointToDisconnect(atPosition.getX(), atPosition.getY());
3640
3641 if (toDisconnect == null) {
3642 pickup(item);
3643 } else {
3644 if (toDisconnect.getHighlightMode() == Item.HighlightMode.Normal) {
3645 DisplayController.setCursorPosition(toDisconnect.getPosition(), false);
3646 pickup(toDisconnect);
3647 } else {
3648 List<Line> lines = toDisconnect.getLines();
3649 // This is to remove constraints from single lines
3650 // with constraints...
3651 // ie. partially deleted rectangles
3652 if (lines.size() == 1) {
3653 toDisconnect.removeAllConstraints();
3654
3655 DisplayController.setCursorPosition(toDisconnect.getPosition(), false);
3656 // This is to ensure the selected mode will be set
3657 // to Normal rather than disconnect when the line is
3658 // anchored
3659 toDisconnect.setHighlightMode(Item.HighlightMode.Normal);
3660 pickup(toDisconnect);
3661 } else {
3662 // If we are then detatch the line and pick up its
3663 // end point...
3664 Frame currentFrame = DisplayController.getCurrentFrame();
3665 Item newPoint = null;
3666
3667 // If the point we are disconnecting is text...
3668 // Then we want to leave the text behind
3669 // And disconnect a point
3670 if (toDisconnect instanceof Text) {
3671 newPoint = new Dot(toDisconnect.getX(), toDisconnect.getY(), -1);
3672 Item.DuplicateItem(toDisconnect, newPoint);
3673 } else {
3674 newPoint = toDisconnect.copy();
3675 }
3676
3677 currentFrame.addItem(newPoint);
3678 // remove the current item from the connected
3679 // list for this item
3680 l.replaceLineEnd(toDisconnect, newPoint);
3681 // remove unneeded constrains
3682 newPoint.removeAllConstraints();
3683
3684 // Set the new points mode to normal before picking
3685 // it up so it will be restored correctly when
3686 // anchored
3687 newPoint.setHighlightMode(Item.HighlightMode.Normal);
3688 toDisconnect.setHighlightMode(Item.HighlightMode.None);
3689 DisplayController.setCursorPosition(toDisconnect.getPosition(), false);
3690 pickup(newPoint);
3691 ItemUtils.EnclosedCheck(toDisconnect.getParentOrCurrentFrame().getSortedItems());
3692 }
3693 }
3694 }
3695 } else {
3696 if (item.isLineEnd()) {
3697 DisplayController.setCursorPosition(item.getPosition(), false);
3698 }
3699 pickup(item);
3700 }
3701 }
3702 }
3703
3704 /**
3705 * Performs picking up of a single item, with special handling for lines.
3706 */
3707 private void handlePickup(Collection<Item> items, Collection<Item> enclosure, Point atPosition, boolean copy) {
3708 List<Item> toPickup = new ArrayList<Item>(items.size());
3709
3710 if (copy) {
3711 List<Item> copies = new ArrayList<Item>();
3712 // Set the selection mode for the items that were clicked in
3713 Collection<Item> enclosed = getFullyEnclosedItems(items);
3714 if (enclosed.size() == 0) {
3715 MessageBay.displayMessage("Insufficient permission to copy items");
3716 } else {
3717 copies = copy(enclosed);
3718 clearParent(copies);
3719 ItemUtils.Justify(copies);
3720 pickup(copies);
3721 for (Item i : items) {
3722 i.setHighlightMode(HighlightMode.None);
3723 }
3724 }
3725 toPickup = copies;
3726 } else {
3727 for (Item ip : items) {
3728 if (ip.hasPermission(UserAppliedPermission.full)) {
3729 toPickup.add(ip);
3730 }
3731 }
3732 }
3733 pickup(toPickup);
3734 }
3735
3736 /**
3737 * Marks the items as not belonging to any specific frame. When picking up items
3738 * the parent will be automatically cleared for items on the current frame but
3739 * not for overlay items. This method ensures that overlay items will also be
3740 * cleared. This is useful when picking up copies of items from an overlay (with
3741 * the right mouse button) to ensure that the copy will be anchored on the
3742 * current frame rather than the overlay. When items are picked up with the
3743 * middle button clearParent should NOT be called.
3744 *
3745 * @param items
3746 * to have their parent cleared
3747 */
3748 private static void clearParent(List<Item> items) {
3749 for (Item i : items) {
3750 // The next line is only necessary for circles...
3751 // Need to clean up/refactory some of this stuff
3752 i.getParentOrCurrentFrame().removeItem(i);
3753 i.setParent(null);
3754 }
3755 }
3756
3757 private static boolean doMerging(Item clicked) {
3758 if (clicked == null) {
3759 return false;
3760 }
3761
3762 if(clicked instanceof WidgetCorner) {
3763 final WidgetCorner wc = (WidgetCorner) clicked;
3764 if(wc.getWidgetSource().ItemsMiddleClickDropped()) {
3765 return true;
3766 }
3767 }
3768
3769 // // Brook: widgets do not merge
3770 // if (clicked instanceof WidgetCorner)
3771 // return false;
3772 //
3773 // // Brook: widgets do not merge
3774 // if (clicked instanceof WidgetEdge)
3775 // return false;
3776
3777 // System.out.println(FreeItems.getInstance().size());
3778 if (isRubberBandingCorner()) {
3779 if (clicked.isLineEnd() || clicked.getAllConnected().contains(FreeItems.getItemAttachedToCursor())) {
3780 return true;
3781 }
3782 }
3783
3784 if (FreeItems.getInstance().size() > 2) {
3785 return false;
3786 }
3787
3788 Item attachedToCursor = FreeItems.getItemAttachedToCursor();
3789
3790 if (clicked instanceof Text && !(attachedToCursor instanceof Text || attachedToCursor.isLineEnd())) {
3791 return false;
3792 }
3793
3794 if (clicked instanceof Picture) {
3795 int mouseX = DisplayController.getMouseX();
3796 int mouseY = DisplayController.getMouseY();
3797 Picture clickedOnPicture = (Picture) clicked;
3798 Frame current_frame = DisplayController.getCurrentFrame();
3799 Colour bg_col = current_frame.getBackgroundColor();
3800 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
3801 // Make 'clicked' null, effectively causing a back() operation
3802 return false;
3803 }
3804 }
3805
3806 return true;
3807 }
3808
3809 private static boolean isRubberBandingCorner() {
3810 return getShapeCorner(FreeItems.getInstance()) != null;
3811 }
3812
3813 public List<Item> deleteItemsAction(Item item) {
3814 List<Item> items = new ArrayList<Item>();
3815 Item merger = FreeItems.getItemAttachedToCursor();
3816 assert (merger != null);
3817
3818 // check permissions
3819 String toDeleteOwner = item.getOwner();
3820 boolean isSameOwner = toDeleteOwner.equals(UserSettings.UserName.get());
3821 boolean isOwnerPermissionChange =
3822 isSameOwner && merger.getText().startsWith("Permission:");
3823 if (!item.hasPermission(UserAppliedPermission.full) && !isOwnerPermissionChange) {
3824 // Items on the message box have parent == null
3825 if (item.getParent() != null) {
3826 if (!item.isFrameName()) {
3827 Item editTarget = item.getEditTarget();
3828 if (editTarget != item && editTarget.hasPermission(UserAppliedPermission.full)) {
3829 item = editTarget;
3830 } else {
3831 MessageBay.displayMessage("Insufficient permission");
3832 return items;
3833 }
3834 }
3835
3836 } else /* Its in the message area */ {
3837 MessageBay.displayMessage("Insufficient permission");
3838 return items;
3839 }
3840 }
3841
3842 Collection<Item> left = null;
3843 // when anchoring a line end onto a text line end, holding shift
3844 // prevents the line ends from being merged
3845 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
3846 left = FreeItems.getInstance();
3847 } else {
3848 left = merge(FreeItems.getInstance(), item);
3849 }
3850 Collection<Item> toDelete = new LinkedList<Item>();
3851 toDelete.addAll(FreeItems.getInstance());
3852 toDelete.removeAll(left);
3853 anchor(left);
3854 FreeItems.getInstance().clear();
3855 DisplayController.getCurrentFrame().removeAllItems(toDelete);
3856 updateCursor();
3857 // Make sure the dot goes away when anchoring a line end behind
3858 // a text line end
3859 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
3860 refreshHighlights();
3861 }
3862 DisplayController.requestRefresh(true);
3863 return items;
3864 // otherwise, anchor the items
3865 }
3866
3867 private static final int RECTANGLE_TO_POINT_THRESHOLD = 20;
3868
3869 public static Collection<Item> merge(List<Item> merger, Item mergee) {
3870 assert (mergee != null);
3871 if (mergee.isFrameName()) {
3872 return mergee.getParent().merge(merger);
3873 }
3874
3875 // if(mergee instanceof XRayable)
3876 // return merger;
3877
3878 // check for rectangle merging
3879 if (merger.size() == 3 && mergee.getLines().size() == 2) {
3880 Item corner = getShapeCorner(merger);
3881 // if this is a corner of a shape
3882 if (corner != null) {
3883 Collection<Item> allConnected = corner.getAllConnected();
3884 // Check if we are collapsing a rectangle
3885 if (allConnected.size() == 8 && allConnected.contains(mergee)) {
3886 DisplayController.setCursorPosition(mergee.getPosition());
3887 DisplayController.getCurrentFrame().removeAllItems(allConnected);
3888
3889 // find the point opposite corner...
3890 Item opposite = null;
3891 List<Line> lines = corner.getLines();
3892 for (Line l : lines) {
3893 allConnected.remove(l.getOppositeEnd(corner));
3894 }
3895 allConnected.remove(corner);
3896 for (Item i : allConnected) {
3897 if (i.isLineEnd()) {
3898 opposite = i;
3899 break;
3900 }
3901 }
3902 assert (opposite != null);
3903
3904 // check if the rectangle is small enough that it should be
3905 // collapsed to a single point
3906 int x1 = Math.abs(opposite.getX() - mergee.getX());
3907 int x2 = Math.abs(opposite.getY() - mergee.getY());
3908 int distance = (int) Math.sqrt(Math.pow(x1, 2) + Math.pow(x2, 2));
3909
3910 if (distance < RECTANGLE_TO_POINT_THRESHOLD) {
3911 mergee.removeAllConstraints();
3912 mergee.removeAllLines();
3913 mergee.setThickness(4 * mergee.getThickness());
3914 return mergee.getAllConnected();
3915 } else {
3916 removeAllLinesExcept(mergee, opposite);
3917 removeAllLinesExcept(opposite, mergee);
3918
3919 return mergee.getAllConnected();
3920 }
3921 }
3922 }
3923 }
3924
3925 List<Item> remain = new ArrayList<Item>();
3926 Item res = null;
3927
3928 for (Item i : merger) {
3929 if (!i.isVisible()) {
3930 continue;
3931 }
3932 // check for link merging
3933 if (i instanceof Text && FrameIO.isValidFrameName((((Text) i).getFirstLine()))
3934 && FrameIO.canAccessFrame((((Text) i).getFirstLine()))) {
3935 // check that we can actually access the frame this link
3936 // corresponds to
3937 mergee.setLink(((Text) i).getFirstLine());
3938 } else {
3939 // check for attribute merging
3940 if (i instanceof Text && !i.isLineEnd()) {
3941 Text txt = (Text) i;
3942
3943 // if this is not an attribute merge
3944 if (!AttributeUtils.setAttribute(mergee, txt)) {
3945 // set mouse position for text merges
3946 if (mergee instanceof Text) {
3947 ((Text) mergee).insertText(txt.getText(), DisplayController.getMouseX(),
3948 DisplayController.getMouseY());
3949 // Delete the item which had its text merged
3950 txt.delete();
3951 return remain;
3952 } else if (mergee instanceof WidgetCorner) {
3953 if (merger.size() == 1 && txt.getLink() != null) {
3954 // If the text item is linked then use that
3955 ((WidgetCorner) mergee).setLink(txt.getAbsoluteLink(), txt);
3956 } else {
3957 remain.addAll(merger);
3958 }
3959 return remain;
3960 } else if (mergee instanceof WidgetEdge) {
3961 if (merger.size() == 1 && txt.getLink() != null) {
3962 // If the text item is linked then use that
3963 ((WidgetEdge) mergee).setLink(txt.getAbsoluteLink(), txt);
3964 } else {
3965 remain.addAll(merger);
3966 }
3967 return remain;
3968 } else if (mergee instanceof Dot) {
3969 DisplayController.setCursorPosition(mergee.getPosition());
3970 txt.setPosition(mergee.getPosition());
3971 txt.setThickness(mergee.getThickness());
3972 Frame parent = mergee.getParent();
3973 parent.removeItem(mergee);
3974 anchor(txt);
3975 // change the end points of the lines to the text
3976 // item
3977 while (mergee.getLines().size() > 0) {
3978 Line l = mergee.getLines().get(0);
3979 l.replaceLineEnd(mergee, txt);
3980 }
3981 break;
3982 }
3983
3984 // TODO tidy this up...
3985 // Dot override doesnt use the x and y coords
3986 // Text does... but could be removed
3987 res = mergee.merge(i, DisplayController.getMouseX(), DisplayController.getMouseY());
3988 if (res != null) {
3989 remain.add(res);
3990 }
3991 }
3992 } else {
3993 if (mergee.isLineEnd()) {
3994 DisplayController.setCursorPosition(mergee.getPosition());
3995 }
3996 // Moving the cursor ensures shapes are anchored correctly
3997 res = mergee.merge(i, DisplayController.getMouseX(), DisplayController.getMouseY());
3998 if (res != null) {
3999 remain.addAll(res.getConnected());
4000 }
4001
4002 }
4003 }
4004 }
4005 updateCursor();
4006
4007 mergee.getParent().setChanged(true);
4008
4009 ItemUtils.EnclosedCheck(mergee.getParent().getSortedItems());
4010 // Mike: Why does parse frame have to be called?!?
4011 FrameUtils.Parse(mergee.getParent());
4012
4013 return remain;
4014 }
4015
4016 private static void removeAllLinesExcept(Item from, Item but) {
4017 List<Line> lines = new LinkedList<Line>();
4018 lines.addAll(from.getLines());
4019 for (Line line : lines) {
4020 if (line.getOppositeEnd(from) != but) {
4021 from.removeLine(line);
4022 }
4023 }
4024
4025 List<Constraint> consts = new LinkedList<Constraint>();
4026 consts.addAll(from.getConstraints());
4027 for (Constraint c : consts) {
4028 if (c.getOppositeEnd(from) != but) {
4029 from.removeConstraint(c);
4030 }
4031 }
4032 }
4033
4034 public void anchorFreeItemsAction(Collection<Item> items) {
4035 if (items != null && FreeItems.getInstance().size() == 1) {
4036 Item item = FreeItems.getItemAttachedToCursor();
4037 if (item instanceof Text) {
4038 Text text = (Text) item;
4039 if (AttributeUtils.setAttribute(text, text, 2)) {
4040 items.removeAll(FrameUtils.getEnclosingLineEnds().iterator().next().getAllConnected());
4041 for (Item i : items) {
4042 AttributeUtils.setAttribute(i, text);
4043 }
4044 FreeItems.getInstance().clear();
4045 }
4046 }
4047 }
4048
4049 // if a line is being rubber-banded, check for auto
4050 // straightening
4051 anchor(FreeItems.getInstance());
4052 // FreeItems.getInstance().clear();
4053 updateCursor();
4054 _offX = _offY = 0;
4055 }
4056
4057 public void copyPlace(Item item) {
4058 List<Item> copies = null;
4059 if (FreeItems.getInstance().size() == 1 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
4060 // Dont stamp if the user is painting... because we dont want to
4061 // save any of the items created!
4062 return;
4063 // if the user is clicking on something, merge the items unless
4064 // it is a point onto something other than a lineEnd or a dot
4065 } else if (item != null
4066 && (!(FreeItems.getItemAttachedToCursor() instanceof Dot) || item instanceof Dot || item.isLineEnd())) {
4067 if(item instanceof WidgetCorner) {
4068 final WidgetCorner wc = (WidgetCorner) item;
4069 wc.getWidgetSource().ItemsRightClickDropped();
4070 return;
4071 }
4072
4073 // TODO Change the items merge methods so the logic is simplified
4074 // check permissions
4075 if (!item.hasPermission(UserAppliedPermission.full) && item.getParent().getNameItem() != item) {
4076 MessageBay.displayMessage("Insufficient permission to merge items");
4077 return;
4078 }
4079 if (item instanceof Text || item instanceof Dot || item instanceof XRayable) {
4080 if (isRubberBandingCorner()) {
4081 copies = mergeGroupAction(item);
4082 // line onto something
4083 } else if (FreeItems.getInstance().size() == 2 /* && clicked instanceof XRayable */) {
4084 copies = mergeTwoItemsAction(item);
4085 } else if (FreeItems.getInstance().size() == 1) {
4086 copies = mergeSingleItemAction(item);
4087 } else {
4088 copies = stampAction();
4089 }
4090 } else {
4091 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4092 if (copies == null) {
4093 copies = copy(FreeItems.getInstance());
4094 }
4095 for (Item i : copies) {
4096 i.setOffset(0, 0);
4097 }
4098 anchor(FreeItems.getInstance());
4099 FreeItems.getInstance().clear();
4100 pickup(copies);
4101 }
4102 // otherwise, anchor the items
4103 } else {
4104 // check if this is anchoring a rectangle
4105 if (isRubberBandingCorner()) {
4106 copies = rubberBandingCornerAction();
4107 } else {
4108 if (FreeItems.rubberBanding()) {
4109 copies = rubberBandingCopyAction(item);
4110 } else {
4111 stampItemsOnCursor(true);
4112 copies = FreeItems.getInstance();
4113 }
4114 }
4115 }
4116 }
4117
4118 /**
4119 * Returns a list of copies of the list passed in
4120 *
4121 * @param toCopy
4122 * The list of items to copy
4123 * @return A List of copied Items
4124 */
4125 private static List<Item> copy(Collection<Item> toCopy) {
4126 return ItemUtils.CopyItems(toCopy);
4127 }
4128
4129 public List<Item> mergeGroupAction(Item item) {
4130 List<Item> copies = new ArrayList<Item>();
4131 // Move the cursor so that the copy is exactly the
4132 // same as the shape that was anchored
4133 DisplayController.setCursorPosition(item.getPosition());
4134 Item d = getFirstFreeLineEnd();
4135 // get a copy of all enclosed items before merging
4136 // lineEnds
4137 Collection<Item> items = FrameUtils.getItemsEnclosedBy(DisplayController.getCurrentFrame(),
4138 d.getEnclosedShape());
4139 // If its not an enclosed shape then pick up the
4140 // connected shape
4141 if (items == null || items.size() == 0) {
4142 items = d.getAllConnected();
4143 } else {
4144 // For some reason the item that was clicked ends up
4145 // in the enclosure and needs to be removed
4146 items.removeAll(item.getConnected());
4147 // the item that was the origin of the enclosed
4148 // shape used to create the enclosure does not get
4149 // returned from getItemsEnclosedBy to the enclosure
4150 // so it must be added
4151 items.addAll(d.getConnected());
4152 }
4153
4154 Collection<Item> toCopy = new LinkedHashSet<Item>();
4155
4156 for (Item ip : items) {
4157 if (ip.hasPermission(UserAppliedPermission.copy)) {
4158 toCopy.add(ip);
4159 }
4160 }
4161 copies = copy(toCopy);
4162 // Now do the merging
4163 Collection<Item> remain = merge(FreeItems.getInstance(), item);
4164 // anchor the points
4165 anchor(remain);
4166 FreeItems.getInstance().clear();
4167 pickup(copies);
4168 return copies;
4169 }
4170
4171 private static Item getFirstFreeLineEnd() {
4172 for (Item i : FreeItems.getInstance()) {
4173 if (i.isLineEnd()) {
4174 return i;
4175 }
4176 }
4177 return null;
4178 }
4179
4180 public List<Item> mergeTwoItemsAction(Item item) {
4181 List<Item> copies = new ArrayList<Item>();
4182 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4183 Collection<Item> leftOver = merge(FreeItems.getInstance(), item);
4184 anchor(leftOver);
4185 if (copies == null) {
4186 copies = copy(FreeItems.getInstance());
4187 }
4188 FreeItems.getInstance().clear();
4189 for (Item i : copies) {
4190 i.setOffset(0, 0);
4191 }
4192 // need to move to prevent cursor dislocation
4193 move(copies, EcosystemManager.getInputManager().getCursorPosition());
4194 pickup(copies);
4195 // point onto point
4196 return copies;
4197 }
4198
4199 public List<Item> mergeSingleItemAction(Item item) {
4200 List<Item> copies = new ArrayList<Item>();
4201 copies = copy(FreeItems.getInstance());
4202 Collection<Item> remain = merge(copies, item);
4203
4204 // ignore items that could not be merged.
4205 anchor(remain);
4206 return copies;
4207 }
4208
4209 public List<Item> stampAction() {
4210 List<Item> copies = new ArrayList<Item>();
4211 stampItemsOnCursor(true);
4212 copies = FreeItems.getInstance();
4213 return copies;
4214 }
4215
4216 /**
4217 *
4218 */
4219 private static void stampItemsOnCursor(boolean save) {
4220 List<Item> copies = copy(FreeItems.getInstance());
4221 // MIKE: what does the below 2 lines do?
4222 for (Item i : copies) {
4223 i.setOffset(0, 0);
4224 i.setSave(save);
4225 }
4226 // The below code has a little problem withflicker when stamp and dragging
4227 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4228 for (Item i : copies) {
4229 i.setHighlightMode(HighlightMode.None);
4230 }
4231 anchor(copies);
4232 }
4233
4234 public List<Item> rubberBandingCornerAction() {
4235 List<Item> copies = new ArrayList<Item>();
4236 Item d = getFirstFreeLineEnd();
4237 // anchor the points
4238 anchor(FreeItems.getInstance());
4239 FreeItems.getInstance().clear();
4240 updateCursor();
4241 // pick up a copy of all enclosed items
4242 Collection<Item> enclosedItems = FrameUtils.getItemsEnclosedBy(DisplayController.getCurrentFrame(),
4243 d.getEnclosedShape());
4244 if (enclosedItems != null) {
4245 enclosedItems.removeAll(d.getAllConnected());
4246 Collection<Item> toCopy = getFullyEnclosedItems(enclosedItems);
4247
4248 if (toCopy.size() > 0) {
4249 // Find the closest item to the mouse cursor
4250 double currentX = DisplayController.getMouseX();
4251 double currentY = DisplayController.getMouseY();
4252 Item closest = null;
4253 double shortestDistance = Double.MAX_VALUE;
4254 for (Item next : toCopy) {
4255 if (next instanceof Line) {
4256 continue;
4257 }
4258 double distance = Point.distanceBetween((int) currentX, (int) currentY, next.getX(), next.getY());
4259 if (distance < shortestDistance) {
4260 shortestDistance = distance;
4261 closest = next;
4262 }
4263 }
4264 // Move the cursor to closest item
4265 DisplayController.setCursorPosition(closest.getPosition());
4266 // Pickup copy of the stuff inside the rectangle
4267 copies = copy(toCopy);
4268 pickup(copies);
4269 // Remove the rectangle
4270 d.getParentOrCurrentFrame().removeAllItems(d.getAllConnected());
4271 } else {
4272 // Pick up a copy of the rectangle
4273 copies = copy(d.getAllConnected());
4274 pickup(copies);
4275 }
4276 }
4277 return copies;
4278 }
4279
4280 /**
4281 * @param enclosedItems
4282 * @return
4283 */
4284 private static Collection<Item> getFullyEnclosedItems(Collection<Item> enclosure) {
4285 // copy the enclosedItems because the list will be modified
4286 Collection<Item> enclosedItems = new LinkedHashSet<Item>(enclosure);
4287 Collection<Item> toCopy = new LinkedHashSet<Item>(enclosedItems.size());
4288
4289 while (enclosedItems.size() > 0) {
4290 Item i = enclosedItems.iterator().next();
4291 if (i.hasPermission(UserAppliedPermission.copy)) {
4292 Collection<Item> items = i.getAllConnected();
4293 // Only copy if the entire shape is enclosed
4294 if (enclosedItems.containsAll(items)) {
4295 toCopy.addAll(items);
4296 }
4297 enclosedItems.removeAll(items);
4298 } else {
4299 enclosedItems.remove(i);
4300 }
4301 }
4302 return toCopy;
4303 }
4304
4305 public List<Item> rubberBandingCopyAction(Item item) {
4306 List<Item> copies = new ArrayList<Item>();
4307 if (item != null) {
4308 Collection<Item> leftOver = merge(FreeItems.getInstance(), item);
4309 anchor(leftOver);
4310 }
4311 // This is executed when the user is putting down a line
4312 // endpoint and unreeling. ie. Normal unreeling
4313 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), false);
4314
4315 if (copies == null) {
4316 copies = copy(FreeItems.getInstance());
4317 }
4318
4319 anchor(FreeItems.getInstance());
4320 for (Item i : copies) {
4321 i.setOffset(0, 0);
4322 }
4323 // need to move to prevent cursor dislocation
4324 move(copies, EcosystemManager.getInputManager().getCursorPosition());
4325 pickup(copies);
4326 return copies;
4327 }
4328
4329 public List<Item> newLineAction(Point position, Item item) {
4330 List<Item> copies = new ArrayList<Item>();
4331 // If we have permission to copy this item then pick it up
4332 if (item != null && item.isLineEnd() && item.hasPermission(UserAppliedPermission.full)) {
4333 item.removeAllConstraints();
4334 pickup(item);
4335 return copies;
4336 }
4337
4338 if (item instanceof WidgetEdge) {
4339 // Don't allow the user to break widget edges.
4340 // Note: had to return here because random dots would
4341 // appear otherwise... cannot understand code below
4342 // with create line.
4343 return copies;
4344 }
4345
4346 // if its on a line then split the line and put a point on it and
4347 // pick that point up. Only if it is not a widget line
4348 if (item instanceof Line && item.hasPermission(UserAppliedPermission.full)) {
4349 Frame current = DisplayController.getCurrentFrame();
4350 // create the two endpoints
4351 Line oldLine = (Line) item;
4352 Item newPoint = oldLine.getStartItem().copy();
4353 newPoint.setPosition(position);
4354
4355 Item end = oldLine.getEndItem();
4356 // create the Line
4357 Line newLine = new Line(newPoint, end, current.getNextItemID());
4358 oldLine.replaceLineEnd(end, newPoint);
4359 newPoint.removeAllConstraints();
4360 pickup(newPoint);
4361 // Update the stats
4362 Collection<Item> created = new LinkedList<Item>();
4363 created.add(newPoint);
4364 created.add(newLine);
4365 SessionStats.CreatedItems(newLine.getAllConnected());
4366 return copies;
4367 }
4368 Line newLine = createLine();
4369 SessionStats.CreatedItems(newLine.getAllConnected());
4370 return copies;
4371 }
4372
4373 private static Line createLine() {
4374 Frame current = DisplayController.getCurrentFrame();
4375 // create the two endpoints
4376 Item end = DisplayController.getCurrentFrame().createDot();
4377 Item start = DisplayController.getCurrentFrame().createDot();
4378
4379 // create the Line
4380 Line line = new Line(start, end, current.getNextItemID());
4381 line.autoArrowheadLength();
4382
4383 // anchor the start
4384 anchor(start);
4385
4386 // attach the line to the cursor
4387 pickup(end);
4388 _lastHighlightedItem = null;
4389
4390 // TODO figure out how to get the end to highlight
4391 // end.setSelectedMode(SelectedMode.Normal);
4392 // end.setSelectedMode(SelectedMode.None);
4393
4394 return line;
4395 }
4396
4397 public List<Item> createRectangleAction() {
4398 final List<Item> copies = new ArrayList<Item>();
4399 Item[] d = new Item[4];
4400 // create dots
4401 Frame current = DisplayController.getCurrentFrame();
4402 for (int i = 0; i < d.length; i++) {
4403 d[i] = current.createDot();
4404 copies.add(d[i]);
4405 }
4406
4407 current.nextDot();
4408
4409 // create lines
4410 copies.add(new Line(d[0], d[1], current.getNextItemID()));
4411 copies.add(new Line(d[1], d[2], current.getNextItemID()));
4412 copies.add(new Line(d[2], d[3], current.getNextItemID()));
4413 copies.add(new Line(d[3], d[0], current.getNextItemID()));
4414
4415 new Constraint(d[0], d[1], current.getNextItemID(), Constraint.HORIZONTAL);
4416 new Constraint(d[2], d[3], current.getNextItemID(), Constraint.HORIZONTAL);
4417 new Constraint(d[1], d[2], current.getNextItemID(), Constraint.VERTICAL);
4418 new Constraint(d[3], d[0], current.getNextItemID(), Constraint.VERTICAL);
4419
4420 anchor(new ArrayList<Item>(copies));
4421 pickup(d[3]);
4422 d[3].setHighlightMode(HighlightMode.Normal);
4423
4424 SessionStats.CreatedItems(copies);
4425 copies.clear();
4426 return copies;
4427 }
4428
4429 /**
4430 * This method handles all left-click actions
4431 */
4432 private void click(Item clicked, Collection<Item> clickedIn, Point position) {
4433
4434 // Gets the current frame
4435 Frame f = DisplayController.getCurrentFrame();
4436
4437 // Checks if the current frame is an overlay
4438 if (f.getOverlays() != null && FrameUtils.getCurrentItem() != null) {
4439 Item i = FrameUtils.getCurrentItem();
4440
4441 // Checks if the item clicked in the overlay is a Rubbish Bin. If it is, delete
4442 // the item attached to the cursor and return.
4443 if (i instanceof WidgetCorner) {
4444
4445 try {
4446 WidgetCorner wc = (WidgetCorner) i;
4447 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
4448
4449 // Should call a button widgets 'itemheldwhileclicked' method, and process
4450 // depending on the widget type - else will return false.
4451 if (bw.itemHeldWhileClicked(bw) == true) {
4452
4453 return;
4454 }
4455 } catch (Exception e) {
4456
4457 e.printStackTrace();
4458 }
4459 }
4460 }
4461
4462 // if the user is pointing at something then either follow the link or
4463 // do TDFC
4464 if (clicked == null) {
4465 // Check if the user is nearby another item...
4466 int mouseX = position.getX();
4467 int mouseY = position.getY();
4468 // System.out.println(mouseX + "," + mouseY);
4469 for (Item i : DisplayController.getCurrentFrame().getSortedItems()) {
4470 // System.out.println(i.getName().toString());
4471 if (i instanceof Text) {
4472 if (i.isNear(mouseX, mouseY)) {
4473 clicked = i;
4474 break;
4475 }
4476 }
4477 }
4478 }
4479
4480 if (clicked instanceof Text) {
4481 Text text = (Text) clicked;
4482 /* Don't follow link when just highlighting text with the left button */
4483 if (text.getText().length() == 0) {
4484 clicked = null;
4485 } else if (text.getSelectionSize() > 0) {
4486 return;
4487 }
4488 }
4489
4490 // If the user clicked into a widgets free space...
4491 if (clicked == null && clickedIn != null && clickedIn.size() >= 4) {
4492
4493 // Check to see if the user clicked into a widgets empty space
4494 Widget iw = null;
4495
4496 for (Item i : clickedIn) {
4497
4498 if (i instanceof WidgetCorner) {
4499 iw = ((WidgetCorner) i).getWidgetSource();
4500 break;
4501 } else if (i instanceof WidgetEdge) {
4502 iw = ((WidgetEdge) i).getWidgetSource();
4503 break;
4504 }
4505 }
4506
4507 if (iw != null) {
4508
4509 // Handle dropping items on widgets
4510 if (iw.ItemsLeftClickDropped()) {
4511 return;
4512 }
4513
4514 // Note: mustn't directly use source for handling the link
4515 // because all link operations will by-pass the widgets special
4516 // handling with links...
4517 Item widgetLink = iw.getItems().get(0);
4518 assert (widgetLink != null);
4519 clicked = widgetLink;
4520 } else {
4521 for (Item i : clickedIn) {
4522 /*
4523 * Find the first linked item or the first unlinked Dot This code assumes that
4524 * items are are ordered from top to bottom. TODO make sure the list will always
4525 * be ordered correctly!!
4526 */
4527 if (i.hasLink() || i instanceof Dot) {
4528 clicked = i;
4529 break;
4530 }
4531 }
4532 }
4533
4534 }
4535
4536 if (clicked instanceof Picture) {
4537 int mouseX = position.getX();
4538 int mouseY = position.getY();
4539 Picture clickedOnPicture = (Picture) clicked;
4540 Frame current_frame = DisplayController.getCurrentFrame();
4541 Colour bg_col = current_frame.getBackgroundColor();
4542 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX, mouseY, bg_col)) {
4543 // Make 'clicked' null, effectively causing a back() operation
4544 clicked = null;
4545 }
4546 }
4547
4548 // This makes it so clicking repeatedly on the frameName doesn't add the
4549 // frames to the backup stack. Only the first frame is added to the
4550 // backup stack.
4551 if (!(clicked != null && clicked.isFrameName())) {
4552 Navigation.ResetLastAddToBack();
4553 }
4554
4555 if (clicked != null) {
4556 // check item permissions
4557 boolean hasLinkOrAction = clicked.hasLink() || clicked.hasAction();
4558
4559 if ((hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.followLinks))
4560 || (!hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.createFrames))) {
4561 Item editTarget = clicked.getEditTarget();
4562 if (editTarget != clicked) {
4563 if (editTarget.hasPermission(UserAppliedPermission.followLinks)) {
4564 clicked = editTarget;
4565 } else {
4566 MessageBay.displayMessage("Insufficient permission to perform action on item");
4567 return;
4568 }
4569 }
4570 }
4571
4572 Item clickedOn = clicked;
4573
4574 // actions take priority
4575 if (clickedOn.hasAction()) {
4576 executeActionAction(clicked);
4577 } else if (clickedOn.getLink() != null) {
4578 followLinkAction(clicked);
4579 // no link is found, perform TDFC
4580 } else {
4581 /*
4582 * if the user is clicking on the frame name then move to the next or previous
4583 * frame regardless of whether or not the frame is protected
4584 */
4585 if (clickedOn.isFrameName()) {
4586 respondToFrameNameClickAction();
4587 }
4588
4589 TDFCAction(clicked);
4590 }
4591
4592 } else {
4593 backAction();
4594 }
4595 }
4596
4597 public void executeActionAction(Item item) {
4598 item.performActions();
4599 item.setHighlightMode(HighlightMode.None);
4600 refreshHighlights();
4601 }
4602
4603 public void followLinkAction(Item item) {
4604 /*
4605 * Dont save the frame if we are moving to an old version of this frame because
4606 * everytime we save with the old tag... the frame is backed up
4607 */
4608 if (!item.isOldTag()) {
4609 FrameIO.SaveFrame(DisplayController.getCurrentFrame());
4610 }
4611
4612 Navigation.setLastNavigationItem(item);
4613 load(item.getAbsoluteLink(), item.getLinkHistory());
4614 // DisplayIO.UpdateTitle();
4615 }
4616
4617 private static void load(String toLoad, boolean addToHistory) {
4618 if (FrameIO.isValidFrameName(toLoad)) {
4619 DisplayController.clearBackedUpFrames();
4620 FrameUtils.DisplayFrame(toLoad, addToHistory, true);
4621 } else {
4622 MessageBay.errorMessage(toLoad + " is not a valid frame name.");
4623 }
4624 }
4625
4626 public void respondToFrameNameClickAction() {
4627 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)) {
4628 Navigation.PreviousFrame(false);
4629 } else {
4630 Navigation.NextFrame(false);
4631 }
4632 }
4633
4634 public List<Item> TDFCAction(Item item) {
4635 // check for TDFC permission
4636 if (!item.hasPermission(UserAppliedPermission.createFrames)) {
4637 MessageBay.displayMessage("Insufficient permission to TDFC (Top Down Frame Creation) from that item");
4638 return null;
4639 }
4640
4641 if (item.isOldTag()) {
4642 return null;
4643 }
4644
4645 try {
4646 tdfc(item);
4647 } catch (RuntimeException e) {
4648 e.printStackTrace();
4649 MessageBay.errorMessage("Top Down Frame Creation (TDFC) error: " + e.getMessage());
4650 }
4651 return null;
4652 }
4653
4654 public void backAction() {
4655 // if user is not pointing at something, this is a back
4656 if (StandardInputEventListeners.kbmStateListener.isKeyDown(Key.CTRL)
4657 || StandardInputEventListeners.kbmStateListener.isKeyDown(Key.SHIFT)) {
4658 forward();
4659 } else {
4660 back();
4661 }
4662 }
4663
4664 private static void forward() {
4665 DisplayController.Forward();
4666
4667 // repaint things if necessary
4668 if (FreeItems.hasItemsAttachedToCursor()) {
4669 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4670 }
4671
4672 if (FreeItems.hasCursor()) {
4673 move(FreeItems.getCursor(), EcosystemManager.getInputManager().getCursorPosition(), true);
4674 }
4675 }
4676
4677 private static void back() {
4678 DisplayController.Back();
4679
4680 // repaint things if necessary
4681 if (FreeItems.hasItemsAttachedToCursor()) {
4682 move(FreeItems.getInstance(), EcosystemManager.getInputManager().getCursorPosition());
4683 }
4684
4685 if (FreeItems.hasCursor()) {
4686 move(FreeItems.getCursor(), EcosystemManager.getInputManager().getCursorPosition(), true);
4687 }
4688 }
4689
4690 /**
4691 * Creates a new Text item and fills it with particular attributes extracted
4692 * from the given Item. Note: Users always have permission to extract
4693 * attributes, so it is not checked.
4694 *
4695 * @param toExtract
4696 * Item containing the Item to extract the attributes from.
4697 */
4698 private static void extractAttributes(Item toExtract) {
4699 if (toExtract == null || toExtract == null) {
4700 return;
4701 }
4702
4703 if (FreeItems.hasItemsAttachedToCursor()) {
4704 return;
4705 }
4706
4707 Item attribs;
4708 Item item = toExtract;
4709 // Extract the frames attributes when the user clicks on the frame name
4710 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
4711 if (item.isFrameName()) {
4712 attribs = AttributeUtils.extractAttributes(item.getParent());
4713 } else {
4714 attribs = AttributeUtils.extractAttributes(item);
4715 }
4716
4717 if (attribs == null) {
4718 MessageBay.displayMessage("All attributes of that item are default values.");
4719 } else {
4720 // Give the attribute text item the color of the item for which
4721 // attributes are being extracted.
4722 // attribs.setColor(item.getColor());
4723 pickup(attribs);
4724 }
4725 }
4726
4727 private void pickupRange(Text text, Point position, boolean copy, boolean inheritAttributes) {
4728 Text ranged;
4729 if (inheritAttributes) {
4730 // If shift is down, copy everything (size, color, etc.) except actions, links
4731 // and data
4732 ranged = text.copy();
4733 ranged.setActions(null);
4734 ranged.setData((List<String>) null);
4735 ranged.setLink(null);
4736 } else {
4737 // If shift isn't down, don't copy any attributes, but base the new text item on
4738 // the appropriate template
4739 final String copySelectedText = text.copySelectedText();
4740 if (copySelectedText.length() < 1) {
4741 return;
4742 }
4743 ranged = DisplayController.getCurrentFrame().getItemTemplate(copySelectedText.charAt(0));
4744 }
4745
4746 // if the user is cutting text from the item
4747 if (!copy) {
4748 // Check if the user is trying to range an item for which they
4749 // do not have permission to do so... or it is the frame name
4750 if (!text.hasPermission(UserAppliedPermission.full) || text.isFrameName()) {
4751 MessageBay.displayMessage("Insufficient permission to cut text");
4752 text.clearSelection();
4753 DisplayController.requestRefresh(true);
4754 return;
4755 }
4756 // if the entire text is selected and its not a line end then pickup the item
4757 boolean entireText = text.getSelectionSize() == text.getLength();
4758 if (entireText && !text.isLineEnd()) {
4759 text.clearSelection();
4760 ranged.delete();
4761 handlePickup(text, position, false, false);
4762 return;
4763 } else {
4764 ranged.setText(text.cutSelectedText());
4765 ranged.setWidth(text.getWidth());
4766 // If its the whole text then replace last ranged with a dot
4767 if (entireText) {
4768 Item dot = Item.replaceText(text);
4769 dot.setHighlightMode(HighlightMode.None);
4770 }
4771 }
4772 // if the user is copying text from the item
4773 } else {
4774 // Check if the user is trying to range an item for which they
4775 // do not have permission to do so... or it is the frame name
4776 if (!text.hasPermission(UserAppliedPermission.copy)) {
4777 MessageBay.displayMessage("Insufficient permission to copy text");
4778 text.clearSelection();
4779 DisplayController.requestRefresh(true);
4780 return;
4781 }
4782 ranged.setText(text.copySelectedText());
4783 }
4784
4785 ranged.setParent(null);
4786 ranged.setPosition(position);
4787 pickup(ranged);
4788 text.clearSelection();
4789 text.setHighlightMode(HighlightMode.None);
4790 refreshHighlights();
4791 DisplayController.requestRefresh(false);
4792 return;
4793 }
4794}
Note: See TracBrowser for help on using the repository browser.