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

Last change on this file since 1561 was 1561, checked in by davidb, 3 years ago

A set of changes that spans three things: beat detection, time stretching; and a debug class motivated by the need to look at a canvas redraw issue most notable when a waveform widget is playing

File size: 64.3 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.event.MouseEvent;
22import java.text.NumberFormat;
23import java.util.ArrayList;
24import java.util.Collection;
25import java.util.Date;
26import java.util.HashSet;
27import java.util.Iterator;
28import java.util.LinkedHashSet;
29import java.util.LinkedList;
30import java.util.List;
31import java.util.Set;
32
33import org.expeditee.actions.Actions;
34import org.expeditee.actions.Misc;
35import org.expeditee.actions.Navigation;
36import org.expeditee.core.AxisAlignedBoxBounds;
37import org.expeditee.core.Colour;
38import org.expeditee.core.OSManager;
39import org.expeditee.core.Point;
40import org.expeditee.core.swing.SwingConversions;
41import org.expeditee.gio.gesture.Gesture;
42import org.expeditee.gio.gesture.GestureListener;
43import org.expeditee.gui.indirect.mouse.IndirectMouseActions;
44import org.expeditee.gui.indirect.mouse.MouseAction;
45import org.expeditee.gui.indirect.mouse.MouseInfo;
46import org.expeditee.gui.Frame;
47import org.expeditee.io.ExpClipReader;
48import org.expeditee.io.ItemSelection;
49import org.expeditee.io.ItemSelection.ExpDataHandler;
50import org.expeditee.items.Circle;
51import org.expeditee.items.Constraint;
52import org.expeditee.items.Dot;
53import org.expeditee.items.Item;
54import org.expeditee.items.Item.HighlightMode;
55import org.expeditee.items.ItemAppearence;
56import org.expeditee.items.ItemUtils;
57import org.expeditee.items.Line;
58import org.expeditee.items.Picture;
59import org.expeditee.items.Text;
60import org.expeditee.items.UserAppliedPermission;
61import org.expeditee.items.XRayable;
62/*import org.expeditee.items.widgets.ButtonWidget; TODO: Reinstate. cts16
63import org.expeditee.items.widgets.InteractiveWidget;
64import org.expeditee.items.widgets.WidgetCorner;
65import org.expeditee.items.widgets.WidgetEdge;*/
66import org.expeditee.settings.UserSettings;
67import org.expeditee.settings.experimental.ExperimentalFeatures;
68import org.expeditee.stats.SessionStats;
69
70public class FrameMouseActions
71{
72
73 private static int _lastMouseClickModifiers = 0;
74
75 private static MouseEvent _lastMouseDragged;
76
77 private boolean _autoStamp = false;
78
79 private FrameMouseActions() {
80 IndirectMouseActions.getInstance().setBackAction(new MouseAction() {
81 @Override
82 public List<Item> exec(MouseInfo info) {
83 //if user is not pointing at something, this is a back
84 if (info.isControlDown || info.isShiftDown)
85 forward();
86 else
87 back();
88 return null;
89 }
90 });
91 IndirectMouseActions.getInstance().setTDFCAction(new MouseAction() {
92 @Override
93 public List<Item> exec(final MouseInfo info) {
94 // check for TDFC permission
95 if (!info.clicked.hasPermission(UserAppliedPermission.createFrames)) {
96 MessageBay.displayMessage("Insufficient permission to TDFC (Top Down Frame Creation) from that item");
97 return null;
98 }
99
100 if (info.clicked.isOldTag())
101 return null;
102
103 try {
104 tdfc(info.clicked);
105 } catch (RuntimeException e) {
106 e.printStackTrace();
107 MessageBay.errorMessage("Top Down Frame Creation (TDFC) error: " + e.getMessage());
108 }
109 return null;
110 }
111 });
112 IndirectMouseActions.getInstance().setRespondToFrameNameClickAction(new MouseAction() {
113 @Override
114 public List<Item> exec(MouseInfo info) {
115 if (info.isControlDown)
116 Navigation.PreviousFrame(false);
117 else
118 Navigation.NextFrame(false);
119 return null;
120 }
121 });
122 IndirectMouseActions.getInstance().setFollowLinkAction(new MouseAction() {
123 @Override
124 public List<Item> exec(MouseInfo info) {
125 /*
126 * Dont save the frame if we are moving to an old version of
127 * this frame because everytime we save with the old tag... the
128 * frame is backed up
129 */
130 if (!info.clicked.isOldTag())
131 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
132
133 Navigation.setLastNavigationItem(info.clicked);
134 load(info.clicked.getAbsoluteLink(), info.clicked.getLinkHistory());
135 // DisplayIO.UpdateTitle();
136 return null;
137 }
138 });
139 IndirectMouseActions.getInstance().setExecuteActionAction(new MouseAction() {
140 @Override
141 public List<Item> exec(MouseInfo info) {
142 info.clicked.performActions();
143 info.clicked.setHighlightMode(HighlightMode.None);
144 getInstance().refreshHighlights();
145 return null;
146 }
147 });
148 IndirectMouseActions.getInstance().setCreateRectangleAction(new MouseAction() {
149 @Override
150 public List<Item> exec(MouseInfo info) {
151 final List<Item> copies = new ArrayList<Item>();
152 Item[] d = new Item[RECTANGLE_CORNERS];
153 // create dots
154 Frame current = DisplayIO.getCurrentFrame();
155 for (int i = 0; i < d.length; i++) {
156 d[i] = current.createDot();
157 copies.add(d[i]);
158 }
159
160 current.nextDot();
161
162 // create lines
163 copies.add(new Line(d[0], d[1], current.getNextItemID()));
164 copies.add(new Line(d[1], d[2], current.getNextItemID()));
165 copies.add(new Line(d[2], d[3], current.getNextItemID()));
166 copies.add(new Line(d[3], d[0], current.getNextItemID()));
167
168 new Constraint(d[0], d[1], current.getNextItemID(), Constraint.HORIZONTAL);
169 new Constraint(d[2], d[3], current.getNextItemID(), Constraint.HORIZONTAL);
170 new Constraint(d[1], d[2], current.getNextItemID(), Constraint.VERTICAL);
171 new Constraint(d[3], d[0], current.getNextItemID(), Constraint.VERTICAL);
172
173 anchor(new ArrayList<Item>(copies));
174 pickup(d[3]);
175 d[3].setHighlightMode(HighlightMode.Normal);
176
177 SessionStats.CreatedItems(copies);
178 copies.clear();
179 return copies;
180 }
181 });
182 IndirectMouseActions.getInstance().setExtendLineAction(new MouseAction() {
183 @Override
184 public List<Item> exec(final MouseInfo info) {
185 Line onLine = (Line) info.clicked;
186 Line newLine = onLine.copy();
187 Item end = newLine.getEndItem();
188 Item start = newLine.getStartItem();
189 end.setPosition(MouseX, MouseY);
190 start.setPosition(MouseX, MouseY);
191 onLine.autoArrowheadLength();
192 // anchor the start
193 anchor(start);
194 // attach the line to the cursor
195 pickup(end);
196
197 List<Item> toMerge = new LinkedList<Item>();
198 toMerge.add(newLine.getStartItem());
199 toMerge.add(newLine);
200
201 // Make sure the highlighting is shown when the end is
202 // anchored
203 end.setHighlightMode(Item.HighlightMode.Normal);
204 merge(toMerge, info.clicked);
205 // anchor(left);
206 // FreeItems.getInstance().clear();
207 FrameGraphics.Repaint();
208 updateCursor();
209 return null;
210 }
211 });
212 IndirectMouseActions.getInstance().setMakeGroupCopyAction(new MouseAction() {
213 @Override
214 public List<Item> exec(final MouseInfo info) {
215 List<Item> copies = new ArrayList<Item>();
216 // Set the selection mode for the items that were clicked in
217 Collection<Item> enclosed = getFullyEnclosedItems(info.clickedIn);
218 if (enclosed.size() == 0) {
219 MessageBay.displayMessage("Insufficient permission to copy items");
220 } else {
221 copies = copy(enclosed);
222 clearParent(copies);
223 ItemUtils.Justify(copies);
224 pickup(copies);
225 for (Item i : info.clickedIn) {
226 i.setHighlightMode(HighlightMode.None);
227 }
228 }
229 return copies;
230 }
231 });
232 IndirectMouseActions.getInstance().setMakeCopyAction(new MouseAction() {
233 @Override
234 public List<Item> exec(MouseInfo info) {
235 List<Item> copies = ItemUtils.UnreelLine(info.clicked, _controlDown);
236 // Copies will NOT be null if the user right clicked on a point
237 if (copies == null) {
238 Collection<Item> originals = info.clicked.getConnected();
239 copies = ItemUtils.CopyItems(originals, _extrude);
240 // if this is the title of the frame, link it to the frame
241 if (originals.size() == 1 && copies.size() == 1) {
242 Item copy = copies.get(0);
243 Item original = originals.iterator().next();
244 if (original.getLink() == null
245 && original.isFrameTitle()) {
246 // save the frame after copying
247 // i.getParent().setChanged(true);
248 copy.setLink(original.getParentOrCurrentFrame()
249 .getName());
250 }
251 }
252
253 FrameGraphics.changeHighlightMode(info.clicked,
254 HighlightMode.None);
255
256 if (!_extrude)
257 clearParent(copies);
258 }
259
260 ItemUtils.Justify(copies);
261
262 pickup(copies);
263 return copies;
264 }
265 });
266 IndirectMouseActions.getInstance().setExtrudeAction(new MouseAction() {
267 @Override
268 public List<Item> exec(MouseInfo info) {
269 List<Item> copies = null;
270 List<Item> originals = new ArrayList<Item>();
271 // remove any lines that dont have both endpoints
272 // floating
273 for (Item i : FreeItems.getInstance()) {
274 if (i.isFloating())
275 originals.add(i);
276 }
277 if (copies == null)
278 copies = ItemUtils.CopyItems(originals, _extrude);
279 for (Item i : copies)
280 i.setOffset(0, 0);
281 anchor(FreeItems.getInstance());
282 // Move isnt working right for extruding!!
283 // move(copies);
284 pickup(copies);
285 return copies;
286 }
287 });
288 IndirectMouseActions.getInstance().setRubberBandingCopyAction(new MouseAction() {
289 @Override
290 public List<Item> exec(MouseInfo info) {
291 List<Item> copies = new ArrayList<Item>();
292 if (info.clicked != null) {
293 Collection<Item> leftOver = merge(FreeItems.getInstance(), info.clicked);
294 anchor(leftOver);
295 }
296 // This is executed when the user is putting down a line
297 // endpoint and unreeling. ie. Normal unreeling
298 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), _controlDown);
299
300 if (copies == null) copies = copy(FreeItems.getInstance());
301
302 anchor(FreeItems.getInstance());
303 for (Item i : copies) i.setOffset(0, 0);
304 // need to move to prevent cursor dislocation
305 move(copies);
306 pickup(copies);
307 return copies;
308 }
309 });
310 IndirectMouseActions.getInstance().setRubberBandingCornerAction(new MouseAction() {
311 @Override
312 public List<Item> exec(final MouseInfo info) {
313 List<Item> copies = new ArrayList<Item>();
314 Item d = getFirstFreeLineEnd();
315 // anchor the points
316 anchor(FreeItems.getInstance());
317 FreeItems.getInstance().clear();
318 updateCursor();
319 // pick up a copy of all enclosed items
320 Collection<Item> enclosedItems = FrameUtils
321 .getItemsEnclosedBy(DisplayIO.getCurrentFrame(), d
322 .getEnclosedShape());
323 if (enclosedItems != null) {
324 enclosedItems.removeAll(d.getAllConnected());
325 Collection<Item> toCopy = getFullyEnclosedItems(enclosedItems);
326
327 if (toCopy.size() > 0) {
328 // Find the closest item to the mouse cursor
329 double currentX = DisplayIO.getMouseX();
330 double currentY = FrameMouseActions.getY();
331 Item closest = null;
332 double shortestDistance = Double.MAX_VALUE;
333 for (Item next : toCopy) {
334 if (next instanceof Line)
335 continue;
336 double distance = Point.distance((int) currentX, (int) currentY, next.getX(), next.getY());
337 if (distance < shortestDistance) {
338 shortestDistance = distance;
339 closest = next;
340 }
341 }
342 // Move the cursor to closest item
343 DisplayIO.setCursorPosition(closest.getPosition());
344 // Pickup copy of the stuff inside the rectange
345 copies = copy(toCopy);
346 pickup(copies);
347 // Remove the rectangle
348 d.getParentOrCurrentFrame().removeAllItems(
349 d.getAllConnected());
350 } else {
351 // Pick up a copy of the rectangle
352 copies = copy(d.getAllConnected());
353 pickup(copies);
354 }
355 }
356 return copies;
357 }
358 });
359 IndirectMouseActions.getInstance().setStampAction(new MouseAction() {
360 @Override
361 public List<Item> exec(MouseInfo info) {
362 List<Item> copies = new ArrayList<Item>();
363 stampItemsOnCursor(true);
364 copies = FreeItems.getInstance();
365 return copies;
366 }
367 });
368 IndirectMouseActions.getInstance().setMergeSingleItemAction(new MouseAction() {
369 @Override
370 public List<Item> exec(final MouseInfo info) {
371 List<Item> copies = new ArrayList<Item>();
372 copies = copy(FreeItems.getInstance());
373 Collection<Item> remain = merge(copies, info.clicked);
374
375 // ignore items that could not be merged.
376 anchor(remain);
377 return copies;
378 }
379 });
380 IndirectMouseActions.getInstance().setMergeTwoItemsAction(new MouseAction() {
381 @Override
382 public List<Item> exec(MouseInfo info) {
383 List<Item> copies = new ArrayList<Item>();
384 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
385 _controlDown);
386 Collection<Item> leftOver = merge(FreeItems
387 .getInstance(), info.clicked);
388 anchor(leftOver);
389 if (copies == null)
390 copies = copy(FreeItems.getInstance());
391 FreeItems.getInstance().clear();
392 for (Item i : copies)
393 i.setOffset(0, 0);
394 // need to move to prevent cursor dislocation
395 move(copies);
396 pickup(copies);
397 // point onto point
398 return copies;
399 }
400 });
401 IndirectMouseActions.getInstance().setMergeGroupAction(new MouseAction() {
402 @Override
403 public List<Item> exec(final MouseInfo info) {
404 List<Item> copies = new ArrayList<Item>();
405 // Move the cursor so that the copy is exactly the
406 // same as the shape that was anchored
407 DisplayIO.setCursorPosition(info.clicked.getPosition());
408 Item d = getFirstFreeLineEnd();
409 // get a copy of all enclosed items before merging
410 // lineEnds
411 Collection<Item> items = FrameUtils.getItemsEnclosedBy(
412 DisplayIO.getCurrentFrame(), d
413 .getEnclosedShape());
414 // If its not an enclosed shape then pick up the
415 // connected shape
416 if (items == null || items.size() == 0) {
417 items = d.getAllConnected();
418 } else {
419 // For some reason the item that was clicked ends up
420 // in the enclosure and needs to be removed
421 items.removeAll(info.clicked.getConnected());
422 // the item that was the origin of the enclosed
423 // shape used to create the enclosure does not get
424 // returned from getItemsEnclosedBy to the enclosure
425 // so it must be added
426 items.addAll(d.getConnected());
427 }
428
429 Collection<Item> toCopy = new LinkedHashSet<Item>();
430
431 for (Item ip : items) {
432 if (ip.hasPermission(UserAppliedPermission.copy))
433 toCopy.add(ip);
434 }
435 copies = copy(toCopy);
436 // Now do the merging
437 Collection<Item> remain = merge(
438 FreeItems.getInstance(), info.clicked);
439 // anchor the points
440 anchor(remain);
441 FreeItems.getInstance().clear();
442 pickup(copies);
443 return copies;
444 }
445 });
446 IndirectMouseActions.getInstance().setNewLineAction(new MouseAction() {
447 @Override
448 public List<Item> exec(MouseInfo info) {
449 List<Item> copies = new ArrayList<Item>();
450 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), Math.round(MouseX), Math.round(MouseY), true);
451 // If we have permission to copy this item then pick it up
452 if (on != null && on.isLineEnd()
453 && on.hasPermission(UserAppliedPermission.full)) {
454 on.removeAllConstraints();
455 pickup(on);
456 return copies;
457 }
458
459/* if (on instanceof WidgetEdge) { TODO: Reinstate. cts16
460 // Don't allow the user to break widget edges.
461 // Note: had to return here because random dots would
462 // appear otherwise... cannot understand code below
463 // with create line.
464 return copies;
465 }*/
466
467 // if its on a line then split the line and put a point on it and
468 // pick that point up. Only if it is not a widget line
469 if (on instanceof Line && on.hasPermission(UserAppliedPermission.full)) {
470 Frame current = DisplayIO.getCurrentFrame();
471 // create the two endpoints
472 Line oldLine = (Line) info.clicked;
473 Item newPoint = oldLine.getStartItem().copy();
474 newPoint.setPosition(MouseX, MouseY);
475
476 Item end = oldLine.getEndItem();
477 // create the Line
478 Line newLine = new Line(newPoint, end, current.getNextItemID());
479 oldLine.replaceLineEnd(end, newPoint);
480 newPoint.removeAllConstraints();
481 pickup(newPoint);
482 // Update the stats
483 Collection<Item> created = new LinkedList<Item>();
484 created.add(newPoint);
485 created.add(newLine);
486 SessionStats.CreatedItems(newLine.getAllConnected());
487 return copies;
488 }
489 Line newLine = createLine();
490 SessionStats.CreatedItems(newLine.getAllConnected());
491 return copies;
492 }
493 });
494 IndirectMouseActions.getInstance().setGroupPickupAction(new MouseAction() {
495 @Override
496 public List<Item> exec(final MouseInfo info) {
497 List<Item> copies = new ArrayList<Item>();
498 ArrayList<Item> toPickup = new ArrayList<Item>(info.clickedIn.size());
499 for (Item ip : info.clickedIn) {
500 if (ip.hasPermission(UserAppliedPermission.full)) toPickup.add(ip);
501 }
502 pickup(toPickup);
503 // otherwise the user is creating a line
504 return copies;
505 }
506 });
507 IndirectMouseActions.getInstance().setDetachLineAction(new MouseAction() {
508 @Override
509 public List<Item> exec(final MouseInfo info) {
510 List<Item> items = new ArrayList<Item>();
511 // BROOK: WIDGET RECTANGLES DONT ALLOW DISCONNECTION
512 if (info.clicked instanceof Line/* && !(info.clicked instanceof WidgetEdge) TODO: Reinstate. cts16*/) {
513 // Check if within 20% of the end of the line
514 Line l = (Line) info.clicked;
515 Item toDisconnect = l.getEndPointToDisconnect(_lastMouseClick.getX(), _lastMouseClick.getY());
516
517 if (toDisconnect == null) {
518 pickup(info.clicked);
519 } else {
520 if (toDisconnect.getHighlightMode() == Item.HighlightMode.Normal) {
521 DisplayIO.setCursorPosition(toDisconnect.getPosition(),
522 false);
523 pickup(toDisconnect);
524 } else {
525 List<Line> lines = toDisconnect.getLines();
526 // This is to remove constraints from single lines
527 // with constraints...
528 // ie. partially deleted rectangles
529 if (lines.size() == 1) {
530 toDisconnect.removeAllConstraints();
531
532 DisplayIO.setCursorPosition(toDisconnect
533 .getPosition(), false);
534 // This is to ensure the selected mode will be set
535 // to Normal rather than disconnect when the line is
536 // anchored
537 toDisconnect
538 .setHighlightMode(Item.HighlightMode.Normal);
539 pickup(toDisconnect);
540 } else {
541 // If we are then detatch the line and pick up its
542 // end point...
543 Frame currentFrame = DisplayIO.getCurrentFrame();
544 Item newPoint = null;
545
546 // If the point we are disconnecting is text...
547 // Then we want to leave the text behind
548 // And disconnect a point
549 if (toDisconnect instanceof Text) {
550 newPoint = new Dot(toDisconnect.getX(),
551 toDisconnect.getY(), -1);
552 Item.DuplicateItem(toDisconnect, newPoint);
553 } else {
554 newPoint = toDisconnect.copy();
555 }
556
557 currentFrame.addItem(newPoint);
558 // remove the current item from the connected
559 // list for this item
560 l.replaceLineEnd(toDisconnect, newPoint);
561 // remove unneeded constrains
562 newPoint.removeAllConstraints();
563
564 // Set the new points mode to normal before picking
565 // it up so it will be restored correctly when
566 // anchored
567 newPoint
568 .setHighlightMode(Item.HighlightMode.Normal);
569 toDisconnect
570 .setHighlightMode(Item.HighlightMode.None);
571 DisplayIO.setCursorPosition(toDisconnect
572 .getPosition(), false);
573 pickup(newPoint);
574 ItemUtils.EnclosedCheck(toDisconnect
575 .getParentOrCurrentFrame().getItems());
576 }
577 }
578 }
579 } else {
580 if (info.clicked.isLineEnd()) {
581 DisplayIO.setCursorPosition(info.clicked.getPosition(), false);
582 }
583 pickup(info.clicked);
584 }
585 return items;
586 }
587 });
588 IndirectMouseActions.getInstance().setAnchorFreeItemsAction(new MouseAction() {
589 @Override
590 public List<Item> exec(MouseInfo info) {
591 List<Item> items = new ArrayList<Item>();
592 if (info.clickedIn != null && FreeItems.getInstance().size() == 1) {
593 Item item = FreeItems.getItemAttachedToCursor();
594 if (item instanceof Text) {
595 Text text = (Text) item;
596 if (AttributeUtils.setAttribute(text, text, 2)) {
597 info.clickedIn.removeAll(FrameUtils
598 .getEnclosingLineEnds().iterator().next()
599 .getAllConnected());
600 for (Item i : info.clickedIn) {
601 AttributeUtils.setAttribute(i, text);
602 }
603 FreeItems.getInstance().clear();
604 }
605 }
606 }
607
608 // if a line is being rubber-banded, check for auto
609 // straightening
610 anchor(FreeItems.getInstance());
611 FreeItems.getInstance().clear();
612 updateCursor();
613 _offX = _offY = 0;
614 return items;
615 }
616 });
617 IndirectMouseActions.getInstance().setDeleteItemsAction(new MouseAction() {
618 @Override
619 public List<Item> exec(final MouseInfo info) {
620 List<Item> items = new ArrayList<Item>();
621 // check permissions
622 if (!info.clicked.hasPermission(UserAppliedPermission.full)) {
623 //Items on the message box have parent == null
624 if (info.clicked.getParent() != null) {
625 if (!info.clicked.isFrameName()) {
626 Item editTarget = info.clicked.getEditTarget();
627 if (editTarget != info.clicked
628 && editTarget
629 .hasPermission(UserAppliedPermission.full)) {
630 info.clicked = editTarget;
631 } else {
632 MessageBay
633 .displayMessage("Insufficient permission");
634 return items;
635 }
636 }
637
638 } else /*Its in the message area*/ {
639 MessageBay.displayMessage("Insufficient permission");
640 return items;
641 }
642 }
643 Item merger = FreeItems.getItemAttachedToCursor();
644 assert (merger != null);
645 Collection<Item> left = null;
646 // when anchoring a line end onto a text line end, holding shift
647 // prevents the line ends from being merged
648 if (info.isShiftDown) {
649 left = FreeItems.getInstance();
650 } else {
651 left = merge(FreeItems.getInstance(), info.clicked);
652 }
653 Collection<Item> toDelete = new LinkedList<Item>();
654 toDelete.addAll(FreeItems.getInstance());
655 toDelete.removeAll(left);
656 anchor(left);
657 FreeItems.getInstance().clear();
658 DisplayIO.getCurrentFrame().removeAllItems(toDelete);
659 updateCursor();
660 // Make sure the dot goes away when anchoring a line end behind
661 // a text line end
662 if (info.isShiftDown) {
663 refreshHighlights();
664 }
665 FrameGraphics.requestRefresh(true);
666 return items;
667 // otherwise, anchor the items
668 }
669 });
670 }
671
672 private static FrameMouseActions _instance = null;
673
674 public static FrameMouseActions getInstance() {
675 if (_instance == null) _instance = new FrameMouseActions();
676
677 return _instance;
678 }
679
680 private static final int RECTANGLE_CORNERS = 4;
681
682 // TODO say where/how used
683 private static final int MOUSE_WHEEL_THRESHOLD = 2;
684
685 private static final int MINIMUM_RANGE_DEPRESS_TIME = 250;
686
687 private static Date _lastMouseClickDate = new Date();
688
689 public static final int LITTLE_MOUSE_PAUSE = 500;
690
691 public static final int ZERO_MOUSE_PAUSE = 0;
692
693 public static final int BIG_MOUSE_PAUSE = 750;
694
695 public static int _alpha = -1;
696
697 /**
698 * The last known mouse X coordinate
699 */
700 public static float MouseX;
701
702 /**
703 * The last known mouse Y coordinate. Relative to the top of the
704 * application.
705 */
706 public static float MouseY;
707
708 // Distance of mouse cursor from the origin of the item that was picked up
709 // The are used in the move method to calculate the distance moved by the
710 // cursor
711 private static int _offX;
712
713 private static int _offY;
714
715 // Keeps track of mouse button events when a delete occurs
716 private static boolean _isDelete = false;
717
718 // Keeps track of mouse button events when the user extracts attributes
719 // occurs
720 private static boolean _isAttribute = false;
721
722 /**
723 * A flag to indicate that the last mouseUp event was part of a two button
724 * click sequence hence the next mouse up should be ignored*
725 */
726 private static boolean _wasDouble = false;
727
728 private static boolean _isNoOp = false;
729
730 private static boolean _extrude = false;
731
732 // keeps track of the item being 'ranged out' if there is one.
733 private static Text _lastRanged = null;
734
735 // keeps track of the picture being cropped if there is one
736 private static Picture _lastCropped = null;
737
738 public static int getContext() {
739 return _context;
740 }
741
742 static int _mouseDown = 0;
743
744 private static MouseEvent _lastMouseClick = null;
745
746 private static Item _lastClickedOn = null;
747
748 private static Collection<Item> _lastClickedIn = null;
749
750 private static boolean _pulseOn = false;
751
752 private static final int PULSE_AMOUNT = 2;
753
754 private static Timer _MouseTimer = new Timer(LITTLE_MOUSE_PAUSE,
755 new ActionListener() {
756 public void actionPerformed(ActionEvent ae) {
757 // check if we are in free space
758 if (_lastClickedOn == null && FreeItems.getInstance().size() == 0) {
759 // System.out.println("SuperBack!");
760 _MouseTimer.setDelay(ZERO_MOUSE_PAUSE);
761 back();
762 } else {
763 if (FrameUtils.getCurrentItem() == null) {
764 // Check if we are toggling arrowhead
765 if (FreeItems.getInstance().size() <= 2) {
766 for (Item i : FreeItems.getInstance()) {
767 if (i instanceof Line) {
768 ((Line) i).toggleArrow();
769 }
770 }
771 FrameGraphics.Repaint();
772 }
773 }
774 _MouseTimer.stop();
775 }
776 }
777 });
778
779 private static void setPulse(boolean pulseOn)
780 {
781 if (_pulseOn == pulseOn) return;
782
783 int amount = PULSE_AMOUNT;
784 if (!pulseOn) amount *= -1;
785 _pulseOn = pulseOn;
786
787
788 if (_lastClickedOn != null) {
789 for (Item i : _lastClickedOn.getAllConnected()) {
790 if (i instanceof Line) {
791 Line line = (Line) i;
792 line.setThickness(line.getThickness() + amount);
793 }
794 }
795 }
796 FrameGraphics.Repaint();
797 }
798
799 private static Timer _ExtrudeMouseTimer = new Timer(BIG_MOUSE_PAUSE,
800 new ActionListener() {
801 public void actionPerformed(ActionEvent ae) {
802 setPulse(true);
803 _extrude = true;
804 _ExtrudeMouseTimer.stop();
805 }
806 });
807
808 /**
809 * Each Item on the Frame is checked to determine if the mouse x,y
810 * coordinates are on the Item (or within the Shape surrounding it). If the
811 * coordinates are on the Item then the Item is checked for a link, if it
812 * has a link the link is followed, if not, nothing is done.
813 */
814 public void mousePressed(MouseEvent e) {
815 ProccessMousePressedEvent(e, e.getModifiersEx());
816 }
817
818 private void ProccessMousePressedEvent(MouseEvent e, int modifiersEx) {
819 // System.out.println("MousePressed " + e.getX() + "," + e.getY() + " " + e.getWhen());
820
821 // TODO WHY DID I NOT COMMENT THIS LINE!! MIKE SAYS
822/* if (LastRobotX != null) {
823 _RobotTimer.stop();
824 LastRobotX = null;
825 LastRobotY = null;
826 mouseMoved(e);
827 }
828
829 if(ExperimentalFeatures.MousePan.get()) {
830 // don't pan if we're not over the frame
831 _overFrame = FrameUtils.getCurrentItem() == null;
832 _isPanOp = false;
833 // update panning position values so position doesn't jump
834 panStartX = e.getX();
835 panStartY = e.getY();
836 MouseX = panStartX;
837 MouseY = panStartY;
838 }*/
839
840 // System.out.println(modifiersEx);
841 if (_mouseDown == 0) _lastMouseClickDate = new Date();
842
843 int buttonPressed = e.getButton();
844 _mouseDown += buttonPressed;
845 //when border of bin is clicked we can pick up the widget has been clicked
846 _lastClickedOn = FrameUtils.getCurrentItem();
847 // load any frame if necessary
848 Item on = _lastClickedOn;
849
850 _lastClickedIn = FrameUtils.getCurrentItems(on);
851 // if (_lastClickedIn != null){
852 // System.out.println(_lastClickedIn.size());}
853
854 /*
855 * This makes it so clicking repeatedly on the frameName doesn't add the
856 * frames to the backup stack. Only the first frame is added to the
857 * backup stack.
858 */
859 if (on == null || buttonPressed != MouseEvent.BUTTON1 || !on.isFrameName()) {
860 Navigation.ResetLastAddToBack();
861 }
862
863 //SessionStats.MouseClicked(e.getButton());
864 if (buttonPressed == MouseEvent.BUTTON1) {
865 //SessionStats.AddFrameEvent("Ld");
866 _extrude = false;
867 } else if (buttonPressed == MouseEvent.BUTTON2) {
868 //SessionStats.AddFrameEvent("Md");
869 _extrude = false;
870 } else if (buttonPressed == MouseEvent.BUTTON3) {
871 //SessionStats.AddFrameEvent("Rd");
872
873 // Check if the user picked up a paint brush
874 if (FreeItems.getInstance().size() == 1 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
875 int delay = (int) (FreeItems.getItemAttachedToCursor().getAutoStamp() * 1000);
876 if (delay < 10) {
877 _autoStamp = true;
878 } else {
879 _autoStampTimer.setDelay(delay);
880 _autoStampTimer.start();
881 }
882 }
883 }
884
885 // Mike says...
886 // For some reason the modifiers for e are different from modifiersEx
887 // The SwingUtilities.convertMouseEvent method changes the modifiers
888 _lastMouseClick = e;
889 _lastMouseClickModifiers = modifiersEx;
890
891 /*
892 * Only start the timer when in free space when the user double clicks
893 * to do super back TODO change this so that there are separate timers
894 * for super back and the other Long depress actions if that is what is
895 * wanted.
896 */
897 if (_lastClickedOn == null && FreeItems.getInstance().size() == 0) {
898 if (e.getClickCount() >= 2) {
899 _MouseTimer.start();
900 }
901 } else if ( _lastClickedOn != null &&
902 FreeItems.getInstance().size() == 0 &&
903 e.getButton() == MouseEvent.BUTTON3)
904 {
905 _ExtrudeMouseTimer.start();
906 } else {
907 _MouseTimer.start();
908 }
909
910 // pre-cache the frame if it is linked
911
912 // If pre-caching is done, it must be done in the background
913
914 // if (on != null && on.getLink() != null && on.isLinkValid()) {
915 // FrameIO.Precache(on.getAbsoluteLink());
916 // }
917
918 // check for delete command
919 if (isDelete(modifiersEx)) {
920 _isDelete = true;
921 // _lastRanged = null;
922 _lastCropped = null;
923 _wasDouble = false;
924 // check for attributes command
925 } else if (isGetAttributes(modifiersEx)) {
926 _isAttribute = true;
927 _wasDouble = false;
928 } else if (isTwoClickNoOp(modifiersEx)) {
929 _isAttribute = false;
930 _wasDouble = false;
931 _isDelete = false;
932 _isNoOp = true;
933 } else {
934 _isDelete = false;
935 }
936
937 // This must happen before the previous code
938 // This is when the user is anchoring something
939 if ( buttonPressed != MouseEvent.BUTTON1 &&
940 (_context == CONTEXT_FREESPACE || _context == CONTEXT_AT_ENCLOSURE) &&
941 FreeItems.hasItemsAttachedToCursor())
942 {
943 FrameGraphics.changeHighlightMode(_lastHighlightedItem, Item.HighlightMode.None);
944
945 _lastHighlightedItem = FreeItems.getItemAttachedToCursor();
946 for (Item i : FreeItems.getInstance()) {
947 i.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
948 }
949 FrameGraphics.Repaint();
950 // this is when the user is picking something up
951 } else if (_lastHighlightedItem != null) {
952 if (!(_lastHighlightedItem instanceof Line)) {
953 _lastHighlightedItem.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
954 } else {
955 for (Item i : _lastHighlightedItem.getAllConnected()) {
956 i.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
957 }
958 }
959 FrameGraphics.Repaint();
960 }
961
962 // if the user is ranging text
963 if (on != null && on instanceof Text && !_isDelete) {
964 _lastRanged = (Text) on;
965 // set start-drag point
966 _lastRanged.setSelectionStart(DisplayIO.getMouseX(), DisplayIO.getMouseY());
967 }
968
969 /*
970 * Want to repaint the text with deleteRange color as soon as the second
971 * button is pressed
972 */
973 if (_lastRanged != null) {
974 _lastRanged.invalidateAll();
975 FrameGraphics.requestRefresh(true);
976 }
977
978 if (on != null && on instanceof Picture && e.getButton() == MouseEvent.BUTTON3 && !_isDelete) {
979 _lastCropped = (Picture) on;
980 // set start crop point
981 _lastCropped.setStartCrop(DisplayIO.getMouseX(), DisplayIO.getMouseY());
982 _lastCropped.setShowCrop(true);
983 }
984 }
985
986 // This is where all the processing happens
987 public void mouseReleased(MouseEvent e) {
988
989 // System.out.println("Released " + e.getX() + "," + e.getY() + " " +
990 // e.getWhen());
991// FrameUtils.ResponseTimer.restart();
992 _autoStampTimer.stop();
993 _autoStamp = false;
994
995 // Auto-hide popups when user clicks into expeditee world
996 // If the user clicks into empty space and a popup-is showing, then
997 // the user probably wants to click away the popup - therefore ignore
998 // the event
999/* boolean shouldConsume = PopupManager.getInstance().shouldConsumeBackClick(); TODO: Reinstate. cts16
1000 PopupManager.getInstance().hideAutohidePopups();
1001 if (shouldConsume && e.getButton() == MouseEvent.BUTTON1) {
1002 return; // consume back click event
1003 }*/
1004
1005 // _lastMovedDistance = new Point(e.getX() - _lastMouseClick.getX(), e
1006 // .getY()
1007 // - _lastMouseClick.getY());
1008
1009 _mouseDown -= e.getButton();
1010 updateCursor();
1011
1012 // System.out.println(e.getX() + ", " + e.getY());
1013
1014 Text lastRanged = _lastRanged;
1015 _lastRanged = null;
1016 // Dont do ranging if the user moves really quickly...
1017 // They are probably trying to pick something up in this case
1018 if (lastRanged != null) {
1019 long depressTime = (new Date()).getTime() - _lastMouseClickDate.getTime();
1020 // double changeInDistance =
1021 // e.getPoint().distance(_currentMouseClick.getPoint());
1022 // double speed = changeInDistance * 1000 / changeInTime;
1023
1024 // System.out.println(depressTime);
1025
1026 if (depressTime < MINIMUM_RANGE_DEPRESS_TIME || lastRanged.getSelectionSize() <= 0) {// Text.MINIMUM_RANGED_CHARS)
1027 // {
1028 lastRanged.clearSelection();
1029 lastRanged = null;
1030 }
1031 }
1032
1033 _ExtrudeMouseTimer.stop();
1034 _MouseTimer.stop();
1035
1036 setPulse(false);
1037
1038 // if the last action was a delete, then ignore the next mouseup
1039 if (_wasDouble) {
1040 _wasDouble = false;
1041 return;
1042 }
1043
1044 // This code must come after the _wasDouble code...
1045 // Otherwise get Stopping Agent method after doing the left+right format
1046 // shortcut
1047 if (Actions.isAgentRunning()) {
1048 Actions.stopAgent();
1049 return;
1050 }
1051
1052 /*
1053 * if (_isNoOp) { if (e.getButton() != MouseEvent.NOBUTTON) { _isNoOp =
1054 * false; _wasDouble = true; // lastRanged.clearSelection();
1055 * FrameGraphics.Repaint(); return; } }
1056 */
1057
1058 // get whatever the user was pointing at
1059 Item clickedOn = _lastClickedOn;
1060 Collection<Item> clickedIn = _lastClickedIn;
1061
1062 MouseX = e.getX();
1063 MouseY = e.getY();
1064
1065 Item releasedOn = FrameUtils.getCurrentItem();
1066 Collection<Item> releasedIn = FrameUtils.getCurrentItems(releasedOn);
1067
1068 // Only a no op if user releases in free space!
1069 if (_isPanOp || (_isNoOp && (releasedOn == null && releasedIn == null))) {
1070 if (_isDelete) {
1071 _isDelete = false;
1072 _wasDouble = true;
1073 }
1074
1075 _isNoOp = false;
1076
1077 if (_lastHighlightedItem != null)
1078 FrameGraphics.changeHighlightMode(_lastHighlightedItem, Item.HighlightMode.None);
1079
1080 if (FreeItems.hasItemsAttachedToCursor()) {
1081 move(FreeItems.getInstance());
1082 }
1083
1084 if (FreeItems.hasCursor()) {
1085 move(FreeItems.getCursor(), true);
1086 }
1087
1088 if(!_isPanOp) {
1089 MessageBay.displayMessage("Action cancelled, mouse moved more than "
1090 + UserSettings.NoOpThreshold.get() + " pixels.");
1091 }
1092 FrameGraphics.Repaint();
1093 return;
1094 } else {
1095 _isNoOp = false;
1096 }
1097
1098 // if this is a delete command
1099 if (_isDelete) {
1100 if (lastRanged != null) {
1101
1102 Item i = FreeItems.getItemAttachedToCursor();
1103 if (i != null && i instanceof Text) {
1104 lastRanged.replaceSelectedText(((Text) i).getText());
1105 FreeItems.getInstance().clear();
1106 } else {
1107 lastRanged.cutSelectedText();
1108 }
1109 lastRanged.clearSelection();
1110 FrameGraphics.Repaint();
1111
1112 } else {
1113 delete(clickedOn);
1114 }
1115 _wasDouble = true;
1116 _isDelete = false;
1117 return;
1118 }
1119
1120 // if this is an attribute extraction command
1121 if (_isAttribute) {
1122 if (clickedOn == null) {
1123 Frame current = DisplayIO.getCurrentFrame();
1124 if (isControlDown()) {
1125 Actions.PerformActionCatchErrors(current, null, "HFormat");
1126 }
1127 if (!isControlDown() || isShiftDown()) {
1128 Actions.PerformActionCatchErrors(current, null, "Format");
1129 }
1130 } else {
1131 extractAttributes(clickedOn);
1132 }
1133 // if the user dragged and displayed some cropping with left and
1134 // right button is a no op for now
1135 // but later could make this the shrinkTo context
1136 if (_lastCropped != null) {
1137 _lastCropped.clearCropping();
1138 _lastCropped = null;
1139 }
1140 _wasDouble = true;
1141 _isAttribute = false;
1142 return;
1143 }
1144
1145 // if the user is ranging-out text
1146 if (lastRanged != null && e.getButton() != MouseEvent.BUTTON1) {
1147
1148 Text ranged;
1149 if (isShiftDown()) {
1150 // If shift is down, copy everything (size, color, etc.) except actions, links and data
1151 ranged = lastRanged.copy();
1152 ranged.setActions(null);
1153 ranged.setData((List<String>) null);
1154 ranged.setLink(null);
1155 } else {
1156 // If shift isn't down, don't copy any attributes, but base the new text item on the appropriate template
1157 ranged = DisplayIO.getCurrentFrame().getItemTemplate(lastRanged.copySelectedText().charAt(0));
1158 }
1159
1160 // if the user is cutting text from the item
1161 if (e.getButton() == MouseEvent.BUTTON2) {
1162 // Check if the user is trying to range an item for which they
1163 // do not have permission to do so... or it is the frame name
1164 if (!lastRanged.hasPermission(UserAppliedPermission.full) || lastRanged.isFrameName()) {
1165 MessageBay.displayMessage("Insufficient permission to cut text");
1166 lastRanged.clearSelection();
1167 FrameGraphics.Repaint();
1168 return;
1169 }
1170 // if the entire text is selected and its not a line end then
1171 // pickup the item
1172 boolean entireText = lastRanged.getSelectionSize() == lastRanged.getLength();
1173 if (entireText && !lastRanged.isLineEnd()) {
1174 lastRanged.clearSelection();
1175 ranged.delete();
1176 middleButton(clickedOn, clickedIn, e.isShiftDown());
1177 return;
1178 } else {
1179 ranged.setText(lastRanged.cutSelectedText());
1180 ranged.setWidth(lastRanged.getWidth());
1181 // If its the whole text then replace last ranged with a dot
1182 if (entireText) {
1183 Item dot = FrameKeyboardActions.replaceText(lastRanged);
1184 dot.setHighlightMode(HighlightMode.None);
1185 }
1186 }
1187 // if the user is copying text from the item
1188 } else if (e.getButton() == MouseEvent.BUTTON3) {
1189 // Check if the user is trying to range an item for which they
1190 // do not have permission to do so... or it is the frame name
1191 if (!lastRanged.hasPermission(UserAppliedPermission.copy)) {
1192 MessageBay.displayMessage("Insufficient permission to copy text");
1193 lastRanged.clearSelection();
1194 FrameGraphics.Repaint();
1195 return;
1196 }
1197 ranged.setText(lastRanged.copySelectedText());
1198 }
1199
1200 ranged.setParent(null);
1201 ranged.setPosition(DisplayIO.getMouseX(), DisplayIO.getMouseY());
1202 pickup(ranged);
1203 lastRanged.clearSelection();
1204 lastRanged.setHighlightMode(HighlightMode.None);
1205 refreshHighlights();
1206 FrameGraphics.refresh(false);
1207 return;
1208 }
1209
1210 // if the user is cropping an image
1211 if (clickedOn != null && clickedOn == _lastCropped) {
1212 if (_lastCropped.isCropTooSmall()) {
1213 _lastCropped = null;
1214 // FrameGraphics
1215 // .WarningMessage("Crop cancelled because it was below the
1216 // minimum size");
1217 } else {
1218 Picture cropped = _lastCropped.copy();
1219 cropped.setParent(null);
1220 // move the cropped image to the cursor
1221 int width = cropped.getWidth();
1222 int height = cropped.getHeight();
1223 if(cropped.getSource().getX() + width < MouseX) {
1224 cropped.getSource().setX(MouseX - width);
1225 }
1226 if(cropped.getSource().getY() + height < MouseY) {
1227 cropped.getSource().setY(MouseY - height);
1228 }
1229 pickup(cropped);
1230 // MIKE put the code below up here
1231 _lastCropped.clearCropping();
1232 FrameGraphics.changeHighlightMode(_lastCropped, HighlightMode.None);
1233 _lastCropped = null;
1234 FrameGraphics.Repaint();
1235 return;
1236 }
1237 }
1238
1239 assert (_lastCropped == null);
1240 // if the user has cropped an image, either the above happend or this is
1241 // a no-op MIKE says WHEN DO WE NEED THE CODE BELOW
1242 // if (_lastCropped != null && !_lastCropped.isCropTooSmall()) {
1243 // _lastCropped.clearCropping();
1244 // _lastCropped = null;
1245 // FrameGraphics.Repaint();
1246 // return;
1247 // }
1248
1249 // if the user is left-clicking
1250 if (e.getButton() == MouseEvent.BUTTON1) {
1251 SessionStats.AddFrameEvent("Lu");
1252 leftButton(clickedOn, clickedIn, e.isShiftDown(), e.isControlDown());
1253 return;
1254 }
1255
1256 if (e.getButton() == MouseEvent.BUTTON2) {
1257 SessionStats.AddFrameEvent("Mu");
1258 middleButton(clickedOn, clickedIn, e.isShiftDown());
1259 return;
1260 }
1261
1262 if (e.getButton() == MouseEvent.BUTTON3) {
1263 SessionStats.AddFrameEvent("Ru");
1264 rightButton(clickedOn, clickedIn);
1265 return;
1266 }
1267
1268 // error, we should have returned by now
1269 System.out.println("Error: mouseReleased should have returned by now. " + e);
1270 }
1271
1272 /**
1273 * This method handles all left-click actions
1274 */
1275 private void leftButton(Item clicked, Collection<Item> clickedIn, boolean isShiftDown, boolean isControlDown)
1276 {
1277
1278 //Gets the current frame
1279 Frame f = DisplayIO.getCurrentFrame();
1280
1281 //Checks if the current frame is an overlay
1282 if (f.getOverlays() != null && FrameUtils.getCurrentItem() != null) {
1283 Item i = FrameUtils.getCurrentItem();
1284
1285 //Checks if the item clicked in the overlay is a Rubbish Bin. If it is, delete the item attached to the cursor and return.
1286/* if(i instanceof WidgetCorner){ TODO: Reinstate. cts16
1287
1288 try{
1289 WidgetCorner wc = (WidgetCorner)i;
1290 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
1291
1292 //Should call a button widgets 'itemheldwhileclicked' method, and process depending on the widget type - else will return false.
1293 if(bw.itemHeldWhileClicked((InteractiveWidget)bw) == true){
1294
1295 return;
1296 }
1297 }
1298 catch (Exception e){
1299
1300 e.printStackTrace();
1301 }
1302 }*/
1303
1304 Item on = _lastClickedOn;
1305 _lastClickedIn = FrameUtils.getCurrentItems(on);
1306 }
1307
1308
1309 // if the user is pointing at something then either follow the link or
1310 // do TDFC
1311 if (clicked == null) {
1312 // Check if the user is nearby another item...
1313 int mouseX = DisplayIO.getMouseX();
1314 int mouseY = DisplayIO.getMouseY();
1315 // System.out.println(mouseX + "," + mouseY);
1316 for (Item i : DisplayIO.getCurrentFrame().getItems()) {
1317 //System.out.println(i.getName().toString());
1318 if (i instanceof Text) {
1319 if (i.isNear(mouseX, mouseY)) {
1320 clicked = i;
1321 break;
1322 }
1323 }
1324 }
1325 }
1326
1327 if (clicked instanceof Text) {
1328 Text text = (Text) clicked;
1329 /* Don't follow link when just highlighting text with the left button */
1330 if (text.getText().length() == 0)
1331 clicked = null;
1332 else if (text.getSelectionSize() > 0) {
1333 return;
1334 }
1335 }
1336
1337 // If the user clicked into a widgets free space...
1338 if (clicked == null && _lastClickedIn != null && _lastClickedIn.size() >= 4) {
1339
1340 // Check to see if the user clicked into a widgets empty space
1341/* InteractiveWidget iw = null; TODO: Reinstate. cts16
1342
1343 for (Item i : _lastClickedIn) {
1344
1345 if (i instanceof WidgetCorner) {
1346 iw = ((WidgetCorner) i).getWidgetSource();
1347 break;
1348 } else if (i instanceof WidgetEdge) {
1349 iw = ((WidgetEdge) i).getWidgetSource();
1350 break;
1351 }
1352 }
1353
1354 if (iw != null) {
1355
1356 // Handle dropping items on widgets
1357 if(iw.ItemsLeftClickDropped()) {
1358 return;
1359 }
1360
1361 // Note: mustn't directly use source for handling the link
1362 // because all link operations will by-pass the widgets special
1363 // handling with links...
1364 Item widgetLink = iw.getItems().get(0);
1365 assert (widgetLink != null);
1366 clicked = widgetLink;
1367 } else*/ {
1368 for (Item i : _lastClickedIn) {
1369 /*
1370 * Find the first linked item or the first unlinked Dot This
1371 * code assumes that items are are ordered from top to
1372 * bottom. TODO make sure the list will always be ordered
1373 * correctly!!
1374 */
1375 if (i.hasLink() || i instanceof Dot) {
1376 clicked = i;
1377 break;
1378 }
1379 }
1380 }
1381
1382 }
1383
1384
1385 if (clicked instanceof Picture) {
1386 int mouseX = DisplayIO.getMouseX();
1387 int mouseY = FrameMouseActions.getY();
1388 Picture clickedOnPicture = (Picture)clicked;
1389 Frame current_frame = DisplayIO.getCurrentFrame();
1390 Colour bg_col = current_frame.getBackgroundColor();
1391 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX,mouseY,bg_col)) {
1392 // Make 'clicked' null, effectively causing a back() operation
1393 clicked = null;
1394 }
1395 }
1396
1397 if (clicked != null) {
1398 // check item permissions
1399 boolean hasLinkOrAction = clicked.hasLink() || clicked.hasAction();
1400
1401 if ((hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.followLinks))
1402 || (!hasLinkOrAction && !clicked.hasPermission(UserAppliedPermission.createFrames)))
1403 {
1404 Item editTarget = clicked.getEditTarget();
1405 if (editTarget != clicked) {
1406 if (editTarget.hasPermission(UserAppliedPermission.followLinks)) {
1407 clicked = editTarget;
1408 } else {
1409 MessageBay.displayMessage("Insufficient permission to perform action on item");
1410 return;
1411 }
1412 }
1413 }
1414
1415 Item clickedOn = clicked;
1416
1417 // actions take priority
1418 if (_lastMouseClick != null && !_lastMouseClick.isControlDown() && clickedOn.hasAction()) {
1419 IndirectMouseActions.getInstance().getExecuteActionAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1420 } else if (clickedOn.getLink() != null) {
1421 IndirectMouseActions.getInstance().getFollowLinkAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1422 // no link is found, perform TDFC
1423 } else {
1424 /*
1425 * if the user is clicking on the frame name then move to the
1426 * next or previous frame regardless of whether or not the frame
1427 * is protected
1428 */
1429 if (clickedOn.isFrameName()) {
1430 IndirectMouseActions.getInstance().getRespondToFrameNameClickAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1431 }
1432
1433 IndirectMouseActions.getInstance().getTDFCAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1434 }
1435
1436 } else {
1437
1438 IndirectMouseActions.getInstance().getBackAction().exec(new MouseInfo(clicked, clickedIn, isShiftDown, isControlDown));
1439 }
1440 }
1441
1442 public static void middleButton() {
1443 Item currentItem = FrameUtils.getCurrentItem();
1444 getInstance().middleButton(currentItem,
1445 FrameUtils.getCurrentItems(currentItem), false);
1446 updateCursor();
1447 }
1448
1449 public static void rightButton() {
1450 Item currentItem = FrameUtils.getCurrentItem();
1451 getInstance().rightButton(currentItem,
1452 FrameUtils.getCurrentItems(currentItem));
1453 updateCursor();
1454 }
1455
1456 public static void leftButton() {
1457 Item currentItem = FrameUtils.getCurrentItem();
1458 getInstance().leftButton(currentItem, FrameUtils.getCurrentItems(currentItem), false, false);
1459 updateCursor();
1460 }
1461
1462 /**
1463 * This method handles all middle-click actions
1464 */
1465 private void middleButton(Item clicked, Collection<Item> clickedIn, boolean isShiftDown)
1466 {
1467
1468 // If the user clicked into a widgets free space...
1469 if (clicked == null && _lastClickedIn != null
1470 && _lastClickedIn.size() >= 4) {
1471
1472 // Check to see if the use clicked into a widgets empty space
1473/* InteractiveWidget iw = null; TODO: Reinstate. cts16
1474
1475 for (Item i : _lastClickedIn) {
1476
1477 if (i instanceof WidgetCorner) {
1478 iw = ((WidgetCorner) i).getWidgetSource();
1479 break;
1480 } else if (i instanceof WidgetEdge) {
1481 iw = ((WidgetEdge) i).getWidgetSource();
1482 break;
1483 }
1484 }
1485
1486 if (iw != null) {
1487
1488 // Handle dropping items on widgets
1489 if(iw.ItemsMiddleClickDropped()) {
1490 return;
1491 }
1492 }*/
1493 }
1494 // if the cursor has Items attached
1495 if (FreeItems.hasItemsAttachedToCursor()) {
1496 // if the user is pointing at something, merge the items (if possible)
1497 if (doMerging(clicked)) {
1498 IndirectMouseActions.getInstance().getDeleteItemsAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1499 } else {
1500 IndirectMouseActions.getInstance().getAnchorFreeItemsAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1501 }
1502 // otherwise if the user is pointing at something, pick it up unless shift is down
1503 } else {
1504 if (clicked instanceof Picture) {
1505 int mouseX = DisplayIO.getMouseX();
1506 int mouseY = FrameMouseActions.getY();
1507 Picture clickedOnPicture = (Picture)clicked;
1508 Frame current_frame = DisplayIO.getCurrentFrame();
1509 Colour bg_col = current_frame.getBackgroundColor();
1510 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX,mouseY,bg_col)) {
1511 clicked = null; // Effectively make it as if they haven't clicked on anything
1512 }
1513 }
1514 if (clicked != null && !isShiftDown) {
1515
1516 // check permissions
1517 if (!clicked.hasPermission(UserAppliedPermission.full)) {
1518 Item editTarget = clicked.getEditTarget();
1519 if (editTarget != clicked && editTarget.hasPermission(UserAppliedPermission.full)) {
1520 clicked = editTarget;
1521 } else {
1522 MessageBay.displayMessage("Insufficient permission to pick up item");
1523 return;
1524 }
1525 }
1526 IndirectMouseActions.getInstance().getDetachLineAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1527 // if we're inside a shape, pick it up unless shift is down
1528 } else if (clickedIn != null && !isShiftDown) {
1529 IndirectMouseActions.getInstance().getGroupPickupAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1530 } else {
1531 IndirectMouseActions.getInstance().getNewLineAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1532 }
1533 }
1534 SessionStats.MovedItems(FreeItems.getInstance());
1535 }
1536
1537 /**
1538 * This method handles all right-click action
1539 */
1540 private void rightButton(Item clicked, Collection<Item> clickedIn) {
1541
1542 // If the user clicked into a widgets free space...
1543 if (clicked == null && _lastClickedIn != null
1544 && _lastClickedIn.size() >= 4) {
1545
1546 // Check to see if the use clicked into a widgets empty space
1547/* InteractiveWidget iw = null; TODO: Reinstate. cts16
1548
1549 for (Item i : _lastClickedIn) {
1550
1551 if (i instanceof WidgetCorner) {
1552 iw = ((WidgetCorner) i).getWidgetSource();
1553 break;
1554 } else if (i instanceof WidgetEdge) {
1555 iw = ((WidgetEdge) i).getWidgetSource();
1556 break;
1557 }
1558 }
1559
1560 if (iw != null) {
1561
1562 // Handle dropping items on widgets
1563 if(iw.ItemsRightClickDropped()) {
1564 return;
1565 }
1566 }*/
1567 }
1568
1569 // if the cursor has Items attached, then anchor a copy of them
1570
1571 List<Item> copies = null;
1572 if (FreeItems.hasItemsAttachedToCursor()) {
1573 if (FreeItems.getInstance().size() == 1 && FreeItems.getItemAttachedToCursor().isAutoStamp()) {
1574 // Dont stamp if the user is painting... because we dont want to
1575 // save any of the items created!
1576 return;
1577 // if the user is clicking on something, merge the items unless
1578 // it is a point onto something other than a lineEnd or a dot
1579 } else if (clicked != null
1580 // TODO Change the items merge methods so the logic is simplified
1581 && (!(FreeItems.getItemAttachedToCursor() instanceof Dot)
1582 || clicked instanceof Dot || clicked.isLineEnd())) {
1583 // check permissions
1584 if (!clicked.hasPermission(UserAppliedPermission.full) && clicked.getParent().getNameItem() != clicked) {
1585 MessageBay.displayMessage("Insufficient permission to merge items");
1586 return;
1587 }
1588 if (clicked instanceof Text || clicked instanceof Dot || clicked instanceof XRayable) {
1589 if (isRubberBandingCorner()) {
1590 copies = IndirectMouseActions.getInstance().getMergeGroupAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1591 // line onto something
1592 } else if (FreeItems.getInstance().size() == 2 /* && clicked instanceof XRayable */) {
1593 copies = IndirectMouseActions.getInstance().getMergeTwoItemsAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1594 } else if (FreeItems.getInstance().size() == 1) {
1595 copies = IndirectMouseActions.getInstance().getMergeSingleItemAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1596 } else {
1597 copies = IndirectMouseActions.getInstance().getStampAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1598 }
1599 } else {
1600 copies = ItemUtils.UnreelLine(FreeItems.getInstance(), _controlDown);
1601 if (copies == null) copies = copy(FreeItems.getInstance());
1602 for (Item i : copies) {
1603 i.setOffset(0, 0);
1604 }
1605 anchor(FreeItems.getInstance());
1606 FreeItems.getInstance().clear();
1607 pickup(copies);
1608 }
1609 // otherwise, anchor the items
1610 } else {
1611 // check if this is anchoring a rectangle
1612 if (isRubberBandingCorner()) {
1613 copies = IndirectMouseActions.getInstance().getRubberBandingCornerAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1614 } else {
1615 if (rubberBanding()) {
1616 copies = IndirectMouseActions.getInstance().getRubberBandingCopyAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1617 } else if (_extrude) {
1618 copies = IndirectMouseActions.getInstance().getExtrudeAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1619 } else {
1620 stampItemsOnCursor(true);
1621 copies = FreeItems.getInstance();
1622 }
1623 }
1624 }
1625 } else {
1626
1627 if (clicked instanceof Picture) {
1628 int mouseX = DisplayIO.getMouseX();
1629 int mouseY = FrameMouseActions.getY();
1630 Picture clickedOnPicture = (Picture)clicked;
1631 Frame current_frame = DisplayIO.getCurrentFrame();
1632 Colour bg_col = current_frame.getBackgroundColor();
1633 if (clickedOnPicture.MouseOverBackgroundPixel(mouseX,mouseY,bg_col)) {
1634 clicked = null; // Effectively make it as if they haven't clicked on anything
1635 }
1636 }
1637
1638 // if the user is pointing at something and shift isn't down, make a copy
1639 if (clicked != null && !isShiftDown()) {
1640 // check permissions
1641 if (clicked.isLineEnd()) {
1642 if (!clicked.hasPermission(UserAppliedPermission.full)) {
1643 MessageBay.displayMessage("Insufficient permission to unreel");
1644 return;
1645 }
1646 } else if (!clicked.hasPermission(UserAppliedPermission.copy)) {
1647 Item editTarget = clicked.getEditTarget();
1648 if (editTarget != clicked
1649 && editTarget.hasPermission(UserAppliedPermission.copy)) {
1650 clicked = editTarget;
1651 } else {
1652 MessageBay.displayMessage("Insufficient permission to copy");
1653 return;
1654 }
1655 }
1656
1657 copies = IndirectMouseActions.getInstance().getMakeCopyAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1658 } else {
1659 // if user is pointing in a closed shape and shift isn't down, make a copy of the items inside
1660 if (clickedIn != null && !isShiftDown()) {
1661 IndirectMouseActions.getInstance().getMakeGroupCopyAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1662 // otherwise, create a rectangle
1663 } else {
1664 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), MouseX, MouseY, true);
1665 // if its on a line then create a line from that line
1666 if (on instanceof Line && on.hasPermission(UserAppliedPermission.full)) {
1667 IndirectMouseActions.getInstance().getExtendLineAction().exec(new MouseInfo(on, clickedIn, false, false));
1668 }
1669 copies = IndirectMouseActions.getInstance().getCreateRectangleAction().exec(new MouseInfo(clicked, clickedIn, false, false));
1670 }
1671 }
1672 }
1673 getInstance().refreshHighlights();
1674 SessionStats.CopiedItems(copies);
1675 updateCursor();
1676 FrameGraphics.Repaint();
1677 }
1678
1679 /**
1680 * @param enclosedItems
1681 * @return
1682 */
1683 private static Collection<Item> getFullyEnclosedItems(
1684 Collection<Item> enclosure) {
1685 // copy the enclosedItems because the list will be modified
1686 Collection<Item> enclosedItems = new LinkedHashSet<Item>(enclosure);
1687 Collection<Item> toCopy = new LinkedHashSet<Item>(enclosedItems.size());
1688
1689 while (enclosedItems.size() > 0) {
1690 Item i = enclosedItems.iterator().next();
1691 if (i.hasPermission(UserAppliedPermission.copy)) {
1692 Collection<Item> items = i.getAllConnected();
1693 // Only copy if the entire shape is enclosed
1694 if (enclosedItems.containsAll(items)) {
1695 toCopy.addAll(items);
1696 }
1697 enclosedItems.removeAll(items);
1698 } else {
1699 enclosedItems.remove(i);
1700 }
1701 }
1702 return toCopy;
1703 }
1704
1705// public void mouseExited(MouseEvent e) {
1706// }
1707
1708 private boolean _overFrame;
1709 private int panStartX, panStartY;
1710 private boolean _isPanOp;
1711
1712 public void mouseDragged(MouseEvent e) {
1713 _lastMouseDragged = e;
1714 //System.out.println("MouseDragged");
1715
1716 // Stop the longDepress mouse timer if the user drags above a threshold
1717 if (_MouseTimer.isRunning()) {
1718 if (Math.abs(e.getX() - _lastMouseClick.getX()) + Math.abs(e.getY() - _lastMouseClick.getY()) > 10) {
1719 _MouseTimer.stop();
1720 }
1721 }
1722
1723 if (_autoStamp) {
1724 stampItemsOnCursor(false);
1725 }
1726
1727 // Have the free items follow the cursor if the user clicks in freespace then moves.
1728 if (FreeItems.getInstance().size() > 0 && _lastClickedOn == null) {
1729 mouseMoved(e);
1730 return;
1731 }
1732
1733 // panning the frame when dragging the mouse while shift-leftclicking
1734/* if(ExperimentalFeatures.MousePan.get() && _overFrame && e.isShiftDown() &&
1735 (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0 &&
1736 (_isPanOp || (Math.max(Math.abs(panStartX - e.getX()), Math.abs(panStartY - e.getY())) > 5)))
1737 {
1738 int dX = (int) (e.getX() - MouseX);
1739 int dY = (int) (e.getY() - MouseY);
1740 Misc.pan(DisplayIO.getCurrentFrame(), dX, dY);
1741 MouseX = e.getX();
1742 MouseY = e.getY();
1743 _isPanOp = true;
1744 }*/
1745
1746 // check if user is dragging across a text item
1747 if (_lastRanged != null) {
1748 // System.out.println(MouseY - e.getY());
1749
1750 MouseX = e.getX();
1751 MouseY = e.getY();
1752
1753 int distance = _lastRanged.getY() - FrameMouseActions.getY();
1754 if (distance <= 0) distance = FrameMouseActions.getY() - _lastRanged.getY() - _lastRanged.getBoundsHeight();
1755
1756 if (distance > UserSettings.NoOpThreshold.get()) {
1757 _lastRanged.clearSelectionEnd();
1758 _isNoOp = true;
1759 } else {
1760 // update the ranged section
1761 _lastRanged.setSelectionEnd(DisplayIO.getMouseX(), DisplayIO.getMouseY());
1762 _isNoOp = false;
1763 }
1764
1765 DisplayIO.setTextCursor(_lastRanged, Text.NONE, false, e.isShiftDown(), e.isControlDown(), false);
1766 FrameGraphics.Repaint();
1767 return;
1768 }
1769
1770 // if the user is dragging across a picture
1771 if (_lastCropped != null) {
1772 // If shift is down then the distance moved is the same in the x and y
1773 MouseX = e.getX();
1774 MouseY = e.getY();
1775
1776 if (e.isControlDown()) {
1777 int deltaX = Math.abs(e.getX() - _lastMouseClick.getX());
1778 int deltaY = Math.abs(e.getY() - _lastMouseClick.getY());
1779 if (deltaX > deltaY) {
1780 MouseY = _lastMouseClick.getY() + deltaX * (e.getY() > _lastMouseClick.getY() ? 1 : -1);
1781 } else {
1782 MouseX = _lastMouseClick.getX() + deltaY * (e.getX() > _lastMouseClick.getX() ? 1 : -1);
1783 }
1784 }
1785 // update the ranged section
1786 _lastCropped.setEndCrop(DisplayIO.getMouseX(), DisplayIO.getMouseY());
1787
1788 FrameGraphics.Repaint();
1789 return;
1790 }
1791
1792 // This is the context of a user clicking in freespace an dragging onto
1793 // the edge of a line
1794 if ( (_mouseDown == MouseEvent.BUTTON2 || _mouseDown == MouseEvent.BUTTON3) &&
1795 _lastClickedOn == null &&
1796 _lastClickedIn == null)
1797 {
1798 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), e.getX(), e.getY(), true);
1799
1800 if (FreeItems.getInstance().size() == 0) {
1801 // if the user can spot-weld, show the virtual spot
1802 if (on instanceof Line) {
1803 Line line = (Line) on;
1804 line.showVirtualSpot(e.getX(), e.getY());
1805 }
1806 if (on != null && on.isLineEnd()) {
1807 _lastHighlightedItem = on;
1808 on.setHighlightMode(Item.HighlightMode.Normal);
1809 } else if (_lastHighlightedItem != null) {
1810 _lastHighlightedItem.setHighlightMode(Item.HighlightMode.None);
1811 _lastHighlightedItem = null;
1812 }
1813 }
1814 }
1815
1816 // Use the below calculation for better speed. If it causes problems
1817 // switch back to the Euclidean distance calculation
1818 if ( Math.abs(MouseX - e.getX()) > UserSettings.NoOpThreshold.get() ||
1819 Math.abs(MouseY - e.getY()) > UserSettings.NoOpThreshold.get())
1820 {
1821 _isNoOp = true;
1822 }
1823
1824 FrameGraphics.Repaint();
1825 }
1826
1827 private static MouseEvent _lastMouseMoved = null;
1828
1829 private static Timer _autoStampTimer = new Timer(200, new ActionListener() {
1830 public void actionPerformed(ActionEvent ae) {
1831 stampItemsOnCursor(false);
1832 }
1833 });
1834
1835 private static boolean _controlDown;
1836
1837 private static boolean _shiftDown;
1838
1839 private static OnNewFrameAction _onFrameAction = null;
1840
1841 public static void setTDFCAction(OnNewFrameAction action) {
1842 _onFrameAction = action;
1843 }
1844
1845 /**
1846 * Updates the stored mouse position and highlights any items as necessary.
1847 */
1848 public void mouseMoved(MouseEvent e) {
1849 mouseMoved(e, false);
1850 }
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863 /*
1864 * private boolean mouseMovedRecently() { Date now = new Date();
1865 *
1866 * return now.getTime() - _lastMouseMovement.getTime() < 150; }
1867 */
1868
1869 public void mouseWheelMoved(MouseWheelEvent arg0) {
1870 Navigation.ResetLastAddToBack();
1871
1872 int clicks = Math.abs(arg0.getWheelRotation());
1873
1874 if (FreeItems.getInstance().size() == 2) {
1875 if ((FreeItems.getInstance().get(0).isLineEnd() && FreeItems.getInstance().get(1) instanceof Line) ||
1876 (FreeItems.getInstance().get(1).isLineEnd() && FreeItems.getInstance().get(0) instanceof Line))
1877 {
1878 Line line;
1879 if (FreeItems.getInstance().get(0) instanceof Line)
1880 line = (Line) FreeItems.getInstance().get(0);
1881 else
1882 line = (Line) FreeItems.getInstance().get(1);
1883
1884 // User must do multiple clicks to toggle the line
1885 if (clicks >= MOUSE_WHEEL_THRESHOLD) {
1886 if (arg0.isShiftDown())
1887 line.toggleArrowHeadRatio(arg0.getWheelRotation());
1888 else
1889 line.toggleArrowHeadLength(arg0.getWheelRotation());
1890 // line.getParent().change();
1891 FrameGraphics.Repaint();
1892 }
1893 }
1894 } else if (arg0.getWheelRotation() != 0 && arg0.isShiftDown()) {
1895
1896 FunctionKey rotationType = FunctionKey.SizeUp;
1897 if (arg0.getWheelRotation() > 0) {
1898 rotationType = FunctionKey.SizeDown;
1899 }
1900
1901 FrameKeyboardActions.functionKey(rotationType, 1, arg0.isShiftDown(), arg0.isControlDown());
1902
1903 } else if (clicks >= MOUSE_WHEEL_THRESHOLD) {
1904 Item item = FrameUtils.getCurrentItem();
1905
1906 // if the user is not pointing to any item
1907 if (item == null) {
1908 FrameKeyboardActions.NextTextItem(null,
1909 arg0.getWheelRotation() > 0);
1910 return;
1911 }
1912
1913 if (item instanceof Line || item instanceof Circle) {
1914 // check permissions
1915 if (!item.hasPermission(UserAppliedPermission.full)) {
1916 MessageBay
1917 .displayMessage("Insufficient permission to edit the Line");
1918 return;
1919 }
1920 item.toggleDashed(arg0.getWheelRotation());
1921 item.getParent().change();
1922 FrameGraphics.Repaint();
1923 return;
1924 } else if (item instanceof Text) {
1925 FrameKeyboardActions.NextTextItem(item,
1926 arg0.getWheelRotation() > 0);
1927 }
1928 }
1929 }
1930
1931 /**
1932 *
1933 * @return the integer value for the last mouse button clicked.
1934 */
1935 public static int getLastMouseButton() {
1936 if (_lastMouseClick == null) return MouseEvent.NOBUTTON;
1937
1938 return _lastMouseClick.getButton();
1939 }
1940
1941 public static boolean isDelete(int modifiersEx) {
1942 int onMask = MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
1943 return (modifiersEx & onMask) == onMask;
1944 }
1945
1946 public static boolean isGetAttributes(int modifiersEx) {
1947 int onMask = MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
1948 return (modifiersEx & onMask) == onMask;
1949 }
1950
1951 public static boolean isTwoClickNoOp(int modifiersEx) {
1952 int onMask = MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
1953 return (modifiersEx & onMask) == onMask;
1954 }
1955
1956 public static boolean wasDeleteClicked() {
1957 if (_lastMouseClick == null)
1958 return false;
1959 return isDelete(_lastMouseClickModifiers);
1960 }
1961
1962 public static void control(KeyEvent ke) {
1963 for (Item i : FreeItems.getInstance()) {
1964 i.invalidateCommonTrait(ItemAppearence.PreMoved);
1965 }
1966
1967 for (Item i : FreeItems.getCursor()) {
1968 i.invalidateCommonTrait(ItemAppearence.PreMoved);
1969 }
1970
1971 _controlDown = ke.isControlDown();
1972
1973 if (_controlDown) {
1974 // TODO why are these two lines needed?!?!
1975 // _offX = 0;
1976 // _offY = 0;
1977 } else {
1978 resetOffset();
1979 }
1980
1981 if (_mouseDown > 0 && _lastMouseDragged != null) {
1982 MouseEvent me = _lastMouseDragged;
1983 _lastMouseDragged = new MouseEvent(ke.getComponent(),
1984 MouseEvent.NOBUTTON, ke.getWhen(), ke.getModifiers(), me
1985 .getX(), me.getY(), 0, false);
1986 _instance.mouseDragged(_lastMouseDragged);
1987 } else if (_lastMouseMoved != null) {
1988 MouseEvent me = _lastMouseMoved;
1989 _lastMouseMoved = new MouseEvent(ke.getComponent(),
1990 MouseEvent.NOBUTTON, ke.getWhen(), ke.getModifiers(), me
1991 .getX(), me.getY(), 0, false);
1992 _instance.mouseMoved(_lastMouseMoved, true);
1993 }
1994 updateCursor();
1995
1996 Help.updateStatus();
1997
1998 for (Item i : FreeItems.getInstance()) {
1999 i.invalidateCommonTrait(ItemAppearence.PostMoved);
2000 }
2001
2002 for (Item i : FreeItems.getCursor()) {
2003 i.invalidateCommonTrait(ItemAppearence.PostMoved);
2004 }
2005 // TODO: Check why the new constrained line is not repainted immediately
2006 FrameGraphics.requestRefresh(true);
2007 FrameGraphics.refresh(true);
2008 }
2009
2010 public static int getX() {
2011 return Math.round(MouseX);
2012 }
2013
2014 public static int getY() {
2015 return Math.round(MouseY);
2016 }
2017
2018 public static Point getPosition() {
2019 return new Point(getX(), getY());
2020 }
2021
2022 public static Point getFreeItemsOffset() {
2023 return new Point(_offX, _offY);
2024 }
2025
2026 public static void shift(KeyEvent e) {
2027 _shiftDown = e.isShiftDown();
2028 Help.updateStatus();
2029 getInstance().refreshHighlights();
2030 }
2031
2032 @Override
2033 public void onGesture(Gesture gesture) {
2034 // TODO Auto-generated method stub
2035
2036 }
2037}
Note: See TracBrowser for help on using the repository browser.