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

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

Added some HELP actions
More mail stuff... and networking stuff
Can now save frames and send messages to peers
Also version control prevent versions from being lost

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