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

Last change on this file since 156 was 156, checked in by ra33, 16 years ago

Added calculate action

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