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

Last change on this file since 1415 was 1415, checked in by bln4, 5 years ago

Renamed Frame.getItems() to Frame.getSortedItems() to better represent its functionality.

-> org.apollo.ApolloGestureActions
-> org.apollo.ApolloSystem
-> org.expeditee.actions.Actions
-> org.expeditee.actions.Debug
-> org.expeditee.actions.ExploratorySearchActions
-> org.expeditee.actions.JfxBrowserActions
-> org.expeditee.actions.Misc
-> org.expeditee.actions.Navigation
-> org.expeditee.actions.ScriptBase
-> org.expeditee.actions.Simple
-> org.expeditee.agents.ComputeTree
-> org.expeditee.agents.CopyTree
-> org.expeditee.agents.DisplayComet
-> org.expeditee.agents.DisplayTree
-> org.expeditee.agents.DisplayTreeLeaves
-> org.expeditee.agents.GraphFramesetLinks
-> org.expeditee.agents.TreeProcessor
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.DisplayController
-> org.expeditee.gui.FrameCreator
-> org.expeditee.gui.FrameIO
-> org.expeditee.io.DefaultTreeWriter
-> org.expeditee.io.JavaWriter
-> org.expeditee.io.PDF2Writer
-> org.expeditee.io.TXTWriter
-> org.expeditee.io.WebParser
-> org.expeditee.io.flowlayout.XGroupItem
-> org.expeditee.items.Dot
-> org.expeditee.items.Item
-> org.expeditee.items.ItemUtils
-> org.expeditee.network.FrameShare
-> org.expeditee.stats.TreeStats


Created ItemsList class to wrap ArrayList<Item>. Frames now use this new class to store its body list (used for display) as well as its primaryBody and surrogateBody.

-> org.expeditee.agents.Format
-> org.expeditee.agents.HFormat
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.Frame
-> org.expeditee.gui.FrameUtils


Refactorted Frame.setResort(bool) to Frame.invalidateSorted() to better function how it is intended to with a more accurate name.

-> org.expeditee.agents.Sort


When writing out .exp files and getting attributes to respond to LEFT + RIGHT click, boolean items are by default true. This has always been the case. An ammendment to this is that defaults can now be established.
Also added 'EnterClick' functionality. If cursored over a item with this property and you press enter, it acts as if you have clicked on it instead.

-> org.expeditee.assets.resources-public.framesets.authentication.1.exp to 6.exp
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gio.input.KBMInputEvent
-> org.expeditee.gio.javafx.JavaFXConversions
-> org.expeditee.gio.swing.SwingConversions
-> org.expeditee.gui.AttributeUtils
-> org.expeditee.io.Conversion
-> org.expeditee.io.DefaultFrameWriter
-> org.expeditee.items.Item


Fixed a bug caused by calling Math.abs on Integer.MIN_VALUE returning unexpected result. Due to zero being a thing, you cannot represent Math.abs(Integer.MIN_VALUE) in a Integer object. The solution is to use Integer.MIN_VALUE + 1 instead of Integer.MIN_VALUE.

-> org.expeditee.core.bounds.CombinationBounds
-> org.expeditee.io.flowlayout.DimensionExtent


Recoded the contains function in EllipticalBounds so that intersection tests containing circles work correctly.

-> org.expeditee.core.bounds.EllipticalBounds


Added toString() to PolygonBounds to allow for useful printing during debugging.

-> org.expeditee.core.bounds.PolygonBounds

Implemented Surrogate Mode!

-> org.expeditee.encryption.io.EncryptedExpReader
-> org.expeditee.encryption.io.EncryptedExpWriter
-> org.expeditee.encryption.items.surrogates.EncryptionDetail
-> org.expeditee.encryption.items.surrogates.Label
-> org.expeditee.gui.FrameUtils
-> org.expeditee.gui.ItemsList
-> org.expeditee.items.Item
-> org.expeditee.items.Text


???? Use Integer.MAX_VALUE cast to a float instead of Float.MAX_VALUE. This fixed some bug which I cannot remember.

-> org.expeditee.gio.TextLayoutManager
-> org.expeditee.gio.swing.SwingTextLayoutManager


Improved solution for dealing with the F10 key taking focus away from Expeditee due to it being a assessibility key.

-> org.expeditee.gio.swing.SwingInputManager


Renamed variable visibleItems in FrameGraphics.paintFrame to itemsToPaintCanditates to better represent functional intent.

-> org.expeditee.gui.FrameGraphics


Improved checking for if personal resources exist before recreating them

-> org.expeditee.gui.FrameIO


Repeated messages to message bay now have a visual feedback instead of just a beep. This visual feedback is in the form of a count of the amount of times it has repeated.

-> org.expeditee.gui.MessageBay


Updated comment on the Vector class to explain what vectors are.

-> org.expeditee.gui.Vector


Added constants to represent all of the property keys in DefaultFrameReader and DefaultFrameWriter.

-> org.expeditee.io.DefaultFrameReader
-> org.expeditee.io.DefaultFrameWriter


Updated the KeyList setting to be more heirarcial with how users will store their Secrets.

-> org.expeditee.settings.identity.secrets.KeyList

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