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

Last change on this file since 167 was 167, checked in by bjn8, 16 years ago

Improvements for widgets and popups

File size: 81.9 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Point;
4import java.awt.event.ActionEvent;
5import java.awt.event.ActionListener;
6import java.awt.event.KeyEvent;
7import java.awt.event.MouseEvent;
8import java.awt.event.MouseListener;
9import java.awt.event.MouseMotionListener;
10import java.awt.event.MouseWheelEvent;
11import java.awt.event.MouseWheelListener;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Date;
15import java.util.HashSet;
16import java.util.Iterator;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Set;
21
22import javax.swing.Timer;
23
24import org.expeditee.actions.Actions;
25import org.expeditee.actions.NavigationActions;
26import org.expeditee.items.Circle;
27import org.expeditee.items.Constraint;
28import org.expeditee.items.Dot;
29import org.expeditee.items.InteractiveWidgetInitialisationFailedException;
30import org.expeditee.items.InteractiveWidgetNotAvailableException;
31import org.expeditee.items.Item;
32import org.expeditee.items.ItemAppearence;
33import org.expeditee.items.ItemUtils;
34import org.expeditee.items.Line;
35import org.expeditee.items.Permission;
36import org.expeditee.items.Picture;
37import org.expeditee.items.Text;
38import org.expeditee.items.WidgetCorner;
39import org.expeditee.items.WidgetEdge;
40import org.expeditee.items.XRayable;
41import org.expeditee.items.Item.HighlightMode;
42import org.expeditee.stats.SessionStats;
43
44public class FrameMouseActions implements MouseListener, MouseMotionListener,
45 MouseWheelListener {
46
47 private static int _lastMouseClickModifiers = 0;
48
49 private static MouseEvent _lastMouseDragged;
50
51 private FrameMouseActions() {
52 }
53
54 private static FrameMouseActions _instance = null;
55
56 public static FrameMouseActions getInstance() {
57 if (_instance == null)
58 _instance = new FrameMouseActions();
59 return _instance;
60 }
61
62 private static final int RECTANGLE_CORNERS = 4;
63
64 // TODO say where/how used
65 private static final int MOUSE_WHEEL_THRESHOLD = 3;
66
67 private static final int MINIMUM_RANGE_DEPRESS_TIME = 250;
68
69 private static final int RECTANGLE_TO_POINT_THRESHOLD = 20;
70
71 private static Date _lastMouseClickDate = new Date();
72
73 public static final int LITTLE_MOUSE_PAUSE = 500;
74
75 public static final int ZERO_MOUSE_PAUSE = 0;
76
77 public static final int BIG_MOUSE_PAUSE = 750;
78
79 public static final int CONTEXT_FREESPACE = 0;
80
81 public static final int CONTEXT_AT_TEXT = 1;
82
83 public static final int CONTEXT_AT_LINE = 2;
84
85 public static final int CONTEXT_AT_DOT = 3;
86
87 public static final int CONTEXT_AT_ENCLOSURE = 4;
88
89 public static int _alpha = -1;
90
91 /**
92 * The last known mouse X coordinate
93 */
94 public static float MouseX;
95
96 /**
97 * The last known mouse Y coordinate. Relative to the top of the
98 * application.
99 */
100 public static float MouseY;
101
102 // Distance of mouse cursor from the origin of the item that was picked up
103 // The are used in the move method to calculate the distance moved by the
104 // cursor
105 private static int _offX;
106
107 private static int _offY;
108
109 // Keeps track of mouse button events when a delete occurs
110 private static boolean _isDelete = false;
111
112 // Keeps track of mouse button events when the user extracts attributes
113 // occurs
114 private static boolean _isAttribute = false;
115
116 /**
117 * A flag to indicate that the last mouseUp event was part of a two button
118 * click sequence hence the next mouse up should be ignored*
119 */
120 private static boolean _wasDouble = false;
121
122 private static boolean _isNoOp = false;
123
124 private static boolean _extrude = false;
125
126 // keeps track of the last highlighted Item
127 private static Item _lastHighlightedItem = null;
128
129 // keeps track of the item being 'ranged out' if there is one.
130 private static Text _lastRanged = null;
131
132 // keeps track of the picture being cropped if there is one
133 private static Picture _lastCropped = null;
134
135 // true if lastItem only has highlighting removed when a new item is
136 // highlighted
137 private static boolean _lastHoldsHighlight = false;
138
139 private static boolean _forceArrowCursor = true;
140
141 // the current context of the cursor
142 private static int _context = 0;
143
144 public static void setForceArrow(boolean val) {
145 _forceArrowCursor = val;
146 }
147
148 public static int getContext() {
149 return _context;
150 }
151
152 static int _mouseDown = 0;
153
154 private static MouseEvent _lastMouseClick = null;
155
156 private static Item _lastClickedOn = null;
157
158 private static Collection<Item> _lastClickedIn = null;
159
160 private static boolean _pulseOn = false;
161
162 private static final int PULSE_AMOUNT = 2;
163
164 private static Timer _MouseTimer = new Timer(LITTLE_MOUSE_PAUSE,
165 new ActionListener() {
166 public void actionPerformed(ActionEvent ae) {
167 // check if we are in free space
168 if (_lastClickedOn == null
169 && FreeItems.getInstance().size() == 0) {
170 // System.out.println("SuperBack!");
171 _MouseTimer.setDelay(ZERO_MOUSE_PAUSE);
172 back();
173 } else {
174 if (FrameUtils.getCurrentItem() == null) {
175 // Check if we are toggling arrowhead
176 if (FreeItems.getInstance().size() <= 2) {
177 for (Item i : FreeItems.getInstance()) {
178 if (i instanceof Line) {
179 ((Line) i).toggleArrow();
180 }
181 }
182 FrameGraphics.Repaint();
183 }
184 }
185 _MouseTimer.stop();
186 }
187 }
188 });
189
190 private static void setPulse(boolean pulseOn) {
191 if (_pulseOn == pulseOn)
192 return;
193 int amount = PULSE_AMOUNT;
194 if (!pulseOn)
195 amount *= -1;
196 _pulseOn = pulseOn;
197
198 if (_lastClickedOn != null) {
199 for (Item i : _lastClickedOn.getAllConnected()) {
200 if (i instanceof Line) {
201 Line line = (Line) i;
202 line.setThickness(line.getThickness() + amount);
203 }
204 }
205 }
206 FrameGraphics.Repaint();
207 }
208
209 private static Timer _ExtrudeMouseTimer = new Timer(BIG_MOUSE_PAUSE,
210 new ActionListener() {
211 public void actionPerformed(ActionEvent ae) {
212 setPulse(true);
213 _extrude = true;
214 _ExtrudeMouseTimer.stop();
215 }
216 });
217
218 public void mouseClicked(MouseEvent e) {
219 }
220
221 /**
222 * Each Item on the Frame is checked to determine if the mouse x,y
223 * coordinates are on the Item (or within the Shape surrounding it). If the
224 * coordinates are on the Item then the Item is checked for a link, if it
225 * has a link the link is followed, if not, nothing is done.
226 */
227 public void mousePressed(MouseEvent e) {
228 ProccessMousePressedEvent(e, e.getModifiersEx());
229 }
230
231 public void ProccessMousePressedEvent(MouseEvent e, int modifiersEx) {
232 // System.out.println("MousePressed " + e.getX() + "," + e.getY() + " "
233 // + e.getWhen());
234
235 // TODO WHY DID I NOT COMMENT THIS LINE!! MIKE SAYS
236 if (LastRobotX != null) {
237 _RobotTimer.stop();
238 LastRobotX = null;
239 LastRobotY = null;
240 mouseMoved(e);
241 }
242
243 NavigationActions.ResetLastAddToBack();
244 // System.out.println(modifiersEx);
245 if (_mouseDown == 0)
246 _lastMouseClickDate = new Date();
247
248 int buttonPressed = e.getButton();
249 _mouseDown += buttonPressed;
250 _lastClickedOn = FrameUtils.getCurrentItem();
251 // load any frame if necessary
252 Item on = _lastClickedOn;
253
254 _lastClickedIn = FrameUtils.getCurrentItems(on);
255 // if (_lastClickedIn != null){
256 // System.out.println(_lastClickedIn.size());}
257
258 SessionStats.MouseClicked(e.getButton());
259 if (buttonPressed == MouseEvent.BUTTON1) {
260 SessionStats.AddFrameEvent("Ld");
261 _extrude = false;
262 } else if (buttonPressed == MouseEvent.BUTTON2) {
263 SessionStats.AddFrameEvent("Md");
264 _extrude = false;
265 } else if (buttonPressed == MouseEvent.BUTTON3) {
266 SessionStats.AddFrameEvent("Rd");
267 }
268
269 // Mike says...
270 // For somereason the modifiers for e are different from modifiersEx
271 // The SwingUtilities.convertMouseEvent method changes the modifiers
272 _lastMouseClick = e;
273 _lastMouseClickModifiers = modifiersEx;
274
275 // Only start the timer when in free space when the user double clicks
276 // to do super back
277 // TODO change this so that there are separate timers for super back and
278 // the other
279 // Long depress actions if that is what is wanted.
280 if (_lastClickedOn == null && FreeItems.getInstance().size() == 0) {
281 // System.out.println(e.getClickCount());
282 if (e.getClickCount() >= 2) {
283 _MouseTimer.start();
284 }
285 } else if (_lastClickedOn != null
286 && FreeItems.getInstance().size() == 0
287 && e.getButton() == MouseEvent.BUTTON3) {
288 _ExtrudeMouseTimer.start();
289
290 } else {
291 _MouseTimer.start();
292 }
293
294 // pre-cache the frame if it is linked
295 if (on != null && on.getLink() != null && on.isLinkValid()) {
296 FrameIO.Precache(on.getAbsoluteLink());
297 }
298
299 // check for delete command
300 if (isDelete(modifiersEx)) {
301 _isDelete = true;
302 // _lastRanged = null;
303 _lastCropped = null;
304 _wasDouble = false;
305 // check for attributes command
306 } else if (isGetAttributes(modifiersEx)) {
307 _isAttribute = true;
308 _wasDouble = false;
309 } else if (isTwoClickNoOp(modifiersEx)) {
310 _isAttribute = false;
311 _wasDouble = false;
312 _isDelete = false;
313 _isNoOp = true;
314 } else
315 _isDelete = false;
316
317 // This must happen before the previous code
318 // This is when the user is anchoring something
319 if (buttonPressed != MouseEvent.BUTTON1
320 && (_context == CONTEXT_FREESPACE || _context == CONTEXT_AT_ENCLOSURE)
321 && FreeItems.itemAttachedToCursor()) {
322 FrameGraphics.changeHighlightMode(_lastHighlightedItem,
323 Item.HighlightMode.None);
324
325 _lastHighlightedItem = FreeItems.getItemAttachedToCursor();
326 for (Item i : FreeItems.getInstance()) {
327 i.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
328 }
329 FrameGraphics.Repaint();
330 // this is when the user is picking something up
331 } else if (_lastHighlightedItem != null) {
332 if (!(_lastHighlightedItem instanceof Line)) {
333 _lastHighlightedItem
334 .setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
335 } else {
336 for (Item i : _lastHighlightedItem.getAllConnected()) {
337 i.setHighlightColor(Item.DEPRESSED_HIGHLIGHT);
338 }
339 }
340 FrameGraphics.Repaint();
341 }
342
343 // if the user is ranging text
344 if (on != null && on instanceof Text && !_isDelete) {
345 _lastRanged = (Text) on;
346 // set start-drag point
347 _lastRanged.setSelectionStart(DisplayIO.getMouseX(),
348 FrameMouseActions.getY());
349 }
350
351 if (on != null && on instanceof Picture
352 && e.getButton() == MouseEvent.BUTTON3 && !_isDelete) {
353 _lastCropped = (Picture) on;
354 // set start crop point
355 _lastCropped.setStartCrop(DisplayIO.getMouseX(), FrameMouseActions
356 .getY());
357 _lastCropped.setShowCrop(true);
358 }
359 }
360
361 // This is where all the processing happens
362 public void mouseReleased(MouseEvent e) {
363
364 // System.out.println("Released " + e.getX() + "," + e.getY() + " " +
365 // e.getWhen());
366 FrameUtils.ResponseTimer.restart();
367
368 // Auto-hide popups when user clicks into expeditee world
369 // If the user clicks into empty space and a popup-is showing, then
370 // the user porbably wants to click away the popup - therefore ignore the event
371 boolean shouldConsume = PopupManager.getInstance().shouldConsumeBackClick();
372 PopupManager.getInstance().hideAllPopups();
373 if (shouldConsume && e.getButton() == MouseEvent.BUTTON1) {
374 return; // consume back click event
375 }
376
377 // _lastMovedDistance = new Point(e.getX() - _lastMouseClick.getX(), e
378 // .getY()
379 // - _lastMouseClick.getY());
380
381 _mouseDown -= e.getButton();
382 updateCursor();
383
384 // System.out.println(e.getX() + ", " + e.getY());
385
386 Text lastRanged = _lastRanged;
387 _lastRanged = null;
388 // Dont do ranging if the user moves really quickly...
389 // They are probably trying to pick something up in this case
390 if (lastRanged != null) {
391 long depressTime = (new Date()).getTime()
392 - _lastMouseClickDate.getTime();
393 // double changeInDistance =
394 // e.getPoint().distance(_currentMouseClick.getPoint());
395 // double speed = changeInDistance * 1000 / changeInTime;
396
397 // System.out.println(depressTime);
398
399 if (depressTime < MINIMUM_RANGE_DEPRESS_TIME
400 || lastRanged.getSelectionSize() <= 0) {// Text.MINIMUM_RANGED_CHARS)
401 // {
402 lastRanged.clearSelection();
403 lastRanged = null;
404 }
405 }
406
407 _ExtrudeMouseTimer.stop();
408 _MouseTimer.stop();
409 setPulse(false);
410
411 // if the last action was a delete, then ignore the next mouseup
412 if (_wasDouble) {
413 _wasDouble = false;
414 return;
415 }
416
417 // This code must come after the _wasDouble code...
418 // Otherwise get Stopping Agent method after doing the left+right format
419 // shortcut
420 if (Actions.isAgentRunning()) {
421 Actions.stopAgent();
422 return;
423 }
424
425 /*
426 * if (_isNoOp) { if (e.getButton() != MouseEvent.NOBUTTON) { _isNoOp =
427 * false; _wasDouble = true; // lastRanged.clearSelection();
428 * FrameGraphics.Repaint(); return; } }
429 */
430
431 // get whatever the user was pointing at
432 Item clickedOn = _lastClickedOn;
433 Collection<Item> clickedIn = _lastClickedIn;
434
435 MouseX = e.getX();
436 MouseY = e.getY();
437
438 Item releasedOn = FrameUtils.getCurrentItem();
439 Collection<Item> releasedIn = FrameUtils.getCurrentItems(releasedOn);
440
441 // Only a no op if user releases in free space!
442 if (_isNoOp && (releasedOn == null && releasedIn == null)) {
443 if (_isDelete) {
444 _isDelete = false;
445 _wasDouble = true;
446 }
447
448 _isNoOp = false;
449
450 if (_lastHighlightedItem != null)
451 FrameGraphics.changeHighlightMode(_lastHighlightedItem,
452 Item.HighlightMode.None);
453
454 if (FreeItems.itemAttachedToCursor()) {
455 move(FreeItems.getInstance());
456 }
457
458 MessageBay
459 .displayMessage("Action cancelled, mouse moved more than "
460 + UserSettings.NoOpThreshold + " pixels.");
461 FrameGraphics.Repaint();
462 return;
463 } else {
464 _isNoOp = false;
465 }
466
467 // if this is a delete command
468 if (_isDelete) {
469 if (lastRanged != null) {
470
471 Item i = FreeItems.getItemAttachedToCursor();
472 if (i != null && i instanceof Text) {
473 lastRanged.replaceSelectedText(((Text) i).getText());
474 FreeItems.getInstance().clear();
475 } else
476 lastRanged.cutSelectedText();
477 lastRanged.clearSelection();
478 FrameGraphics.Repaint();
479
480 } else {
481 delete(clickedOn);
482 }
483 _wasDouble = true;
484 _isDelete = false;
485 return;
486 }
487
488 // if this is an attribute extraction command
489 if (_isAttribute) {
490 if (clickedOn == null) {
491 Frame current = DisplayIO.getCurrentFrame();
492 Actions.PerformAction(current, null, "Format");
493 } else {
494 extractAttributes(clickedOn);
495 }
496 // if the user dragged and displayed some cropping with left and
497 // right button is a no op for now
498 // but later could make this the shrinkTo context
499 if (_lastCropped != null) {
500 _lastCropped.clearCropping();
501 _lastCropped = null;
502 }
503 _wasDouble = true;
504 _isAttribute = false;
505 return;
506 }
507
508 // if the user is ranging-out text
509 if (lastRanged != null) {
510 if (e.getButton() == MouseEvent.BUTTON1) {
511 return;
512 }
513
514 Text ranged = lastRanged.copy();
515
516 // if the user is cutting text from the item
517 if (e.getButton() == MouseEvent.BUTTON2) {
518 // Check if the user is trying to range an item for which they
519 // do not have permission to do so... or it is the frame name
520 if (!lastRanged.hasPermission(Permission.full)
521 || lastRanged.isFrameName()) {
522 MessageBay
523 .displayMessage("Insufficient permission to cut text");
524 lastRanged.clearSelection();
525 FrameGraphics.Repaint();
526 return;
527 }
528 // if the entire text is selected and its not a line end then
529 // pickup the item
530 boolean entireText = lastRanged.getSelectionSize() == lastRanged
531 .getLength();
532 if (entireText && !lastRanged.isLineEnd()) {
533 pickup(lastRanged);
534 lastRanged.clearSelection();
535 FrameGraphics.refresh(true);
536 return;
537 } else {
538 ranged.setText(lastRanged.cutSelectedText());
539 ranged.setWidth(lastRanged.getWidth());
540 // If its the whole text then replace last ranged with a dot
541 if (entireText) {
542 Item dot = FrameKeyboardActions.replaceText(lastRanged);
543 dot.setHighlightMode(HighlightMode.None);
544 }
545 }
546 // if the user is copying text from the item
547 } else if (e.getButton() == MouseEvent.BUTTON3) {
548 // Check if the user is trying to range an item for which they
549 // do not have permission to do so... or it is the frame name
550 if (!lastRanged.hasPermission(Permission.copy)) {
551 MessageBay
552 .displayMessage("Insufficient permission to copy text");
553 lastRanged.clearSelection();
554 FrameGraphics.Repaint();
555 return;
556 }
557 ranged.setText(lastRanged.copySelectedText());
558 ranged.setWidth(lastRanged.getWidth());
559 }
560
561 ranged.setLink(null);
562 ranged.setParent(null);
563 ranged.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
564 pickup(ranged);
565 lastRanged.clearSelection();
566 lastRanged.setHighlightMode(HighlightMode.None);
567 refreshHighlights();
568 FrameGraphics.refresh(false);
569 return;
570 }
571
572 // if the user is cropping an image
573 if (clickedOn != null && clickedOn == _lastCropped) {
574 if (_lastCropped.isCropTooSmall()) {
575 _lastCropped = null;
576 // FrameGraphics
577 // .WarningMessage("Crop cancelled because it was below the
578 // minimum size");
579 } else {
580 Picture cropped = _lastCropped.copy();
581 cropped.setParent(null);
582 pickup(cropped);
583 // MIKE put the code below up here
584 _lastCropped.clearCropping();
585 FrameGraphics.changeHighlightMode(_lastCropped,
586 HighlightMode.None);
587 _lastCropped = null;
588 FrameGraphics.Repaint();
589 return;
590 }
591 }
592
593 assert (_lastCropped == null);
594 // if the user has cropped an image, either the above happend or this is
595 // a no-op MIKE says WHEN DO WE NEED THE CODE BELOW
596 // if (_lastCropped != null && !_lastCropped.isCropTooSmall()) {
597 // _lastCropped.clearCropping();
598 // _lastCropped = null;
599 // FrameGraphics.Repaint();
600 // return;
601 // }
602
603 // if the user is left-clicking
604 if (e.getButton() == MouseEvent.BUTTON1) {
605 SessionStats.AddFrameEvent("Lu");
606 leftButton(clickedOn, clickedIn);
607 return;
608 }
609
610 if (e.getButton() == MouseEvent.BUTTON2) {
611 SessionStats.AddFrameEvent("Mu");
612 middleButton(clickedOn, clickedIn, e.isShiftDown());
613 return;
614 }
615
616 if (e.getButton() == MouseEvent.BUTTON3) {
617 SessionStats.AddFrameEvent("Ru");
618 rightButton(clickedOn, clickedIn);
619 return;
620 }
621
622 // error, we should have returned by now
623 System.out.println("Error: mouseReleased should have returned by now. "
624 + e);
625 }
626
627 /**
628 * This method handles all left-click actions
629 */
630 private void leftButton(Item clicked, Collection<Item> clickedIn) {
631 // if the user is pointing at something then either follow the link or
632 // do TDFC
633 if (clicked == null) {
634 // Check if the user is nearby another item...
635 int mouseX = DisplayIO.getMouseX();
636 int mouseY = FrameMouseActions.getY();
637 // System.out.println(mouseX + "," + mouseY);
638 for (Item i : DisplayIO.getCurrentFrame().getItems()) {
639 if (i instanceof Text) {
640 if (i.isNear(mouseX, mouseY)) {
641 clicked = i;
642 break;
643 }
644 }
645 }
646 }
647
648 if (clicked instanceof Text) {
649 if (((Text) clicked).getText().length() == 0)
650 clicked = null;
651 }
652
653 if (clicked != null && !(clicked instanceof Line)
654 && !(clicked instanceof Dot)) {
655 // check item permissions
656 if (!clicked.hasPermission(Permission.followLinks)) {
657 MessageBay
658 .displayMessage("Insufficient permissions to perform action on item");
659 return;
660 }
661
662 Item clickedOn = clicked;
663
664 // actions take priority
665 if (_lastMouseClick != null && !_lastMouseClick.isControlDown()
666 && clickedOn.hasAction()) {
667 clickedOn.performActions();
668 clickedOn.setHighlightMode(HighlightMode.None);
669 getInstance().refreshHighlights();
670 return;
671 } else if (clickedOn.getLink() != null) {
672 // Dont save the frame if we are moving to an old version of
673 // this frame
674 // because everytime we save with the old tag... the frame is
675 // backed up
676 if (!clickedOn.isOldTag())
677 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
678
679 NavigationActions.setLastNavigationItem(clickedOn);
680 load(clickedOn.getAbsoluteLink());
681 // DisplayIO.UpdateTitle();
682 return;
683 // no link is found, perform TDFC
684 } else {
685 // check for TDFC permission
686 if (!clicked.hasPermission(Permission.createFrames)) {
687 MessageBay
688 .displayMessage("Insufficient permission to TDFC from that item");
689 return;
690 }
691
692 if (clickedOn.isOldTag())
693 return;
694
695 // if the user is clicking on the frame name
696 if (clickedOn.isFrameName()) {
697 Frame next = FrameIO.LoadNext();
698 FrameUtils.DisplayFrame(next, true);
699 return;
700 }
701 try {
702 tdfc(clickedOn);
703 } catch (RuntimeException e) {
704 MessageBay.errorMessage(e.getMessage());
705 }
706 return;
707 }
708 // if user is not pointing at something,this is a back
709 } else {
710 back();
711 return;
712 }
713 }
714
715 private static boolean doMerging(Item clicked) {
716 if (clicked == null)
717 return false;
718
719 // Brook: widgets do not merge
720 if (clicked instanceof WidgetCorner)
721 return false;
722
723 // Brook: widgets do not merge
724 if (clicked instanceof WidgetEdge)
725 return false;
726
727 // System.out.println(FreeItems.getInstance().size());
728 if (isRubberBandingCorner()) {
729 if (clicked.isLineEnd()
730 || clicked.getAllConnected().contains(
731 FreeItems.getItemAttachedToCursor())) {
732 return true;
733 }
734 }
735
736 if (FreeItems.getInstance().size() > 2)
737 return false;
738
739 Item attachedToCursor = FreeItems.getItemAttachedToCursor();
740
741 if (clicked instanceof Text
742 && !(attachedToCursor instanceof Text || attachedToCursor
743 .isLineEnd())) {
744 return false;
745 }
746
747 return true;
748 }
749
750 public static void middleButton() {
751 Item currentItem = FrameUtils.getCurrentItem();
752 getInstance().middleButton(currentItem,
753 FrameUtils.getCurrentItems(currentItem), false);
754 updateCursor();
755 }
756
757 public static void rightButton() {
758 Item currentItem = FrameUtils.getCurrentItem();
759 getInstance().rightButton(currentItem,
760 FrameUtils.getCurrentItems(currentItem));
761 updateCursor();
762 }
763
764 public static void leftButton() {
765 Item currentItem = FrameUtils.getCurrentItem();
766 getInstance().leftButton(currentItem,
767 FrameUtils.getCurrentItems(currentItem));
768 updateCursor();
769 }
770
771 /**
772 * This method handles all middle-click actions
773 */
774 private void middleButton(Item clicked, Collection<Item> clickedIn,
775 boolean isShiftDown) {
776 // if the cursor has Items attached
777 if (FreeItems.itemAttachedToCursor()) {
778 // if the user is pointing at something, merge the items (if
779 // possible)
780 if (doMerging(clicked)) {
781 // check permissions
782 if (!clicked.hasPermission(Permission.full)
783 && clicked.getParent() != null
784 && clicked.getParent().getNameItem() != clicked) {
785 MessageBay.displayMessage("Insufficient permission");
786 return;
787 }
788 Item merger = FreeItems.getItemAttachedToCursor();
789 assert (merger != null);
790 Collection<Item> left = null;
791 // when anchoring a line end onto a text line end, holding shift
792 // prevents the line ends from being merged
793 if (isShiftDown) {
794 left = FreeItems.getInstance();
795 } else {
796 left = merge(FreeItems.getInstance(), clicked);
797 }
798 anchor(left);
799 FreeItems.getInstance().clear();
800 updateCursor();
801 // Make sure the dot goes away when anchoring a line end behind
802 // a text line end
803 if (isShiftDown) {
804 refreshHighlights();
805 }
806 FrameGraphics.requestRefresh(true);
807 return;
808 // otherwise, anchor the items
809 } else {
810 if (clickedIn != null && FreeItems.getInstance().size() == 1) {
811 Item item = FreeItems.getItemAttachedToCursor();
812 if (item instanceof Text) {
813 Text text = (Text) item;
814 if (AttributeUtils.setAttribute(text, text)) {
815 clickedIn.removeAll(FrameUtils
816 .getEnclosingLineEnds().iterator().next()
817 .getAllConnected());
818 for (Item i : clickedIn) {
819 AttributeUtils.setAttribute(i, text);
820 }
821 FreeItems.getInstance().clear();
822 }
823 }
824 }
825
826 // if a line is being rubber-banded, check for auto
827 // straightening
828 anchor(FreeItems.getInstance());
829 FreeItems.getInstance().clear();
830 updateCursor();
831 _offX = _offY = 0;
832 return;
833 }
834 // otherwise if the user is pointing at something,pick it up
835 } else if (clicked != null) {
836 // check permissions
837 if (!clicked.hasPermission(Permission.full)) {
838 MessageBay
839 .displayMessage("Insufficient permission to pick up item");
840 return;
841 }
842
843 // BROOK: WIDGET RECTANGLES DONT ALLOW DISCONNECTION
844 if (clicked instanceof Line && !(clicked instanceof WidgetEdge)) {
845 // Check if within 20% of the end of the line
846 Line l = (Line) clicked;
847 Item toDisconnect = l.getEndPointToDisconnect(_lastMouseClick
848 .getX(), _lastMouseClick.getY());
849
850 if (toDisconnect == null) {
851 pickup(clicked);
852 } else {
853 if (toDisconnect.getHighlightMode() == Item.HighlightMode.Normal) {
854 DisplayIO.setCursorPosition(toDisconnect.getPosition(),
855 false);
856 pickup(toDisconnect);
857 } else {
858 List<Line> lines = toDisconnect.getLines();
859 // This is to remove constraints from single lines
860 // with constraints...
861 // ie. partially deleted rectangles
862 if (lines.size() == 1) {
863 toDisconnect.removeAllConstraints();
864
865 DisplayIO.setCursorPosition(toDisconnect
866 .getPosition(), false);
867 // This is to ensure the selected mode will be set
868 // to Normal rather than disconnect when the line is
869 // anchored
870 toDisconnect
871 .setHighlightMode(Item.HighlightMode.Normal);
872 pickup(toDisconnect);
873 } else {
874 // If we are then detatch the line and pick up its
875 // end point...
876 Frame currentFrame = DisplayIO.getCurrentFrame();
877 Item newPoint = null;
878
879 // If the point we are disconnecting is text...
880 // Then we want to leave the text behind
881 // And disconnect a point
882 if (toDisconnect instanceof Text) {
883 newPoint = new Dot(toDisconnect.getX(),
884 toDisconnect.getY(), -1);
885 Item.DuplicateItem(toDisconnect, newPoint);
886 } else {
887 newPoint = toDisconnect.copy();
888 }
889
890 currentFrame.addItem(newPoint);
891 // remove the current item from the connected
892 // list for this item
893 l.replaceLineEnd(toDisconnect, newPoint);
894 // remove unneeded constrains
895 newPoint.removeAllConstraints();
896
897 // Set the new points mode to normal before picking
898 // it up so it will be restored correctly when
899 // anchored
900 newPoint
901 .setHighlightMode(Item.HighlightMode.Normal);
902 toDisconnect
903 .setHighlightMode(Item.HighlightMode.None);
904 DisplayIO.setCursorPosition(toDisconnect
905 .getPosition(), false);
906 pickup(newPoint);
907 ItemUtils.EnclosedCheck(toDisconnect
908 .getParentOrCurrentFrame().getItems());
909 }
910 }
911 }
912 } else {
913 if (clicked.isLineEnd()) {
914 DisplayIO.setCursorPosition(clicked.getPosition(), false);
915 }
916 pickup(clicked);
917 }
918 // otherwise create a line
919 } else if (clickedIn != null) {
920 ArrayList<Item> toPickup = new ArrayList<Item>(clickedIn.size());
921 for (Item ip : clickedIn)
922 if (ip.hasPermission(Permission.full))
923 toPickup.add(ip);
924 pickup(toPickup);
925 // otherwise the user is creating a line
926 } else {
927 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), Math
928 .round(MouseX), Math.round(MouseY));
929 // If we have permission to copy this item then pick it up
930 if (on != null && on.isLineEnd()
931 && on.hasPermission(Permission.full)) {
932 on.removeAllConstraints();
933 pickup(on);
934 return;
935 }
936
937 // if its on a line then split the line and put a point on it and
938 // pick that point up
939 if (on instanceof Line && on.hasPermission(Permission.full)) {
940 Frame current = DisplayIO.getCurrentFrame();
941 // create the two endpoints
942 Line oldLine = (Line) on;
943 Item newPoint = oldLine.getStartItem().copy();
944 newPoint.setPosition(MouseX, MouseY);
945
946 Item end = oldLine.getEndItem();
947 // create the Line
948 Line newLine = new Line(newPoint, end, current.getNextItemID());
949 oldLine.replaceLineEnd(end, newPoint);
950 newPoint.removeAllConstraints();
951 pickup(newPoint);
952 // Update the stats
953 Collection<Item> created = new LinkedList<Item>();
954 created.add(newPoint);
955 created.add(newLine);
956 SessionStats.CreatedItems(newLine.getAllConnected());
957 return;
958 }
959 Line newLine = createLine();
960 SessionStats.CreatedItems(newLine.getAllConnected());
961 return;
962 }
963 SessionStats.MovedItems(FreeItems.getInstance());
964 }
965
966 private static Item getFirstFreeLineEnd() {
967 for (Item i : FreeItems.getInstance())
968 if (i.isLineEnd())
969 return i;
970 return null;
971 }
972
973 private static boolean isRubberBandingCorner() {
974 return getShapeCorner(FreeItems.getInstance()) != null;
975 }
976
977 /**
978 * Gets the rectangle corner from the list of items that are part of a
979 * rectangle.
980 *
981 * @param partialRectangle
982 * a corner and its two connecting lines.
983 * @return the rectangle corner or null if the list of items is not part of
984 * a rectangle.
985 */
986 private static Item getShapeCorner(List<Item> partialRectangle) {
987 if (partialRectangle.size() < 3)
988 return null;
989 Item lineEnd = null;
990 // only one lineEnd will be present for rectangles
991 // All other items must be lines
992 for (Item i : partialRectangle) {
993 if (i.isLineEnd()) {
994 if (lineEnd == null) {
995 lineEnd = i;
996 } else {
997 return null;
998 }
999 } else if (!(i instanceof Line)) {
1000 return null;
1001 }
1002 }
1003 // if this is at least the corner of two connected lines
1004 if (lineEnd != null && lineEnd.getAllConnected().size() >= 5)
1005 return lineEnd;
1006
1007 return null;
1008 }
1009
1010 /**
1011 * This method handles all right-click action
1012 */
1013 private void rightButton(Item clicked, Collection<Item> clickedIn) {
1014 // if the cursor has Items attached, then anchor a copy of them
1015
1016 List<Item> copies = null;
1017 if (FreeItems.itemAttachedToCursor()) {
1018 // if the user is clicking on something, merge the items
1019 // unless it is a point onto somethin other than a lineEnd or a dot
1020 if (clicked != null
1021 // TODO Change the items merge methods so the logic is simplified
1022 && (!(FreeItems.getItemAttachedToCursor() instanceof Dot)
1023 || clicked instanceof Dot || clicked.isLineEnd())) {
1024 // check permissions
1025 if (!clicked.hasPermission(Permission.full)
1026 && clicked.getParent().getNameItem() != clicked) {
1027 MessageBay
1028 .displayMessage("Insufficient permission to merge items");
1029 return;
1030 }
1031 if (clicked instanceof Text || clicked instanceof Dot
1032 || clicked instanceof XRayable) {
1033 if (isRubberBandingCorner()) {
1034 // Move the cursor so that the copy is exactly the
1035 // same as the shape that was anchored
1036 DisplayIO.setCursorPosition(clicked.getPosition());
1037 Item d = getFirstFreeLineEnd();
1038 // get a copy of all enclosed items before merging
1039 // lineEnds
1040 Collection<Item> items = FrameUtils.getItemsEnclosedBy(
1041 DisplayIO.getCurrentFrame(), d
1042 .getEnclosedShape());
1043 // If its not an enclosed shape then pick up the
1044 // connected shape
1045 if (items == null || items.size() == 0) {
1046 items = d.getAllConnected();
1047 } else {
1048 // For some reason the item that was clicked ends up
1049 // in the enclosure and needs to be removed
1050 items.removeAll(clicked.getConnected());
1051 // the item that was the origin of the enclosed
1052 // shape used to create the enclosure does not get
1053 // returned from getItemsEnclosedBy to the enclosure
1054 // so it must be added
1055 items.addAll(d.getConnected());
1056 }
1057
1058 Collection<Item> toCopy = new LinkedHashSet<Item>();
1059
1060 for (Item ip : items) {
1061 if (ip.hasPermission(Permission.copy))
1062 toCopy.add(ip);
1063 }
1064 copies = copy(toCopy);
1065 // Now do the merging
1066 Collection<Item> remain = merge(
1067 FreeItems.getInstance(), clicked);
1068 // anchor the points
1069 anchor(remain);
1070 FreeItems.getInstance().clear();
1071 pickup(copies);
1072 // line onto something
1073 } else if (FreeItems.getInstance().size() == 2
1074 /* && clicked instanceof XRayable */) {
1075 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
1076 _controlDown);
1077 Collection<Item> leftOver = merge(FreeItems
1078 .getInstance(), clicked);
1079 anchor(leftOver);
1080 if (copies == null)
1081 copies = copy(FreeItems.getInstance());
1082 FreeItems.getInstance().clear();
1083 for (Item i : copies)
1084 i.setOffset(0, 0);
1085 // need to move to prevent cursor dislocation
1086 move(copies);
1087 pickup(copies);
1088 // point onto point
1089 } else if (FreeItems.getInstance().size() == 1) {
1090 copies = copy(FreeItems.getInstance());
1091 Collection<Item> remain = merge(copies, clicked);
1092
1093 // ignore items that could not be merged.
1094 anchor(remain);
1095 } else {
1096 stampItemsOnCursor();
1097 copies = FreeItems.getInstance();
1098 }
1099 } else {
1100 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
1101 _controlDown);
1102 if (copies == null)
1103 copies = copy(FreeItems.getInstance());
1104 for (Item i : copies) {
1105 i.setOffset(0, 0);
1106 }
1107 anchor(FreeItems.getInstance());
1108 FreeItems.getInstance().clear();
1109 pickup(copies);
1110 }
1111 // otherwise, anchor the items
1112 } else {
1113 // check if this is anchoring a rectangle
1114 if (isRubberBandingCorner()) {
1115 Item d = getFirstFreeLineEnd();
1116 // anchor the points
1117 anchor(FreeItems.getInstance());
1118 FreeItems.getInstance().clear();
1119 updateCursor();
1120 // pick up a copy of all enclosed items
1121 Collection<Item> enclosedItems = FrameUtils
1122 .getItemsEnclosedBy(DisplayIO.getCurrentFrame(), d
1123 .getEnclosedShape());
1124 if (enclosedItems != null) {
1125 enclosedItems.removeAll(d.getAllConnected());
1126 Collection<Item> toCopy = getFullyEnclosedItems(enclosedItems);
1127
1128 if (toCopy.size() > 0) {
1129 // Find the closest item to the mouse cursor
1130 double currentX = DisplayIO.getMouseX();
1131 double currentY = FrameMouseActions.getY();
1132 Item closest = null;
1133 double shortestDistance = Double.MAX_VALUE;
1134 for (Item next : toCopy) {
1135 if (next instanceof Line)
1136 continue;
1137 double distance = Point.distance(currentX,
1138 currentY, next.getX(), next.getY());
1139 if (distance < shortestDistance) {
1140 shortestDistance = distance;
1141 closest = next;
1142 }
1143 }
1144 // Move the cursor to closest item
1145 DisplayIO.setCursorPosition(closest.getPosition());
1146 // Pickup copy of the stuff inside the rectange
1147 copies = copy(toCopy);
1148 pickup(copies);
1149 // Remove the rectangle
1150 d.getParentOrCurrentFrame().removeAllItems(
1151 d.getAllConnected());
1152 } else {
1153 // Pick up a copy of the rectangle
1154 copies = copy(d.getAllConnected());
1155 pickup(copies);
1156 }
1157 }
1158 } else {
1159 if (rubberBanding()) {
1160 if (clicked != null) {
1161 Collection<Item> leftOver = merge(FreeItems
1162 .getInstance(), clicked);
1163 anchor(leftOver);
1164 }
1165 // This is executed when the user is putting down a line
1166 // endpoint and unreeling. ie. Normal unreeling
1167 copies = ItemUtils.UnreelLine(FreeItems.getInstance(),
1168 _controlDown);
1169
1170 if (copies == null)
1171 copies = copy(FreeItems.getInstance());
1172 anchor(FreeItems.getInstance());
1173 for (Item i : copies)
1174 i.setOffset(0, 0);
1175 // need to move to prevent cursor dislocation
1176 move(copies);
1177 pickup(copies);
1178 } else if (_extrude) {
1179 List<Item> originals = new ArrayList<Item>();
1180 // remove any lines that dont have both endpoints
1181 // floating
1182 for (Item i : FreeItems.getInstance()) {
1183 if (i.isFloating())
1184 originals.add(i);
1185 }
1186 if (copies == null)
1187 copies = ItemUtils.CopyItems(originals, _extrude);
1188 for (Item i : copies)
1189 i.setOffset(0, 0);
1190 anchor(FreeItems.getInstance());
1191 // Move isnt working right for extruding!!
1192 // move(copies);
1193 pickup(copies);
1194 } else {
1195 stampItemsOnCursor();
1196 copies = FreeItems.getInstance();
1197 }
1198 }
1199 }
1200 } else {
1201 // if the user is pointing at something, this is a copy
1202 if (clicked != null) {
1203 // check permissions
1204 if (clicked.isLineEnd()) {
1205 if (!clicked.hasPermission(Permission.full)) {
1206 MessageBay
1207 .displayMessage("Insufficient permission to unreel");
1208 return;
1209 }
1210 } else if (!clicked.hasPermission(Permission.copy)) {
1211 MessageBay
1212 .displayMessage("Insufficient permission to copy");
1213 return;
1214 }
1215
1216 copies = ItemUtils.UnreelLine(clicked, _controlDown);
1217 // Copies will NOT be null if the user right clicked on a point
1218 if (copies == null) {
1219 Collection<Item> originals = clicked.getConnected();
1220 copies = ItemUtils.CopyItems(originals, _extrude);
1221 // if this is the title of the frame, link it to the frame
1222 if (originals.size() == 1 && copies.size() == 1) {
1223 Item copy = copies.get(0);
1224 Item original = originals.iterator().next();
1225 if (original.getLink() == null
1226 && original.isFrameTitle()) {
1227 // save the frame after copying
1228 // i.getParent().setChanged(true);
1229 copy.setLink(original.getParentOrCurrentFrame()
1230 .getName());
1231 }
1232 }
1233
1234 FrameGraphics.changeHighlightMode(clicked,
1235 HighlightMode.None);
1236
1237 if (!_extrude)
1238 clearParent(copies);
1239 }
1240
1241 pickup(copies);
1242 } else {
1243 // if user is pointing in a closed shape, copy the items inside
1244 if (clickedIn != null) {
1245 // Set the selection mode for the items that were clicked in
1246 Collection<Item> enclosed = getFullyEnclosedItems(clickedIn);
1247 if (enclosed.size() == 0) {
1248 MessageBay
1249 .displayMessage("Insufficient permission to copy items");
1250 } else {
1251 copies = copy(enclosed);
1252 clearParent(copies);
1253 pickup(copies);
1254 for (Item i : clickedIn) {
1255 i.setHighlightMode(HighlightMode.None);
1256 }
1257 }
1258 // otherwise, create a rectangle
1259 } else {
1260 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(),
1261 MouseX, MouseY);
1262 // if its on a line then create a line from that line
1263 if (on instanceof Line && on.hasPermission(Permission.full)) {
1264
1265 Line onLine = (Line) on;
1266 Line newLine = onLine.copy();
1267 Item end = newLine.getEndItem();
1268 Item start = newLine.getStartItem();
1269 end.setPosition(MouseX, MouseY);
1270 start.setPosition(MouseX, MouseY);
1271 onLine.autoArrowheadLength();
1272 // anchor the start
1273 anchor(start);
1274 // attach the line to the cursor
1275 pickup(end);
1276
1277 List<Item> toMerge = new LinkedList<Item>();
1278 toMerge.add(newLine.getStartItem());
1279 toMerge.add(newLine);
1280
1281 // Make sure the highlighting is shown when the end is
1282 // anchored
1283 end.setHighlightMode(Item.HighlightMode.Normal);
1284 merge(toMerge, on);
1285 // anchor(left);
1286 // FreeItems.getInstance().clear();
1287 FrameGraphics.Repaint();
1288 updateCursor();
1289 return;
1290 }
1291
1292 copies = new ArrayList<Item>();
1293 Item[] d = new Item[RECTANGLE_CORNERS];
1294 // create dots
1295 Frame current = DisplayIO.getCurrentFrame();
1296 for (int i = 0; i < d.length; i++) {
1297 d[i] = current.createDot();
1298 copies.add(d[i]);
1299 }
1300
1301 // create lines
1302 copies.add(new Line(d[0], d[1], current.getNextItemID()));
1303 copies.add(new Line(d[1], d[2], current.getNextItemID()));
1304 copies.add(new Line(d[2], d[3], current.getNextItemID()));
1305 copies.add(new Line(d[3], d[0], current.getNextItemID()));
1306
1307 new Constraint(d[0], d[1], current.getNextItemID(),
1308 Constraint.HORIZONTAL);
1309 new Constraint(d[2], d[3], current.getNextItemID(),
1310 Constraint.HORIZONTAL);
1311 new Constraint(d[1], d[2], current.getNextItemID(),
1312 Constraint.VERTICAL);
1313 new Constraint(d[3], d[0], current.getNextItemID(),
1314 Constraint.VERTICAL);
1315
1316 anchor(new ArrayList<Item>(copies));
1317 pickup(d[3]);
1318 d[3].setHighlightMode(HighlightMode.Normal);
1319
1320 SessionStats.CreatedItems(copies);
1321 copies.clear();
1322 }
1323 }
1324 }
1325 getInstance().refreshHighlights();
1326 SessionStats.CopiedItems(copies);
1327 updateCursor();
1328 FrameGraphics.Repaint();
1329 }
1330
1331 /**
1332 *
1333 */
1334 private static void stampItemsOnCursor() {
1335 List<Item> copies = copy(FreeItems.getInstance());
1336 // MIKE: what does the below 2 lines do?
1337 for (Item i : copies)
1338 i.setOffset(0, 0);
1339 // The below code has a little problem withflicker when stamp
1340 // and dragging
1341 move(FreeItems.getInstance());
1342 for (Item i : copies) {
1343 i.setHighlightMode(HighlightMode.None);
1344 }
1345 anchor(copies);
1346 }
1347
1348 /**
1349 * @param enclosedItems
1350 * @return
1351 */
1352 private static Collection<Item> getFullyEnclosedItems(
1353 Collection<Item> enclosure) {
1354 // copy the enclosedItems because the list will be modified
1355 Collection<Item> enclosedItems = new LinkedHashSet<Item>(enclosure);
1356 Collection<Item> toCopy = new LinkedHashSet<Item>(enclosedItems.size());
1357
1358 while (enclosedItems.size() > 0) {
1359 Item i = enclosedItems.iterator().next();
1360 if (i.hasPermission(Permission.copy)) {
1361 Collection<Item> items = i.getAllConnected();
1362 // Only copy if the entire shape is enclosed
1363 if (enclosedItems.containsAll(items)) {
1364 toCopy.addAll(items);
1365 }
1366 enclosedItems.removeAll(items);
1367 } else {
1368 enclosedItems.remove(0);
1369 }
1370 }
1371 return toCopy;
1372 }
1373
1374 /**
1375 * Marks the items as not belonging to any specific frame. When picking up
1376 * items the parent will be automatically cleared for items on the current
1377 * frame but not for overlay items. This method ensures that overlay items
1378 * will also be cleared. This is useful when picking up copies of items from
1379 * an overlay (with the right mouse button) to ensure that the copy will be
1380 * anchored on the current frame rather than the overlay. When items are
1381 * picked up with the middle button clearParent should NOT be called.
1382 *
1383 * @param items
1384 * to have their parent cleared
1385 */
1386 private static void clearParent(List<Item> items) {
1387 for (Item i : items) {
1388 // The next line is only necessary for circles...
1389 // Need to clean up/refactory some of this stuff
1390 i.getParentOrCurrentFrame().removeItem(i);
1391 i.setParent(null);
1392 }
1393 }
1394
1395 public void mouseEntered(MouseEvent e) {
1396 }
1397
1398 public void mouseExited(MouseEvent e) {
1399 }
1400
1401 public void mouseDragged(MouseEvent e) {
1402 _lastMouseDragged = e;
1403 // System.out.println("MouseDragged");
1404
1405 // check if user is dragging across a text item
1406 if (_lastRanged != null) {
1407 // System.out.println(MouseY - e.getY());
1408
1409 MouseX = e.getX();
1410 MouseY = e.getY();
1411
1412 int distance = _lastRanged.getY() - FrameMouseActions.getY();
1413 if (distance <= 0)
1414 distance = FrameMouseActions.getY() - _lastRanged.getY()
1415 - _lastRanged.getBoundsHeight();
1416
1417 if (distance > UserSettings.NoOpThreshold) {
1418 _lastRanged.clearSelectionEnd();
1419 _isNoOp = true;
1420 } else {
1421 // update the ranged section
1422 _lastRanged.setSelectionEnd(DisplayIO.getMouseX(),
1423 FrameMouseActions.getY());
1424 _isNoOp = false;
1425 }
1426
1427 DisplayIO.setTextCursor(_lastRanged, Text.NONE);
1428 FrameGraphics.Repaint();
1429 return;
1430 }
1431
1432 // if the user is dragging across a picture
1433 if (_lastCropped != null) {
1434 // If shift is down then the distance moved is the same in the x and
1435 // y
1436 MouseX = e.getX();
1437 MouseY = e.getY();
1438
1439 if (e.isControlDown()) {
1440 int deltaX = Math.abs(e.getX() - _lastMouseClick.getX());
1441 int deltaY = Math.abs(e.getY() - _lastMouseClick.getY());
1442 if (deltaX > deltaY) {
1443 MouseY = _lastMouseClick.getY() + deltaX
1444 * (e.getY() > _lastMouseClick.getY() ? 1 : -1);
1445 } else {
1446 MouseX = _lastMouseClick.getX() + deltaY
1447 * (e.getX() > _lastMouseClick.getX() ? 1 : -1);
1448 }
1449 }
1450 // update the ranged section
1451 _lastCropped.setEndCrop(DisplayIO.getMouseX(), FrameMouseActions
1452 .getY());
1453
1454 FrameGraphics.Repaint();
1455 return;
1456 }
1457
1458 if ((_mouseDown == MouseEvent.BUTTON2 || _mouseDown == MouseEvent.BUTTON3)
1459 && _lastClickedOn == null && _lastClickedIn == null) {
1460 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), e.getX(),
1461 e.getY());
1462
1463 if (FreeItems.getInstance().size() == 0) {
1464 // if the user can spot-weld, show the virtual spot
1465 if (on instanceof Line) {
1466 Line line = (Line) on;
1467 line.showVirtualSpot(e.getX(), e.getY());
1468 }
1469 if (on != null && on.isLineEnd()) {
1470 _lastHighlightedItem = on;
1471 on.setHighlightMode(Item.HighlightMode.Normal);
1472 } else if (_lastHighlightedItem != null) {
1473 _lastHighlightedItem
1474 .setHighlightMode(Item.HighlightMode.None);
1475 _lastHighlightedItem = null;
1476 }
1477 }
1478 }
1479
1480 // Use the below calculation for better speed. If it causes problems
1481 // switch back to the Euclidean distance calculation
1482 if (Math.abs(MouseX - e.getX()) > UserSettings.NoOpThreshold
1483 || Math.abs(MouseY - e.getY()) > UserSettings.NoOpThreshold)
1484 _isNoOp = true;
1485
1486 FrameGraphics.Repaint();
1487 }
1488
1489 private static MouseEvent _lastMouseMoved = null;
1490
1491 private static Integer LastRobotX = null;
1492
1493 private static Integer LastRobotY = null;
1494
1495 // For some reason... sometimes the mouse move gets lost when moving the
1496 // mouse really quickly after clicking...
1497 // Use this timer to make sure it gets reset eventually if the Robot
1498 // generated event never arrives.
1499 private static Timer _RobotTimer = new Timer(200, new ActionListener() {
1500 public void actionPerformed(ActionEvent ae) {
1501 _RobotTimer.stop();
1502 LastRobotX = null;
1503 LastRobotY = null;
1504 // System.out.println("RobotTimer");
1505 }
1506 });
1507
1508 private static boolean _controlDown;
1509
1510 public static void setLastRobotMove(float x, float y) {
1511 // Make sure the system is in the right state while waiting for the
1512 // Robots event to arrive.
1513 MouseX = x;
1514 MouseY = y;
1515 // System.out.println("MouseMoved: " + MouseX + "," + MouseY + " " +
1516 // System.currentTimeMillis());
1517 LastRobotX = Math.round(x);
1518 LastRobotY = Math.round(y);
1519 _RobotTimer.start();
1520 }
1521
1522 public static boolean isWaitingForRobot() {
1523 return LastRobotX != null;
1524 }
1525
1526 /**
1527 * Updates the stored mouse position and highlights any items as necessary.
1528 */
1529 public void mouseMoved(MouseEvent e) {
1530 mouseMoved(e, false);
1531 }
1532
1533 private void mouseMoved(MouseEvent e, boolean shiftStateChanged) {
1534 // System.out.println(e.getX() + "," + e.getY() + " " + e.getWhen());
1535 if (LastRobotX != null) {
1536 // Wait until the last Robot mouse move event arrives before
1537 // processing other events
1538 if (/* FreeItems.getInstance().size() == 0 || */
1539 (LastRobotX == e.getX() && LastRobotY == e.getY())) {
1540 LastRobotX = null;
1541 LastRobotY = null;
1542 _RobotTimer.stop();
1543 } else {
1544 // System.out.println("Ignored: " +
1545 // FreeItems.getInstance().size());
1546 return;
1547 }
1548 }
1549
1550 MouseX = e.getX();
1551 MouseY = e.getY();
1552
1553 // Moving the mouse a certain distance removes the last edited text if
1554 // it is empty
1555 Text lastEdited = FrameUtils.getLastEdited();
1556 if (lastEdited != null && lastEdited.getText().length() == 0
1557 && lastEdited.getPosition().distance(e.getPoint()) > 20) {
1558 FrameUtils.setLastEdited(null);
1559 }
1560
1561 // If shift is down then the movement is constrained
1562 if (_controlDown && FreeItems.getInstance().size() > 0) {
1563 // Check if we are rubber banding a line
1564 if (shiftStateChanged && rubberBanding()) {
1565 // Get the line end that is being rubber banded
1566 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems
1567 .getInstance().get(0)
1568 : FreeItems.getInstance().get(1);
1569 Line line = (Line) (FreeItems.getInstance().get(0).isLineEnd() ? FreeItems
1570 .getInstance().get(1)
1571 : FreeItems.getInstance().get(0));
1572 Item otherEnd = line.getOppositeEnd(thisEnd);
1573 int deltaX = Math.abs(e.getX() - otherEnd.getX());
1574 int deltaY = Math.abs(e.getY() - otherEnd.getY());
1575 // Check if its a vertical line
1576 if (deltaX < deltaY / 2) {
1577 // otherEnd.setX(thisEnd.getX());
1578 // MouseX = otherEnd.getX();
1579 if (shiftStateChanged) {
1580 new Constraint(thisEnd, otherEnd, thisEnd
1581 .getParentOrCurrentFrame().getNextItemID(),
1582 Constraint.VERTICAL);
1583 }
1584 }
1585 // Check if its horizontal
1586 else if (deltaY <= deltaX / 2) {
1587 // MouseY = otherEnd.getY();
1588 // otherEnd.setY(thisEnd.getY());
1589 if (shiftStateChanged) {
1590 new Constraint(thisEnd, otherEnd, thisEnd
1591 .getParentOrCurrentFrame().getNextItemID(),
1592 Constraint.HORIZONTAL);
1593 }
1594 } else {
1595 // Add DIAGONAL constraints
1596 // if (deltaX > deltaY) {
1597 // otherEnd.setY(thisEnd.getY() + deltaX
1598 // * (e.getY() < otherEnd.getY() ? 1 : -1));
1599 // } else {
1600 // otherEnd.setX(thisEnd.getX() + deltaY
1601 // * (e.getX() < otherEnd.getX() ? 1 : -1));
1602 // }
1603 if (shiftStateChanged) {
1604 int constraint = Constraint.DIAGONAL_NEG;
1605 // Check if the slope is positive
1606 if ((thisEnd.getY() - otherEnd.getY())
1607 / (double) (thisEnd.getX() - otherEnd.getX()) > 0.0) {
1608 constraint = Constraint.DIAGONAL_POS;
1609 }
1610
1611 new Constraint(thisEnd, otherEnd, thisEnd
1612 .getParentOrCurrentFrame().getNextItemID(),
1613 constraint);
1614 }
1615 }
1616 }// If its a lineend attached to two lines lengthen the shorter
1617 // so it is the same length as the longer line
1618 else if (FreeItems.getInstance().size() == 3) {
1619 // check if we are rubber banding the corner of a shape
1620 Item thisEnd = getShapeCorner(FreeItems.getInstance());
1621 if (thisEnd != null) {
1622 Line line1 = thisEnd.getLines().get(0);
1623 Line line2 = thisEnd.getLines().get(1);
1624 // Check if the two lines are constrained and hence it is a
1625 // rectangle
1626 Integer c1 = line1.getPossibleConstraint();
1627 Integer c2 = line2.getPossibleConstraint();
1628
1629 if (c1 != null && c2 != null) {
1630 // This is the case of a constrained rectangle
1631 if ((c2 == Constraint.VERTICAL || c2 == Constraint.HORIZONTAL)
1632 && (c1 == Constraint.VERTICAL || c1 == Constraint.HORIZONTAL)
1633 && (c1 != c2)) {
1634 Line vLine = line2;
1635 Line hLine = line1;
1636 if (c1 == Constraint.VERTICAL) {
1637 vLine = line1;
1638 hLine = line2;
1639 }
1640 Item hOtherEnd = hLine.getOppositeEnd(thisEnd);
1641 Item vOtherEnd = vLine.getOppositeEnd(thisEnd);
1642
1643 double vLength = Math
1644 .abs(vOtherEnd.getY() - MouseY);
1645 double hLength = Math
1646 .abs(hOtherEnd.getX() - MouseX);
1647
1648 if (vLength > hLength) {
1649 MouseX = Math.round(hOtherEnd.getX() + vLength
1650 * (MouseX > hOtherEnd.getX() ? 1 : -1));
1651 } else /* if (hLength > vLength) */{
1652 MouseY = Math.round(vOtherEnd.getY() + hLength
1653 * (MouseY > vOtherEnd.getY() ? 1 : -1));
1654 }
1655 }
1656 // } else if (c2 != null) {
1657 //
1658 // } // Other wise it is a not constrained shape so
1659 // constrain
1660 // the two lines lengths to be equal
1661 } else {
1662 Item lineEnd1 = line1.getOppositeEnd(thisEnd);
1663 Item lineEnd2 = line2.getOppositeEnd(thisEnd);
1664 double l1 = Line.getLength(lineEnd1.getPosition(), e
1665 .getPoint());
1666 double l2 = Line.getLength(lineEnd2.getPosition(), e
1667 .getPoint());
1668 double l3 = Line.getLength(lineEnd1.getPosition(),
1669 lineEnd2.getPosition());
1670 // l1 needs to be the shorter end
1671 if (l1 > l2) {
1672 Item temp = lineEnd1;
1673 lineEnd1 = lineEnd2;
1674 lineEnd2 = temp;
1675 double tempL = l1;
1676 l1 = l2;
1677 l2 = tempL;
1678 }
1679 // Now use the cosine rule to calculate the angle
1680 // between l1 and l3
1681 double cosTheta = (l1 * l1 + l3 * l3 - l2 * l2)
1682 / (2 * l1 * l3);
1683 // now calculate the new length for the lines using cos
1684 // rule
1685 double l_new = l3 / (2 * cosTheta);
1686 double ratio = l_new / l1;
1687 MouseX = Math.round((e.getX() - lineEnd1.getX())
1688 * ratio)
1689 + lineEnd1.getX();
1690 MouseY = Math.round((e.getY() - lineEnd1.getY())
1691 * ratio)
1692 + lineEnd1.getY();
1693
1694 }
1695 }
1696 }
1697 } else if (shiftStateChanged && !_controlDown && rubberBanding()) {
1698 // Get the line end that is being rubber banded
1699 Item thisEnd = FreeItems.getInstance().get(0).isLineEnd() ? FreeItems
1700 .getInstance().get(0)
1701 : FreeItems.getInstance().get(1);
1702 thisEnd.removeAllConstraints();
1703 }
1704
1705 if (_lastMouseMoved == null)
1706 _lastMouseMoved = e;
1707
1708 _lastMouseMoved = e;
1709
1710 refreshHighlights();
1711
1712 if (FreeItems.itemAttachedToCursor()) {
1713 move(FreeItems.getInstance());
1714 // System.out.println(FreeItems.getInstance().size());
1715 }
1716
1717 if (_forceArrowCursor)
1718 updateCursor();
1719
1720 _forceArrowCursor = true;
1721 }
1722
1723 public void refreshHighlights() {
1724 // ByMike: Get the item the mouse is hovering over
1725 Item click = FrameUtils.getCurrentItem();
1726 Item on = null;
1727 // System.out.println(click);
1728 if (click != null) {
1729 on = click;
1730 // set the context
1731 if (on instanceof Line)
1732 _context = CONTEXT_AT_LINE;
1733 else if (on instanceof Dot)
1734 _context = CONTEXT_AT_DOT;
1735 else if (on instanceof Text) {
1736 _context = CONTEXT_AT_TEXT;
1737 }
1738 if (FreeItems.getInstance().size() > 0)
1739 _alpha = 60;
1740 else
1741 _alpha = -1;
1742 } else {
1743 _context = CONTEXT_FREESPACE;
1744 _alpha = -1;
1745 }
1746
1747 // if the user is pointing at an item, highlight it
1748 if (on != null && !FreeItems.getInstance().contains(on)) {
1749 // if the user can spot-weld, show the virtual spot
1750 if (FreeItems.getInstance().size() == 2 && on instanceof Line) {
1751 Line line = (Line) on;
1752 Item freeItem0 = FreeItems.getInstance().get(0);
1753 Item freeItem1 = FreeItems.getInstance().get(1);
1754 Item lineEnd = freeItem0.isLineEnd() ? freeItem0 : (freeItem1
1755 .isLineEnd() ? freeItem1 : null);
1756 if (lineEnd != null) {
1757 line.showVirtualSpot(lineEnd, DisplayIO.getMouseX(),
1758 FrameMouseActions.getY());
1759 } else
1760 // The user is pointing at another point or text item
1761 // etc
1762 FrameGraphics.changeHighlightMode(on,
1763 Item.HighlightMode.Normal);
1764 } else {
1765 // FrameGraphics.ChangeSelectionMode(on,
1766 // Item.SelectedMode.Connected);
1767 // TODO: The method below is for the most part redundant
1768 on = FrameGraphics.Highlight(on);
1769 }
1770 // if the last item highlighted is still highlighted, clear it
1771 if (_lastHoldsHighlight) {
1772 _lastHoldsHighlight = false;
1773 for (Item i : DisplayIO.getCurrentFrame().getItems())
1774 if (i.isHighlighted() && i != on)
1775 FrameGraphics.changeHighlightMode(i,
1776 Item.HighlightMode.None);
1777 }
1778
1779 // if the user is not pointing at an item, check for enclosure
1780 // highlighting
1781 } else if (on == null) {
1782 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds();
1783 if (enclosure != null && enclosure.size() > 0) {
1784 Item firstLineEnd = enclosure.iterator().next();
1785 if (firstLineEnd.getLines().size() > 1 &&
1786 // check that the enclosure is not part of a point being
1787 // dragged in space
1788 !ContainsOneOf(enclosure, FreeItems.getInstance())) {
1789 on = firstLineEnd.getLines().get(0);
1790 // System.out.println(on == null ? "Null" :
1791 // on.toString());
1792 FrameGraphics.changeHighlightMode(on,
1793 Item.HighlightMode.Enclosed);
1794 } else if (firstLineEnd instanceof XRayable) {
1795 on = firstLineEnd;
1796 FrameGraphics.changeHighlightMode(firstLineEnd,
1797 Item.HighlightMode.Enclosed);
1798 }
1799 } else if (_lastHighlightedItem != null) {
1800 // System.out.println("LastHighlightedItem");
1801 _lastHoldsHighlight = false;
1802 }
1803 }
1804
1805 // disable cursor changes when the cursor has items attached
1806 if (FreeItems.itemAttachedToCursor()
1807 && DisplayIO.getCursor() != Item.TEXT_CURSOR)
1808 _forceArrowCursor = false;
1809
1810 // setLastHighlightedItem(on);
1811
1812 if (_lastHighlightedItem != null && _lastHighlightedItem != on
1813 && !_lastHoldsHighlight) {
1814 // Turn off the highlighting only if
1815 // the last highlighted item is not connected to the currentItem
1816 // Otherwise we get flickering in transition from connected to
1817 // normal mode while moving the cursor along a line.
1818 if (on == null
1819 || (!on.getAllConnected().contains(_lastHighlightedItem))) {
1820 FrameGraphics.changeHighlightMode(_lastHighlightedItem,
1821 Item.HighlightMode.None);
1822 }
1823 }
1824
1825 _lastHighlightedItem = on;
1826
1827 }
1828
1829 private boolean ContainsOneOf(Collection<Item> enclosure,
1830 Collection<Item> freeItems) {
1831 if (freeItems == null)
1832 return false;
1833 for (Item i : freeItems) {
1834 if (enclosure.contains(i))
1835 return true;
1836 }
1837 return false;
1838 }
1839
1840 /**
1841 * Checks if lines are being rubber banded.
1842 *
1843 * @return true if the user is rubberBanding one or more lines
1844 */
1845 private static boolean rubberBanding() {
1846 if (FreeItems.getInstance().size() != 2) {
1847 return false;
1848 }
1849
1850 // if rubber-banding, there will be 1 lineend and the rest will be lines
1851 boolean foundLineEnd = false;
1852 for (Item i : FreeItems.getInstance()) {
1853 if (i.isLineEnd()) {
1854 if (foundLineEnd) {
1855 return false;
1856 }
1857 foundLineEnd = true;
1858 } else if (!(i instanceof Line) || !i.isVisible()) {
1859 return false;
1860 }
1861 }
1862 return true;
1863 }
1864
1865 /**
1866 * Updates the current mouse cursor to whatever it should be. i.e. Hidden
1867 * when rubber-banding lines, otherwise default (arrow)
1868 */
1869 public static void updateCursor() {
1870 if (rubberBanding()) {
1871 DisplayIO.setCursor(Item.HIDDEN_CURSOR);
1872 return;
1873 }
1874 // This is to make sure the TEXT_CURSOR doesnt get inadvertantly turned
1875 // off!
1876 Item on = FrameUtils.getCurrentItem();
1877 if (on != null && on instanceof Text) {
1878 return;
1879 }
1880 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1881 }
1882
1883 public static void setHighlightHold(boolean hold) {
1884 _lastHoldsHighlight = hold;
1885 }
1886
1887 public static void resetOffset() {
1888 if (FreeItems.itemAttachedToCursor()) {
1889 _offX = DisplayIO.getMouseX()
1890 - FreeItems.getInstance().get(0).getX()
1891 + FreeItems.getInstance().get(0).getOffset().x;
1892 _offY = getY() - FreeItems.getInstance().get(0).getY()
1893 + FreeItems.getInstance().get(0).getOffset().y;
1894 }
1895 }
1896
1897 private static void move(Collection<Item> toMove) {
1898
1899 // Gets the origin of the first item to move
1900 int xPos = (DisplayIO.getMouseX() - _offX);
1901
1902 Item firstDot = toMove.iterator().next();
1903
1904 int deltax = firstDot.getX() - xPos;
1905 int deltay = firstDot.getY() - (getY() - _offY);
1906
1907 for (Item move : toMove) {
1908 move.setPosition(move.getX() - deltax, move.getY() - deltay);
1909
1910 if (move instanceof Text) {
1911 ((Text) move).setAlpha(_alpha);
1912 }
1913 }
1914
1915 FrameGraphics.Repaint();
1916 }
1917
1918 private static void load(String toLoad) {
1919 if (FrameIO.isValidFrameName(toLoad)) {
1920 FrameUtils.DisplayFrame(toLoad);
1921 } else {
1922 MessageBay.errorMessage(toLoad + " is not a valid frame name.");
1923 }
1924 }
1925
1926 private static void back() {
1927 DisplayIO.Back();
1928
1929 // repaint things if necessary
1930 if (FreeItems.itemAttachedToCursor())
1931 move(FreeItems.getInstance());
1932 }
1933
1934 /**
1935 * Returns true if the mouse moved during TDFC. This will happen if there is
1936 * a start annotation item on the frame.
1937 *
1938 * @param linker
1939 * @return
1940 */
1941 public static boolean tdfc(Item linker) throws RuntimeException {
1942 if (linker instanceof Line)
1943 return false;
1944
1945 // if this is a non-usable item
1946 if (linker.getID() < 0)
1947 return false;
1948
1949 boolean mouseMoved;
1950
1951 linker.getParent().setChanged(true);
1952
1953 Frame next = FrameIO.CreateNewFrame(linker);
1954
1955 linker.setLink("" + next.getNumber());
1956
1957 for (Item i : next.getTextItems()) {
1958 // Set the link for @Parent annotation item if one
1959 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_PARENT)
1960 && i.getLink() == null) {
1961 Frame parent = linker.getParentOrCurrentFrame();
1962 i.setLink(parent.getName());
1963 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_BACKUP, false)) {
1964 // Delink backup tag if it is on the frame
1965 i.setLink(null);
1966 }
1967 }
1968
1969 FrameUtils.DisplayFrame(next, true);
1970 FrameUtils.setTdfcItem(linker);
1971
1972 mouseMoved = next.moveMouseToDefaultLocation();
1973 // this needs to be done if the user doesnt move the mouse before doing
1974 // tdfc while the cursor is set to the text cursor
1975 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1976 // This needs to be done in case there was a @start on the frame which
1977 // triggers changed to be set to true when it should stay as false
1978 next.setChanged(false);
1979 return mouseMoved;
1980 }
1981
1982 /**
1983 * Creates a new Text item and fills it with particular attributes extracted
1984 * from the given Item. Note: Users always have permission to extract
1985 * attributes, so it is not checked.
1986 *
1987 * @param toExtract
1988 * Item containing the Item to extract the attributes from.
1989 */
1990 private static void extractAttributes(Item toExtract) {
1991 if (toExtract == null || toExtract == null)
1992 return;
1993
1994 if (FreeItems.itemAttachedToCursor())
1995 return;
1996
1997 Item attribs;
1998 Item item = toExtract;
1999 // Extract the frames attributes when the user clicks on the frame name
2000 FrameGraphics.changeHighlightMode(item, HighlightMode.None);
2001 if (item.isFrameName())
2002 attribs = AttributeUtils.extractAttributes(item.getParent());
2003 else {
2004 attribs = AttributeUtils.extractAttributes(item);
2005 }
2006
2007 if (attribs == null)
2008 MessageBay
2009 .displayMessage("All attributes of that item are default values.");
2010 else {
2011 // Give the attribute text item the color of the item for which
2012 // attributes are being extracted.
2013 // attribs.setColor(item.getColor());
2014 pickup(attribs);
2015 }
2016 }
2017
2018 public static void delete(Item toDelete) {
2019 FrameUtils.setLastEdited(null);
2020 _offX = _offY = 0;
2021
2022 Frame current = DisplayIO.getCurrentFrame();
2023 // check if the user is pointing at the frame's framename
2024 if (toDelete != null
2025 && toDelete == current.getNameItem()) {
2026 current.clear(false);
2027 FrameGraphics.Repaint();
2028 return;
2029 }
2030
2031 // if the user is deleting items attached to the cursor
2032 if (FreeItems.itemAttachedToCursor()) {
2033 // if this is an item-swap
2034 if (FreeItems.getInstance().size() == 1
2035 && FreeItems.getInstance().get(0) instanceof Text
2036 && toDelete != null && toDelete instanceof Text) {
2037
2038 // check permissions
2039 if (!toDelete.hasPermission(Permission.full)) {
2040 MessageBay
2041 .displayMessage("Insufficient permission to swap Item text");
2042 return;
2043 }
2044 Text anchored = (Text) toDelete;
2045 Text free = (Text) FreeItems.getInstance().get(0);
2046 SessionStats.DeletedItem(free);
2047 // List<String> temp = anchored.getText();
2048 anchored.setTextList(free.getTextList());
2049
2050 // free.setTextList(temp);
2051 FreeItems.getInstance().clear();
2052
2053 anchored.getParent().setChanged(true);
2054
2055 // update the offset since the text has changed
2056 _offX = DisplayIO.getMouseX() - anchored.getX()
2057 + anchored.getOffset().x;
2058 _offY = getY() - anchored.getY() + anchored.getOffset().y;
2059 } else {
2060 deleteItems(FreeItems.getInstance());
2061 }
2062 // reset the mouse cursor
2063 updateCursor();
2064 FrameGraphics.Repaint();
2065 return;
2066 // the user is not pointing at an item
2067 } else if (toDelete == null) {
2068
2069 // if the user is pointing inside a closed shape, delete all
2070 // items inside it
2071 Collection<Item> items = FrameUtils.getCurrentItems(null);
2072
2073 if (items != null) {
2074 Collection<Item> toRemove = new LinkedHashSet<Item>(items
2075 .size());
2076 for (Item ip : items) {
2077 if (ip.hasPermission(Permission.full)) {
2078 // Only include lines if one of their enpoints are also
2079 // being removed
2080 if (ip instanceof Line) {
2081 Line l = (Line) ip;
2082 Item end = l.getEndItem();
2083 Item start = l.getStartItem();
2084
2085 // If one end of a line is being delted, remove the
2086 // other end if all its connecting lines are being
2087 // delted
2088 if (items.contains(end)) {
2089 if (!items.contains(start)
2090 && items.containsAll(start.getLines())) {
2091 toRemove.add(start);
2092 }
2093 } else if (items.contains(start)) {
2094 if (items.containsAll(end.getLines())) {
2095 toRemove.add(end);
2096 }
2097 } else {
2098 continue;
2099 }
2100 }
2101 toRemove.add(ip);
2102 }
2103 }
2104
2105 deleteItems(toRemove);
2106
2107 // reset the mouse cursor
2108 updateCursor();
2109 FrameGraphics.Repaint();
2110
2111 // otherwise this is an undo command
2112 } else {
2113 DisplayIO.getCurrentFrame().undo();
2114 }
2115 return;
2116 // this is a delete command
2117 } else {
2118 // check permissions
2119 if (!toDelete.hasPermission(Permission.full)) {
2120 MessageBay
2121 .displayMessage("Insufficient permission to delete item");
2122 return;
2123 }
2124
2125 Frame parent = toDelete.getParent();
2126 if (parent != null) {
2127 parent.setChanged(true);
2128 }
2129 Collection<Item> toUndo = null;
2130 if (toDelete.isLineEnd()) {
2131 toUndo = deleteLineEnd(toDelete);
2132 } else if (toDelete instanceof Line
2133 && toDelete.getHighlightMode() == Item.HighlightMode.Disconnect) {
2134 Line line = (Line) toDelete;
2135 Item start = line.getStartItem();
2136 Item end = line.getEndItem();
2137 Collection<Item> delete = new LinkedList<Item>();
2138 delete.add(toDelete);
2139 if (end.getLines().size() == 1) {
2140 delete.add(end);
2141 } else {
2142 end.removeLine(line);
2143 }
2144 if (start.getLines().size() == 1) {
2145 delete.add(start);
2146 } else {
2147 start.removeLine(line);
2148 }
2149 toUndo = delete;
2150 } else {
2151 toUndo = toDelete.getConnected(); // copy(toDelete.getConnected());
2152 }
2153 SessionStats.DeletedItems(toUndo);
2154 if (parent != null) {
2155 parent.addAllToUndo(toUndo);
2156 parent.removeAllItems(toUndo); // toDelete.getConnected()
2157 }
2158 // reset the mouse cursor
2159 updateCursor();
2160 ItemUtils.EnclosedCheck(parent.getItems());
2161 if (toDelete.hasOverlay()) {
2162 FrameUtils.Parse(parent, false);
2163 FrameGraphics.requestRefresh(false);
2164 }
2165 FrameGraphics.Repaint();
2166 }
2167 // check
2168 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
2169 }
2170
2171 private static void deleteItems(Collection<Item> itemList) {
2172 boolean bReparse = false;
2173 SessionStats.DeletedItems(itemList);
2174 List<Frame> modifiedFrames = new LinkedList<Frame>();
2175 // Get a list of all the modified frames
2176 for (Item i : itemList) {
2177 Frame parent = i.getParent();
2178 if (parent != null)
2179 modifiedFrames.add(parent);
2180 i.setHighlightMode(HighlightMode.None);
2181 bReparse |= i.hasOverlay();
2182 }
2183 // If they are all free items then add the current frame
2184 if (modifiedFrames.size() == 0) {
2185 modifiedFrames.add(DisplayIO.getCurrentFrame());
2186 }
2187
2188 Collection<Item> toUndo = new LinkedHashSet<Item>();
2189 // disconnect any connected items
2190 for (Item i : itemList) {
2191 // Make sure text items attached to cursor are reset back to the
2192 // transparency they should have.
2193 if (i instanceof Text) {
2194 ((Text) i).setAlpha(-1);
2195 }
2196
2197 if (i.getLines().size() > 0) {
2198 Collection<Item> toDelete = deleteLineEnd(i);
2199 // add the copied items to the undo stack
2200 for (Item itemToUndo : toDelete)
2201 if (!toUndo.contains(itemToUndo))
2202 toUndo.add(itemToUndo);
2203 } else if (!toUndo.contains(i)) {
2204 toUndo.add(i); // Why was is this a copy
2205 }
2206 }
2207 for (Frame f : modifiedFrames) {
2208 f.removeAllItems(itemList);
2209 ItemUtils.EnclosedCheck(f.getItems());
2210 }
2211 // TODO: How should undelete deal with undo when items are removed from
2212 // the current frame as well as the overlay frame
2213 Frame currentFrame = DisplayIO.getCurrentFrame();
2214 currentFrame.addAllToUndo(itemList);
2215 itemList.clear();
2216 if (bReparse)
2217 FrameUtils.Parse(currentFrame, false);
2218
2219 }
2220
2221 private static Collection<Item> deleteLineEnd(Item lineEnd) {
2222
2223 if (lineEnd instanceof WidgetCorner) { // Brook
2224
2225 WidgetCorner wc = (WidgetCorner) lineEnd;
2226 List<Item> wccopy;
2227
2228 try {
2229 wccopy = wc.getWidgetSource().copy().getItems();
2230 } catch (InteractiveWidgetNotAvailableException e) {
2231 e.printStackTrace();
2232 return new LinkedList<Item>();
2233 } catch (InteractiveWidgetInitialisationFailedException e) {
2234 e.printStackTrace();
2235 return new LinkedList<Item>();
2236 }
2237
2238 if (lineEnd.getParent() != null) {
2239 lineEnd.getParent().removeAllItems(
2240 wc.getWidgetSource().getItems());
2241 }
2242 return wccopy;
2243 } else {
2244
2245 // // create a backup copy of the dot and its lines
2246 // List<Item> copy = copy(lineEnd.getConnected());
2247 //
2248 // // Remove lines from their anchored dots
2249 // // note: the line is kept so that it can be properly restored
2250 // for (Item ic : copy) {
2251 // if (ic instanceof Line) {
2252 // Line line = (Line) ic;
2253 // // Remove the line from the item that is not the copy of the
2254 // // line end being deletedF
2255 // if (!copy.contains(line.getStartItem()))
2256 // line.getStartItem().removeLine(line);
2257 // if (!copy.contains(line.getEndItem()))
2258 // line.getEndItem().removeLine(line);
2259 // }
2260 // }
2261
2262 Collection<Item> copy = lineEnd.getConnected();
2263
2264 // remove all lines being deleted
2265 for (Item ic : lineEnd.getConnected()) {
2266 if (ic instanceof Line
2267 && ((Line) ic).getOppositeEnd(lineEnd) != null) {
2268 Line line = (Line) ic;
2269
2270 Item d = line.getOppositeEnd(lineEnd);
2271 d.removeLine(line);
2272
2273 // if the dot was only part of one line, it can be
2274 // removed
2275 if (d.getLines().size() == 0) {
2276 if (d.getParent() != null)
2277 d.getParent().removeItem(d);
2278 if (!copy.contains(d))
2279 copy.add(d);
2280 }
2281
2282 if (lineEnd.getParent() != null)
2283 lineEnd.getParent().removeItem(ic);
2284 }
2285 }
2286 return copy;
2287 }
2288 }
2289
2290 private static void removeAllLinesExcept(Item from, Item but) {
2291 List<Line> lines = new LinkedList<Line>();
2292 lines.addAll(from.getLines());
2293 for (Line line : lines)
2294 if (line.getOppositeEnd(from) != but)
2295 from.removeLine(line);
2296
2297 List<Constraint> consts = new LinkedList<Constraint>();
2298 consts.addAll(from.getConstraints());
2299 for (Constraint c : consts)
2300 if (c.getOppositeEnd(from) != but)
2301 from.removeConstraint(c);
2302 }
2303
2304 public static Collection<Item> merge(List<Item> merger, Item mergee) {
2305 assert (mergee != null);
2306 if (mergee.isFrameName()) {
2307 return mergee.getParent().merge(merger);
2308 }
2309
2310 // if(mergee instanceof XRayable)
2311 // return merger;
2312
2313 // check for rectangle merging
2314 if (merger.size() == 3 && mergee.getLines().size() == 2) {
2315 Item corner = getShapeCorner(merger);
2316 // if this is a corner of a shape
2317 if (corner != null) {
2318 Collection<Item> allConnected = corner.getAllConnected();
2319 // Check if we are collapsing a rectangle
2320 if (allConnected.size() == 8 && allConnected.contains(mergee)) {
2321 DisplayIO.setCursorPosition(mergee.getPosition());
2322 DisplayIO.getCurrentFrame().removeAllItems(allConnected);
2323
2324 // find the point opposite corner...
2325 Item opposite = null;
2326 List<Line> lines = corner.getLines();
2327 for (Line l : lines) {
2328 allConnected.remove(l.getOppositeEnd(corner));
2329 }
2330 allConnected.remove(corner);
2331 for (Item i : allConnected) {
2332 if (i.isLineEnd()) {
2333 opposite = i;
2334 break;
2335 }
2336 }
2337 assert (opposite != null);
2338
2339 // check if the rectangle is small enough that it should be
2340 // collapsed to a single point
2341 int x1 = Math.abs(opposite.getX() - mergee.getX());
2342 int x2 = Math.abs(opposite.getY() - mergee.getY());
2343 int distance = (int) Math.sqrt(Math.pow(x1, 2)
2344 + Math.pow(x2, 2));
2345
2346 if (distance < RECTANGLE_TO_POINT_THRESHOLD) {
2347 mergee.removeAllConstraints();
2348 mergee.removeAllLines();
2349 mergee.setThickness(4 * mergee.getThickness());
2350 return mergee.getAllConnected();
2351 } else {
2352 removeAllLinesExcept(mergee, opposite);
2353 removeAllLinesExcept(opposite, mergee);
2354
2355 return mergee.getAllConnected();
2356 }
2357 }
2358 }
2359 }
2360
2361 List<Item> remain = new ArrayList<Item>();
2362 Item res = null;
2363
2364 for (Item i : merger) {
2365 if (!i.isVisible())
2366 continue;
2367 // check for link merging
2368 if (i instanceof Text
2369 && FrameIO.isValidFrameName((((Text) i).getFirstLine()))
2370 && FrameIO.canAccessFrame((((Text) i).getFirstLine()))) {
2371 // check that we can actually access the frame this link
2372 // corresponds to
2373
2374 mergee.setLink(((Text) i).getFirstLine());
2375 } else {
2376 // check for attribute merging
2377 if (i instanceof Text && !i.isLineEnd()) {
2378 Text txt = (Text) i;
2379
2380 // if this is not an attribute merge
2381 if (!AttributeUtils.setAttribute(mergee, txt)) {
2382 // set mouse position for text merges
2383 if (mergee instanceof Text)
2384 ((Text) mergee).insertText(txt.getText(), DisplayIO
2385 .getMouseX(), FrameMouseActions.getY());
2386
2387 else if (mergee instanceof Dot) {
2388 DisplayIO.setCursorPosition(mergee.getPosition());
2389 txt.setPosition(mergee.getPosition());
2390 txt.setThickness(mergee.getThickness());
2391 Frame parent = mergee.getParent();
2392 parent.removeItem(mergee);
2393 anchor(txt);
2394 // change the end points of the lines to the text
2395 // item
2396 while (mergee.getLines().size() > 0) {
2397 Line l = mergee.getLines().get(0);
2398 l.replaceLineEnd(mergee, txt);
2399 }
2400 break;
2401 }
2402
2403 // TODO tidy this up...
2404 // Dot override doesnt use the x and y coords
2405 // Text does... but could be removed
2406 res = mergee.merge(i, DisplayIO.getMouseX(), getY());
2407 if (res != null) {
2408 remain.add(res);
2409 }
2410 }
2411 } else {
2412 if (mergee.isLineEnd()) {
2413 DisplayIO.setCursorPosition(mergee.getPosition());
2414 }
2415 // Moving the cursor ensures shapes are anchored correctly
2416 res = mergee.merge(i, DisplayIO.getMouseX(), getY());
2417 if (res != null)
2418 remain.addAll(res.getConnected());
2419
2420 }
2421 }
2422 }
2423 updateCursor();
2424 mergee.getParent().setChanged(true);
2425 ItemUtils.EnclosedCheck(mergee.getParent().getItems());
2426 // Mike: Why does parse frame have to be called?!?
2427 FrameUtils.Parse(mergee.getParent());
2428
2429 return remain;
2430 }
2431
2432 /**
2433 * Picks up an item on a frame.
2434 *
2435 * @param toGrab
2436 * item to be picked up
2437 * @param removeItem
2438 * true if the item should be removed from the frame
2439 */
2440 public static void pickup(Item toGrab) {
2441 if (toGrab.isFrameName())
2442 return;
2443
2444 if (!toGrab.hasPermission(Permission.full)) {
2445 MessageBay
2446 .displayMessage("Insufficient permission pickup the item");
2447 return;
2448 }
2449
2450 if (toGrab instanceof Circle)
2451 toGrab.setHighlightMode(HighlightMode.Connected);
2452 else
2453 toGrab.setHighlightMode(HighlightMode.Normal);
2454
2455 // Brook: If the widget corner is being picked up. Instead refer to
2456 // picking up the edge for fixed-sized widgets so it is not so confusing
2457 if (toGrab instanceof WidgetCorner) {
2458 WidgetCorner wc = (WidgetCorner) toGrab;
2459 if (wc.getWidgetSource().isFixedSize()) {
2460 for (Item i : toGrab.getConnected()) {
2461 if (i instanceof WidgetEdge) {
2462 toGrab = i;
2463 break;
2464 }
2465 }
2466 }
2467 }
2468 pickup(toGrab.getConnected());
2469 }
2470
2471 public static void pickup(Collection<Item> toGrab) {
2472 boolean bReparse = false;
2473 String currentFrame = DisplayIO.getCurrentFrame().getName();
2474 Iterator<Item> iter = toGrab.iterator();
2475 while (iter.hasNext()) {
2476 Item i = iter.next();
2477 if (!i.hasPermission(Permission.full)) {
2478 iter.remove();
2479 continue;
2480 }
2481 if (i.equals(_lastHighlightedItem))
2482 _lastHighlightedItem = null;
2483 // i.setSelectedMode(SelectedMode.None);
2484 // Check if it has a relative link if so make it absolute
2485 i.setAbsoluteLink();
2486 // parent may be null
2487 if (i.getParent() != null) {
2488 i.getParent().removeItem(i);
2489 if (currentFrame.equals(i.getParent().getName()))
2490 i.setParent(null);
2491 }
2492 FreeItems.getInstance().add(i);
2493 i.setFloating(true);
2494 if (i.hasVector()) {
2495 bReparse = true;
2496
2497 Frame overlayFrame = FrameIO.LoadFrame(i.getAbsoluteLink());
2498 Collection<Item> copies = ItemUtils.CopyItems(overlayFrame
2499 .getNonAnnotationItems(), i.getVector());
2500 for (Item copy : copies) {
2501 FreeItems.getInstance().add(copy);
2502 copy.setFloating(true);
2503 copy.setParent(null);
2504 }
2505 }
2506 }
2507 _lastHighlightedItem = null;
2508 updateCursor();
2509
2510 // if there are multiple items in the list, determine which to use for
2511 // offset calculations
2512 if (toGrab.size() > 1) {
2513 for (Item i : toGrab) {
2514 // MIKE: Movement goes haywire if these are removed because Line
2515 // class returns 0 for getX
2516 if (!(i instanceof Line) && !(i instanceof XRayable)) {
2517 _offX = DisplayIO.getMouseX() - i.getX() + i.getOffset().x;
2518 _offY = getY() - i.getY() + i.getOffset().y;
2519
2520 // make the offset item the first item in the list (so
2521 // move method knows which item to use)
2522 FreeItems.getInstance().set(
2523 FreeItems.getInstance().indexOf(i),
2524 FreeItems.getInstance().get(0));
2525 FreeItems.getInstance().set(0, i);
2526 break;
2527 }
2528 }
2529
2530 move(FreeItems.getInstance());
2531 ItemUtils.EnclosedCheck(toGrab);
2532 // otherwise, just use the first item
2533 } else if (toGrab.size() == 1) {
2534 Item soleItem = toGrab.iterator().next();
2535 _offX = DisplayIO.getMouseX() - soleItem.getX()
2536 + soleItem.getOffset().x;
2537 _offY = getY() - soleItem.getY() + soleItem.getOffset().y;
2538 // Now call move so that if we are on a message in the message box
2539 // It doesnt appear up the top of the scree!!
2540 move(toGrab);
2541 } else {
2542 MessageBay
2543 .displayMessage("Insufficient permission pickup the items");
2544 }
2545 if (bReparse)
2546 FrameUtils.Parse(DisplayIO.getCurrentFrame(), false);
2547
2548 FrameGraphics.Repaint();
2549 }
2550
2551 private static Line createLine() {
2552 Frame current = DisplayIO.getCurrentFrame();
2553 // create the two endpoints
2554 Item end = DisplayIO.getCurrentFrame().createDot();
2555 Item start = DisplayIO.getCurrentFrame().createDot();
2556
2557 // create the Line
2558 Line line = new Line(start, end, current.getNextItemID());
2559 line.autoArrowheadLength();
2560
2561 // anchor the start
2562 anchor(start);
2563
2564 // attach the line to the cursor
2565 pickup(end);
2566 _lastHighlightedItem = null;
2567
2568 // TODO figure out how to get the end to highlight
2569 // end.setSelectedMode(SelectedMode.Normal);
2570 // end.setSelectedMode(SelectedMode.None);
2571
2572 return line;
2573 }
2574
2575 /**
2576 * Returns a list of copies of the list passed in
2577 *
2578 * @param toCopy
2579 * The list of items to copy
2580 * @return A List of copied Items
2581 */
2582 private static List<Item> copy(Collection<Item> toCopy) {
2583 return ItemUtils.CopyItems(toCopy);
2584 }
2585
2586 public static void anchor(Item toAnchor, boolean checkEnclosure) {
2587 // Only anchor items we have full permission over... ie dont anchor
2588 // vector items
2589 if (!toAnchor.hasPermission(Permission.full))
2590 return;
2591
2592 toAnchor.anchor();
2593
2594 if (checkEnclosure) {
2595 ItemUtils.EnclosedCheck(toAnchor.getParentOrCurrentFrame()
2596 .getItems());
2597 FrameGraphics.Repaint();
2598 }
2599 }
2600
2601 public static void anchor(Item toAnchor) {
2602 anchor(toAnchor, true);
2603 }
2604
2605 public static void anchor(Collection<Item> toAnchor) {
2606 boolean bReparse = false;
2607 // Need to make sure we check enclosure for overlays etc
2608 Set<Frame> checkEnclosure = new HashSet<Frame>();
2609 for (Item i : toAnchor) {
2610 // if (!i.hasVector())
2611 anchor(i, false);
2612 checkEnclosure.add(i.getParentOrCurrentFrame());
2613 bReparse |= i.hasOverlay();
2614 }
2615 toAnchor.clear();
2616 // Check enclosure for all the frames of the items that were anchored
2617 for (Frame f : checkEnclosure) {
2618 ItemUtils.EnclosedCheck(f.getItems());
2619 }
2620 if (bReparse)
2621 FrameUtils.Parse(DisplayIO.getCurrentFrame(), false);
2622 FrameGraphics.Repaint();
2623 }
2624
2625 /*
2626 * private boolean mouseMovedRecently() { Date now = new Date();
2627 *
2628 * return now.getTime() - _lastMouseMovement.getTime() < 150; }
2629 */
2630
2631 public void mouseWheelMoved(MouseWheelEvent arg0) {
2632 NavigationActions.ResetLastAddToBack();
2633
2634 int clicks = arg0.getClickCount();
2635
2636 if (FreeItems.getInstance().size() == 2) {
2637 if ((FreeItems.getInstance().get(0).isLineEnd() && FreeItems
2638 .getInstance().get(1) instanceof Line)
2639 || (FreeItems.getInstance().get(1).isLineEnd() && FreeItems
2640 .getInstance().get(0) instanceof Line)) {
2641
2642 Line line;
2643 if (FreeItems.getInstance().get(0) instanceof Line)
2644 line = (Line) FreeItems.getInstance().get(0);
2645 else
2646 line = (Line) FreeItems.getInstance().get(1);
2647
2648 // User must do multiple clicks to toggle the line
2649 if (clicks == MOUSE_WHEEL_THRESHOLD) {
2650 if (arg0.isShiftDown())
2651 line.toggleArrowHeadRatio(arg0.getWheelRotation());
2652 else
2653 line.toggleArrowHeadLength(arg0.getWheelRotation());
2654 // line.getParent().change();
2655 FrameGraphics.Repaint();
2656 }
2657 }
2658 } else if (arg0.getWheelRotation() != 0 && arg0.isShiftDown()) {
2659
2660 FunctionKey rotationType = FunctionKey.SizeUp;
2661 if (arg0.getWheelRotation() > 0) {
2662 rotationType = FunctionKey.SizeDown;
2663 }
2664
2665 Item ip = FrameUtils.getCurrentItem();
2666 if (ip != null && clicks > 1) {
2667 float size = ip.getSize();
2668 if (ip instanceof Dot || ip instanceof Line
2669 || ip instanceof Circle) {
2670 size = ip.getThickness();
2671 }
2672 // base the number of clicks on the size of the object
2673 clicks = (int) Math.ceil(size / 20.0 * clicks);
2674 }
2675 FrameKeyboardActions.functionKey(rotationType, clicks, arg0
2676 .isShiftDown(), arg0.isControlDown());
2677
2678 } else if (clicks == MOUSE_WHEEL_THRESHOLD) {
2679 Item item = FrameUtils.getCurrentItem();
2680
2681 // if the user is not pointing to any item
2682 if (item == null) {
2683 FrameKeyboardActions.NextTextItem(null,
2684 arg0.getWheelRotation() > 0);
2685 return;
2686 }
2687
2688 if (item instanceof Line || item instanceof Circle) {
2689 // check permissions
2690 if (!item.hasPermission(Permission.full)) {
2691 MessageBay
2692 .displayMessage("Insufficient permission to edit the Line");
2693 return;
2694 }
2695 item.toggleDashed(arg0.getWheelRotation());
2696 item.getParent().change();
2697 FrameGraphics.Repaint();
2698 return;
2699 } else if (item instanceof Text) {
2700 FrameKeyboardActions.NextTextItem(item,
2701 arg0.getWheelRotation() > 0);
2702 }
2703 }
2704 }
2705
2706 /**
2707 *
2708 * @return the integer value for the last mouse button clicked.
2709 */
2710 public static int getLastMouseButton() {
2711 if (_lastMouseClick == null)
2712 return MouseEvent.NOBUTTON;
2713
2714 return _lastMouseClick.getButton();
2715 }
2716
2717 public static boolean isDelete(int modifiersEx) {
2718
2719 int onMask = MouseEvent.BUTTON3_DOWN_MASK
2720 | MouseEvent.BUTTON2_DOWN_MASK;
2721 return (modifiersEx & onMask) == onMask;
2722 }
2723
2724 public static boolean isGetAttributes(int modifiersEx) {
2725 int onMask = MouseEvent.BUTTON3_DOWN_MASK
2726 | MouseEvent.BUTTON1_DOWN_MASK;
2727 return (modifiersEx & onMask) == onMask;
2728 }
2729
2730 public static boolean isTwoClickNoOp(int modifiersEx) {
2731 int onMask = MouseEvent.BUTTON2_DOWN_MASK
2732 | MouseEvent.BUTTON1_DOWN_MASK;
2733 return (modifiersEx & onMask) == onMask;
2734 }
2735
2736 public static boolean wasDeleteClicked() {
2737 if (_lastMouseClick == null)
2738 return false;
2739 return isDelete(_lastMouseClickModifiers);
2740 }
2741
2742 public static void control(KeyEvent ke) {
2743 for (Item i : FreeItems.getInstance()) {
2744 i.invalidateCommonTrait(ItemAppearence.PreMoved);
2745 }
2746
2747 _controlDown = ke.isControlDown();
2748
2749 if (_controlDown) {
2750 // TODO why are these two lines needed?!?!
2751 // _offX = 0;
2752 // _offY = 0;
2753 } else {
2754 resetOffset();
2755 }
2756
2757 if (_mouseDown > 0 && _lastMouseDragged != null) {
2758 MouseEvent me = _lastMouseDragged;
2759 _lastMouseDragged = new MouseEvent(ke.getComponent(),
2760 MouseEvent.NOBUTTON, ke.getWhen(), ke.getModifiers(), me
2761 .getX(), me.getY(), 0, false);
2762 _instance.mouseDragged(_lastMouseDragged);
2763 } else if (_lastMouseMoved != null) {
2764 MouseEvent me = _lastMouseMoved;
2765 _lastMouseMoved = new MouseEvent(ke.getComponent(),
2766 MouseEvent.NOBUTTON, ke.getWhen(), ke.getModifiers(), me
2767 .getX(), me.getY(), 0, false);
2768 _instance.mouseMoved(_lastMouseMoved, true);
2769 }
2770 updateCursor();
2771
2772 for (Item i : FreeItems.getInstance()) {
2773 i.invalidateCommonTrait(ItemAppearence.PostMoved);
2774 }
2775 // TODO: Check why the new constrained line is not repainted immediately
2776 FrameGraphics.requestRefresh(true);
2777 FrameGraphics.refresh(true);
2778 }
2779
2780 public static int getX() {
2781 return Math.round(MouseX);
2782 }
2783
2784 public static int getY() {
2785 return Math.round(MouseY);
2786 }
2787
2788 public static Item getlastHighlightedItem() {
2789 return _lastHighlightedItem;
2790 }
2791}
Note: See TracBrowser for help on using the repository browser.