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

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

Widgets are now linkable.
Also made some improvements for popups - improved interaction with free items.

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