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

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