source: trunk/src/org/expeditee/gui/FrameMouseActions.java@ 943

Last change on this file since 943 was 943, checked in by bln4, 9 years ago

Implementation of IndirectMouseActions. Programmers are now able to intercept actions relating to mouse input events and make them act as they please.

Beginnings of IndirectKeyboardActions...

File size: 108.9 KB
Line 
1/**
2 * FrameMouseActions.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.gui;
20
21import java.awt.Image;
22import java.awt.Point;
23import java.awt.Rectangle;
24import java.awt.Toolkit;
25import java.awt.datatransfer.Clipboard;
26import java.awt.datatransfer.DataFlavor;
27import java.awt.datatransfer.Transferable;
28import java.awt.event.ActionEvent;
29import java.awt.event.ActionListener;
30import java.awt.event.KeyEvent;
31import java.awt.event.MouseEvent;
32import java.awt.event.MouseListener;
33import java.awt.event.MouseMotionListener;
34import java.awt.event.MouseWheelEvent;
35import java.awt.event.MouseWheelListener;
36import java.text.NumberFormat;
37import java.util.ArrayList;
38import java.util.Collection;
39import java.util.Date;
40import java.util.HashSet;
41import java.util.Iterator;
42import java.util.LinkedHashSet;
43import java.util.LinkedList;
44import java.util.List;
45import java.util.Set;
46
47import javax.swing.Timer;
48
49import org.expeditee.actions.Actions;
50import org.expeditee.actions.Misc;
51import org.expeditee.actions.Navigation;
52import org.expeditee.gui.indirect.mouse.IndirectMouseActions;
53import org.expeditee.gui.indirect.mouse.MouseAction;
54import org.expeditee.gui.indirect.mouse.MouseAction.MouseInfo;
55import org.expeditee.io.ExpClipReader;
56import org.expeditee.io.ItemSelection;
57import org.expeditee.io.ItemSelection.ExpDataHandler;
58import org.expeditee.items.Circle;
59import org.expeditee.items.Constraint;
60import org.expeditee.items.Dot;
61import org.expeditee.items.Item;
62import org.expeditee.items.Item.HighlightMode;
63import org.expeditee.items.ItemAppearence;
64import org.expeditee.items.ItemUtils;
65import org.expeditee.items.Line;
66import org.expeditee.items.Picture;
67import org.expeditee.items.Text;
68import org.expeditee.items.UserAppliedPermission;
69import org.expeditee.items.XRayable;
70import org.expeditee.items.widgets.InteractiveWidget;
71import org.expeditee.items.widgets.WidgetCorner;
72import org.expeditee.items.widgets.WidgetEdge;
73import org.expeditee.settings.UserSettings;
74import org.expeditee.settings.experimental.ExperimentalFeatures;
75import org.expeditee.stats.SessionStats;
76
77public class FrameMouseActions implements MouseListener, MouseMotionListener,
78 MouseWheelListener {
79
80 private static int _lastMouseClickModifiers = 0;
81
82 private static MouseEvent _lastMouseDragged;
83
84 private boolean _autoStamp = false;
85
86 private FrameMouseActions() {
87 IndirectMouseActions.getInstance().setBackAction(new MouseAction() {
88 @Override
89 public List<Item> exec(MouseInfo info) {
90 //if user is not pointing at something,this is a back
91 if (info.isControlDown || info.isShiftDown)
92 forward();
93 else
94 back();
95 return null;
96 }
97 });
98 IndirectMouseActions.getInstance().setTDFCAction(new MouseAction() {
99 @Override
100 public List<Item> exec(final MouseInfo info) {
101 // check for TDFC permission
102 if (!info.clicked.hasPermission(UserAppliedPermission.createFrames)) {
103 MessageBay
104 .displayMessage("Insufficient permission to TDFC (Top Down Frame Creation) from that item");
105 return null;
106 }
107
108 if (info.clicked.isOldTag())
109 return null;
110
111 try {
112 tdfc(info.clicked);
113 } catch (RuntimeException e) {
114 e.printStackTrace();
115 MessageBay.errorMessage("Top Down Frame Creation (TDFC) error: " + e.getMessage());
116 }
117 return null;
118 }
119 });
120 IndirectMouseActions.getInstance().setRespondToFrameNameClickAction(new MouseAction() {
121 @Override
122 public List<Item> exec(MouseInfo info) {
123 if (info.isControlDown)
124 Navigation.PreviousFrame(false);
125 else
126 Navigation.NextFrame(false);
127 return null;
128 }
129 });
130 IndirectMouseActions.getInstance().setFollowLinkAction(new MouseAction() {
131 @Override
132 public List<Item> exec(MouseInfo info) {
133 /*
134 * Dont save the frame if we are moving to an old version of
135 * this frame because everytime we save with the old tag... the
136 * frame is backed up
137 */
138 if (!info.clicked.isOldTag())
139 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
140
141 Navigation.setLastNavigationItem(info.clicked);
142 load(info.clicked.getAbsoluteLink(), info.clicked.getLinkHistory());
143 // DisplayIO.UpdateTitle();
144 return null;
145 }
146 });
147 IndirectMouseActions.getInstance().setExecuteActionAction(new MouseAction() {
148 @Override
149 public List<Item> exec(MouseInfo info) {
150 info.clicked.performActions();
151 info.clicked.setHighlightMode(HighlightMode.None);
152 getInstance().refreshHighlights();
153 return null;
154 }
155 });
156 IndirectMouseActions.getInstance().setCreateRectangleAction(new MouseAction() {
157 @Override
158 public List<Item> exec(MouseInfo info) {
159 final List<Item> copies = new ArrayList<Item>();
160 Item[] d = new Item[RECTANGLE_CORNERS];
161 // create dots
162 Frame current = DisplayIO.getCurrentFrame();
163 for (int i = 0; i < d.length; i++) {
164 d[i] = current.createDot();
165 copies.add(d[i]);
166 }
167
168 current.nextDot();
169
170 // create lines
171 copies.add(new Line(d[0], d[1], current.getNextItemID()));
172 copies.add(new Line(d[1], d[2], current.getNextItemID()));
173 copies.add(new Line(d[2], d[3], current.getNextItemID()));
174 copies.add(new Line(d[3], d[0], current.getNextItemID()));
175
176 new Constraint(d[0], d[1], current.getNextItemID(),
177 Constraint.HORIZONTAL);
178 new Constraint(d[2], d[3], current.getNextItemID(),
179 Constraint.HORIZONTAL);
180 new Constraint(d[1], d[2], current.getNextItemID(),
181 Constraint.VERTICAL);
182 new Constraint(d[3], d[0], current.getNextItemID(),
183 Constraint.VERTICAL);
184
185 anchor(new ArrayList<Item>(copies));
186 pickup(d[3]);
187 d[3].setHighlightMode(HighlightMode.Normal);
188
189 SessionStats.CreatedItems(copies);
190 copies.clear();
191 return copies;
192 }
193 });
194 IndirectMouseActions.getInstance().setExtendLineAction(new MouseAction() {
195 @Override
196 public List<Item> exec(final MouseInfo info) {
197 Line onLine = (Line) info.clicked;
198 Line newLine = onLine.copy();
199 Item end = newLine.getEndItem();
200 Item start = newLine.getStartItem();
201 end.setPosition(MouseX, MouseY);
202 start.setPosition(MouseX, MouseY);
203 onLine.autoArrowheadLength();
204 // anchor the start
205 anchor(start);
206 // attach the line to the cursor
207 pickup(end);
208
209 List<Item> toMerge = new LinkedList<Item>();
210 toMerge.add(newLine.getStartItem());
211 toMerge.add(newLine);
212
213 // Make sure the highlighting is shown when the end is
214 // anchored
215 end.setHighlightMode(Item.HighlightMode.Normal);
216 merge(toMerge, info.clicked);
217 // anchor(left);
218 // FreeItems.getInstance().clear();
219 FrameGraphics.Repaint();
220 updateCursor();
221 return null;
222 }
223 });
224 IndirectMouseActions.getInstance().setMakeGroupCopyAction(new MouseAction() {
225 @Override
226 public List<Item> exec(final MouseInfo info) {
227 List<Item> copies = new ArrayList<Item>();
228 // Set the selection mode for the items that were clicked in
229 Collection<Item> enclosed = getFullyEnclosedItems(info.clickedIn);
230 if (enclosed.size() == 0) {
231 MessageBay
232 .displayMessage("Insufficient permission to copy items");
233 } else {
234 copies = copy(enclosed);
235 clearParent(copies);
236 ItemUtils.Justify(copies);
237 pickup(copies);
238 for (Item i : info.clickedIn) {
239 i.setHighlightMode(HighlightMode.None);
240 }
241 }
242 return copies;
243 }
244 });
245 IndirectMouseActions.getInstance().setMakeCopyAction(new MouseAction() {
246 @Override
247 public List<Item> exec(MouseInfo info) {
248 List<Item> copies = ItemUtils.UnreelLine(info.clicked, _controlDown);
249 // Copies will NOT be null if the user right clicked on a point
250 if (copies == null) {
251 Collection<Item> originals = info.clicked.getConnected();
252 copies = ItemUtils.CopyItems(originals, _extrude);
253 // if this is the title of the frame, link it to the frame
254 if (originals.size() == 1 && copies.size() == 1) {
255 Item copy = copies.get(0);
256 Item original = originals.iterator().next();
257 if (original.getLink() == null
258 && original.isFrameTitle()) {
259 // save the frame after copying
260 // i.getParent().setChanged(true);
261 copy.setLink(original.getParentOrCurrentFrame()
262 .getName());
263 }
264 }
265
266 FrameGraphics.changeHighlightMode(info.clicked,
267 HighlightMode.None);
268
269 if (!_extrude)
270 clearParent(copies);
271 }
272
273 ItemUtils.Justify(copies);
274
275 pickup(copies);
276 return copies;
277 }
278 });
279 IndirectMouseActions.getInstance().setExtrudeAction(new MouseAction() {
280 @Override
281 public List<Item> exec(MouseInfo info) {
282 List<Item> copies = null;
283 List<Item> originals = new ArrayList<Item>();
284 // remove any lines that dont have both endpoints
285 // floating
286 for (Item i : FreeItems.getInstance()) {
287 if (i.isFloating())
288 originals.add(i);
289 }
290 if (copies == null)
291 copies = ItemUtils.CopyItems(originals, _extrude);
292 for (Item i : copies)
293 i.setOffset(0, 0);
294 anchor(FreeItems.getInstance());
295 // Move isnt working right for extruding!!
296 // move(copies);
297 pickup(copies);
298 return copies;
299 }
300 });
301 IndirectMouseActions.getInstance().setRubberBandingCopyAction(new MouseAction() {
302 @Override
303 public List<Item> exec(MouseInfo info) {
304 List<Item> copies = new ArrayList<Item>();
305 if (info.clicked != null) {
306 Collection<Item> leftOver = merge(FreeItems
307 .getInstance(), info.clicked);
308 anchor(leftOver);
309 }
310 // This is executed when the user is putting down a line
311 // endpoint and unreeling. ie. Normal unreeling
312 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
313 _controlDown);
314
315 if (copies == null)
316 copies = copy(FreeItems.getInstance());
317 anchor(FreeItems.getInstance());
318 for (Item i : copies)
319 i.setOffset(0, 0);
320 // need to move to prevent cursor dislocation
321 move(copies);
322 pickup(copies);
323 return copies;
324 }
325 });
326 IndirectMouseActions.getInstance().setRubberBandingCornerAction(new MouseAction() {
327 @Override
328 public List<Item> exec(final MouseInfo info) {
329 List<Item> copies = new ArrayList<Item>();
330 Item d = getFirstFreeLineEnd();
331 // anchor the points
332 anchor(FreeItems.getInstance());
333 FreeItems.getInstance().clear();
334 updateCursor();
335 // pick up a copy of all enclosed items
336 Collection<Item> enclosedItems = FrameUtils
337 .getItemsEnclosedBy(DisplayIO.getCurrentFrame(), d
338 .getEnclosedShape());
339 if (enclosedItems != null) {
340 enclosedItems.removeAll(d.getAllConnected());
341 Collection<Item> toCopy = getFullyEnclosedItems(enclosedItems);
342
343 if (toCopy.size() > 0) {
344 // Find the closest item to the mouse cursor
345 double currentX = DisplayIO.getMouseX();
346 double currentY = FrameMouseActions.getY();
347 Item closest = null;
348 double shortestDistance = Double.MAX_VALUE;
349 for (Item next : toCopy) {
350 if (next instanceof Line)
351 continue;
352 double distance = Point.distance(currentX,
353 currentY, next.getX(), next.getY());
354 if (distance < shortestDistance) {
355 shortestDistance = distance;
356 closest = next;
357 }
358 }
359 // Move the cursor to closest item
360 DisplayIO.setCursorPosition(closest.getPosition());
361 // Pickup copy of the stuff inside the rectange
362 copies = copy(toCopy);
363 pickup(copies);
364 // Remove the rectangle
365 d.getParentOrCurrentFrame().removeAllItems(
366 d.getAllConnected());
367 } else {
368 // Pick up a copy of the rectangle
369 copies = copy(d.getAllConnected());
370 pickup(copies);
371 }
372 }
373 return copies;
374 }
375 });
376 IndirectMouseActions.getInstance().setStampAction(new MouseAction() {
377 @Override
378 public List<Item> exec(MouseInfo info) {
379 List<Item> copies = new ArrayList<Item>();
380 stampItemsOnCursor(true);
381 copies = FreeItems.getInstance();
382 return copies;
383 }
384 });
385 IndirectMouseActions.getInstance().setMergeSingleItemAction(new MouseAction() {
386 @Override
387 public List<Item> exec(final MouseInfo info) {
388 List<Item> copies = new ArrayList<Item>();
389 copies = copy(FreeItems.getInstance());
390 Collection<Item> remain = merge(copies, info.clicked);
391
392 // ignore items that could not be merged.
393 anchor(remain);
394 return copies;
395 }
396 });
397 IndirectMouseActions.getInstance().setMergeTwoItemsAction(new MouseAction() {
398 @Override
399 public List<Item> exec(MouseInfo info) {
400 List<Item> copies = new ArrayList<Item>();
401 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
402 _controlDown);
403 Collection<Item> leftOver = merge(FreeItems
404 .getInstance(), info.clicked);
405 anchor(leftOver);
406 if (copies == null)
407 copies = copy(FreeItems.getInstance());
408 FreeItems.getInstance().clear();
409 for (Item i : copies)
410 i.setOffset(0, 0);
411 // need to move to prevent cursor dislocation
412 move(copies);
413 pickup(copies);
414 // point onto point
415 return copies;
416 }
417 });
418 IndirectMouseActions.getInstance().setMergeGroupAction(new MouseAction() {
419 @Override
420 public List<Item> exec(final MouseInfo info) {
421 List<Item> copies = new ArrayList<Item>();
422 // Move the cursor so that the copy is exactly the
423 // same as the shape that was anchored
424 DisplayIO.setCursorPosition(info.clicked.getPosition());
425 Item d = getFirstFreeLineEnd();
426 // get a copy of all enclosed items before merging
427 // lineEnds
428 Collection<Item> items = FrameUtils.getItemsEnclosedBy(
429 DisplayIO.getCurrentFrame(), d
430 .getEnclosedShape());
431 // If its not an enclosed shape then pick up the
432 // connected shape
433 if (items == null || items.size() == 0) {
434 items = d.getAllConnected();
435 } else {
436 // For some reason the item that was clicked ends up
437 // in the enclosure and needs to be removed
438 items.removeAll(info.clicked.getConnected());
439 // the item that was the origin of the enclosed
440 // shape used to create the enclosure does not get
441 // returned from getItemsEnclosedBy to the enclosure
442 // so it must be added
443 items.addAll(d.getConnected());
444 }
445
446 Collection<Item> toCopy = new LinkedHashSet<Item>();
447
448 for (Item ip : items) {
449 if (ip.hasPermission(UserAppliedPermission.copy))
450 toCopy.add(ip);
451 }
452 copies = copy(toCopy);
453 // Now do the merging
454 Collection<Item> remain = merge(
455 FreeItems.getInstance(), info.clicked);
456 // anchor the points
457 anchor(remain);
458 FreeItems.getInstance().clear();
459 pickup(copies);
460 return copies;
461 }
462 });
463 IndirectMouseActions.getInstance().setNewLineAction(new MouseAction() {
464 @Override
465 public List<Item> exec(MouseInfo info) {
466 List<Item> copies = new ArrayList<Item>();
467 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), Math
468 .round(MouseX), Math.round(MouseY), true);
469 // If we have permission to copy this item then pick it up
470 if (on != null && on.isLineEnd()
471 && on.hasPermission(UserAppliedPermission.full)) {
472 on.removeAllConstraints();
473 pickup(on);
474 return copies;
475 }
476
477 if (on instanceof WidgetEdge) {
478 // Don't allow the user to break widget edges.
479 // Note: had to return here because random dots would
480 // appear otherwise... cannot understand code below
481 // with create line.
482 return copies;
483 }
484
485 // if its on a line then split the line and put a point on it and
486 // pick that point up. Only if it is not a widget line
487 if (on instanceof Line && on.hasPermission(UserAppliedPermission.full)) {
488 Frame current = DisplayIO.getCurrentFrame();
489 // create the two endpoints
490 Line oldLine = (Line) info.clicked;
491 Item newPoint = oldLine.getStartItem().copy();
492 newPoint.setPosition(MouseX, MouseY);
493
494 Item end = oldLine.getEndItem();
495 // create the Line
496 Line newLine = new Line(newPoint, end, current.getNextItemID());
497 oldLine.replaceLineEnd(end, newPoint);
498 newPoint.removeAllConstraints();
499 pickup(newPoint);
500 // Update the stats
501 Collection<Item> created = new LinkedList<Item>();
502 created.add(newPoint);
503 created.add(newLine);
504 SessionStats.CreatedItems(newLine.getAllConnected());
505 return copies;
506 }
507 Line newLine = createLine();
508 SessionStats.CreatedItems(newLine.getAllConnected());
509 return copies;
510 }
511 });
512 IndirectMouseActions.getInstance().setGroupPickupAction(new MouseAction() {
513 @Override
514 public List<Item> exec(final MouseInfo info) {
515 List<Item> copies = new ArrayList<Item>();
516 ArrayList<Item> toPickup = new ArrayList<Item>(info.clickedIn.size());
517 for (Item ip : info.clickedIn)
518 if (ip.hasPermission(UserAppliedPermission.full))
519 toPickup.add(ip);
520 pickup(toPickup);
521 // otherwise the user is creating a line
522 return copies;
523 }
524 });
525 IndirectMouseActions.getInstance().setDetachLineAction(new MouseAction() {
526 @Override
527 public List<Item> exec(final MouseInfo info) {
528 List<Item> items = new ArrayList<Item>();
529 // BROOK: WIDGET RECTANGLES DONT ALLOW DISCONNECTION
530 if (info.clicked instanceof Line && !(info.clicked instanceof WidgetEdge)) {
531 // Check if within 20% of the end of the line
532 Line l = (Line) info.clicked;
533 Item toDisconnect = l.getEndPointToDisconnect(_lastMouseClick
534 .getX(), _lastMouseClick.getY());
535
536 if (toDisconnect == null) {
537 pickup(info.clicked);
538 } else {
539 if (toDisconnect.getHighlightMode() == Item.HighlightMode.Normal) {
540 DisplayIO.setCursorPosition(toDisconnect.getPosition(),
541 false);
542 pickup(toDisconnect);
543 } else {
544 List<Line> lines = toDisconnect.getLines();
545 // This is to remove constraints from single lines
546 // with constraints...
547 // ie. partially deleted rectangles
548 if (lines.size() == 1) {
549 toDisconnect.removeAllConstraints();
550
551 DisplayIO.setCursorPosition(toDisconnect
552 .getPosition(), false);
553 // This is to ensure the selected mode will be set
554 // to Normal rather than disconnect when the line is
555 // anchored
556 toDisconnect
557 .setHighlightMode(Item.HighlightMode.Normal);
558 pickup(toDisconnect);
559 } else {
560 // If we are then detatch the line and pick up its
561 // end point...
562 Frame currentFrame = DisplayIO.getCurrentFrame();
563 Item newPoint = null;
564
565 // If the point we are disconnecting is text...
566 // Then we want to leave the text behind
567 // And disconnect a point
568 if (toDisconnect instanceof Text) {
569 newPoint = new Dot(toDisconnect.getX(),
570 toDisconnect.getY(), -1);
571 Item.DuplicateItem(toDisconnect, newPoint);
572 } else {
573 newPoint = toDisconnect.copy();
574 }
575
576 currentFrame.addItem(newPoint);
577 // remove the current item from the connected
578 // list for this item
579 l.replaceLineEnd(toDisconnect, newPoint);
580 // remove unneeded constrains
581 newPoint.removeAllConstraints();
582
583 // Set the new points mode to normal before picking
584 // it up so it will be restored correctly when
585 // anchored
586 newPoint
587 .setHighlightMode(Item.HighlightMode.Normal);
588 toDisconnect
589 .setHighlightMode(Item.HighlightMode.None);
590 DisplayIO.setCursorPosition(toDisconnect
591 .getPosition(), false);
592 pickup(newPoint);
593 ItemUtils.EnclosedCheck(toDisconnect
594 .getParentOrCurrentFrame().getItems());
595 }
596 }
597 }
598 } else {
599 if (info.clicked.isLineEnd()) {
600 DisplayIO.setCursorPosition(info.clicked.getPosition(), false);
601 }
602 pickup(info.clicked);
603 }
604 return items;
605 }
606 });
607 IndirectMouseActions.getInstance().setAnchorFreeItemsAction(new MouseAction() {
608 @Override
609 public List<Item> exec(MouseInfo info) {
610 List<Item> items = new ArrayList<Item>();
611 if (info.clickedIn != null && FreeItems.getInstance().size() == 1) {
612 Item item = FreeItems.getItemAttachedToCursor();
613 if (item instanceof Text) {
614 Text text = (Text) item;
615 if (AttributeUtils.setAttribute(text, text, 2)) {
616 info.clickedIn.removeAll(FrameUtils
617 .getEnclosingLineEnds().iterator().next()
618 .getAllConnected());
619 for (Item i : info.clickedIn) {
620 AttributeUtils.setAttribute(i, text);
621 }
622 FreeItems.getInstance().clear();
623 }
624 }
625 }
626
627 // if a line is being rubber-banded, check for auto
628 // straightening
629 anchor(FreeItems.getInstance());
630 FreeItems.getInstance().clear();
631 updateCursor();
632 _offX = _offY = 0;
633 return items;
634 }
635 });
636 IndirectMouseActions.getInstance().setDeleteItemsAction(new MouseAction() {
637 @Override
638 public List<Item> exec(final MouseInfo info) {
639 List<Item> items = new ArrayList<Item>();
640 // check permissions
641 if (!info.clicked.hasPermission(UserAppliedPermission.full)) {
642 //Items on the message box have parent == null
643 if (info.clicked.getParent() != null) {
644 if (!info.clicked.isFrameName()) {
645 Item editTarget = info.clicked.getEditTarget();
646 if (editTarget != info.clicked
647 && editTarget
648 .hasPermission(UserAppliedPermission.full)) {
649 info.clicked = editTarget;
650 } else {
651 MessageBay
652 .displayMessage("Insufficient permission");
653 return items;
654 }
655 }
656
657 } else /*Its in the message area*/ {
658 MessageBay.displayMessage("Insufficient permission");
659 return items;
660 }
661 }
662 Item merger = FreeItems.getItemAttachedToCursor();
663 assert (merger != null);
664 Collection<Item> left = null;
665 // when anchoring a line end onto a text line end, holding shift
666 // prevents the line ends from being merged
667 if (info.isShiftDown) {
668 left = FreeItems.getInstance();
669 } else {
670 left = merge(FreeItems.getInstance(), info.clicked);
671 }
672 Collection<Item> toDelete = new LinkedList<Item>();
673 toDelete.addAll(FreeItems.getInstance());
674 toDelete.removeAll(left);
675 anchor(left);
676 FreeItems.getInstance().clear();
677 DisplayIO.getCurrentFrame().removeAllItems(toDelete);
678 updateCursor();
679 // Make sure the dot goes away when anchoring a line end behind
680 // a text line end
681 if (info.isShiftDown) {
682 refreshHighlights();
683 }
684 FrameGraphics.requestRefresh(true);
685 return items;
686 // otherwise, anchor the items
687 }
688 });
689 }
690
691 private static FrameMouseActions _instance = null;
692
693 public static FrameMouseActions getInstance() {
694 if (_instance == null)
695 _instance = new FrameMouseActions();
696 return _instance;
697 }
698
699 private static final int RECTANGLE_CORNERS = 4;
700
701 // TODO say where/how used
702 private static final int MOUSE_WHEEL_THRESHOLD = 2;
703
704 private static final int MINIMUM_RANGE_DEPRESS_TIME = 250;
705
706 private static final int RECTANGLE_TO_POINT_THRESHOLD = 20;
707
708 private static Date _lastMouseClickDate = new Date();
709
710 public static final int LITTLE_MOUSE_PAUSE = 500;
711
712 public static final int ZERO_MOUSE_PAUSE = 0;
713
714 public static final int BIG_MOUSE_PAUSE = 750;
715
716 public static final int CONTEXT_FREESPACE = 0;
717
718 public static final int CONTEXT_AT_TEXT = 1;
719
720 public static final int CONTEXT_AT_LINE = 2;
721
722 public static final int CONTEXT_AT_DOT = 3;
723
724 public static final int CONTEXT_AT_ENCLOSURE = 4;
725
726 public static int _alpha = -1;
727
728 /**
729 * The last known mouse X coordinate
730 */
731 public static float MouseX;
732
733 /**
734 * The last known mouse Y coordinate. Relative to the top of the
735 * application.
736 */
737 public static float MouseY;
738
739 // Distance of mouse cursor from the origin of the item that was picked up
740 // The are used in the move method to calculate the distance moved by the
741 // cursor
742 private static int _offX;
743
744 private static int _offY;
745
746 // Keeps track of mouse button events when a delete occurs
747 private static boolean _isDelete = false;
748
749 // Keeps track of mouse button events when the user extracts attributes
750 // occurs
751 private static boolean _isAttribute = false;
752
753 /**
754 * A flag to indicate that the last mouseUp event was part of a two button
755 * click sequence hence the next mouse up should be ignored*
756 */
757 private static boolean _wasDouble = false;
758
759 private static boolean _isNoOp = false;
760
761 private static boolean _extrude = false;
762
763 // keeps track of the last highlighted Item
764 private static Item _lastHighlightedItem = null;
765
766 // keeps track of the item being 'ranged out' if there is one.
767 private static Text _lastRanged = null;
768
769 // keeps track of the picture being cropped if there is one
770 private static Picture _lastCropped = null;
771
772 // true if lastItem only has highlighting removed when a new item is
773 // highlighted
774 private static boolean _lastHoldsHighlight = false;
775
776 private static boolean _forceArrowCursor = true;
777
778 // the current context of the cursor
779 private static int _context = 0;
780
781 public static void setForceArrow(boolean val) {
782 _forceArrowCursor = val;
783 }
784
785 public static int getContext() {
786 return _context;
787 }
788
789 static int _mouseDown = 0;
790
791 private static MouseEvent _lastMouseClick = null;
792
793 private static Item _lastClickedOn = null;
794
795 private static Collection<Item> _lastClickedIn = null;
796
797 private static boolean _pulseOn = false;
798
799 private static final int PULSE_AMOUNT = 2;
800
801 private static Timer _MouseTimer = new Timer(LITTLE_MOUSE_PAUSE,
802 new ActionListener() {
803 public void actionPerformed(ActionEvent ae) {
804 // check if we are in free space
805 if (_lastClickedOn == null
806 && FreeItems.getInstance().size() == 0) {
807 // System.out.println("SuperBack!");
808 _MouseTimer.setDelay(ZERO_MOUSE_PAUSE);
809 back();
810 } else {
811 if (FrameUtils.getCurrentItem() == null) {
812 // Check if we are toggling arrowhead
813 if (FreeItems.getInstance().size() <= 2) {
814 for (Item i : FreeItems.getInstance()) {
815 if (i instanceof Line) {
816 ((Line) i).toggleArrow();
817 }
818 }
819 FrameGraphics.Repaint();
820 }
821 }
822 _MouseTimer.stop();
823 }
824 }
825 });
826
827 private static void setPulse(boolean pulseOn) {
828 if (_pulseOn == pulseOn) {
829 return;
830 }
831 int amount = PULSE_AMOUNT;
832 if (!pulseOn) {
833 amount *= -1;
834 }
835 _pulseOn = pulseOn;
836
837
838 if (_lastClickedOn != null) {
839 for (Item i : _lastClickedOn.getAllConnected()) {
840 if (i instanceof Line) {
841 Line line = (Line) i;
842 line.setThickness(line.getThickness() + amount);
843 }
844 }
845 }
846 FrameGraphics.Repaint();
847 }
848
849 private static Timer _ExtrudeMouseTimer = new Timer(BIG_MOUSE_PAUSE,
850 new ActionListener() {
851 public void actionPerformed(ActionEvent ae) {
852 setPulse(true);
853 _extrude = true;
854 _ExtrudeMouseTimer.stop();
855 }
856 });
857
858 public void mouseClicked(MouseEvent e) {
859 }
860
861 /**
862 * Each Item on the Frame is checked to determine if the mouse x,y
863 * coordinates are on the Item (or within the Shape surrounding it). If the
864 * coordinates are on the Item then the Item is checked for a link, if it
865 * has a link the link is followed, if not, nothing is done.
866 */
867 public void mousePressed(MouseEvent e) {
868 ProccessMousePressedEvent(e, e.getModifiersEx());
869 }
870
871 private void ProccessMousePressedEvent(MouseEvent e, int modifiersEx) {
872 // System.out.println("MousePressed " + e.getX() + "," + e.getY() + " "
873 // + e.getWhen());
874
875 // TODO WHY DID I NOT COMMENT THIS LINE!! MIKE SAYS
876 if (LastRobotX != null) {
877 _RobotTimer.stop();
878 LastRobotX = null;
879 LastRobotY = null;
880 mouseMoved(e);
881 }
882
883 if(ExperimentalFeatures.MousePan.get()) {
884 // don't pan if we're not over the frame
885 _overFrame = FrameUtils.getCurrentItem() == null;
886 _isPanOp = false;
887 // update panning position values so position doesn't jump
888 panStartX = e.getX();
889 panStartY = e.getY();
890 MouseX = panStartX;
891 MouseY = panStartY;
892 }
893
894 // System.out.println(modifiersEx);
895 if (_mouseDown == 0)
896 _lastMouseClickDate = new Date();
897
898 int buttonPressed = e.getButton();
899 _mouseDown += buttonPressed;
900 _lastClickedOn = FrameUtils.getCurrentItem();
901 // load any frame if necessary
902 Item on = _lastClickedOn;
903
904 _lastClickedIn = FrameUtils.getCurrentItems(on);
905 // if (_lastClickedIn != null){
906 // System.out.println(_lastClickedIn.size());}
907
908 /*
909 * This makes it so clicking repeatedly on the frameName doesnt add the
910 * frames to the backup stack. Only the first frame is added to the
911 * backup stack.
912 */
913 if (on == null || buttonPressed != MouseEvent.BUTTON1
914 || !on.isFrameName()) {
915 Navigation.ResetLastAddToBack();
916 }
917
918 SessionStats.MouseClicked(e.getButton());
919 if (buttonPressed == MouseEvent.BUTTON1) {
920 SessionStats.AddFrameEvent("Ld");
921 _extrude = false;
922 } else if (buttonPressed == MouseEvent.BUTTON2) {
923 SessionStats.AddFrameEvent("Md");
924 _extrude = false;
925 } else if (buttonPressed == MouseEvent.BUTTON3) {
926 SessionStats.AddFrameEvent("Rd");
927
928 // Check if the user picked up a paint brush
929 if (FreeItems.getInstance().size() == 1
930 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
931 int delay = (int) (FreeItems.getItemAttachedToCursor()
932 .getAutoStamp() * 1000);
933 if (delay < 10) {
934 _autoStamp = true;
935 } else {
936 _autoStampTimer.setDelay(delay);
937 _autoStampTimer.start();
938 }
939 }
940 }
941
942 // Mike says...
943 // For somereason the modifiers for e are different from modifiersEx
944 // The SwingUtilities.convertMouseEvent method changes the modifiers
945 _lastMouseClick = e;
946 _lastMouseClickModifiers = modifiersEx;
947
948 /*
949 * Only start the timer when in free space when the user double clicks
950 * to do super back TODO change this so that there are separate timers
951 * for super back and the other Long depress actions if that is what is
952 * wanted.
953 */
954 if (_lastClickedOn == null && FreeItems.getInstance().size() == 0) {
955 // System.out.println(e.getClickCount());
956 if (e.getClickCount() >= 2) {
957 _MouseTimer.start();
958 }
959 } else if (_lastClickedOn != null
960 && FreeItems.getInstance().size() == 0
961 && e.getButton() == MouseEvent.BUTTON3) {
962 _ExtrudeMouseTimer.start();
963
964 } else {
965 _MouseTimer.start();
966 }
967
968 // pre-cache the frame if it is linked
969
970 // If pre-caching is done, it must be done in the background
971
972 // if (on != null && on.getLink() != null && on.isLinkValid()) {
973 // FrameIO.Precache(on.getAbsoluteLink());
974 // }
975
976 // check for delete command
977 if (isDelete(modifiersEx)) {
978 _isDelete = true;
979 // _lastRanged = null;
980 _lastCropped = null;
981 _wasDouble = false;
982 // check for attributes command
983 } else if (isGetAttributes(modifiersEx)) {
984 _isAttribute = true;
985 _wasDouble = false;
986 } else if (isTwoClickNoOp(modifiersEx)) {
987 _isAttribute = false;
988 _wasDouble = false;
989 _isDelete = false;
990 _isNoOp = true;
991 } else
992 _isDelete = false;
993
994 // This must happen before the previous code
995 // This is when the user is anchoring something
996 if (buttonPressed != MouseEvent.BUTTON1
997 && (_context == CONTEXT_FREESPACE || _context == CONTEXT_AT_ENCLOSURE)
998 && FreeItems.itemsAttachedToCursor()) {
999 FrameGraphics.changeHighlightMode(_lastHighlightedItem,
1000 Item.HighlightMode.None);
1001
1002 _lastHighlightedItem = FreeItems.getItemAttachedToCursor();
1003 for (Item i : FreeItems.getInstance()) {
1004 i.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
1005 }
1006 FrameGraphics.Repaint();
1007 // this is when the user is picking something up
1008 } else if (_lastHighlightedItem != null) {
1009 if (!(_lastHighlightedItem instanceof Line)) {
1010 _lastHighlightedItem
1011 .setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
1012 } else {
1013 for (Item i : _lastHighlightedItem.getAllConnected()) {
1014 i.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
1015 }
1016 }
1017 FrameGraphics.Repaint();
1018 }
1019
1020 // if the user is ranging text
1021 if (on != null && on instanceof Text && !_isDelete) {
1022 _lastRanged = (Text) on;
1023 // set start-drag point
1024 _lastRanged.setSelectionStart(DisplayIO.getMouseX(),
1025 FrameMouseActions.getY());
1026 }
1027
1028 /*
1029 * Want to repaint the text with deleteRange color as soon as the second
1030 * button is pressed
1031 */
1032 if (_lastRanged != null) {
1033 _lastRanged.invalidateAll();
1034 FrameGraphics.requestRefresh(true);
1035 }
1036
1037 if (on != null && on instanceof Picture
1038 && e.getButton() == MouseEvent.BUTTON3 && !_isDelete) {
1039 _lastCropped = (Picture) on;
1040 // set start crop point
1041 _lastCropped.setStartCrop(DisplayIO.getMouseX(), FrameMouseActions
1042 .getY());
1043 _lastCropped.setShowCrop(true);
1044 }
1045 }
1046
1047 // This is where all the processing happens
1048 public void mouseReleased(MouseEvent e) {
1049
1050 // System.out.println("Released " + e.getX() + "," + e.getY() + " " +
1051 // e.getWhen());
1052 FrameUtils.ResponseTimer.restart();
1053 _autoStampTimer.stop();
1054 _autoStamp = false;
1055
1056 // Auto-hide popups when user clicks into expeditee world
1057 // If the user clicks into empty space and a popup-is showing, then
1058 // the user porbably wants to click away the popup - therefore ignore
1059 // the event
1060 boolean shouldConsume = PopupManager.getInstance()
1061 .shouldConsumeBackClick();
1062 PopupManager.getInstance().hideAutohidePopups();
1063 if (shouldConsume && e.getButton() == MouseEvent.BUTTON1) {
1064 return; // consume back click event
1065 }
1066
1067 // _lastMovedDistance = new Point(e.getX() - _lastMouseClick.getX(), e
1068 // .getY()
1069 // - _lastMouseClick.getY());
1070
1071 _mouseDown -= e.getButton();
1072 updateCursor();
1073
1074 // System.out.println(e.getX() + ", " + e.getY());
1075
1076 Text lastRanged = _lastRanged;
1077 _lastRanged = null;
1078 // Dont do ranging if the user moves really quickly...
1079 // They are probably trying to pick something up in this case
1080 if (lastRanged != null) {
1081 long depressTime = (new Date()).getTime()
1082 - _lastMouseClickDate.getTime();
1083 // double changeInDistance =
1084 // e.getPoint().distance(_currentMouseClick.getPoint());
1085 // double speed = changeInDistance * 1000 / changeInTime;
1086
1087 // System.out.println(depressTime);
1088
1089 if (depressTime < MINIMUM_RANGE_DEPRESS_TIME
1090 || lastRanged.getSelectionSize() <= 0) {// Text.MINIMUM_RANGED_CHARS)
1091 // {
1092 lastRanged.clearSelection();
1093 lastRanged = null;
1094 }
1095 }
1096
1097 _ExtrudeMouseTimer.stop();
1098 _MouseTimer.stop();
1099
1100 setPulse(false);
1101
1102 // if the last action was a delete, then ignore the next mouseup
1103 if (_wasDouble) {
1104 _wasDouble = false;
1105 return;
1106 }
1107
1108 // This code must come after the _wasDouble code...
1109 // Otherwise get Stopping Agent method after doing the left+right format
1110 // shortcut
1111 if (Actions.isAgentRunning()) {
1112 Actions.stopAgent();
1113 return;
1114 }
1115
1116 /*
1117 * if (_isNoOp) { if (e.getButton() != MouseEvent.NOBUTTON) { _isNoOp =
1118 * false; _wasDouble = true; // lastRanged.clearSelection();
1119 * FrameGraphics.Repaint(); return; } }
1120 */
1121
1122 // get whatever the user was pointing at
1123 Item clickedOn = _lastClickedOn;
1124 Collection<Item> clickedIn = _lastClickedIn;
1125
1126 MouseX = e.getX();
1127 MouseY = e.getY();
1128
1129 Item releasedOn = FrameUtils.getCurrentItem();
1130 Collection<Item> releasedIn = FrameUtils.getCurrentItems(releasedOn);
1131
1132 // Only a no op if user releases in free space!
1133 if (_isPanOp || (_isNoOp && (releasedOn == null && releasedIn == null))) {
1134 if (_isDelete) {
1135 _isDelete = false;
1136 _wasDouble = true;
1137 }
1138
1139 _isNoOp = false;
1140
1141 if (_lastHighlightedItem != null)
1142 FrameGraphics.changeHighlightMode(_lastHighlightedItem,
1143 Item.HighlightMode.None);
1144
1145 if (FreeItems.itemsAttachedToCursor()) {
1146 move(FreeItems.getInstance());
1147 }
1148
1149 if (FreeItems.hasCursor()) {
1150 move(FreeItems.getCursor(), true);
1151 }
1152
1153 if(!_isPanOp) {
1154 MessageBay.displayMessage("Action cancelled, mouse moved more than "
1155 + UserSettings.NoOpThreshold.get() + " pixels.");
1156 }
1157 FrameGraphics.Repaint();
1158 return;
1159 } else {
1160 _isNoOp = false;
1161 }
1162
1163 // if this is a delete command
1164 if (_isDelete) {
1165 if (lastRanged != null) {
1166
1167 Item i = FreeItems.getItemAttachedToCursor();
1168 if (i != null && i instanceof Text) {
1169 lastRanged.replaceSelectedText(((Text) i).getText());
1170 FreeItems.getInstance().clear();
1171 } else
1172 lastRanged.cutSelectedText();
1173 lastRanged.clearSelection();
1174 FrameGraphics.Repaint();
1175
1176 } else {
1177 delete(clickedOn);
1178 }
1179 _wasDouble = true;
1180 _isDelete = false;
1181 return;
1182 }
1183
1184 // if this is an attribute extraction command
1185 if (_isAttribute) {
1186 if (clickedOn == null) {
1187 Frame current = DisplayIO.getCurrentFrame();
1188 if (isControlDown()) {
1189 Actions.PerformActionCatchErrors(current, null, "HFormat");
1190 }
1191 if (!isControlDown() || isShiftDown()) {
1192 Actions.PerformActionCatchErrors(current, null, "Format");
1193 }
1194 } else {
1195 extractAttributes(clickedOn);
1196 }
1197 // if the user dragged and displayed some cropping with left and
1198 // right button is a no op for now
1199 // but later could make this the shrinkTo context
1200 if (_lastCropped != null) {
1201 _lastCropped.clearCropping();
1202 _lastCropped = null;
1203 }
1204 _wasDouble = true;
1205 _isAttribute = false;
1206 return;
1207 }
1208
1209 // if the user is ranging-out text
1210 if (lastRanged != null && e.getButton() != MouseEvent.BUTTON1) {
1211
1212 Text ranged;
1213 if (isShiftDown()) {
1214 // If shift is down, copy everything (size, color, etc.) except actions, links and data
1215 ranged = lastRanged.copy();
1216 ranged.setActions(null);
1217 ranged.setData((List<String>) null);
1218 ranged.setLink(null);
1219 } else {
1220 // If shift isn't down, don't copy any attributes, but base the new text item on the appropriate template
1221 ranged = DisplayIO.getCurrentFrame().getItemTemplate(lastRanged.copySelectedText().charAt(0));
1222 }
1223
1224 // if the user is cutting text from the item
1225 if (e.getButton() == MouseEvent.BUTTON2) {
1226 // Check if the user is trying to range an item for which they
1227 // do not have permission to do so... or it is the frame name
1228 if (!lastRanged.hasPermission(UserAppliedPermission.full)
1229 || lastRanged.isFrameName()) {
1230 MessageBay
1231 .displayMessage("Insufficient permission to cut text");
1232 lastRanged.clearSelection();
1233 FrameGraphics.Repaint();
1234 return;
1235 }
1236 // if the entire text is selected and its not a line end then
1237 // pickup the item
1238 boolean entireText = lastRanged.getSelectionSize() == lastRanged
1239 .getLength();
1240 if (entireText && !lastRanged.isLineEnd()) {
1241 lastRanged.clearSelection();
1242 ranged.delete();
1243 middleButton(clickedOn, clickedIn, e.isShiftDown());
1244 return;
1245 } else {
1246 ranged.setText(lastRanged.cutSelectedText());
1247 ranged.setWidth(lastRanged.getWidth());
1248 // If its the whole text then replace last ranged with a dot
1249 if (entireText) {
1250 Item dot = FrameKeyboardActions.replaceText(lastRanged);
1251 dot.setHighlightMode(HighlightMode.None);
1252 }
1253 }
1254 // if the user is copying text from the item
1255 } else if (e.getButton() == MouseEvent.BUTTON3) {
1256 // Check if the user is trying to range an item for which they
1257 // do not have permission to do so... or it is the frame name
1258 if (!lastRanged.hasPermission(UserAppliedPermission.copy)) {
1259 MessageBay
1260 .displayMessage("Insufficient permission to copy text");
1261 lastRanged.clearSelection();
1262 FrameGraphics.Repaint();
1263 return;
1264 }
1265 ranged.setText(lastRanged.copySelectedText());
1266 }
1267
1268 ranged.setParent(null);
1269 ranged.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
1270 pickup(ranged);
1271 lastRanged.clearSelection();
1272 lastRanged.setHighlightMode(HighlightMode.None);
1273 refreshHighlights();
1274 FrameGraphics.refresh(false);
1275 return;
1276 }
1277
1278 // if the user is cropping an image
1279 if (clickedOn != null && clickedOn == _lastCropped) {
1280 if (_lastCropped.isCropTooSmall()) {
1281 _lastCropped = null;
1282 // FrameGraphics
1283 // .WarningMessage("Crop cancelled because it was below the
1284 // minimum size");
1285 } else {
1286 Picture cropped = _lastCropped.copy();
1287 cropped.setParent(null);
1288 // move the cropped image to the cursor
1289 int width = cropped.getWidth();
1290 int height = cropped.getHeight();
1291 if(cropped.getSource().getX() + width < MouseX) {
1292 cropped.getSource().setX(MouseX - width);
1293 }
1294 if(cropped.getSource().getY() + height < MouseY) {
1295 cropped.getSource().setY(MouseY - height);
1296 }
1297 pickup(cropped);
1298 // MIKE put the code below up here
1299 _lastCropped.clearCropping();
1300 FrameGraphics.changeHighlightMode(_lastCropped,
1301 HighlightMode.None);
1302 _lastCropped = null;
1303 FrameGraphics.Repaint();
1304 return;
1305 }
1306 }
1307
1308 assert (_lastCropped == null);
1309 // if the user has cropped an image, either the above happend or this is
1310 // a no-op MIKE says WHEN DO WE NEED THE CODE BELOW
1311 // if (_lastCropped != null && !_lastCropped.isCropTooSmall()) {
1312 // _lastCropped.clearCropping();
1313 // _lastCropped = null;
1314 // FrameGraphics.Repaint();
1315 // return;
1316 // }
1317
1318 // if the user is left-clicking
1319 if (e.getButton() == MouseEvent.BUTTON1) {
1320 SessionStats.AddFrameEvent("Lu");
1321 leftButton(clickedOn, clickedIn, e.isShiftDown(), e.isControlDown());
1322 return;
1323 }
1324
1325 if (e.getButton() == MouseEvent.BUTTON2) {
1326 SessionStats.AddFrameEvent("Mu");
1327 middleButton(clickedOn, clickedIn, e.isShiftDown());
1328 return;
1329 }
1330
1331 if (e.getButton() == MouseEvent.BUTTON3) {
1332 SessionStats.AddFrameEvent("Ru");
1333 rightButton(clickedOn, clickedIn);
1334 return;
1335 }
1336
1337 // error, we should have returned by now
1338 System.out.println("Error: mouseReleased should have returned by now. "
1339 + e);
1340 }
1341
1342 /**
1343 * This method handles all left-click actions
1344 */
1345 private void leftButton(Item clicked, Collection<Item> clickedIn,
1346 boolean isShiftDown, boolean isControlDown) {
1347
1348 // if the user is pointing at something then either follow the link or
1349 // do TDFC
1350 if (clicked == null) {
1351 // Check if the user is nearby another item...
1352 int mouseX = DisplayIO.getMouseX();
1353 int mouseY = FrameMouseActions.getY();
1354 // System.out.println(mouseX + "," + mouseY);
1355 for (Item i : DisplayIO.getCurrentFrame().getItems()) {
1356 if (i instanceof Text) {
1357 if (i.isNear(mouseX, mouseY)) {
1358 clicked = i;
1359 break;
1360 }
1361 }
1362 }
1363 }
1364
1365 if (clicked instanceof Text) {
1366 Text text = (Text) clicked;
1367 /* Dont follow link when just highlighting text with the left button */
1368 if (text.getText().length() == 0)
1369 clicked = null;
1370 else if (text.getSelectionSize() > 0) {
1371 return;
1372 }
1373 }
1374
1375 // If the user clicked into a widgets free space...
1376 if (clicked == null && _lastClickedIn != null
1377 && _lastClickedIn.size() >= 4) {
1378
1379 // Check to see if the use clicked into a widgets empty space
1380 InteractiveWidget iw = null;
1381
1382 for (Item i : _lastClickedIn) {
1383
1384 if (i instanceof WidgetCorner) {
1385 iw = ((WidgetCorner) i).getWidgetSource();
1386 break;
1387 } else if (i instanceof WidgetEdge) {
1388 iw = ((WidgetEdge) i).getWidgetSource();
1389 break;
1390 }
1391 }
1392
1393 if (iw != null) {
1394
1395 // Handle dropping items on widgets
1396 if(iw.ItemsLeftClickDropped()) {
1397 return;
1398 }
1399
1400 // Note: musten't directly use source for handling the link
1401 // because all link operations will by-pass the widgets special
1402 // handling with links...
1403 Item widgetLink = iw.getItems().get(0);
1404 assert (widgetLink != null);
1405 clicked = widgetLink;
1406 } else {
1407 for (Item i : _lastClickedIn) {
1408 /*
1409 * Find the first linked item or the first unlinked Dot This
1410 * code assumes that items are are ordered from top to
1411 * bottom. TODO make sure the list will always be ordered
1412 * correctly!!
1413 */
1414 if (i.hasLink() || i instanceof Dot) {
1415 clicked = i;
1416 break;
1417 }
1418 }
1419 }
1420
1421 }
1422
1423 if (clicked != null) {
1424 // check item permissions
1425 boolean hasLinkOrAction = clicked.hasLink() || clicked.hasAction();
1426
1427 if ((hasLinkOrAction && !clicked
1428 .hasPermission(UserAppliedPermission.followLinks))
1429 || (!hasLinkOrAction && !clicked
1430 .hasPermission(UserAppliedPermission.createFrames))) {
1431 Item editTarget = clicked.getEditTarget();
1432 if (editTarget != clicked) {
1433 if (editTarget.hasPermission(UserAppliedPermission.followLinks)) {
1434 clicked = editTarget;
1435 } else {
1436 MessageBay
1437 .displayMessage("Insufficient permission to perform action on item");
1438 return;
1439 }
1440 }
1441 }
1442
1443 Item clickedOn = clicked;
1444
1445 // actions take priority
1446 if (_lastMouseClick != null && !_lastMouseClick.isControlDown()
1447 && clickedOn.hasAction()) {
1448 IndirectMouseActions.getInstance().getExecuteActionAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1449 } else if (clickedOn.getLink() != null) {
1450 IndirectMouseActions.getInstance().getFollowLinkAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1451 // no link is found, perform TDFC
1452 } else {
1453 /*
1454 * if the user is clicking on the frame name then move to the
1455 * next or previous frame regardless of whether or not the frame
1456 * is protected
1457 */
1458 if (clickedOn.isFrameName()) {
1459 IndirectMouseActions.getInstance().getRespondToFrameNameClickAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1460 }
1461
1462 IndirectMouseActions.getInstance().getTDFCAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1463 }
1464
1465 } else {
1466 IndirectMouseActions.getInstance().getBackAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1467 }
1468 }
1469
1470 private static boolean doMerging(Item clicked) {
1471 if (clicked == null)
1472 return false;
1473
1474 // // Brook: widgets do not merge
1475 // if (clicked instanceof WidgetCorner)
1476 // return false;
1477 //
1478 // // Brook: widgets do not merge
1479 // if (clicked instanceof WidgetEdge)
1480 // return false;
1481
1482 // System.out.println(FreeItems.getInstance().size());
1483 if (isRubberBandingCorner()) {
1484 if (clicked.isLineEnd()
1485 || clicked.getAllConnected().contains(
1486 FreeItems.getItemAttachedToCursor())) {
1487 return true;
1488 }
1489 }
1490
1491 if (FreeItems.getInstance().size() > 2)
1492 return false;
1493
1494 Item attachedToCursor = FreeItems.getItemAttachedToCursor();
1495
1496 if (clicked instanceof Text
1497 && !(attachedToCursor instanceof Text || attachedToCursor
1498 .isLineEnd())) {
1499 return false;
1500 }
1501
1502 return true;
1503 }
1504
1505 public static void middleButton() {
1506 Item currentItem = FrameUtils.getCurrentItem();
1507 getInstance().middleButton(currentItem,
1508 FrameUtils.getCurrentItems(currentItem), false);
1509 updateCursor();
1510 }
1511
1512 public static void rightButton() {
1513 Item currentItem = FrameUtils.getCurrentItem();
1514 getInstance().rightButton(currentItem,
1515 FrameUtils.getCurrentItems(currentItem));
1516 updateCursor();
1517 }
1518
1519 public static void leftButton() {
1520 Item currentItem = FrameUtils.getCurrentItem();
1521 getInstance().leftButton(currentItem,
1522 FrameUtils.getCurrentItems(currentItem), false, false);
1523 updateCursor();
1524 }
1525
1526 /**
1527 * This method handles all middle-click actions
1528 */
1529 private void middleButton(Item clicked, Collection<Item> clickedIn,
1530 boolean isShiftDown) {
1531
1532 // If the user clicked into a widgets free space...
1533 if (clicked == null && _lastClickedIn != null
1534 && _lastClickedIn.size() >= 4) {
1535
1536 // Check to see if the use clicked into a widgets empty space
1537 InteractiveWidget iw = null;
1538
1539 for (Item i : _lastClickedIn) {
1540
1541 if (i instanceof WidgetCorner) {
1542 iw = ((WidgetCorner) i).getWidgetSource();
1543 break;
1544 } else if (i instanceof WidgetEdge) {
1545 iw = ((WidgetEdge) i).getWidgetSource();
1546 break;
1547 }
1548 }
1549
1550 if (iw != null) {
1551
1552 // Handle dropping items on widgets
1553 if(iw.ItemsMiddleClickDropped()) {
1554 return;
1555 }
1556 }
1557 }
1558 // if the cursor has Items attached
1559 if (FreeItems.itemsAttachedToCursor()) {
1560 // if the user is pointing at something, merge the items (if
1561 // possible)
1562 if (doMerging(clicked)) {
1563 IndirectMouseActions.getInstance().getDeleteItemsAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1564 } else {
1565 IndirectMouseActions.getInstance().getAnchorFreeItemsAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1566 }
1567 // otherwise if the user is pointing at something, pick it up unless shift is down
1568 } else if (clicked != null && !isShiftDown) {
1569
1570 // check permissions
1571 if (!clicked.hasPermission(UserAppliedPermission.full)) {
1572 Item editTarget = clicked.getEditTarget();
1573 if (editTarget != clicked
1574 && editTarget.hasPermission(UserAppliedPermission.full)) {
1575 clicked = editTarget;
1576 } else {
1577 MessageBay
1578 .displayMessage("Insufficient permission to pick up item");
1579 return;
1580 }
1581 }
1582 IndirectMouseActions.getInstance().getDetachLineAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1583 // if we're inside a shape, pick it up unless shift is down
1584 } else if (clickedIn != null && !isShiftDown) {
1585 IndirectMouseActions.getInstance().getGroupPickupAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1586 } else {
1587 IndirectMouseActions.getInstance().getNewLineAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1588 }
1589 SessionStats.MovedItems(FreeItems.getInstance());
1590 }
1591
1592 private static Item getFirstFreeLineEnd() {
1593 for (Item i : FreeItems.getInstance())
1594 if (i.isLineEnd())
1595 return i;
1596 return null;
1597 }
1598
1599 private static boolean isRubberBandingCorner() {
1600 return getShapeCorner(FreeItems.getInstance()) != null;
1601 }
1602
1603 /**
1604 * Gets the rectangle corner from the list of items that are part of a
1605 * rectangle.
1606 *
1607 * @param partialRectangle
1608 * a corner and its two connecting lines.
1609 * @return the rectangle corner or null if the list of items is not part of
1610 * a rectangle.
1611 */
1612 private static Item getShapeCorner(List<Item> partialRectangle) {
1613 if (partialRectangle.size() < 3)
1614 return null;
1615 Item lineEnd = null;
1616 // only one lineEnd will be present for rectangles
1617 // All other items must be lines
1618 for (Item i : partialRectangle) {
1619 if (i.isLineEnd()) {
1620 if (lineEnd == null) {
1621 lineEnd = i;
1622 } else {
1623 return null;
1624 }
1625 } else if (!(i instanceof Line)) {
1626 return null;
1627 }
1628 }
1629 // if this is at least the corner of two connected lines
1630 if (lineEnd != null && lineEnd.getAllConnected().size() >= 5)
1631 return lineEnd;
1632
1633 return null;
1634 }
1635
1636 /**
1637 * This method handles all right-click action
1638 */
1639 private void rightButton(Item clicked, Collection<Item> clickedIn) {
1640
1641 // If the user clicked into a widgets free space...
1642 if (clicked == null && _lastClickedIn != null
1643 && _lastClickedIn.size() >= 4) {
1644
1645 // Check to see if the use clicked into a widgets empty space
1646 InteractiveWidget iw = null;
1647
1648 for (Item i : _lastClickedIn) {
1649
1650 if (i instanceof WidgetCorner) {
1651 iw = ((WidgetCorner) i).getWidgetSource();
1652 break;
1653 } else if (i instanceof WidgetEdge) {
1654 iw = ((WidgetEdge) i).getWidgetSource();
1655 break;
1656 }
1657 }
1658
1659 if (iw != null) {
1660
1661 // Handle dropping items on widgets
1662 if(iw.ItemsRightClickDropped()) {
1663 return;
1664 }
1665 }
1666 }
1667
1668 // if the cursor has Items attached, then anchor a copy of them
1669
1670 List<Item> copies = null;
1671 if (FreeItems.itemsAttachedToCursor()) {
1672 if (FreeItems.getInstance().size() == 1
1673 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
1674 // Dont stamp if the user is painting... because we dont want to
1675 // save any of the items created!
1676 return;
1677 // if the user is clicking on something, merge the items
1678 // unless it is a point onto somethin other than a lineEnd or a
1679 // dot
1680 } else if (clicked != null
1681 // TODO Change the items merge methods so the logic is simplified
1682 && (!(FreeItems.getItemAttachedToCursor() instanceof Dot)
1683 || clicked instanceof Dot || clicked.isLineEnd())) {
1684 // check permissions
1685 if (!clicked.hasPermission(UserAppliedPermission.full)
1686 && clicked.getParent().getNameItem() != clicked) {
1687 MessageBay
1688 .displayMessage("Insufficient permission to merge items");
1689 return;
1690 }
1691 if (clicked instanceof Text || clicked instanceof Dot
1692 || clicked instanceof XRayable) {
1693 if (isRubberBandingCorner()) {
1694 copies = IndirectMouseActions.getInstance().getMergeGroupAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1695 // line onto something
1696 } else if (FreeItems.getInstance().size() == 2
1697 /* && clicked instanceof XRayable */) {
1698 copies = IndirectMouseActions.getInstance().getMergeTwoItemsAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1699 } else if (FreeItems.getInstance().size() == 1) {
1700 copies = IndirectMouseActions.getInstance().getMergeSingleItemAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1701 } else {
1702 copies = IndirectMouseActions.getInstance().getStampAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1703 }
1704 } else {
1705 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
1706 _controlDown);
1707 if (copies == null)
1708 copies = copy(FreeItems.getInstance());
1709 for (Item i : copies) {
1710 i.setOffset(0, 0);
1711 }
1712 anchor(FreeItems.getInstance());
1713 FreeItems.getInstance().clear();
1714 pickup(copies);
1715 }
1716 // otherwise, anchor the items
1717 } else {
1718 // check if this is anchoring a rectangle
1719 if (isRubberBandingCorner()) {
1720 copies = IndirectMouseActions.getInstance().getRubberBandingCornerAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1721 } else {
1722 if (rubberBanding()) {
1723 copies = IndirectMouseActions.getInstance().getRubberBandingCopyAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1724 } else if (_extrude) {
1725 copies = IndirectMouseActions.getInstance().getExtrudeAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1726 } else {
1727 stampItemsOnCursor(true);
1728 copies = FreeItems.getInstance();
1729 }
1730 }
1731 }
1732 } else {
1733 // if the user is pointing at something and shift isn't down, make a copy
1734 if (clicked != null && !isShiftDown()) {
1735 // check permissions
1736 if (clicked.isLineEnd()) {
1737 if (!clicked.hasPermission(UserAppliedPermission.full)) {
1738 MessageBay
1739 .displayMessage("Insufficient permission to unreel");
1740 return;
1741 }
1742 } else if (!clicked.hasPermission(UserAppliedPermission.copy)) {
1743 Item editTarget = clicked.getEditTarget();
1744 if (editTarget != clicked
1745 && editTarget.hasPermission(UserAppliedPermission.copy)) {
1746 clicked = editTarget;
1747 } else {
1748 MessageBay
1749 .displayMessage("Insufficient permission to copy");
1750 return;
1751 }
1752 }
1753
1754 copies = IndirectMouseActions.getInstance().getMakeCopyAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1755 } else {
1756 // if user is pointing in a closed shape and shift isn't down, make a copy of the items inside
1757 if (clickedIn != null && !isShiftDown()) {
1758 IndirectMouseActions.getInstance().getMakeGroupCopyAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1759 // otherwise, create a rectangle
1760 } else {
1761 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(),
1762 MouseX, MouseY, true);
1763 // if its on a line then create a line from that line
1764 if (on instanceof Line && on.hasPermission(UserAppliedPermission.full)) {
1765 IndirectMouseActions.getInstance().getExtendLineAction().exec(new MouseInfo(on, clickedIn, false, false));
1766 }
1767 copies = IndirectMouseActions.getInstance().getCreateRectangleAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1768 }
1769 }
1770 }
1771 getInstance().refreshHighlights();
1772 SessionStats.CopiedItems(copies);
1773 updateCursor();
1774 FrameGraphics.Repaint();
1775 }
1776
1777 /**
1778 *
1779 */
1780 private static void stampItemsOnCursor(boolean save) {
1781 List<Item> copies = copy(FreeItems.getInstance());
1782 // MIKE: what does the below 2 lines do?
1783 for (Item i : copies) {
1784 i.setOffset(0, 0);
1785 i.setSave(save);
1786 }
1787 // The below code has a little problem withflicker when stamp
1788 // and dragging
1789 move(FreeItems.getInstance());
1790 for (Item i : copies) {
1791 i.setHighlightMode(HighlightMode.None);
1792 }
1793 anchor(copies);
1794 }
1795
1796 /**
1797 * @param enclosedItems
1798 * @return
1799 */
1800 private static Collection<Item> getFullyEnclosedItems(
1801 Collection<Item> enclosure) {
1802 // copy the enclosedItems because the list will be modified
1803 Collection<Item> enclosedItems = new LinkedHashSet<Item>(enclosure);
1804 Collection<Item> toCopy = new LinkedHashSet<Item>(enclosedItems.size());
1805
1806 while (enclosedItems.size() > 0) {
1807 Item i = enclosedItems.iterator().next();
1808 if (i.hasPermission(UserAppliedPermission.copy)) {
1809 Collection<Item> items = i.getAllConnected();
1810 // Only copy if the entire shape is enclosed
1811 if (enclosedItems.containsAll(items)) {
1812 toCopy.addAll(items);
1813 }
1814 enclosedItems.removeAll(items);
1815 } else {
1816 enclosedItems.remove(i);
1817 }
1818 }
1819 return toCopy;
1820 }
1821
1822 /**
1823 * Marks the items as not belonging to any specific frame. When picking up
1824 * items the parent will be automatically cleared for items on the current
1825 * frame but not for overlay items. This method ensures that overlay items
1826 * will also be cleared. This is useful when picking up copies of items from
1827 * an overlay (with the right mouse button) to ensure that the copy will be
1828 * anchored on the current frame rather than the overlay. When items are
1829 * picked up with the middle button clearParent should NOT be called.
1830 *
1831 * @param items
1832 * to have their parent cleared
1833 */
1834 private static void clearParent(List<Item> items) {
1835 for (Item i : items) {
1836 // The next line is only necessary for circles...
1837 // Need to clean up/refactory some of this stuff
1838 i.getParentOrCurrentFrame().removeItem(i);
1839 i.setParent(null);
1840 }
1841 }
1842
1843 private static boolean inWindow = false;
1844 /**
1845 * event called when mouse exits window
1846 * (can't use MouseListener callback since that callback doesn't
1847 * correctly receive all mouse exit events)
1848 */
1849 public static void mouseExitedWindow(MouseEvent e) {
1850 inWindow = false;
1851 // System.out.println("Left window");
1852 if(FreeItems.itemsAttachedToCursor()) {
1853 boolean cut = true;
1854 for(Item i : FreeItems.getInstance()) {
1855 for(Item j : i.getAllConnected()) {
1856 if(!FreeItems.getInstance().contains(j)) {
1857 cut = false;
1858 break;
1859 }
1860 }
1861 }
1862 if(cut) {
1863 ItemSelection.cut();
1864 }
1865 }
1866 }
1867
1868 public void mouseEntered(MouseEvent e) {
1869 // check if we are entering the window from outside of the window, or if we were just over a widget
1870 if(!inWindow) {
1871 inWindow = true;
1872 // if there is expeditee data on the clipboard that has not yet been autoPasted, autoPaste it
1873 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1874 Transferable content = c.getContents(null);
1875 try {
1876 if(content.isDataFlavorSupported(ItemSelection.expDataFlavor)) { // Expeditee data
1877 ExpDataHandler expData = (ExpDataHandler)content.getTransferData(ItemSelection.expDataFlavor);
1878 if(expData.autoPaste) {
1879 List<Item> items = new ExpClipReader(FrameMouseActions.getX(), FrameMouseActions.getY()).read(expData.items);
1880 // generate new IDs and pickup
1881 FrameMouseActions.pickup(ItemUtils.CopyItems(items));
1882 // update the clipboard contents so they won't be autoPasted again
1883 expData.autoPaste = false;
1884 String stringData = "";
1885 Image imageData = null;
1886 if(content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
1887 stringData = (String) content.getTransferData(DataFlavor.stringFlavor);
1888 }
1889 if(content.isDataFlavorSupported(DataFlavor.imageFlavor)) {
1890 imageData = (Image) content.getTransferData(DataFlavor.imageFlavor);
1891 }
1892 c.setContents(new ItemSelection(stringData, imageData, expData), null);
1893 }
1894 }
1895 } catch(Exception ex) {
1896 ex.printStackTrace();
1897 }
1898 }
1899 }
1900
1901 public void mouseExited(MouseEvent e) {
1902 }
1903
1904 private boolean _overFrame;
1905 private int panStartX, panStartY;
1906 private boolean _isPanOp;
1907 public void mouseDragged(MouseEvent e) {
1908 _lastMouseDragged = e;
1909 // System.out.println("MouseDragged");
1910
1911 // Stop the longDepress mouse timer if the user drags above a threshold
1912 if (_MouseTimer.isRunning()) {
1913 if (Math.abs(e.getX() - _lastMouseClick.getX())
1914 + Math.abs(e.getY() - _lastMouseClick.getY()) > 10)
1915 _MouseTimer.stop();
1916 }
1917
1918 if (_autoStamp) {
1919 stampItemsOnCursor(false);
1920 }
1921
1922 /*
1923 * Have the free items follow the cursor if the user clicks in freespace
1924 * then moves.
1925 */
1926 if (FreeItems.getInstance().size() > 0 && _lastClickedOn == null) {
1927 mouseMoved(e);
1928 return;
1929 }
1930
1931 // panning the frame when dragging the mouse while shift-leftclicking
1932 if(ExperimentalFeatures.MousePan.get() && _overFrame && e.isShiftDown() &&
1933 (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0 &&
1934 (_isPanOp || (Math.max(Math.abs(panStartX - e.getX()), Math.abs(panStartY - e.getY())) > 5))) {
1935 int dX = (int) (e.getX() - MouseX);
1936 int dY = (int) (e.getY() - MouseY);
1937 Misc.pan(DisplayIO.getCurrentFrame(), dX, dY);
1938 MouseX = e.getX();
1939 MouseY = e.getY();
1940 _isPanOp = true;
1941 }
1942
1943 // check if user is dragging across a text item
1944 if (_lastRanged != null) {
1945 // System.out.println(MouseY - e.getY());
1946
1947 MouseX = e.getX();
1948 MouseY = e.getY();
1949
1950 int distance = _lastRanged.getY() - FrameMouseActions.getY();
1951 if (distance <= 0)
1952 distance = FrameMouseActions.getY() - _lastRanged.getY()
1953 - _lastRanged.getBoundsHeight();
1954
1955 if (distance > UserSettings.NoOpThreshold.get()) {
1956 _lastRanged.clearSelectionEnd();
1957 _isNoOp = true;
1958 } else {
1959 // update the ranged section
1960 _lastRanged.setSelectionEnd(DisplayIO.getMouseX(),
1961 FrameMouseActions.getY());
1962 _isNoOp = false;
1963 }
1964
1965 DisplayIO.setTextCursor(_lastRanged, Text.NONE, false, e
1966 .isShiftDown(), e.isControlDown(), false);
1967 FrameGraphics.Repaint();
1968 return;
1969 }
1970
1971 // if the user is dragging across a picture
1972 if (_lastCropped != null) {
1973 // If shift is down then the distance moved is the same in the x and
1974 // y
1975 MouseX = e.getX();
1976 MouseY = e.getY();
1977
1978 if (e.isControlDown()) {
1979 int deltaX = Math.abs(e.getX() - _lastMouseClick.getX());
1980 int deltaY = Math.abs(e.getY() - _lastMouseClick.getY());
1981 if (deltaX > deltaY) {
1982 MouseY = _lastMouseClick.getY() + deltaX
1983 * (e.getY() > _lastMouseClick.getY() ? 1 : -1);
1984 } else {
1985 MouseX = _lastMouseClick.getX() + deltaY
1986 * (e.getX() > _lastMouseClick.getX() ? 1 : -1);
1987 }
1988 }
1989 // update the ranged section
1990 _lastCropped.setEndCrop(DisplayIO.getMouseX(), FrameMouseActions
1991 .getY());
1992
1993 FrameGraphics.Repaint();
1994 return;
1995 }
1996
1997 /*
1998 * This is the context of a user clicking in freespace an dragging onto
1999 * the edge of a line
2000 */
2001 if ((_mouseDown == MouseEvent.BUTTON2 || _mouseDown == MouseEvent.BUTTON3)
2002 && _lastClickedOn == null && _lastClickedIn == null) {
2003 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), e.getX(),
2004 e.getY(), true);
2005
2006 if (FreeItems.getInstance().size() == 0) {
2007 // if the user can spot-weld, show the virtual spot
2008 if (on instanceof Line) {
2009 Line line = (Line) on;
2010 line.showVirtualSpot(e.getX(), e.getY());
2011 }
2012 if (on != null && on.isLineEnd()) {
2013 _lastHighlightedItem = on;
2014 on.setHighlightMode(Item.HighlightMode.Normal);
2015 } else if (_lastHighlightedItem != null) {
2016 _lastHighlightedItem
2017 .setHighlightMode(Item.HighlightMode.None);
2018 _lastHighlightedItem = null;
2019 }
2020 }
2021 }
2022
2023 // Use the below calculation for better speed. If it causes problems
2024 // switch back to the Euclidean distance calculation
2025 if (Math.abs(MouseX - e.getX()) > UserSettings.NoOpThreshold.get()
2026 || Math.abs(MouseY - e.getY()) > UserSettings.NoOpThreshold.get())
2027 _isNoOp = true;
2028
2029 FrameGraphics.Repaint();
2030 }
2031
2032 private static MouseEvent _lastMouseMoved = null;
2033
2034 private static Integer LastRobotX = null;
2035
2036 private static Integer LastRobotY = null;
2037
2038 // For some reason... sometimes the mouse move gets lost when moving the
2039 // mouse really quickly after clicking...
2040 // Use this timer to make sure it gets reset eventually if the Robot
2041 // generated event never arrives.
2042 private static Timer _RobotTimer = new Timer(200, new ActionListener() {
2043 public void actionPerformed(ActionEvent ae) {
2044 _RobotTimer.stop();
2045 LastRobotX = null;
2046 LastRobotY = null;
2047 // System.out.println("RobotTimer");
2048 }
2049 });
2050
2051 private static Timer _autoStampTimer = new Timer(200, new ActionListener() {
2052 public void actionPerformed(ActionEvent ae) {
2053 stampItemsOnCursor(false);
2054 }
2055 });
2056
2057 private static boolean _controlDown;
2058
2059 private static boolean _shiftDown;
2060
2061 private static OnNewFrameAction _onFrameAction = null;
2062
2063 public static void setTDFCAction(OnNewFrameAction action) {
2064 _onFrameAction = action;
2065 }
2066
2067 public static boolean isControlDown() {
2068 return _controlDown;
2069 }
2070
2071 public static boolean isShiftDown() {
2072 return _shiftDown;
2073 }
2074
2075 public static void setLastRobotMove(float x, float y) {
2076 // Make sure the system is in the right state while waiting for the
2077 // Robots event to arrive.
2078 MouseX = x;
2079 MouseY = y;
2080 // System.out.println("MouseMoved: " + MouseX + "," + MouseY + " " +
2081 // System.currentTimeMillis());
2082 LastRobotX = Math.round(x);
2083 LastRobotY = Math.round(y);
2084 _RobotTimer.start();
2085 }
2086
2087 public static boolean isWaitingForRobot() {
2088 return LastRobotX != null;
2089 }
2090
2091 /**
2092 * Updates the stored mouse position and highlights any items as necessary.
2093 */
2094 public void mouseMoved(MouseEvent e) {
2095 mouseMoved(e, false);
2096 }
2097
2098 private void mouseMoved(MouseEvent e, boolean shiftStateChanged) {
2099 // System.out.println("mouseMoved");
2100 // System.out.println(_context);
2101 if (_context == CONTEXT_FREESPACE)
2102 FrameKeyboardActions.resetEnclosedItems();
2103 // System.out.println(e.getX() + "," + e.getY() + " " + e.getWhen());
2104 if (LastRobotX != null) {
2105 // Wait until the last Robot mouse move event arrives before
2106 // processing other events
2107 if (/* FreeItems.getInstance().size() == 0 || */
2108 (LastRobotX == e.getX() && LastRobotY == e.getY())) {
2109 LastRobotX = null;
2110 LastRobotY = null;
2111 _RobotTimer.stop();
2112 } else {
2113 // System.out.println("Ignored: " +
2114 // FreeItems.getInstance().size());
2115 return;
2116 }
2117 }
2118
2119 MouseX = e.getX();
2120 MouseY = e.getY();
2121
2122 // System.out.println(MouseX + "," + MouseY);
2123
2124 // Moving the mouse a certain distance removes the last edited text if
2125 // it is empty
2126 Text lastEdited = FrameUtils.getLastEdited();
2127 if (lastEdited != null && lastEdited.getText().length() == 0
2128 && lastEdited.getPosition().distance(e.getPoint()) > 20) {
2129 FrameUtils.setLastEdited(null);
2130 }
2131
2132 // If shift is down then the movement is constrained
2133 if (_controlDown && FreeItems.getInstance().size() > 0) {
2134 // Check if we are rubber banding a line
2135 if (shiftStateChanged && rubberBanding()) {
2136 // Get the line end that is being rubber banded
2137 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems
2138 .getInstance().get(0)
2139 : FreeItems.getInstance().get(1);
2140 Line line = (Line) (FreeItems.getInstance().get(0).isLineEnd() ? FreeItems
2141 .getInstance().get(1)
2142 : FreeItems.getInstance().get(0));
2143 Item otherEnd = line.getOppositeEnd(thisEnd);
2144 int deltaX = Math.abs(e.getX() - otherEnd.getX());
2145 int deltaY = Math.abs(e.getY() - otherEnd.getY());
2146 // Check if its a vertical line
2147 if (deltaX < deltaY / 2) {
2148 // otherEnd.setX(thisEnd.getX());
2149 // MouseX = otherEnd.getX();
2150 if (shiftStateChanged) {
2151 new Constraint(thisEnd, otherEnd, thisEnd
2152 .getParentOrCurrentFrame().getNextItemID(),
2153 Constraint.VERTICAL);
2154 }
2155 }
2156 // Check if its horizontal
2157 else if (deltaY <= deltaX / 2) {
2158 // MouseY = otherEnd.getY();
2159 // otherEnd.setY(thisEnd.getY());
2160 if (shiftStateChanged) {
2161 new Constraint(thisEnd, otherEnd, thisEnd
2162 .getParentOrCurrentFrame().getNextItemID(),
2163 Constraint.HORIZONTAL);
2164 }
2165 } else {
2166 // Add DIAGONAL constraints
2167 // if (deltaX > deltaY) {
2168 // otherEnd.setY(thisEnd.getY() + deltaX
2169 // * (e.getY() < otherEnd.getY() ? 1 : -1));
2170 // } else {
2171 // otherEnd.setX(thisEnd.getX() + deltaY
2172 // * (e.getX() < otherEnd.getX() ? 1 : -1));
2173 // }
2174 if (shiftStateChanged) {
2175 int constraint = Constraint.DIAGONAL_NEG;
2176 // Check if the slope is positive
2177 if ((thisEnd.getY() - otherEnd.getY())
2178 / (double) (thisEnd.getX() - otherEnd.getX()) > 0.0) {
2179 constraint = Constraint.DIAGONAL_POS;
2180 }
2181
2182 new Constraint(thisEnd, otherEnd, thisEnd
2183 .getParentOrCurrentFrame().getNextItemID(),
2184 constraint);
2185 }
2186 }
2187 }// If its a lineend attached to two lines lengthen the shorter
2188 // so it is the same length as the longer line
2189 else if (FreeItems.getInstance().size() == 3) {
2190 // check if we are rubber banding the corner of a shape
2191 Item thisEnd = getShapeCorner(FreeItems.getInstance());
2192 if (thisEnd != null) {
2193 Line line1 = thisEnd.getLines().get(0);
2194 Line line2 = thisEnd.getLines().get(1);
2195 // Check if the two lines are constrained and hence it is a
2196 // rectangle
2197 Integer c1 = line1.getPossibleConstraint();
2198 Integer c2 = line2.getPossibleConstraint();
2199
2200 if (c1 != null && c2 != null) {
2201 // This is the case of a constrained rectangle
2202 if ((c2 == Constraint.VERTICAL || c2 == Constraint.HORIZONTAL)
2203 && (c1 == Constraint.VERTICAL || c1 == Constraint.HORIZONTAL)
2204 && (c1 != c2)) {
2205 Line vLine = line2;
2206 Line hLine = line1;
2207 if (c1 == Constraint.VERTICAL) {
2208 vLine = line1;
2209 hLine = line2;
2210 }
2211 Item hOtherEnd = hLine.getOppositeEnd(thisEnd);
2212 Item vOtherEnd = vLine.getOppositeEnd(thisEnd);
2213
2214 double vLength = Math
2215 .abs(vOtherEnd.getY() - MouseY);
2216 double hLength = Math
2217 .abs(hOtherEnd.getX() - MouseX);
2218
2219 if (vLength > hLength) {
2220 MouseX = Math.round(hOtherEnd.getX() + vLength
2221 * (MouseX > hOtherEnd.getX() ? 1 : -1));
2222 } else /* if (hLength > vLength) */{
2223 MouseY = Math.round(vOtherEnd.getY() + hLength
2224 * (MouseY > vOtherEnd.getY() ? 1 : -1));
2225 }
2226 }
2227 // } else if (c2 != null) {
2228 //
2229 // } // Other wise it is a not constrained shape so
2230 // constrain
2231 // the two lines lengths to be equal
2232 } else {
2233 Item lineEnd1 = line1.getOppositeEnd(thisEnd);
2234 Item lineEnd2 = line2.getOppositeEnd(thisEnd);
2235 double l1 = Line.getLength(lineEnd1.getPosition(), e
2236 .getPoint());
2237 double l2 = Line.getLength(lineEnd2.getPosition(), e
2238 .getPoint());
2239 double l3 = Line.getLength(lineEnd1.getPosition(),
2240 lineEnd2.getPosition());
2241 // l1 needs to be the shorter end
2242 if (l1 > l2) {
2243 Item temp = lineEnd1;
2244 lineEnd1 = lineEnd2;
2245 lineEnd2 = temp;
2246 double tempL = l1;
2247 l1 = l2;
2248 l2 = tempL;
2249 }
2250 // Now use the cosine rule to calculate the angle
2251 // between l1 and l3
2252 double cosTheta = (l1 * l1 + l3 * l3 - l2 * l2)
2253 / (2 * l1 * l3);
2254 // now calculate the new length for the lines using cos
2255 // rule
2256 double l_new = l3 / (2 * cosTheta);
2257 double ratio = l_new / l1;
2258 MouseX = Math.round((e.getX() - lineEnd1.getX())
2259 * ratio)
2260 + lineEnd1.getX();
2261 MouseY = Math.round((e.getY() - lineEnd1.getY())
2262 * ratio)
2263 + lineEnd1.getY();
2264
2265 }
2266 }
2267 }
2268 } else if (shiftStateChanged && !_controlDown && rubberBanding()) {
2269 // Get the line end that is being rubber banded
2270 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems
2271 .getInstance().get(0)
2272 : FreeItems.getInstance().get(1);
2273 thisEnd.removeAllConstraints();
2274 }
2275
2276 if (_lastMouseMoved == null)
2277 _lastMouseMoved = e;
2278
2279 _lastMouseMoved = e;
2280
2281 refreshHighlights();
2282
2283 if (FreeItems.hasCursor()) {
2284 move(FreeItems.getCursor(), true);
2285 }
2286
2287 if (FreeItems.itemsAttachedToCursor()) {
2288 move(FreeItems.getInstance());
2289 // System.out.println(FreeItems.getInstance().size());
2290 }
2291
2292 if (_forceArrowCursor)
2293 updateCursor();
2294
2295 _forceArrowCursor = true;
2296 }
2297
2298 public void refreshHighlights() {
2299 // ByMike: Get the item the mouse is hovering over
2300 Item click = FrameUtils.getCurrentItem();
2301 Item on = null;
2302 // System.out.println(click);
2303 if (click != null) {
2304 on = click;
2305 // set the context
2306 if (on instanceof Line)
2307 _context = CONTEXT_AT_LINE;
2308 else if (on instanceof Dot)
2309 _context = CONTEXT_AT_DOT;
2310 else if (on instanceof Text) {
2311 _context = CONTEXT_AT_TEXT;
2312 }
2313 if (FreeItems.getInstance().size() > 0)
2314 _alpha = 60;
2315 else
2316 _alpha = -1;
2317 } else {
2318 _context = CONTEXT_FREESPACE;
2319 _alpha = -1;
2320 }
2321
2322 // if the user is pointing at an item, highlight it
2323 if (on != null && !FreeItems.getInstance().contains(on)) {
2324 // if the user can spot-weld, show the virtual spot
2325 if (FreeItems.getInstance().size() == 2 && on instanceof Line) {
2326 Line line = (Line) on;
2327 Item freeItem0 = FreeItems.getInstance().get(0);
2328 Item freeItem1 = FreeItems.getInstance().get(1);
2329 Item lineEnd = freeItem0.isLineEnd() ? freeItem0 : (freeItem1
2330 .isLineEnd() ? freeItem1 : null);
2331 if (lineEnd != null) {
2332 if (_mouseDown == 0)
2333 line.showVirtualSpot(lineEnd, DisplayIO.getMouseX(),
2334 FrameMouseActions.getY());
2335 } else
2336 // The user is pointing at another point or text item
2337 // etc
2338 FrameGraphics.changeHighlightMode(on,
2339 Item.HighlightMode.Normal);
2340 } else {
2341 // FrameGraphics.ChangeSelectionMode(on,
2342 // Item.SelectedMode.Connected);
2343 // TODO: The method below is for the most part redundant
2344 on = FrameGraphics.Highlight(on.getEditTarget());
2345 }
2346 // if the last item highlighted is still highlighted, clear it
2347 if (_lastHoldsHighlight) {
2348 _lastHoldsHighlight = false;
2349 for (Item i : DisplayIO.getCurrentFrame().getItems())
2350 if (i.isHighlighted() && i != on)
2351 FrameGraphics.changeHighlightMode(i,
2352 Item.HighlightMode.None);
2353 }
2354
2355 // if the user is not pointing at an item, check for enclosure
2356 // highlighting
2357 } else if (on == null) {
2358 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds();
2359 if (enclosure != null && enclosure.size() > 0) {
2360 Item firstLineEnd = enclosure.iterator().next();
2361 HighlightMode hm;
2362 if(isShiftDown()) {
2363 hm = HighlightMode.Connected;
2364 } else {
2365 hm = HighlightMode.Enclosed;
2366 }
2367 if (firstLineEnd.getLines().size() > 1 &&
2368 // check that the enclosure is not part of a point being
2369 // dragged in space
2370 !ContainsOneOf(enclosure, FreeItems.getInstance())) {
2371 on = firstLineEnd.getLines().get(0);
2372 // System.out.println(on == null ? "Null" :
2373 // on.toString());
2374 FrameGraphics.changeHighlightMode(on, hm);
2375 } else if (firstLineEnd instanceof XRayable) {
2376 on = firstLineEnd;
2377 FrameGraphics.changeHighlightMode(firstLineEnd, hm);
2378 }
2379 _context = CONTEXT_AT_ENCLOSURE;
2380 } else if (_lastHighlightedItem != null) {
2381 // System.out.println("LastHighlightedItem");
2382 _lastHoldsHighlight = false;
2383 }
2384 }
2385
2386 // disable cursor changes when the cursor has items attached
2387 if (FreeItems.itemsAttachedToCursor()
2388 && DisplayIO.getCursor() != Item.TEXT_CURSOR)
2389 _forceArrowCursor = false;
2390
2391 // setLastHighlightedItem(on);
2392
2393 if (_lastHighlightedItem != null && _lastHighlightedItem != on
2394 && !_lastHoldsHighlight) {
2395 // Turn off the highlighting only if
2396 // the last highlighted item is not connected to the currentItem
2397 // Otherwise we get flickering in transition from connected to
2398 // normal mode while moving the cursor along a line.
2399 if (on == null
2400 || (!on.getAllConnected().contains(_lastHighlightedItem))) {
2401 FrameGraphics.changeHighlightMode(_lastHighlightedItem,
2402 Item.HighlightMode.None);
2403 }
2404 }
2405
2406 _lastHighlightedItem = on;
2407
2408 }
2409
2410 private boolean ContainsOneOf(Collection<Item> enclosure,
2411 Collection<Item> freeItems) {
2412 if (freeItems == null)
2413 return false;
2414 for (Item i : freeItems) {
2415 if (enclosure.contains(i))
2416 return true;
2417 }
2418 return false;
2419 }
2420
2421 /**
2422 * Checks if lines are being rubber banded.
2423 *
2424 * @return true if the user is rubberBanding one or more lines
2425 */
2426 private static boolean rubberBanding() {
2427 if (FreeItems.getInstance().size() != 2) {
2428 return false;
2429 }
2430
2431 // if rubber-banding, there will be 1 lineend and the rest will be lines
2432 boolean foundLineEnd = false;
2433 for (Item i : FreeItems.getInstance()) {
2434 if (i.isLineEnd()) {
2435 if (foundLineEnd) {
2436 return false;
2437 }
2438 foundLineEnd = true;
2439 } else if (!(i instanceof Line) || !i.isVisible()) {
2440 return false;
2441 }
2442 }
2443 return true;
2444 }
2445
2446 /**
2447 * Updates the current mouse cursor to whatever it should be. i.e. Hidden
2448 * when rubber-banding lines, otherwise default (arrow)
2449 */
2450 public static void updateCursor() {
2451 if (rubberBanding()) {
2452 DisplayIO.setCursor(Item.HIDDEN_CURSOR);
2453 return;
2454 }
2455 // This is to make sure the TEXT_CURSOR doesnt get inadvertantly turned
2456 // off!
2457 Item on = FrameUtils.getCurrentItem();
2458 if (on != null && on instanceof Text) {
2459 return;
2460 }
2461 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2462 }
2463
2464 public static void setHighlightHold(boolean hold) {
2465 _lastHoldsHighlight = hold;
2466 }
2467
2468 public static void resetOffset() {
2469 if (FreeItems.itemsAttachedToCursor()) {
2470 _offX = DisplayIO.getMouseX()
2471 - FreeItems.getInstance().get(0).getX()
2472 + FreeItems.getInstance().get(0).getOffset().x;
2473 _offY = getY() - FreeItems.getInstance().get(0).getY()
2474 + FreeItems.getInstance().get(0).getOffset().y;
2475 }
2476 }
2477
2478 /**
2479 * Moves the items to the current mouse position (plus the current offset)
2480 *
2481 * @param toMove
2482 */
2483 static void move(Collection<Item> toMove) {
2484 move(toMove, false);
2485 }
2486
2487 static void move(Collection<Item> toMove, boolean cursor) {
2488
2489 // Gets the origin of the first item to move
2490 int xPos = (DisplayIO.getMouseX() - (cursor ? 0 : _offX));
2491
2492 Item firstDot = toMove.iterator().next();
2493
2494 int deltax = firstDot.getX() - xPos;
2495 int deltay = firstDot.getY() - (getY() - (cursor ? 0 : _offY));
2496
2497 for (Item move : toMove) {
2498 move.setPosition(move.getX() - deltax, move.getY() - deltay);
2499
2500 if (!cursor && move instanceof Text) {
2501 ((Text) move).setAlpha(_alpha);
2502 }
2503 }
2504
2505 FrameGraphics.requestRefresh(true);
2506 // FrameGraphics.refresh(true);
2507 }
2508
2509 private static void load(String toLoad, boolean addToHistory) {
2510 if (FrameIO.isValidFrameName(toLoad)) {
2511 DisplayIO.clearBackedUpFrames();
2512 FrameUtils.DisplayFrame(toLoad, addToHistory, true);
2513 } else {
2514 MessageBay.errorMessage(toLoad + " is not a valid frame name.");
2515 }
2516 }
2517
2518 private static void back() {
2519 DisplayIO.Back();
2520
2521 // repaint things if necessary
2522 if (FreeItems.itemsAttachedToCursor())
2523 move(FreeItems.getInstance());
2524
2525 if (FreeItems.hasCursor())
2526 move(FreeItems.getCursor(), true);
2527 }
2528
2529 private static void forward() {
2530 DisplayIO.Forward();
2531
2532 // repaint things if necessary
2533 if (FreeItems.itemsAttachedToCursor())
2534 move(FreeItems.getInstance());
2535
2536 if (FreeItems.hasCursor())
2537 move(FreeItems.getCursor(), true);
2538 }
2539
2540 /**
2541 * Returns true if the mouse moved during TDFC. This will happen if there is
2542 * a start annotation item on the frame.
2543 *
2544 * @param linker
2545 * @return
2546 */
2547 public static boolean tdfc(Item linker) throws RuntimeException {
2548 // if this is a non-usable item
2549 if (linker.getID() < 0)
2550 return false;
2551
2552 // Check if its an image that can be resized to fit a box
2553 // around it
2554 String text = linker.getText();
2555 boolean isVector = text.equals("@v") || text.equals("@av");
2556 boolean isFrameImage = text.equals("@f");
2557 boolean isBitmap = false; // text.equals("@b");
2558
2559 if (isVector || isFrameImage || isBitmap) {
2560 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds(linker
2561 .getPosition());
2562 if (enclosure != null) {
2563 for (Item i : enclosure) {
2564 if (i.isLineEnd() && i.isEnclosed()) {
2565 if (!isVector)
2566 DisplayIO.getCurrentFrame().removeAllItems(
2567 enclosure);
2568 Rectangle rect = i.getEnclosedRectangle();
2569 long width = Math.round(rect.getWidth());
2570 if (isVector) {
2571 NumberFormat nf = Vector.getNumberFormatter();
2572 linker.setText(linker.getText()
2573 + ": "
2574 + nf.format((width / FrameGraphics
2575 .getMaxFrameSize().getWidth())));
2576 } else {
2577 linker.setText(linker.getText() + ": " + width);
2578 }
2579
2580 linker.setPosition(new Point(rect.x, rect.y));
2581 linker.setThickness(i.getThickness());
2582 linker.setBorderColor(i.getColor());
2583 break;
2584 }
2585 }
2586 if (!isVector)
2587 FrameMouseActions.deleteItems(enclosure, false);
2588 }
2589 }
2590
2591 boolean mouseMoved;
2592
2593 linker.getParent().setChanged(true);
2594
2595 Frame next = FrameIO.CreateNewFrame(linker, _onFrameAction);
2596
2597 linker.setLink("" + next.getNumber());
2598
2599 for (Item i : next.getTextItems()) {
2600 // Set the link for @Parent annotation item if one
2601 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT)
2602 && i.getLink() == null) {
2603 Frame parent = linker.getParentOrCurrentFrame();
2604 i.setLink(parent.getName());
2605 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_BACKUP, false)) {
2606 // Delink backup tag if it is on the frame
2607 i.setLink(null);
2608 }
2609 }
2610
2611 FrameUtils.DisplayFrame(next, true, true);
2612 FrameUtils.setTdfcItem(linker);
2613
2614 mouseMoved = next.moveMouseToDefaultLocation();
2615 // this needs to be done if the user doesnt move the mouse before doing
2616 // tdfc while the cursor is set to the text cursor
2617 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2618 // This needs to be done in case there was a @start on the frame which
2619 // triggers changed to be set to true when it should stay as false
2620 next.setChanged(false);
2621 return mouseMoved;
2622 }
2623
2624 /**
2625 * Creates a new Text item and fills it with particular attributes extracted
2626 * from the given Item. Note: Users always have permission to extract
2627 * attributes, so it is not checked.
2628 *
2629 * @param toExtract
2630 * Item containing the Item to extract the attributes from.
2631 */
2632 private static void extractAttributes(Item toExtract) {
2633 if (toExtract == null || toExtract == null)
2634 return;
2635
2636 if (FreeItems.itemsAttachedToCursor())
2637 return;
2638
2639 Item attribs;
2640 Item item = toExtract;
2641 // Extract the frames attributes when the user clicks on the frame name
2642 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
2643 if (item.isFrameName())
2644 attribs = AttributeUtils.extractAttributes(item.getParent());
2645 else {
2646 attribs = AttributeUtils.extractAttributes(item);
2647 }
2648
2649 if (attribs == null)
2650 MessageBay
2651 .displayMessage("All attributes of that item are default values.");
2652 else {
2653 // Give the attribute text item the color of the item for which
2654 // attributes are being extracted.
2655 // attribs.setColor(item.getColor());
2656 pickup(attribs);
2657 }
2658 }
2659
2660 public static void delete(Item toDelete) {
2661 boolean bRecalculate = false;
2662
2663 FrameUtils.setLastEdited(null);
2664 _offX = _offY = 0;
2665
2666 Frame currentFrame = DisplayIO.getCurrentFrame();
2667 // check if the user is pointing at the frame's framename
2668 if (toDelete != null && toDelete == currentFrame.getNameItem()) {
2669 currentFrame.clear(false);
2670 FrameGraphics.Repaint();
2671 return;
2672 }
2673
2674 // if the user is deleting items attached to the cursor
2675 if (FreeItems.itemsAttachedToCursor()) {
2676 // if this is an item-swap
2677 if (FreeItems.getInstance().size() == 1
2678 && FreeItems.getInstance().get(0) instanceof Text
2679 && toDelete != null && toDelete instanceof Text) {
2680
2681 // check permissions
2682 if (!toDelete.hasPermission(UserAppliedPermission.full)) {
2683 MessageBay
2684 .displayMessage("Insufficient permission to swap Item text");
2685 return;
2686 }
2687 Text anchored = (Text) toDelete;
2688 Text free = (Text) FreeItems.getInstance().get(0);
2689 SessionStats.DeletedItem(free);
2690 // List<String> temp = anchored.getText();
2691 anchored.setText(free.getText());
2692 anchored.setFormula(free.getFormula());
2693
2694 // free.setTextList(temp);
2695 FreeItems.getInstance().clear();
2696
2697 anchored.getParent().setChanged(true);
2698
2699 bRecalculate |= free.recalculateWhenChanged();
2700 bRecalculate |= anchored.recalculateWhenChanged();
2701
2702 // update the offset since the text has changed
2703 _offX = DisplayIO.getMouseX() - anchored.getX()
2704 + anchored.getOffset().x;
2705 _offY = getY() - anchored.getY() + anchored.getOffset().y;
2706 } else {
2707 // if shift is pressed delete the entire shape attached to the dot
2708 if(isShiftDown()) {
2709 List<Item> tmp = new ArrayList<Item>(FreeItems.getInstance());
2710 for(Item i : tmp) {
2711 // remove entire rectangles instead of just the corner
2712 if(i instanceof Dot) {
2713 FreeItems.getInstance().addAll(i.getAllConnected());
2714 for(Item j : i.getAllConnected()) {
2715 if(j instanceof Dot) {
2716 FreeItems.getInstance().addAll(j.getLines());
2717 }
2718 }
2719 }
2720 }
2721 }
2722 deleteItems(FreeItems.getInstance());
2723 }
2724 // reset the mouse cursor
2725 updateCursor();
2726 // the user is not pointing at an item
2727 } else if (toDelete == null) {
2728
2729 // if the user is pointing inside a closed shape, delete it
2730
2731 Collection<Item> items = null;
2732 // if shift is down, only delete the enclosing shape (ignore the items inside)
2733 if(isShiftDown()) {
2734 Collection<Item> tmp = FrameUtils.getEnclosingLineEnds();
2735 if(tmp != null) {
2736 items = new ArrayList<Item>();
2737 items.addAll(tmp);
2738 for(Item i : tmp) {
2739 if(i instanceof Dot) {
2740 items.addAll(((Dot)i).getLines());
2741 }
2742 }
2743 }
2744 } else {
2745 items = FrameUtils.getCurrentItems(null);
2746 }
2747
2748 if (items != null) {
2749 Collection<Item> toRemove = new LinkedHashSet<Item>(items
2750 .size());
2751 for (Item ip : items) {
2752 if (ip.hasPermission(UserAppliedPermission.full)) {
2753 // Only include lines if one of their enpoints are also
2754 // being removed
2755 if (ip instanceof Line) {
2756 Line l = (Line) ip;
2757 Item end = l.getEndItem();
2758 Item start = l.getStartItem();
2759
2760 // If one end of a line is being delted, remove the
2761 // other end if all its connecting lines are being
2762 // delted
2763 if (items.contains(end)) {
2764 if (!items.contains(start)
2765 && items.containsAll(start.getLines())) {
2766 toRemove.add(start);
2767 }
2768 } else if (items.contains(start)) {
2769 if (items.containsAll(end.getLines())) {
2770 toRemove.add(end);
2771 }
2772 } else {
2773 continue;
2774 }
2775 }
2776 toRemove.add(ip);
2777 }
2778 }
2779
2780 deleteItems(toRemove);
2781
2782 // reset the mouse cursor
2783 updateCursor();
2784 FrameGraphics.Repaint();
2785
2786 // otherwise this is an undo command
2787 } else {
2788 if(isControlDown()) {
2789 DisplayIO.getCurrentFrame().redo();
2790 } else {
2791 DisplayIO.getCurrentFrame().undo();
2792 }
2793 }
2794 return;
2795 // this is a delete command
2796 } else {
2797 // check permissions
2798 if (!toDelete.hasPermission(UserAppliedPermission.full)) {
2799 Item editTarget = toDelete.getEditTarget();
2800 if (editTarget != toDelete
2801 && editTarget.hasPermission(UserAppliedPermission.full)) {
2802 toDelete = editTarget;
2803 } else {
2804 MessageBay
2805 .displayMessage("Insufficient permission to delete item");
2806 return;
2807 }
2808 }
2809
2810 Frame parent = toDelete.getParent();
2811 if (parent != null) {
2812 parent.setChanged(true);
2813 }
2814 Collection<Item> toUndo = null;
2815 if (toDelete.isLineEnd()) {
2816 // delete the entire connected shape if shift is down
2817 if(isShiftDown()) {
2818 List<Item> tmp = new ArrayList<Item>();
2819 tmp.add(toDelete);
2820 // remove entire shape instead of just the corner
2821 tmp.addAll(toDelete.getAllConnected());
2822 for(Item j : toDelete.getAllConnected()) {
2823 if(j instanceof Dot) {
2824 tmp.addAll(j.getLines());
2825 }
2826 }
2827 deleteItems(tmp);
2828 return;
2829 } else {
2830 toUndo = deleteLineEnd(toDelete);
2831 }
2832 // delete the entire connected shape if shift is down, unless we're hovering the end of the line
2833 } else if (toDelete instanceof WidgetEdge) { // must notify
2834 // widgets that they
2835 // are being deleted
2836 ((WidgetEdge) toDelete).getWidgetSource().onDelete();
2837 toUndo = toDelete.getConnected();
2838 } else if (toDelete instanceof Line && isShiftDown() ||
2839 toDelete.getHighlightMode() == Item.HighlightMode.Disconnect) {
2840 Line line = (Line) toDelete;
2841 Item start = line.getStartItem();
2842 Item end = line.getEndItem();
2843 Collection<Item> delete = new LinkedList<Item>();
2844 delete.add(toDelete);
2845 if (end.getLines().size() == 1) {
2846 delete.add(end);
2847 } else {
2848 end.removeLine(line);
2849 }
2850 if (start.getLines().size() == 1) {
2851 delete.add(start);
2852 } else {
2853 start.removeLine(line);
2854 }
2855 toUndo = delete;
2856 } else {
2857 bRecalculate |= toDelete.recalculateWhenChanged();
2858 toUndo = toDelete.getConnected(); // copy(toDelete.getConnected());
2859 }
2860 SessionStats.DeletedItems(toUndo);
2861 if (parent != null) {
2862 parent.addToUndoDelete(toUndo);
2863 parent.removeAllItems(toUndo); // toDelete.getConnected()
2864 }
2865 // reset the mouse cursor
2866 updateCursor();
2867 if (parent != null)
2868 // ItemUtils.EnclosedCheck(parent.getItems());
2869 ItemUtils.Justify(parent);
2870 if (toDelete.hasOverlay()) {
2871 FrameUtils.Parse(parent, false, false);
2872 FrameGraphics.requestRefresh(false);
2873 }
2874
2875 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2876
2877 }
2878
2879 currentFrame.notifyObservers(bRecalculate);
2880
2881 FrameGraphics.Repaint();
2882 }
2883
2884 public static void deleteItems(Collection<Item> itemList) {
2885 deleteItems(itemList, true);
2886 }
2887
2888 public static void deleteItems(Collection<Item> itemList, boolean addToUndo) {
2889 boolean bReparse = false;
2890 boolean bRecalculate = false;
2891
2892 SessionStats.DeletedItems(itemList);
2893 List<Frame> modifiedFrames = new LinkedList<Frame>();
2894 // Get a list of all the modified frames
2895 for (Item i : itemList) {
2896 Frame parent = i.getParent();
2897 if (parent != null)
2898 modifiedFrames.add(parent);
2899 i.setHighlightMode(HighlightMode.None);
2900 bReparse |= i.hasOverlay();
2901 bRecalculate |= i.recalculateWhenChanged();
2902 }
2903 // If they are all free items then add the current frame
2904 if (modifiedFrames.size() == 0) {
2905 modifiedFrames.add(DisplayIO.getCurrentFrame());
2906 }
2907
2908 Collection<Item> toUndo = new LinkedHashSet<Item>();
2909 // disconnect any connected items
2910 for (Item i : itemList) {
2911
2912 // Only delete the current item if have not already deleted.
2913 // This is especially important for heavy duty widgets - so they
2914 // do not have to expire several times per delete.
2915 if (toUndo.contains(i))
2916 continue;
2917
2918 // Make sure text items attached to cursor are reset back to the
2919 // transparency they should have.
2920 if (i instanceof Text) {
2921 ((Text) i).setAlpha(-1);
2922 }
2923
2924 if (i.getLines().size() > 0) {
2925
2926 Collection<Item> toDelete = deleteLineEnd(i);
2927 if (addToUndo) {
2928 // add the copied items to the undo stack
2929 for (Item itemToUndo : toDelete) {
2930
2931 if (!toUndo.contains(itemToUndo))
2932 toUndo.add(itemToUndo);
2933
2934 }
2935 }
2936 } else if (!toUndo.contains(i)) {
2937 if (addToUndo)
2938 toUndo.add(i); // Why was is this a copy
2939 }
2940 }
2941
2942 for (Frame f : modifiedFrames) {
2943 f.removeAllItems(itemList);
2944 // ItemUtils.EnclosedCheck(f.getItems());
2945 ItemUtils.Justify(f);
2946 }
2947 // TODO: How should undelete deal with undo when items are removed from
2948 // the current frame as well as the overlay frame
2949 Frame currentFrame = DisplayIO.getCurrentFrame();
2950 currentFrame.addToUndoDelete(itemList);
2951 itemList.clear();
2952 if (bReparse) {
2953 FrameUtils.Parse(currentFrame, false, false);
2954 /*
2955 * TODO check if I need to recalculate even if reparse occurs, here
2956 * and in anchor, pickup etc
2957 */
2958 } else {
2959 currentFrame.notifyObservers(bRecalculate);
2960 }
2961
2962 }
2963
2964 private static Collection<Item> deleteLineEnd(Item lineEnd) {
2965
2966 if (lineEnd instanceof WidgetCorner) { // Brook
2967
2968 WidgetCorner wc = (WidgetCorner) lineEnd;
2969 Frame parent = wc.getWidgetSource().getParentFrame();
2970
2971 // Remove from the parent frame
2972 if (parent != null) {
2973 parent.removeAllItems(wc.getWidgetSource().getItems());
2974 }
2975
2976 wc.getWidgetSource().onDelete(); // Changes the widgets
2977 // corner/edges ID's...
2978
2979 return wc.getWidgetSource().getItems();
2980
2981 } else {
2982
2983 // // create a backup copy of the dot and its lines
2984 // List<Item> copy = copy(lineEnd.getConnected());
2985 //
2986 // // Remove lines from their anchored dots
2987 // // note: the line is kept so that it can be properly restored
2988 // for (Item ic : copy) {
2989 // if (ic instanceof Line) {
2990 // Line line = (Line) ic;
2991 // // Remove the line from the item that is not the copy of the
2992 // // line end being deletedF
2993 // if (!copy.contains(line.getStartItem()))
2994 // line.getStartItem().removeLine(line);
2995 // if (!copy.contains(line.getEndItem()))
2996 // line.getEndItem().removeLine(line);
2997 // }
2998 // }
2999
3000 Collection<Item> copy = lineEnd.getConnected();
3001
3002 // remove all lines being deleted
3003 for (Item ic : lineEnd.getConnected()) {
3004 if (ic instanceof Line
3005 && ((Line) ic).getOppositeEnd(lineEnd) != null) {
3006 Line line = (Line) ic;
3007
3008 // Invalidate the line to make sure we dont get any ghost
3009 // arrowheads.
3010 ic.invalidateAll();
3011
3012 Item d = line.getOppositeEnd(lineEnd);
3013 d.removeLine(line);
3014
3015 // if the dot was only part of one line, it can be
3016 // removed
3017 if (d.getLines().size() == 0) {
3018 if (d.getParent() != null)
3019 d.getParent().removeItem(d);
3020 if (!copy.contains(d))
3021 copy.add(d);
3022 }
3023
3024 if (lineEnd.getParent() != null)
3025 lineEnd.getParent().removeItem(ic);
3026 }
3027 }
3028 return copy;
3029 }
3030 }
3031
3032 private static void removeAllLinesExcept(Item from, Item but) {
3033 List<Line> lines = new LinkedList<Line>();
3034 lines.addAll(from.getLines());
3035 for (Line line : lines)
3036 if (line.getOppositeEnd(from) != but)
3037 from.removeLine(line);
3038
3039 List<Constraint> consts = new LinkedList<Constraint>();
3040 consts.addAll(from.getConstraints());
3041 for (Constraint c : consts)
3042 if (c.getOppositeEnd(from) != but)
3043 from.removeConstraint(c);
3044 }
3045
3046 public static Collection<Item> merge(List<Item> merger, Item mergee) {
3047 assert (mergee != null);
3048 if (mergee.isFrameName()) {
3049 return mergee.getParent().merge(merger);
3050 }
3051
3052 // if(mergee instanceof XRayable)
3053 // return merger;
3054
3055 // check for rectangle merging
3056 if (merger.size() == 3 && mergee.getLines().size() == 2) {
3057 Item corner = getShapeCorner(merger);
3058 // if this is a corner of a shape
3059 if (corner != null) {
3060 Collection<Item> allConnected = corner.getAllConnected();
3061 // Check if we are collapsing a rectangle
3062 if (allConnected.size() == 8 && allConnected.contains(mergee)) {
3063 DisplayIO.setCursorPosition(mergee.getPosition());
3064 DisplayIO.getCurrentFrame().removeAllItems(allConnected);
3065
3066 // find the point opposite corner...
3067 Item opposite = null;
3068 List<Line> lines = corner.getLines();
3069 for (Line l : lines) {
3070 allConnected.remove(l.getOppositeEnd(corner));
3071 }
3072 allConnected.remove(corner);
3073 for (Item i : allConnected) {
3074 if (i.isLineEnd()) {
3075 opposite = i;
3076 break;
3077 }
3078 }
3079 assert (opposite != null);
3080
3081 // check if the rectangle is small enough that it should be
3082 // collapsed to a single point
3083 int x1 = Math.abs(opposite.getX() - mergee.getX());
3084 int x2 = Math.abs(opposite.getY() - mergee.getY());
3085 int distance = (int) Math.sqrt(Math.pow(x1, 2)
3086 + Math.pow(x2, 2));
3087
3088 if (distance < RECTANGLE_TO_POINT_THRESHOLD) {
3089 mergee.removeAllConstraints();
3090 mergee.removeAllLines();
3091 mergee.setThickness(4 * mergee.getThickness());
3092 return mergee.getAllConnected();
3093 } else {
3094 removeAllLinesExcept(mergee, opposite);
3095 removeAllLinesExcept(opposite, mergee);
3096
3097 return mergee.getAllConnected();
3098 }
3099 }
3100 }
3101 }
3102
3103 List<Item> remain = new ArrayList<Item>();
3104 Item res = null;
3105
3106 for (Item i : merger) {
3107 if (!i.isVisible())
3108 continue;
3109 // check for link merging
3110 if (i instanceof Text
3111 && FrameIO.isValidFrameName((((Text) i).getFirstLine()))
3112 && FrameIO.canAccessFrame((((Text) i).getFirstLine()))) {
3113 // check that we can actually access the frame this link
3114 // corresponds to
3115 mergee.setLink(((Text) i).getFirstLine());
3116 } else {
3117 // check for attribute merging
3118 if (i instanceof Text && !i.isLineEnd()) {
3119 Text txt = (Text) i;
3120
3121 // if this is not an attribute merge
3122 if (!AttributeUtils.setAttribute(mergee, txt)) {
3123 // set mouse position for text merges
3124 if (mergee instanceof Text) {
3125 ((Text) mergee).insertText(txt.getText(), DisplayIO
3126 .getMouseX(), FrameMouseActions.getY());
3127 //Delete the item which had its text merged
3128 txt.delete();
3129 return remain;
3130 } else if (mergee instanceof WidgetCorner) {
3131 if (merger.size() == 1 && txt.getLink() != null) {
3132 // If the text item is linked then use that
3133 ((WidgetCorner) mergee).setLink(txt
3134 .getAbsoluteLink(), txt);
3135 } else {
3136 remain.addAll(merger);
3137 }
3138 return remain;
3139 } else if (mergee instanceof WidgetEdge) {
3140 if (merger.size() == 1 && txt.getLink() != null) {
3141 // If the text item is linked then use that
3142 ((WidgetEdge) mergee).setLink(txt
3143 .getAbsoluteLink(), txt);
3144 } else {
3145 remain.addAll(merger);
3146 }
3147 return remain;
3148 } else if (mergee instanceof Dot) {
3149 DisplayIO.setCursorPosition(mergee.getPosition());
3150 txt.setPosition(mergee.getPosition());
3151 txt.setThickness(mergee.getThickness());
3152 Frame parent = mergee.getParent();
3153 parent.removeItem(mergee);
3154 anchor(txt);
3155 // change the end points of the lines to the text
3156 // item
3157 while (mergee.getLines().size() > 0) {
3158 Line l = mergee.getLines().get(0);
3159 l.replaceLineEnd(mergee, txt);
3160 }
3161 break;
3162 }
3163
3164 // TODO tidy this up...
3165 // Dot override doesnt use the x and y coords
3166 // Text does... but could be removed
3167 res = mergee.merge(i, DisplayIO.getMouseX(), getY());
3168 if (res != null) {
3169 remain.add(res);
3170 }
3171 }
3172 } else {
3173 if (mergee.isLineEnd()) {
3174 DisplayIO.setCursorPosition(mergee.getPosition());
3175 }
3176 // Moving the cursor ensures shapes are anchored correctly
3177 res = mergee.merge(i, DisplayIO.getMouseX(), getY());
3178 if (res != null)
3179 remain.addAll(res.getConnected());
3180
3181 }
3182 }
3183 }
3184 updateCursor();
3185
3186 mergee.getParent().setChanged(true);
3187
3188 ItemUtils.EnclosedCheck(mergee.getParent().getItems());
3189 // Mike: Why does parse frame have to be called?!?
3190 FrameUtils.Parse(mergee.getParent());
3191
3192 return remain;
3193 }
3194
3195 /**
3196 * Picks up an item on a frame.
3197 *
3198 * @param toGrab
3199 * item to be picked up
3200 * @param removeItem
3201 * true if the item should be removed from the frame
3202 */
3203 public static void pickup(Item toGrab) {
3204 if (toGrab.isFrameName())
3205 return;
3206
3207 if (!toGrab.hasPermission(UserAppliedPermission.full)) {
3208 if (toGrab.getEditTarget() != toGrab) {
3209 pickup(toGrab.getEditTarget());
3210 } else {
3211 MessageBay
3212 .displayMessage("Insufficient permission pickup the item");
3213 }
3214 return;
3215 }
3216
3217 if (toGrab instanceof Circle)
3218 toGrab.setHighlightMode(HighlightMode.Connected);
3219 // Dont set the highlight mode if a vector is being picked up
3220 else if (toGrab.isVisible()) {
3221 toGrab.setHighlightMode(HighlightMode.Normal);
3222 }
3223
3224 // Brook: If the widget corner is being picked up. Instead refer to
3225 // picking up the edge for fixed-sized widgets so it is not so confusing
3226 if (toGrab instanceof WidgetCorner) {
3227 WidgetCorner wc = (WidgetCorner) toGrab;
3228 if (wc.getWidgetSource().isFixedSize()) {
3229 for (Item i : toGrab.getConnected()) {
3230 if (i instanceof WidgetEdge) {
3231 toGrab = i;
3232 break;
3233 }
3234 }
3235 }
3236 }
3237 pickup(toGrab.getConnected());
3238 }
3239
3240 public static void pickup(Collection<Item> toGrab) {
3241 if (toGrab == null || toGrab.size() == 0)
3242 return;
3243
3244 boolean bReparse = false;
3245 boolean bRecalculate = false;
3246
3247 Frame currentFrame = DisplayIO.getCurrentFrame();
3248 String currentFrameName = currentFrame.getName();
3249 Iterator<Item> iter = toGrab.iterator();
3250 while (iter.hasNext()) {
3251 Item i = iter.next();
3252 if (!i.hasPermission(UserAppliedPermission.full)) {
3253 iter.remove();
3254 continue;
3255 }
3256 if (i.equals(_lastHighlightedItem))
3257 _lastHighlightedItem = null;
3258
3259 bRecalculate |= i.recalculateWhenChanged();
3260 // i.setSelectedMode(SelectedMode.None);
3261 // Check if it has a relative link if so make it absolute
3262 i.setAbsoluteLink();
3263 // parent may be null
3264 if (i.getParent() != null) {
3265 i.getParent().removeItem(i);
3266 if (currentFrameName.equals(i.getParent().getName()))
3267 i.setParent(null);
3268 }
3269 FreeItems.getInstance().add(i);
3270 i.setFloating(true);
3271 // If its a vector pick up a copy of the stuff on the vector frame
3272 if (i.hasVector()) {
3273 bReparse = true;
3274
3275 Frame overlayFrame = FrameIO.LoadFrame(i.getAbsoluteLink());
3276 Collection<Item> copies = ItemUtils.CopyItems(overlayFrame
3277 .getNonAnnotationItems(false), i.getVector());
3278 for (Item copy : copies) {
3279 FreeItems.getInstance().add(copy);
3280 copy.setEditTarget(i);
3281 copy.setFloating(true);
3282 copy.setParent(null);
3283 // copy.setHighlightMode(HighlightMode.Connected);
3284 }
3285 }
3286 }
3287 currentFrame.change();
3288
3289 _lastHighlightedItem = null;
3290 updateCursor();
3291
3292 // if there are multiple items in the list, determine which to use for
3293 // offset calculations
3294 if (toGrab.size() > 1) {
3295 for (Item i : toGrab) {
3296 // MIKE: Movement goes haywire if these are removed because Line
3297 // class returns 0 for getX
3298 if (!(i instanceof Line) && !(i instanceof XRayable)) {
3299 _offX = DisplayIO.getMouseX() - i.getX() + i.getOffset().x;
3300 _offY = getY() - i.getY() + i.getOffset().y;
3301
3302 // make the offset item the first item in the list (so
3303 // move method knows which item to use)
3304 FreeItems.getInstance().set(
3305 FreeItems.getInstance().indexOf(i),
3306 FreeItems.getInstance().get(0));
3307 FreeItems.getInstance().set(0, i);
3308 break;
3309 }
3310 }
3311
3312 move(FreeItems.getInstance());
3313 ItemUtils.EnclosedCheck(toGrab);
3314 // otherwise, just use the first item
3315 } else if (toGrab.size() == 1) {
3316 Item soleItem = toGrab.iterator().next();
3317 _offX = DisplayIO.getMouseX() - soleItem.getX()
3318 + soleItem.getOffset().x;
3319 _offY = getY() - soleItem.getY() + soleItem.getOffset().y;
3320 // Now call move so that if we are on a message in the message box
3321 // It doesnt appear up the top of the scree!!
3322 move(toGrab);
3323 } else {
3324 MessageBay
3325 .displayMessage("Insufficient permission to pickup the items");
3326 }
3327 if (bReparse)
3328 FrameUtils.Parse(currentFrame, false, false);
3329 else
3330 currentFrame.notifyObservers(bRecalculate);
3331
3332 FrameGraphics.Repaint();
3333 }
3334
3335 private static Line createLine() {
3336 Frame current = DisplayIO.getCurrentFrame();
3337 // create the two endpoints
3338 Item end = DisplayIO.getCurrentFrame().createDot();
3339 Item start = DisplayIO.getCurrentFrame().createDot();
3340
3341 // create the Line
3342 Line line = new Line(start, end, current.getNextItemID());
3343 line.autoArrowheadLength();
3344
3345 // anchor the start
3346 anchor(start);
3347
3348 // attach the line to the cursor
3349 pickup(end);
3350 _lastHighlightedItem = null;
3351
3352 // TODO figure out how to get the end to highlight
3353 // end.setSelectedMode(SelectedMode.Normal);
3354 // end.setSelectedMode(SelectedMode.None);
3355
3356 return line;
3357 }
3358
3359 /**
3360 * Returns a list of copies of the list passed in
3361 *
3362 * @param toCopy
3363 * The list of items to copy
3364 * @return A List of copied Items
3365 */
3366 private static List<Item> copy(Collection<Item> toCopy) {
3367 return ItemUtils.CopyItems(toCopy);
3368 }
3369
3370 public static void anchor(Item toAnchor, boolean checkEnclosure) {
3371 // Only anchor items we have full permission over... i.e. don't anchor vector items
3372 if (!toAnchor.hasPermission(UserAppliedPermission.full))
3373 return;
3374
3375 toAnchor.anchor();
3376
3377 if (checkEnclosure) {
3378 ItemUtils.EnclosedCheck(toAnchor.getParentOrCurrentFrame()
3379 .getItems());
3380 FrameGraphics.Repaint();
3381 }
3382 }
3383
3384 public static void anchor(Item toAnchor) {
3385 anchor(toAnchor, true);
3386 }
3387
3388 public static void anchor(Collection<Item> toAnchor) {
3389 boolean bReparse = false;
3390 boolean bRecalculate = false;
3391 // Need to make sure we check enclosure for overlays etc
3392 Set<Frame> checkEnclosure = new HashSet<Frame>();
3393
3394 // Create a clone of toAnchor since in the proccess of anchoring items
3395 // they can change the state of the toAnchor collection and thus create
3396 // concurrent modification exceptions.
3397 // This is especially needed for widgets being removed when anchored:
3398 // since they
3399 // currently are composed of 8 items this is vital. In the new revision
3400 // of
3401 // widgets being implemented as a single item this this can be
3402 // depreciated
3403 // however it may be useful for other applications.
3404 Collection<Item> toAnchorCopy = new ArrayList<Item>(toAnchor);
3405
3406 for (Item i : toAnchorCopy) {
3407 if (toAnchor.contains(i)) { // since to anchor could change while
3408 // anchoring
3409 // if (!i.hasVector())
3410 anchor(i, false);
3411 checkEnclosure.add(i.getParentOrCurrentFrame());
3412 bReparse |= i.hasOverlay();
3413 bRecalculate |= i.recalculateWhenChanged();
3414 }
3415 }
3416
3417 toAnchor.clear();
3418 // Check enclosure for all the frames of the items that were anchored
3419 for (Frame f : checkEnclosure) {
3420 // ItemUtils.EnclosedCheck(f.getItems());
3421 ItemUtils.Justify(f);
3422 }
3423
3424 Frame currentFrame = DisplayIO.getCurrentFrame();
3425 if (bReparse)
3426 FrameUtils.Parse(currentFrame, false, false);
3427 else {
3428 currentFrame.notifyObservers(bRecalculate);
3429 }
3430 FrameGraphics.Repaint();
3431 }
3432
3433 /*
3434 * private boolean mouseMovedRecently() { Date now = new Date();
3435 *
3436 * return now.getTime() - _lastMouseMovement.getTime() < 150; }
3437 */
3438
3439 public void mouseWheelMoved(MouseWheelEvent arg0) {
3440 Navigation.ResetLastAddToBack();
3441
3442 int clicks = Math.abs(arg0.getWheelRotation());
3443
3444 if (FreeItems.getInstance().size() == 2) {
3445 if ((FreeItems.getInstance().get(0).isLineEnd() && FreeItems
3446 .getInstance().get(1) instanceof Line)
3447 || (FreeItems.getInstance().get(1).isLineEnd() && FreeItems
3448 .getInstance().get(0) instanceof Line)) {
3449
3450 Line line;
3451 if (FreeItems.getInstance().get(0) instanceof Line)
3452 line = (Line) FreeItems.getInstance().get(0);
3453 else
3454 line = (Line) FreeItems.getInstance().get(1);
3455
3456 // User must do multiple clicks to toggle the line
3457 if (clicks >= MOUSE_WHEEL_THRESHOLD) {
3458 if (arg0.isShiftDown())
3459 line.toggleArrowHeadRatio(arg0.getWheelRotation());
3460 else
3461 line.toggleArrowHeadLength(arg0.getWheelRotation());
3462 // line.getParent().change();
3463 FrameGraphics.Repaint();
3464 }
3465 }
3466 } else if (arg0.getWheelRotation() != 0 && arg0.isShiftDown()) {
3467
3468 FunctionKey rotationType = FunctionKey.SizeUp;
3469 if (arg0.getWheelRotation() > 0) {
3470 rotationType = FunctionKey.SizeDown;
3471 }
3472
3473 FrameKeyboardActions.functionKey(rotationType, 1, arg0
3474 .isShiftDown(), arg0.isControlDown());
3475
3476 } else if (clicks >= MOUSE_WHEEL_THRESHOLD) {
3477 Item item = FrameUtils.getCurrentItem();
3478
3479 // if the user is not pointing to any item
3480 if (item == null) {
3481 FrameKeyboardActions.NextTextItem(null,
3482 arg0.getWheelRotation() > 0);
3483 return;
3484 }
3485
3486 if (item instanceof Line || item instanceof Circle) {
3487 // check permissions
3488 if (!item.hasPermission(UserAppliedPermission.full)) {
3489 MessageBay
3490 .displayMessage("Insufficient permission to edit the Line");
3491 return;
3492 }
3493 item.toggleDashed(arg0.getWheelRotation());
3494 item.getParent().change();
3495 FrameGraphics.Repaint();
3496 return;
3497 } else if (item instanceof Text) {
3498 FrameKeyboardActions.NextTextItem(item,
3499 arg0.getWheelRotation() > 0);
3500 }
3501 }
3502 }
3503
3504 /**
3505 *
3506 * @return the integer value for the last mouse button clicked.
3507 */
3508 public static int getLastMouseButton() {
3509 if (_lastMouseClick == null)
3510 return MouseEvent.NOBUTTON;
3511
3512 return _lastMouseClick.getButton();
3513 }
3514
3515 public static boolean isDelete(int modifiersEx) {
3516
3517 int onMask = MouseEvent.BUTTON3_DOWN_MASK
3518 | MouseEvent.BUTTON2_DOWN_MASK;
3519 return (modifiersEx & onMask) == onMask;
3520 }
3521
3522 public static boolean isGetAttributes(int modifiersEx) {
3523 int onMask = MouseEvent.BUTTON3_DOWN_MASK
3524 | MouseEvent.BUTTON1_DOWN_MASK;
3525 return (modifiersEx & onMask) == onMask;
3526 }
3527
3528 public static boolean isTwoClickNoOp(int modifiersEx) {
3529 int onMask = MouseEvent.BUTTON2_DOWN_MASK
3530 | MouseEvent.BUTTON1_DOWN_MASK;
3531 return (modifiersEx & onMask) == onMask;
3532 }
3533
3534 public static boolean wasDeleteClicked() {
3535 if (_lastMouseClick == null)
3536 return false;
3537 return isDelete(_lastMouseClickModifiers);
3538 }
3539
3540 public static void control(KeyEvent ke) {
3541 for (Item i : FreeItems.getInstance()) {
3542 i.invalidateCommonTrait(ItemAppearence.PreMoved);
3543 }
3544
3545 for (Item i : FreeItems.getCursor()) {
3546 i.invalidateCommonTrait(ItemAppearence.PreMoved);
3547 }
3548
3549 _controlDown = ke.isControlDown();
3550
3551 if (_controlDown) {
3552 // TODO why are these two lines needed?!?!
3553 // _offX = 0;
3554 // _offY = 0;
3555 } else {
3556 resetOffset();
3557 }
3558
3559 if (_mouseDown > 0 && _lastMouseDragged != null) {
3560 MouseEvent me = _lastMouseDragged;
3561 _lastMouseDragged = new MouseEvent(ke.getComponent(),
3562 MouseEvent.NOBUTTON, ke.getWhen(), ke.getModifiers(), me
3563 .getX(), me.getY(), 0, false);
3564 _instance.mouseDragged(_lastMouseDragged);
3565 } else if (_lastMouseMoved != null) {
3566 MouseEvent me = _lastMouseMoved;
3567 _lastMouseMoved = new MouseEvent(ke.getComponent(),
3568 MouseEvent.NOBUTTON, ke.getWhen(), ke.getModifiers(), me
3569 .getX(), me.getY(), 0, false);
3570 _instance.mouseMoved(_lastMouseMoved, true);
3571 }
3572 updateCursor();
3573
3574 Help.updateStatus();
3575
3576 for (Item i : FreeItems.getInstance()) {
3577 i.invalidateCommonTrait(ItemAppearence.PostMoved);
3578 }
3579
3580 for (Item i : FreeItems.getCursor()) {
3581 i.invalidateCommonTrait(ItemAppearence.PostMoved);
3582 }
3583 // TODO: Check why the new constrained line is not repainted immediately
3584 FrameGraphics.requestRefresh(true);
3585 FrameGraphics.refresh(true);
3586 }
3587
3588 public static int getX() {
3589 return Math.round(MouseX);
3590 }
3591
3592 public static int getY() {
3593 return Math.round(MouseY);
3594 }
3595
3596 public static Item getlastHighlightedItem() {
3597 return _lastHighlightedItem;
3598 }
3599
3600 public static Point getPosition() {
3601 return new Point(getX(), getY());
3602 }
3603
3604 public static Point getFreeItemsOffset() {
3605 return new Point(_offX, _offY);
3606 }
3607
3608 public static void shift(KeyEvent e) {
3609 _shiftDown = e.isShiftDown();
3610 Help.updateStatus();
3611 getInstance().refreshHighlights();
3612 }
3613}
Note: See TracBrowser for help on using the repository browser.