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

Last change on this file since 998 was 998, checked in by davidb, 8 years ago

Added new versions of last commit that have been tidied up

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