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

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

Fixed a bunch of problems with rectangles and resizing the window, as well as adding some more unit tests etc.

File size: 58.1 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Point;
4import java.awt.event.ActionEvent;
5import java.awt.event.ActionListener;
6import java.awt.event.MouseEvent;
7import java.awt.event.MouseListener;
8import java.awt.event.MouseMotionListener;
9import java.awt.event.MouseWheelEvent;
10import java.awt.event.MouseWheelListener;
11import java.util.ArrayList;
12import java.util.Date;
13import java.util.LinkedList;
14import java.util.List;
15
16import javax.swing.Timer;
17
18import org.expeditee.actions.Actions;
19import org.expeditee.actions.NavigationActions;
20import org.expeditee.items.Constraint;
21import org.expeditee.items.Dot;
22import org.expeditee.items.InteractiveWidgetNotAvailableException;
23import org.expeditee.items.Item;
24import org.expeditee.items.ItemUtils;
25import org.expeditee.items.Line;
26import org.expeditee.items.Picture;
27import org.expeditee.items.Text;
28import org.expeditee.items.WidgetCorner;
29import org.expeditee.items.WidgetEdge;
30import org.expeditee.stats.SessionStats;
31
32public class FrameMouseActions implements MouseListener, MouseMotionListener,
33 MouseWheelListener {
34
35 private static int _lastMouseClickModifiers = 0;
36
37 private FrameMouseActions() {
38 }
39
40 private static FrameMouseActions _instance = null;
41
42 public static FrameMouseActions getInstance() {
43 if (_instance == null)
44 _instance = new FrameMouseActions();
45 return _instance;
46 }
47
48 // TODO say where/how used
49 private static final int MOUSE_WHEEL_THRESHOLD = 3;
50
51 private static final int MINIMUM_RANGE_DEPRESS_TIME = 250;
52
53 private static final int RECTANGLE_TO_POINT_THRESHOLD = 20;
54
55 private static Date _lastMouseClickDate = new Date();
56
57 public static final int LITTLE_MOUSE_PAUSE = 500;
58
59 public static final int ZERO_MOUSE_PAUSE = 0;
60
61 public static final int BIG_MOUSE_PAUSE = 750;
62
63 public static final int CONTEXT_FREESPACE = 0;
64
65 public static final int CONTEXT_AT_TEXT = 1;
66
67 public static final int CONTEXT_AT_LINE = 2;
68
69 public static final int CONTEXT_AT_DOT = 3;
70
71 public static final int CONTEXT_AT_ENCLOSURE = 4;
72
73 public static int _alpha = -1;
74
75 /**
76 * The last known mouse X coordinate
77 */
78 public static int MouseX;
79
80 /**
81 * The last known mouse Y coordinate. Relative to the top of the
82 * application.
83 */
84 public static int MouseY;
85
86 // Offset coordinates from the mouse position when grabbing items
87 private static int _offX;
88
89 // MIKE: Thinks the offsets are used in getting items to draw in the message
90 // window?
91 private static int _offY;
92
93 // Keeps track of mouse button events when a delete occurs
94 private static boolean _isDelete = false;
95
96 // Keeps track of mouse button events when the user extracts attributes
97 // occurs
98 private static boolean _isAttribute = false;
99
100 /**
101 * A flag to indicate that the last mouseUp event was part of a two button
102 * click sequence hence the next mouse up should be ignored*
103 */
104 private static boolean _wasDouble = false;
105
106 private static boolean _isNoOp = false;
107
108 private static boolean _extrude = false;
109
110 // keeps track of the last highlighted Item
111 private static Item _lastHighlightedItem = null;
112
113 // keeps track of the item being 'ranged out' if there is one.
114 private static Text _lastRanged = null;
115
116 // keeps track of the picture being cropped if there is one
117 private static Picture _lastCropped = null;
118
119 // true if lastItem only has highlighting removed when a new item is
120 // highlighted
121 private static boolean _lastHoldsHighlight = false;
122
123 private static Item _tdfcItem = null;
124
125 private static boolean _forceArrowCursor = true;
126
127 // the current context of the cursor
128 private static int _context = 0;
129
130 public static void setForceArrow(boolean val) {
131 _forceArrowCursor = val;
132 }
133
134 public static int getContext() {
135 return _context;
136 }
137
138 static int _mouseDown = 0;
139
140 private static MouseEvent _lastMouseClick = null;
141
142 private static Item _lastClickedOn = null;
143
144 private static List<Item> _lastClickedIn = null;
145
146 private static boolean _pulseOn = false;
147
148 private static final int PULSE_AMOUNT = 2;
149
150 private static Timer _MouseTimer = new Timer(LITTLE_MOUSE_PAUSE,
151 new ActionListener() {
152 public void actionPerformed(ActionEvent ae) {
153 // check if we are in free space
154 if (_lastClickedOn == null && Frame.FreeItems.size() == 0) {
155 // System.out.println("SuperBack!");
156 _MouseTimer.setDelay(ZERO_MOUSE_PAUSE);
157 back();
158 } else {
159 if (FrameUtils.getCurrentItem() == null) {
160 // Check if we are toggling arrowhead
161 if (Frame.FreeItems.size() <= 2) {
162 for (Item i : Frame.FreeItems) {
163 if (i instanceof Line) {
164 ((Line) i).toggleArrow();
165 }
166 }
167 FrameGraphics.Repaint();
168 }
169 }
170 _MouseTimer.stop();
171 }
172 }
173 });
174
175 private static void setPulse(boolean pulseOn) {
176 if (_pulseOn == pulseOn)
177 return;
178 int amount = PULSE_AMOUNT;
179 if (!pulseOn)
180 amount *= -1;
181 _pulseOn = pulseOn;
182
183 for (Item i : _lastClickedOn.getAllConnected()) {
184 if (i instanceof Line) {
185 Line line = (Line) i;
186 line.setThickness(line.getThickness() + amount);
187 }
188 }
189 FrameGraphics.Repaint();
190 }
191
192 private static Timer _ExtrudeMouseTimer = new Timer(BIG_MOUSE_PAUSE,
193 new ActionListener() {
194 public void actionPerformed(ActionEvent ae) {
195 setPulse(true);
196 _extrude = true;
197 _ExtrudeMouseTimer.stop();
198 }
199 });
200
201 public void mouseClicked(MouseEvent e) {
202 }
203
204 /**
205 * Each Item on the Frame is checked to determine if the mouse x,y
206 * coordinates are on the Item (or within the Shape surrounding it). If the
207 * coordinates are on the Item then the Item is checked for a link, if it
208 * has a link the link is followed, if not, nothing is done.
209 */
210 public void mousePressed(MouseEvent e) {
211 ProccessMousePressedEvent(e, e.getModifiersEx());
212 }
213
214 public void ProccessMousePressedEvent(MouseEvent e, int modifiersEx) {
215 // System.out.println(modifiersEx);
216
217 _mouseDown += e.getButton();
218 _lastMouseClickDate = new Date();
219 _lastClickedOn = FrameUtils.getCurrentItem();
220 // load any frame if necessary
221 Item on = _lastClickedOn;
222
223 _lastClickedIn = FrameUtils.getCurrentItems();
224 // if (_lastClickedIn != null){
225 // System.out.println(_lastClickedIn.size());}
226
227 SessionStats.MouseClicked(e.getButton());
228 if (e.getButton() == MouseEvent.BUTTON1) {
229 SessionStats.AddFrameEvent("Ld");
230 _extrude = false;
231 } else if (e.getButton() == MouseEvent.BUTTON2) {
232 SessionStats.AddFrameEvent("Md");
233 _extrude = false;
234 } else if (e.getButton() == MouseEvent.BUTTON3) {
235 SessionStats.AddFrameEvent("Rd");
236 }
237
238 // Mike says...
239 // For somereason the modifiers for e are different from modifiersEx
240 // The SwingUtilities.convertMouseEvent method changes the modifiers
241 _lastMouseClick = e;
242 _lastMouseClickModifiers = modifiersEx;
243
244 // Only start the timer when in free space when the user double clicks
245 // to do super back
246 // TODO change this so that there are separate timers for super back and
247 // the other
248 // Long depress actions if that is what is wanted.
249 if (_lastClickedOn == null && Frame.FreeItems.size() == 0) {
250 // System.out.println(e.getClickCount());
251 if (e.getClickCount() >= 2) {
252 _MouseTimer.start();
253 }
254 } else if (_lastClickedOn != null && Frame.FreeItems.size() == 0
255 && e.getButton() == MouseEvent.BUTTON3) {
256 _ExtrudeMouseTimer.start();
257
258 } else {
259 _MouseTimer.start();
260 }
261
262 // pre-cache the frame if it is linked
263 if (on != null && on.getLink() != null && on.isLinkValid()) {
264 FrameIO.Precache(on.getAbsoluteLink());
265 }
266
267 // check for delete command
268 if (isDelete(modifiersEx)) {
269 _isDelete = true;
270 // _lastRanged = null;
271 _lastCropped = null;
272 _wasDouble = false;
273 // check for attributes command
274 } else if (isGetAttributes(modifiersEx)) {
275 _isAttribute = true;
276 _wasDouble = false;
277 } else if (isTwoClickNoOp(modifiersEx)) {
278 _isAttribute = false;
279 _wasDouble = false;
280 _isDelete = false;
281 _isNoOp = true;
282 } else
283 _isDelete = false;
284
285 {
286 // This must happen before the previous code
287 // This is when the user is anchoring something
288 if (_context == CONTEXT_FREESPACE && Frame.itemAttachedToCursor()) {
289 FrameGraphics.ChangeSelectionMode(_lastHighlightedItem,
290 Item.SelectedMode.None);
291
292 _lastHighlightedItem = Frame.getItemAttachedToCursor();
293 for (Item i : Frame.FreeItems) {
294 i.restoreLastMode(Item.DEPRESSED_HIGHLIGHT);
295 }
296 FrameGraphics.Repaint();
297 // this is when the user is picking something up
298 } else if (_lastHighlightedItem != null) {
299 if (!(_lastHighlightedItem instanceof Line)) {
300 _lastHighlightedItem
301 .setSelectionColor(Item.DEPRESSED_HIGHLIGHT);
302 } else {
303 for (Item i : _lastHighlightedItem.getAllConnected()) {
304 i.setSelectionColor(Item.DEPRESSED_HIGHLIGHT);
305 }
306 }
307 FrameGraphics.Repaint();
308 }
309 }
310
311 // if the user is ranging text
312 if (on != null && on instanceof Text && !_isDelete) {
313 _lastRanged = (Text) on;
314 // set start-drag point
315 _lastRanged.setSelectionStart(DisplayIO.getMouseX(), DisplayIO
316 .getMouseY());
317 }
318
319 if (on != null && on instanceof Picture
320 && e.getButton() == MouseEvent.BUTTON3 && !_isDelete) {
321 _lastCropped = (Picture) on;
322 // set start crop point
323 _lastCropped.setStartCrop(DisplayIO.getMouseX(), DisplayIO
324 .getMouseY());
325 _lastCropped.setShowCrop(true);
326 }
327 }
328
329 // This is where all the processing happens
330 public void mouseReleased(MouseEvent e) {
331 FrameUtils.ResponseTimer.restart();
332
333 _mouseDown -= e.getButton();
334 updateCursor();
335
336 // System.out.println(e.getX() + ", " + e.getY());
337
338 Text lastRanged = _lastRanged;
339 _lastRanged = null;
340 // Dont do ranging if the user moves really quickly...
341 // They are probably trying to pick something up in this case
342 if (lastRanged != null) {
343 long depressTime = (new Date()).getTime()
344 - _lastMouseClickDate.getTime();
345 // double changeInDistance =
346 // e.getPoint().distance(_currentMouseClick.getPoint());
347 // double speed = changeInDistance * 1000 / changeInTime;
348
349 // System.out.println(depressTime);
350
351 if (depressTime < MINIMUM_RANGE_DEPRESS_TIME
352 || lastRanged.getSelectedSize() <= 0) {// Text.MINIMUM_RANGED_CHARS)
353 // {
354 lastRanged.clearSelection();
355 lastRanged = null;
356 }
357 }
358
359 _ExtrudeMouseTimer.stop();
360 _MouseTimer.stop();
361 setPulse(false);
362
363 // if the last action was a delete, then ignore the next mouseup
364 if (_wasDouble) {
365 _wasDouble = false;
366 return;
367 }
368
369 // This code must come after the _wasDouble code...
370 // Otherwise get Stopping Agent method after doing the left+right format
371 // shortcut
372 if (Actions.isAgentRunning()) {
373 Actions.stopAgent();
374 return;
375 }
376
377 /*
378 * if (_isNoOp) { if (e.getButton() != MouseEvent.NOBUTTON) { _isNoOp =
379 * false; _wasDouble = true; // lastRanged.clearSelection();
380 * FrameGraphics.Repaint(); return; } }
381 */
382
383 // get whatever the user was pointing at
384 Item clickedOn = _lastClickedOn;
385 List<Item> clickedIn = _lastClickedIn;
386
387 MouseX = e.getX();
388 MouseY = e.getY();
389
390 List<Item> releasedIn = FrameUtils.getCurrentItems();
391 Item releasedOn = FrameUtils.getCurrentItem();
392
393 if (_isNoOp
394 && ((releasedOn != null && clickedOn != null && releasedOn != clickedOn) || (releasedOn == null && releasedIn == null))) {
395 if (_isDelete) {
396 _isDelete = false;
397 _wasDouble = true;
398 }
399
400 _isNoOp = false;
401
402 if (_lastHighlightedItem != null)
403 FrameGraphics.ChangeSelectionMode(_lastHighlightedItem,
404 Item.SelectedMode.None);
405
406 if (Frame.itemAttachedToCursor()) {
407 move(Frame.FreeItems);
408 }
409
410 FrameGraphics
411 .DisplayMessage("Action cancelled, mouse moved more than "
412 + UserSettings.NoOpThreshold + " pixels.");
413 FrameGraphics.Repaint();
414 return;
415 } else
416 _isNoOp = false;
417
418 // if this is a delete command
419 if (_isDelete) {
420 if (lastRanged != null) {
421
422 Item i = Frame.getItemAttachedToCursor();
423 if (i != null && i instanceof Text) {
424 lastRanged.replaceSelectedText(((Text) i).getTextNoList());
425 Frame.FreeItems.clear();
426 } else
427 lastRanged.cutSelectedText();
428 lastRanged.clearSelection();
429 FrameGraphics.Repaint();
430
431 } else {
432 delete(clickedOn);
433 }
434 _wasDouble = true;
435 _isDelete = false;
436 return;
437 }
438
439 // if this is an attribute extraction command
440 if (_isAttribute) {
441 if (clickedOn == null) {
442 // First look for the @NoFormat tag
443 Frame current = DisplayIO.getCurrentFrame();
444 Actions.PerformAction(current, null, "Format");
445 /*
446 * if (ItemUtils.ContainsTag(current.getItems(), "@NoFormat")) {
447 * FrameGraphics .DisplayMessage("Frame is protected by
448 * @NoFormat tag."); } else { new Format().process(current); }
449 */
450 } else {
451 extractAttributes(clickedOn);
452 }
453 _wasDouble = true;
454 _isAttribute = false;
455 return;
456 }
457
458 // if the user is ranging-out text
459 if (lastRanged != null) {
460
461 Text ranged = lastRanged.copy();
462
463 // if the user is cutting text from the item
464 if (e.getButton() == MouseEvent.BUTTON2) {
465 // if the entire text is selected then pickup the item
466 if (lastRanged.getSelectedSize() == lastRanged.getLength()) {
467 pickup(lastRanged);
468 lastRanged.clearSelection();
469 FrameGraphics.Repaint();
470 return;
471 }
472
473 ranged.setText(lastRanged.cutSelectedText());
474 // if the user is copying text from the item
475 } else if (e.getButton() == MouseEvent.BUTTON3)
476 ranged.setText(lastRanged.copySelectedText());
477
478 ranged.setLink(null);
479 ranged.setParent(null);
480 ranged.setPosition(DisplayIO.getMouseX(), DisplayIO.getMouseY());
481 pickup(ranged);
482
483 lastRanged.clearSelection();
484 FrameGraphics.Repaint();
485 return;
486 }
487
488 // if the user is cropping an image
489 if (clickedOn != null && clickedOn == _lastCropped
490 && _lastCropped.getCroppedSize() > 0) {
491 Picture cropped = _lastCropped.copy();
492 cropped.setParent(null);
493 pickup(cropped);
494
495 }
496
497 // if the user has cropped an imate, either the above happend or this is
498 // a no-op
499 if (_lastCropped != null && _lastCropped.getCroppedSize() > 0) {
500 _lastCropped.clearCropping();
501 _lastCropped = null;
502 FrameGraphics.Repaint();
503 return;
504 }
505
506 // if the user is left-clicking
507 if (e.getButton() == MouseEvent.BUTTON1) {
508 SessionStats.AddFrameEvent("Lu");
509 leftButton(clickedOn);
510 return;
511 }
512
513 if (e.getButton() == MouseEvent.BUTTON2) {
514 SessionStats.AddFrameEvent("Mu");
515 middleButton(clickedOn, clickedIn);
516 return;
517 }
518
519 if (e.getButton() == MouseEvent.BUTTON3) {
520 SessionStats.AddFrameEvent("Ru");
521 rightButton(clickedOn, clickedIn);
522 return;
523 }
524
525 // error, we should have returned by now
526 System.out.println("Error: mouseReleased should have returned by now. "
527 + e);
528 }
529
530 /**
531 * This method handles all left-click actions
532 */
533 public static void leftButton(Item clicked) {
534 // if the user is pointing at something then either follow the link or
535 // do TDFC
536 if (clicked == null) {
537 // Check if the user is nearby another item...
538 int mouseX = DisplayIO.getMouseX();
539 int mouseY = DisplayIO.getMouseY();
540 // System.out.println(mouseX + "," + mouseY);
541 for (Item i : DisplayIO.getCurrentFrame().getItems()) {
542 if (i instanceof Text) {
543 if (i.isNear(mouseX, mouseY)) {
544 clicked = i;
545 break;
546 }
547 }
548 }
549 }
550
551 if (clicked instanceof Text) {
552 if (((Text) clicked).getTextNoList().length() == 0)
553 clicked = null;
554 }
555
556 if (clicked != null) {
557 // check item permissions
558 if (clicked.Permission < Item.PERMISSION_FOLLOW_LINKS) {
559 FrameGraphics
560 .DisplayMessage("Insufficient Permissions for action on item");
561 return;
562 }
563
564 Item clickedOn = clicked;
565
566 // actions take priority
567 if (_lastMouseClick != null && !_lastMouseClick.isShiftDown()
568 && clickedOn.getAction() != null) {
569 clickedOn.performActions();
570 return;
571 } else if (clickedOn.getLink() != null) {
572 // Dont save the frame if we are moving to an old version of
573 // this frame
574 // because everytime we save with the old tag... the frame is
575 // backed up
576 if (!clickedOn.isOldTag())
577 FrameIO.SaveFrame(DisplayIO.getCurrentFrame());
578
579 NavigationActions.setLastNavigationItem(clickedOn);
580 load(clickedOn.getAbsoluteLink());
581 // DisplayIO.UpdateTitle();
582 return;
583 // no link is found, perform TDFC
584 } else {
585 // check for TDFC permission
586 if (clicked.Permission < Item.PERMISSION_TDFC) {
587 FrameGraphics
588 .DisplayMessage("Insufficient permission to TDFC from that item");
589 return;
590 }
591
592 if (clickedOn.isOldTag())
593 return;
594
595 // if the user is clicking on the frame name
596 if (clickedOn.isFrameName()) {
597 Frame next = FrameIO.LoadNext();
598 FrameUtils.DisplayFrame(next, true);
599 return;
600 }
601
602 tdfc(clickedOn);
603 return;
604 }
605 // if user is not pointing at something,this is a back
606 } else {
607 back();
608 return;
609 }
610 }
611
612 private static boolean doMerging(Item clicked) {
613 if (clicked == null)
614 return false;
615
616 // Brook: widgets do not merge
617 if (clicked instanceof WidgetCorner)
618 return false;
619
620 // Brook: widgets do not merge
621 if (clicked instanceof WidgetEdge)
622 return false;
623
624 // System.out.println(Frame.FreeItems.size());
625 if (isRubberBandingRectangle()
626 && clicked.getAllConnected().contains(
627 Frame.getItemAttachedToCursor()))
628 return true;
629
630 if (Frame.FreeItems.size() > 2)
631 return false;
632
633 Item attachedToCursor = Frame.getItemAttachedToCursor();
634
635 if (clicked instanceof Text
636 && !(attachedToCursor instanceof Text || attachedToCursor
637 .isLineEnd())) {
638 return false;
639 }
640
641 return true;
642 }
643
644 /**
645 * This method handles all middle-click actions
646 */
647 private static void middleButton(Item clicked, List<Item> clickedIn) {
648 // if the cursor has Items attached
649 if (Frame.itemAttachedToCursor()) {
650 // if the user is pointing at something, merge the items (if
651 // possible)
652 if (doMerging(clicked)) {
653 // check permissions
654 if (clicked.Permission < Item.PERMISSION_FULL
655 && clicked.getParent() != null
656 && clicked.getParent().getFrameNameItem() != clicked) {
657 FrameGraphics.DisplayMessage("Insufficient Permission");
658 return;
659 }
660
661 List<Item> left = merge(Frame.FreeItems, clicked);
662 anchor(left);
663
664 Frame.FreeItems.clear();
665 FrameGraphics.Repaint();
666 updateCursor();
667 return;
668 // otherwise, anchor the items
669 } else {
670 // if a line is being rubber-banded, check for auto
671 // straightening
672 anchor(Frame.FreeItems);
673 Frame.FreeItems.clear();
674 updateCursor();
675 _offX = _offY = 0;
676 return;
677 }
678 } else {
679 // otherwise if the user is pointing at something,pick it up
680 if (clicked != null) {
681 // check permissions
682 if (clicked.Permission < Item.PERMISSION_FULL) {
683 FrameGraphics
684 .DisplayMessage("Insufficient Permission to pick up item");
685 return;
686 }
687
688 // BROOK: WIDGET RECTANGLES DONT ALLOW DISCONNECTION
689 if (clicked instanceof Line && !(clicked instanceof WidgetEdge)) {
690 // Check if within 20% of the end of the line
691 Line l = (Line) clicked;
692 Item toDisconnect = l.getEndPointToDisconnect(
693 _lastMouseClick.getX(), _lastMouseClick.getY());
694
695 if (toDisconnect != null) {
696 if (toDisconnect.getSelectedMode() == Item.SelectedMode.Normal) {
697 DisplayIO.setCursorPosition(toDisconnect
698 .getPosition(), false);
699 pickup(toDisconnect);
700 return;
701 } else {
702 List<Line> lines = toDisconnect.getLines();
703 // This is to remove constraints from single lines
704 // with constraints...
705 // ie. partially deleted rectangles
706 if (lines.size() == 1) {
707 toDisconnect.removeAllConstraints();
708
709 DisplayIO.setCursorPosition(toDisconnect
710 .getPosition(), false);
711 pickup(toDisconnect);
712 return;
713 }
714 // If we are then detatch the line and pick up its
715 // end point...
716 Frame currentFrame = DisplayIO.getCurrentFrame();
717 Item newPoint = null;
718
719 // If the point we are disconnecting is text...
720 // Then we want to leave the text behind
721 // And disconnect a point
722 if (toDisconnect instanceof Text) {
723 newPoint = new Dot(toDisconnect.getX(),
724 toDisconnect.getY(), -1);
725 Item.DuplicateItem(toDisconnect, newPoint);
726 } else {
727 newPoint = toDisconnect.copy();
728 }
729
730 currentFrame.addItem(newPoint);
731 // remove the current item from the connected
732 // list for this item
733 l.replaceEnd(toDisconnect, newPoint);
734 // remove unneeded constrains
735 newPoint.removeAllConstraints();
736
737 toDisconnect
738 .setSelectedMode(Item.SelectedMode.None);
739 DisplayIO.setCursorPosition(toDisconnect
740 .getPosition(), false);
741 pickup(newPoint);
742 ItemUtils.EnclosedCheck(toDisconnect
743 .getParentOrCurrentFrame().getItems());
744 return;
745 }
746 }
747 }
748 if (clicked.isLineEnd())
749 DisplayIO.setCursorPosition(clicked.getPosition(), false);
750 pickup(clicked);
751 return;
752 // otherwise create a line
753 } else {
754 if (clickedIn != null) {
755 ArrayList<Item> toPickup = new ArrayList<Item>(clickedIn
756 .size());
757 for (Item ip : clickedIn)
758 if (ip.Permission >= Item.PERMISSION_FULL)
759 toPickup.add(ip);
760
761 pickup(toPickup);
762 // otherwise the user is creating a line
763 } else {
764 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(),
765 MouseX, MouseY);
766 Line newLine = createLine();
767 // if the user can spot-weld, show the virtual spot
768 if (on instanceof Line) {
769 Line line = (Line) on;
770 newLine.setThickness(line.getThickness());
771 newLine.setLinePattern(line.getLinePattern());
772 newLine.setColor(line.getColor());
773 line.forceMerge(newLine.getStartItem(), MouseX, MouseY);
774 }
775 }
776 return;
777 }
778 }
779 }
780
781 private static Dot getFirstFreeDot() {
782 for (Item i : Frame.FreeItems)
783 if (i instanceof Dot)
784 return (Dot) i;
785 return null;
786 }
787
788 private static boolean isRubberBandingRectangle() {
789 if (Frame.FreeItems.size() != 3)
790 return false;
791 Dot d = null;
792 // only one dot will be present for rectangles
793 for (Item i : Frame.FreeItems)
794 if (i instanceof Dot && d == null)
795 d = (Dot) i;
796 else if (i instanceof Dot && d != null)
797 d = null;
798
799 // System.out.println(d.getAllConnected());
800
801 // if this is indeed a rectangle anchor
802 if (d != null && d.getAllConnected().size() > 5)
803 return true;
804
805 return false;
806 }
807
808 /**
809 * This method handles all right-click action
810 */
811 private static void rightButton(Item clicked, List<Item> clickedIn) {
812 // if the cursor has Items attached, then anchor a copy of them
813 if (Frame.itemAttachedToCursor()) {
814 // if the user is clicking on something, merge the items
815 // unless it is a point onto somethin other than a point
816 if (clicked != null
817 && (!(Frame.getItemAttachedToCursor() instanceof Dot && !(clicked instanceof Dot)))) {
818 // check permissions
819 if (clicked.Permission < Item.PERMISSION_FULL
820 && clicked.getParent().getFrameNameItem() != clicked) {
821 FrameGraphics
822 .DisplayMessage("Insufficient Permission to merge items");
823 return;
824 }
825
826 if (clicked instanceof Text || clicked instanceof Dot) {
827 List<Item> copies = null;
828
829 if (isRubberBandingRectangle()) {
830 List<Item> remain = merge(Frame.FreeItems, clicked);
831
832 Dot d = getFirstFreeDot();
833 // anchor the points
834 anchor(remain);
835 Frame.FreeItems.clear();
836 updateCursor();
837 // pick up a copy of all enclosed items
838 List<Item> items = FrameUtils.getItemsEnclosedBy(
839 DisplayIO.getCurrentFrame(), d
840 .getEnclosedShape());
841 if (items != null) {
842 ArrayList<Item> toCopy = new ArrayList<Item>(items
843 .size());
844 for (Item ip : items)
845 if (ip.Permission >= Item.PERMISSION_COPY)
846 toCopy.add(ip);
847
848 copies = copy(toCopy);
849 pickup(copies);
850 }
851
852 } else if (Frame.FreeItems.size() == 2) {
853 copies = ItemUtils.UnreelLine(Frame.FreeItems);
854 List<Item> leftOver = merge(Frame.FreeItems, clicked);
855 anchor(leftOver);
856 if (copies == null)
857 copies = copy(Frame.FreeItems);
858 Frame.FreeItems.clear();
859 for (Item i : copies)
860 i.setOffset(0, 0);
861 // need to move to prevent cursor dislocation
862 move(copies);
863 pickup(copies);
864
865 } else if (Frame.FreeItems.size() == 1) {
866 copies = copy(Frame.FreeItems);
867 List<Item> remain = merge(copies, clicked);
868
869 // ignore items that could not be merged.
870 anchor(remain);
871 } else {
872 // NoOp
873 }
874 updateCursor();
875 FrameGraphics.Repaint();
876 } else {
877 List<Item> copies = ItemUtils.UnreelLine(Frame.FreeItems);
878 if (copies == null)
879 copies = copy(Frame.FreeItems);
880 for (Item i : copies) {
881 i.setOffset(0, 0);
882 }
883 anchor(Frame.FreeItems);
884 Frame.FreeItems.clear();
885 updateCursor();
886
887 FrameGraphics.Repaint();
888
889 clearParent(copies);
890 pickup(copies);
891 }
892 return;
893
894 // otherwise, anchor the items
895 } else {
896
897 // check if this is anchoring a rectangle
898 if (isRubberBandingRectangle()) {
899 Dot d = getFirstFreeDot();
900
901 // anchor the points
902 anchor(Frame.FreeItems);
903 Frame.FreeItems.clear();
904 updateCursor();
905 // pick up a copy of all enclosed items
906 List<Item> items = FrameUtils.getItemsEnclosedBy(DisplayIO
907 .getCurrentFrame(), d.getEnclosedShape());
908 if (items != null) {
909 ArrayList<Item> toCopy = new ArrayList<Item>(items
910 .size());
911 for (Item ip : items)
912 if (ip.Permission >= Item.PERMISSION_COPY)
913 toCopy.add(ip);
914 toCopy.removeAll(d.getAllConnected());
915
916 if (toCopy.size() > 0) {
917 //Find the closest item to the mouse cursor
918 double currentX = DisplayIO.getMouseX();
919 double currentY = DisplayIO.getMouseY();
920 Item closest = null;
921 double shortestDistance = Double.MAX_VALUE;
922 for(int i = 0; i < toCopy.size(); i++) {
923 Item next = toCopy.get(i);
924 if (next instanceof Line)
925 continue;
926 double distance = Point.distance(currentX, currentY, next.getX(), next.getY());
927 if (distance < shortestDistance) {
928 shortestDistance = distance;
929 closest = next;
930 }
931 }
932 //Move the cursor to closest item
933 DisplayIO.setCursorPosition(closest.getPosition());
934 //Pickup copy of the stuff inside the rectange
935 pickup(copy(toCopy));
936 //Remove the rectangle
937 DisplayIO.getCurrentFrame().removeAllItems(
938 d.getAllConnected());
939 } else {
940 //Pick up a copy of the rectangle
941 pickup(copy(d.getAllConnected()));
942 }
943 }
944 return;
945 }
946 List<Item> copies = null;
947 if (Frame.FreeItems.size() == 2) {
948 if (clicked != null) {
949 List<Item> leftOver = merge(Frame.FreeItems, clicked);
950 anchor(leftOver);
951 }
952 // This is executed when the user is putting down a line
953 // endpoint and unreeling. ie. Normal unreeling
954 copies = ItemUtils.UnreelLine(Frame.FreeItems);
955
956 if (copies == null)
957 copies = copy(Frame.FreeItems);
958 anchor(Frame.FreeItems);
959 for (Item i : copies)
960 i.setOffset(0, 0);
961 // need to move to prevent cursor dislocation
962 move(copies);
963 pickup(copies);
964 FrameGraphics.Repaint();
965 return;
966 }
967
968 if (_extrude) {
969 List<Item> originals = new ArrayList<Item>();
970 // remove any lines that dont have both endpoints floating
971 for (Item i : Frame.FreeItems) {
972 if (i.isFloating())
973 originals.add(i);
974 }
975 if (copies == null)
976 copies = ItemUtils.CopyItems(originals, _extrude);
977 for (Item i : copies)
978 i.setOffset(0, 0);
979 anchor(Frame.FreeItems);
980 // Move isnt working right for extruding!!
981 // move(copies);
982 pickup(copies);
983 updateCursor();
984 FrameGraphics.Repaint();
985 return;
986 }
987
988 if (copies == null)
989 copies = ItemUtils.CopyItems(Frame.FreeItems);
990 // MIKE: what does the below 2 lines do?
991 for (Item i : copies)
992 i.setOffset(0, 0);
993 // The below code has a little problem withflicker when stamp
994 // and dragging
995 move(Frame.FreeItems);
996 anchor(copies);
997 updateCursor();
998 FrameGraphics.Repaint();
999 return;
1000 }
1001
1002 } else {
1003 // if the user is pointing at something, this is a copy
1004 if (clicked != null) {
1005 // check permissions
1006 if (clicked.Permission < Item.PERMISSION_COPY) {
1007 FrameGraphics
1008 .DisplayMessage("Insufficient permission to copy item");
1009 return;
1010 }
1011
1012 List<Item> copies = ItemUtils.UnreelLine(clicked);
1013 // Copies will NOT be null if the user right clicked on a point
1014 if (copies == null) {
1015 List<Item> originals = clicked.getConnected();
1016 copies = ItemUtils.CopyItems(originals, _extrude);
1017 }
1018 pickup(copies);
1019 return;
1020 } else {
1021 // if user is pointing in a closed shape, copy the items inside
1022 if (clickedIn != null) {
1023 List<Item> toCopy = new ArrayList<Item>(clickedIn.size());
1024 for (Item ip : clickedIn)
1025 if (ip.Permission >= Item.PERMISSION_COPY)
1026 toCopy.add(ip);
1027
1028 List<Item> copies = copy(toCopy);
1029 clearParent(copies);
1030 pickup(copies);
1031 // otherwise, create a rectangle
1032 } else {
1033
1034 int x = DisplayIO.getMouseX();
1035 int y = DisplayIO.getMouseY();
1036
1037 // create dots
1038 Dot d1 = new Dot(x, y, DisplayIO.getCurrentFrame()
1039 .getNextItemID());
1040 Dot d2 = new Dot(x, y, DisplayIO.getCurrentFrame()
1041 .getNextItemID());
1042 Dot d3 = new Dot(x, y, DisplayIO.getCurrentFrame()
1043 .getNextItemID());
1044 Dot d4 = new Dot(x, y, DisplayIO.getCurrentFrame()
1045 .getNextItemID());
1046
1047 // create lines
1048 Line l1 = new Line(d1, d2, DisplayIO.getCurrentFrame()
1049 .getNextItemID());
1050 Line l2 = new Line(d2, d3, DisplayIO.getCurrentFrame()
1051 .getNextItemID());
1052 Line l3 = new Line(d3, d4, DisplayIO.getCurrentFrame()
1053 .getNextItemID());
1054 Line l4 = new Line(d4, d1, DisplayIO.getCurrentFrame()
1055 .getNextItemID());
1056
1057 new Constraint(d1, d2, DisplayIO.getCurrentFrame()
1058 .getNextItemID(), Constraint.HORIZONTAL);
1059 new Constraint(d3, d4, DisplayIO.getCurrentFrame()
1060 .getNextItemID(), Constraint.HORIZONTAL);
1061 new Constraint(d2, d3, DisplayIO.getCurrentFrame()
1062 .getNextItemID(), Constraint.VERTICAL);
1063 new Constraint(d4, d1, DisplayIO.getCurrentFrame()
1064 .getNextItemID(), Constraint.VERTICAL);
1065
1066 anchor(d1);
1067 anchor(d2);
1068 anchor(d3);
1069 anchor(d4);
1070
1071 anchor(l1);
1072 anchor(l2);
1073 anchor(l3);
1074 anchor(l4);
1075
1076 pickup(d4);
1077
1078 FrameGraphics.Repaint();
1079 return;
1080 }
1081 }
1082 }
1083 }
1084
1085 private static void clearParent(List<Item> items) {
1086 for (Item i : items)
1087 i.setParent(null);
1088 }
1089
1090 public void mouseEntered(MouseEvent e) {
1091 }
1092
1093 public void mouseExited(MouseEvent e) {
1094 }
1095
1096 public void mouseDragged(MouseEvent e) {
1097 // System.out.println("MouseDragged");
1098
1099 // check if user is dragging across a text item
1100 if (_lastRanged != null) {
1101 // System.out.println(MouseY - e.getY());
1102
1103 MouseX = e.getX();
1104 MouseY = e.getY();
1105
1106 int distance = _lastRanged.getY() - DisplayIO.getMouseY();
1107 if (distance <= 0)
1108 distance = DisplayIO.getMouseY() - _lastRanged.getY()
1109 - _lastRanged.getBoundsHeight();
1110
1111 if (distance > UserSettings.NoOpThreshold) {
1112 _lastRanged.clearSelectionEnd();
1113 _isNoOp = true;
1114 } else {
1115 // update the ranged section
1116 _lastRanged.setSelectionEnd(DisplayIO.getMouseX(), DisplayIO
1117 .getMouseY());
1118 _isNoOp = false;
1119 }
1120
1121 DisplayIO.setTextCursor(_lastRanged.getSize());
1122 FrameGraphics.Repaint();
1123 return;
1124 }
1125
1126 // if the user is dragging across a picture
1127 if (_lastCropped != null) {
1128 MouseX = e.getX();
1129 MouseY = e.getY();
1130
1131 // update the ranged section
1132 _lastCropped.setEndCrop(DisplayIO.getMouseX(), DisplayIO
1133 .getMouseY());
1134 DisplayIO.setCursor(Item.CROP_CURSOR);
1135 FrameGraphics.Repaint();
1136 return;
1137 }
1138
1139 if (_mouseDown == MouseEvent.BUTTON2 && _lastClickedOn == null
1140 && _lastClickedIn == null) {
1141 Item on = FrameUtils.onItem(DisplayIO.getCurrentFrame(), e.getX(),
1142 e.getY());
1143 // if the user can spot-weld, show the virtual spot
1144 if (Frame.FreeItems.size() == 0 && on instanceof Line) {
1145 Line line = (Line) on;
1146 line.showVirtualSpot(e.getX(), e.getY());
1147 }
1148 }
1149
1150 /*
1151 * // Euclidean distance int distance = (int)
1152 * Math.sqrt(Math.pow(Math.abs(MouseX - e.getX()), 2) +
1153 * Math.pow(Math.abs(MouseY - e.getY()), 2));
1154 *
1155 * if (distance > UserSettings.NoOpThreshold)
1156 */
1157 // Use the below calculation for better speed. If it causes problems
1158 // switch back to the Euclidean distance calculation
1159 if (Math.abs(MouseX - e.getX()) > UserSettings.NoOpThreshold
1160 || Math.abs(MouseY - e.getY()) > UserSettings.NoOpThreshold)
1161 _isNoOp = true;
1162
1163 FrameGraphics.Repaint();
1164 }
1165
1166 MouseEvent _lastMouseEvent = null;
1167
1168 /**
1169 * Updates the stored mouse position and highlights any items as necessary.
1170 */
1171 public void mouseMoved(MouseEvent e) {
1172 MouseX = e.getX();
1173 MouseY = e.getY();
1174
1175 if (_lastMouseEvent == null)
1176 _lastMouseEvent = e;
1177
1178 /*
1179 * int distance = (int) Math.sqrt(Math.pow(Math
1180 * .abs(_lastMouseEvent.getX() - e.getX()), 2) +
1181 * Math.pow(Math.abs(_lastMouseEvent.getY() - e.getY()), 2));
1182 */
1183
1184 // Just do the sum of the distance moved in x and y direction for
1185 // speed!!
1186 /*
1187 * int distance = Math.abs(_lastMouseEvent.getX() - e.getX()) +
1188 * Math.abs(_lastMouseEvent.getY() - e.getY());
1189 */
1190
1191 boolean checkHighlight = true;
1192
1193 // Mike: This code checks if the mouse is moving above a
1194 // threshold speed. If so it doesnt execute the code that checks if
1195 // items underneath the cursor need to be highlighted.
1196 /*
1197 * if (Frame.itemAttachedToCursor()) { long time = e.getWhen() -
1198 * _lastMouseEvent.getWhen(); // System.out.println(time + " D:" +
1199 * distance); if (time > 0 && distance * 3 > time) { checkHighlight =
1200 * false; // System.out.println("Dont check highlight!"); if
1201 * (_lastHighlightedItem != null && !_lastHoldsHighlight) {
1202 * if(_lastHighlightedItem instanceof Text) {
1203 * FrameGraphics.ChangeSelectionMode(_lastHighlightedItem,
1204 * Item.SelectedMode.None); } } } }
1205 */
1206
1207 _lastMouseEvent = e;
1208
1209 if (checkHighlight) {
1210 // ByMike: Get the item the mouse is hovering over
1211 Item click = FrameUtils.getCurrentItem();
1212 Item on = null;
1213 // System.out.println(click);
1214 if (click != null) {
1215 on = click;
1216 // set the context
1217 if (on instanceof Line)
1218 _context = CONTEXT_AT_LINE;
1219 else if (on instanceof Dot)
1220 _context = CONTEXT_AT_DOT;
1221 else if (on instanceof Text) {
1222 // Checks that we are actually pointing on a character
1223 // not just space in the text box's bounding box
1224 if (((Text) on).contains(DisplayIO.getMouseX(), DisplayIO
1225 .getMouseY())) {
1226 _context = CONTEXT_AT_TEXT;
1227 } else {
1228 // if we are pointing in space in the text box... it is
1229 // the same as pointing in freespace
1230 on = null;
1231 _context = CONTEXT_FREESPACE;
1232 }
1233 }
1234
1235 _alpha = 60;
1236 } else {
1237 _context = CONTEXT_FREESPACE;
1238 _alpha = -1;
1239 }
1240
1241 // if the user is pointing at an item, highlight it
1242 if (on != null && !Frame.FreeItems.contains(on)) {
1243 // if the user can spot-weld, show the virtual spot
1244 if (Frame.FreeItems.size() == 2 && on instanceof Line) {
1245 Line line = (Line) on;
1246 if (Frame.FreeItems.get(0).isLineEnd())
1247 line.showVirtualSpot(Frame.FreeItems.get(0), DisplayIO
1248 .getMouseX(), DisplayIO.getMouseY());
1249 else if (Frame.FreeItems.get(1).isLineEnd())
1250 line.showVirtualSpot(Frame.FreeItems.get(1), DisplayIO
1251 .getMouseX(), DisplayIO.getMouseY());
1252 else
1253 // The user is pointing at another point or text item
1254 // etc
1255 FrameGraphics.ChangeSelectionMode(on,
1256 Item.SelectedMode.Normal);
1257 } else {
1258 // FrameGraphics.ChangeSelectionMode(on,
1259 // Item.SelectedMode.Connected);
1260 // TODO: The method below is for the most part redundant
1261 FrameGraphics.Highlight(on);
1262 }
1263 // if the last item highlighted is still highlighted, clear it
1264 if (_lastHoldsHighlight) {
1265 _lastHoldsHighlight = false;
1266 for (Item i : DisplayIO.getCurrentFrame().getItems())
1267 if (i.isHighlighted() && i != on)
1268 FrameGraphics.ChangeSelectionMode(i,
1269 Item.SelectedMode.None);
1270 }
1271
1272 // if the user is not pointing at an item, check for enclosure
1273 // highlighting
1274 } else if (on == null) {
1275 List<Item> enclosure = FrameUtils.getEnclosingDots();
1276 if (enclosure != null && enclosure.size() > 1) {
1277 if (enclosure.get(0).getLines().size() > 1 &&
1278 // check that the enclosure is not part of a point being
1279 // dragged in space
1280 !ContainsOneOf(enclosure, Frame.FreeItems)) {
1281 on = enclosure.get(0).getLines().get(0);
1282 FrameGraphics.ChangeSelectionMode(on,
1283 Item.SelectedMode.Enclosed);
1284 }
1285 } else if (_lastHighlightedItem != null) {
1286 _lastHoldsHighlight = false;
1287 }
1288 }
1289
1290 // disable cursor changes when the cursor has items attached
1291 if (Frame.itemAttachedToCursor()
1292 && DisplayIO.getCursor() != Item.TEXT_CURSOR)
1293 _forceArrowCursor = false;
1294
1295 if (_lastHighlightedItem != null && _lastHighlightedItem != on
1296 && !_lastHoldsHighlight) {
1297 if (!_lastHighlightedItem.getAllConnected().contains(on))
1298 FrameGraphics.ChangeSelectionMode(_lastHighlightedItem,
1299 Item.SelectedMode.None);
1300 }
1301
1302 _lastHighlightedItem = on;
1303
1304 }
1305
1306 if (Frame.itemAttachedToCursor()) {
1307 move(Frame.FreeItems);
1308 // System.out.println(Frame.FreeItems.size());
1309 }
1310
1311 if (_forceArrowCursor)
1312 updateCursor();
1313
1314 // if (_lastItem == null)
1315 // DisplayIO.UpdateTitle();
1316
1317 _forceArrowCursor = true;
1318 }
1319
1320 private boolean ContainsOneOf(List<Item> enclosure, List<Item> freeItems) {
1321 if (freeItems == null)
1322 return false;
1323 for (Item i : freeItems) {
1324 if (enclosure.contains(i))
1325 return true;
1326 }
1327 return false;
1328 }
1329
1330 /**
1331 * Checks if lines are being rubber banded.
1332 *
1333 * @return true if the user is rubberBanding one or more lines
1334 */
1335 private static boolean rubberBanding() {
1336 if (Frame.FreeItems.size() != 2) {
1337 return false;
1338 }
1339
1340 // if rubber-banding, there will be 1 lineend and the rest will be lines
1341 boolean foundLineEnd = false;
1342 for (Item i : Frame.FreeItems) {
1343 if (i.isLineEnd()) {
1344 if (foundLineEnd) {
1345 return false;
1346 }
1347 foundLineEnd = true;
1348 } else if (!(i instanceof Line)) {
1349 return false;
1350 }
1351 }
1352 return true;
1353 }
1354
1355 /**
1356 * Updates the current mouse cursor to whatever it should be. i.e. Hidden
1357 * when rubber-banding lines, otherwise default (arrow)
1358 */
1359 private static void updateCursor() {
1360 if (rubberBanding()) {
1361 DisplayIO.setCursor(Item.HIDDEN_CURSOR);
1362 } else {
1363 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1364 }
1365 }
1366
1367 public static void setHighlightHold(boolean hold) {
1368 _lastHoldsHighlight = hold;
1369 }
1370
1371 public static void resetOffset() {
1372 if (Frame.itemAttachedToCursor()) {
1373 _offX = DisplayIO.getMouseX() - Frame.FreeItems.get(0).getX()
1374 + Frame.FreeItems.get(0).getOffset().x;
1375 _offY = MouseY - Frame.FreeItems.get(0).getY()
1376 + Frame.FreeItems.get(0).getOffset().y;
1377 }
1378 }
1379
1380 private static void move(List<Item> toMove) {
1381 int xPos = (DisplayIO.getMouseX() - _offX);
1382
1383 Item firstDot = toMove.get(0);
1384
1385 /*
1386 * for (Item i: toMove) { if (i instanceof Dot){ firstDot = i; break; } }
1387 */
1388
1389 int deltax = firstDot.getX() - xPos;
1390 int deltay = firstDot.getY() - (MouseY - _offY);
1391
1392 for (Item move : toMove) {
1393 move.setPosition(move.getX() - deltax, move.getY() - deltay);
1394
1395 if (move instanceof Text)
1396 ((Text) move).setAlpha(_alpha);
1397 }
1398
1399 FrameGraphics.Repaint();
1400 }
1401
1402 private static void load(String toLoad) {
1403 if (FrameIO.isValidFrameName(toLoad)) {
1404 checkTDFCItemWaiting();
1405 FrameUtils.DisplayFrame(toLoad);
1406 } else {
1407 FrameGraphics.ErrorMessage(toLoad + " is not a valid frame name.");
1408 }
1409 }
1410
1411 private static void checkTDFCItemWaiting() {
1412 // if there is a TDFC Item waiting
1413 if (getTdfcItem() != null) {
1414 Frame currentFrame = DisplayIO.getCurrentFrame();
1415 boolean change = currentFrame.hasChanged();
1416 boolean saved = currentFrame.isSaved();
1417 // Save the parent of the item if it has not been saved
1418 // TODO change it so it doesnt use load frame here... if
1419 // possible
1420 if (!change && !saved) {
1421 getTdfcItem().setLink(null);
1422 getTdfcItem().getParent().setChanged(true);
1423 FrameIO.SaveFrame(getTdfcItem().getParent());
1424 FrameGraphics.Repaint();
1425 } else {
1426 SessionStats.CreatedFrame();
1427 // DisplayIO.UpdateTitle();
1428 }
1429
1430 setTdfcItem(null);
1431 }
1432 }
1433
1434 private static void back() {
1435 checkTDFCItemWaiting();
1436
1437 DisplayIO.Back();
1438
1439 // repaint things if necessary
1440 if (Frame.itemAttachedToCursor())
1441 move(Frame.FreeItems);
1442 }
1443
1444 /**
1445 * Returns true if the mouse moved during TDFC. This will happen if there is
1446 * a start annotation item on the frame.
1447 *
1448 * @param linker
1449 * @return
1450 */
1451 public static boolean tdfc(Item linker) {
1452 if (linker instanceof Line)
1453 return false;
1454
1455 // if this is a non-usable item
1456 if (linker.getID() < 0)
1457 return false;
1458
1459 linker.getParent().setChanged(true);
1460 checkTDFCItemWaiting();
1461
1462 Frame next = FrameIO.CreateNewFrame(linker);
1463
1464 linker.setLink("" + next.getFrameNumber());
1465 setTdfcItem(linker);
1466
1467 for (Text i : next.getBodyTextItems(true)) {
1468 // Set the link for @Parent annotation item if one
1469 if (ItemUtils.isTag(i, ItemUtils.TAG_PARENT) && i.getLink() == null) {
1470 Frame parent = linker.getParentOrCurrentFrame();
1471 i.setLink(parent.getFrameName());
1472 } else if (ItemUtils.isTag(i, ItemUtils.TAG_BACKUP)) {
1473 // Delink backup tag if it is on the frame
1474 i.setLink(null);
1475 }
1476 }
1477
1478 FrameUtils.DisplayFrame(next, true);
1479
1480 boolean mouseMoved = next.moveMouseToDefaultLocation();
1481 // this needs to be done if the user doesnt move the mouse before doing
1482 // tdfc while the cursor is set to the text cursor
1483 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1484 // This needs to be done in case there was a @start on the frame which
1485 // triggers changed to be set to true when it should stay as false
1486 next.setChanged(false);
1487
1488 return mouseMoved;
1489 }
1490
1491 /**
1492 * Creates a new Text item and fills it with particular attributes extracted
1493 * from the given Item. Note: Users always have permission to extract
1494 * attributes, so it is not checked.
1495 *
1496 * @param toExtract
1497 * Item containing the Item to extract the attributes from.
1498 */
1499 private static void extractAttributes(Item toExtract) {
1500 if (toExtract == null || toExtract == null)
1501 return;
1502
1503 if (Frame.itemAttachedToCursor())
1504 return;
1505
1506 Text attribs;
1507 Item item = toExtract;
1508 // Extract the frames attributes when the user clicks on the frame name
1509 if (item.isFrameName())
1510 attribs = AttributeUtils.ExtractAttributes(item.getParent());
1511 else
1512 attribs = AttributeUtils.ExtractAttributes(item);
1513
1514 if (attribs == null)
1515 FrameGraphics
1516 .DisplayMessage("All attributes of that item are default values.");
1517 else {
1518 // Give the attribute text item the color of the item for which
1519 // attributes are being extracted.
1520 attribs.setColor(item.getColor());
1521 pickup(attribs);
1522 }
1523 }
1524
1525 private static void delete(Item toDelete) {
1526 FrameUtils.LastEdited = null;
1527 _offX = _offY = 0;
1528
1529 // check if the user is pointing at the frame's framename
1530 if (toDelete != null
1531 && toDelete == DisplayIO.getCurrentFrame().getFrameNameItem()) {
1532 DisplayIO.getCurrentFrame().clear();
1533 FrameGraphics.Repaint();
1534 return;
1535 }
1536
1537 // if the user is deleting items attached to the cursor
1538 if (Frame.itemAttachedToCursor()) {
1539 // if this is an item-swap
1540 if (Frame.FreeItems.size() == 1
1541 && Frame.FreeItems.get(0) instanceof Text
1542 && toDelete != null && toDelete instanceof Text) {
1543
1544 // check permissions
1545 if (toDelete.Permission < Item.PERMISSION_FULL) {
1546 FrameGraphics
1547 .DisplayMessage("Insufficient permission to swap Item text");
1548 return;
1549 }
1550
1551 Text anchored = (Text) toDelete;
1552 Text free = (Text) Frame.FreeItems.get(0);
1553
1554 // List<String> temp = anchored.getText();
1555 anchored.setTextList(free.getText());
1556
1557 // free.setTextList(temp);
1558 Frame.FreeItems.clear();
1559
1560 anchored.getParent().setChanged(true);
1561
1562 // update the offset since the text has changed
1563 _offX = DisplayIO.getMouseX() - anchored.getX()
1564 + anchored.getOffset().x;
1565 _offY = MouseY - anchored.getY() + anchored.getOffset().y;
1566 } else {
1567 deleteItems(Frame.FreeItems);
1568 }
1569 // reset the mouse cursor
1570 updateCursor();
1571 FrameGraphics.Repaint();
1572 return;
1573 // the user is not pointing at an item
1574 } else if (toDelete == null) {
1575
1576 // if the user is pointing inside a closed shape, delete all
1577 // items inside it
1578 List<Item> items = FrameUtils.getCurrentItems();
1579
1580 if (items != null) {
1581 ArrayList<Item> toRemove = new ArrayList<Item>(items.size());
1582 for (Item ip : items)
1583 if (ip.Permission >= Item.PERMISSION_FULL) {
1584 toRemove.add(ip);
1585 }
1586
1587 deleteItems(toRemove);
1588
1589 // reset the mouse cursor
1590 updateCursor();
1591 FrameGraphics.Repaint();
1592
1593 // otherwise this is an undo command
1594 } else {
1595 DisplayIO.getCurrentFrame().undo();
1596 }
1597 return;
1598 // this is a delete command
1599 } else {
1600 // check permissions
1601 if (toDelete.Permission < Item.PERMISSION_FULL) {
1602 FrameGraphics
1603 .DisplayMessage("Insufficient permission to delete item");
1604 return;
1605 }
1606
1607 Frame current = toDelete.getParent();
1608
1609 current.setChanged(true);
1610
1611 List<Item> toUndo = new LinkedList<Item>();
1612
1613 if (toDelete.isLineEnd()) {
1614 toUndo.addAll(deleteDot(toDelete));
1615 } else
1616 toUndo.addAll(copy(toDelete.getConnected()));
1617
1618 current.addAllToUndo(toUndo);
1619 current.removeAllItems(toDelete.getConnected());
1620 // reset the mouse cursor
1621 updateCursor();
1622 ItemUtils.EnclosedCheck(current.getItems());
1623 FrameGraphics.Repaint();
1624 }
1625 // check
1626 DisplayIO.setCursor(Item.DEFAULT_CURSOR);
1627 }
1628
1629 private static void deleteItems(List<Item> itemList) {
1630 List<Frame> modifiedFrames = new LinkedList<Frame>();
1631 // Get a list of all the modified frames
1632 for (Item i : itemList) {
1633 Frame parent = i.getParent();
1634 if (parent != null)
1635 modifiedFrames.add(parent);
1636 }
1637
1638 List<Item> toUndo = new LinkedList<Item>();
1639 List<Item> seen = new LinkedList<Item>();
1640
1641 // disconnect any connected items
1642 for (Item i : itemList) {
1643 if (i.getLines().size() > 0) {
1644 List<Item> copy = deleteDot(i);
1645 // add the copied items to the undo stack
1646 for (Item cop : copy)
1647 if (!toUndo.contains(cop))
1648 toUndo.add(cop);
1649 } else {
1650 if (!seen.contains(i))
1651 toUndo.addAll(copy(i));
1652 }
1653 seen.add(i);
1654 }
1655 for (Frame f : modifiedFrames) {
1656 f.removeAllItems(itemList);
1657 ItemUtils.EnclosedCheck(f.getItems());
1658 }
1659 // TODO: How should undelete deal with undo when items are removed from
1660 // the current frame as well as the overlay frame
1661 DisplayIO.getCurrentFrame().addAllToUndo(itemList);
1662 itemList.clear();
1663 }
1664
1665 private static List<Item> deleteDot(Item dot) {
1666
1667 if (dot instanceof WidgetCorner) { // Brook
1668
1669 WidgetCorner wc = (WidgetCorner) dot;
1670 List<Item> wccopy;
1671
1672 try {
1673 wccopy = wc.getWidgetSource().copy().getItems();
1674 } catch (InteractiveWidgetNotAvailableException e) {
1675 e.printStackTrace();
1676 return new LinkedList<Item>();
1677 }
1678
1679 if (dot.getParent() != null) {
1680 dot.getParent().removeAllItems(wc.getWidgetSource().getItems());
1681 }
1682 return wccopy;
1683 } else {
1684
1685 // create a backup copy of the dot
1686 List<Item> copy = copy(dot.getConnected());
1687 // remove lines that are connected to anchored dots
1688 // note: the line is kept so that it can be properly restored
1689 for (Item ic : copy) {
1690 if (ic instanceof Line) {
1691 Line line = (Line) ic;
1692 if (!copy.contains(line.getStartItem()))
1693 line.getStartItem().removeLine(line);
1694 if (!copy.contains(line.getEndItem()))
1695 line.getEndItem().removeLine(line);
1696 }
1697 }
1698
1699 // remove all lines being deleted
1700 for (Item ic : dot.getConnected()) {
1701 if (ic instanceof Line
1702 && ((Line) ic).getOppositeEnd(dot) != null) {
1703 Line line = (Line) ic;
1704
1705 Item d = line.getOppositeEnd(dot);
1706 d.removeLine(line);
1707
1708 // if the dot was only part of one line, it can be
1709 // removed
1710 if (d.getLines().size() == 0) {
1711 if (d.getParent() != null)
1712 d.getParent().removeItem(d);
1713 if (!copy.contains(d))
1714 copy.add(d);
1715 }
1716
1717 if (dot.getParent() != null)
1718 dot.getParent().removeItem(ic);
1719 }
1720 }
1721 return copy;
1722 }
1723 }
1724
1725 private static void removeAllLinesExcept(Item from, Item but) {
1726 List<Line> lines = new LinkedList<Line>();
1727 lines.addAll(from.getLines());
1728 for (Line line : lines)
1729 if (line.getOppositeEnd(from) != but)
1730 from.removeLine(line);
1731
1732 List<Constraint> consts = new LinkedList<Constraint>();
1733 consts.addAll(from.getConstraints());
1734 for (Constraint c : consts)
1735 if (c.getOppositeEnd(from) != but)
1736 from.removeConstraint(c);
1737 }
1738
1739 public static List<Item> merge(List<Item> merger, Item mergee) {
1740 if (mergee.getParent().getFrameNameItem() == mergee) {
1741 return mergee.getParent().merge(merger);
1742 }
1743
1744 // check for rectangle merging
1745 if (merger.size() == 3 && mergee.getLines().size() == 2) {
1746 boolean rectangle = false;
1747 Dot dot = null;
1748 // check that this is indeed a rectangle
1749 // This wont work now that corners can be Text items
1750 for (Item check : merger) {
1751 if (dot == null && check.isLineEnd())
1752 dot = (Dot) check;
1753 else if (check != dot && !dot.getLines().contains(check)) {
1754 rectangle = false;
1755 break;
1756 } else
1757 rectangle = true;
1758 }
1759
1760 // if this is a rectangle
1761 if (rectangle) {
1762 List<Item> connected = dot.getAllConnected();
1763 DisplayIO.getCurrentFrame().removeAllItems(connected);
1764
1765 // find the point opposite the point that was clicked
1766 // (mergee)...
1767 Dot opposite = null;
1768 List<Line> lines = dot.getLines();
1769 for (Line l : lines) {
1770 if (l.getOppositeEnd(dot) != mergee) {
1771 opposite = (Dot) l.getOppositeEnd(dot);
1772 break;
1773 }
1774 }
1775
1776 // check if the rectangle is small enough that it should be
1777 // collapsed to a single point
1778 int x1 = Math.abs(opposite.getX() - mergee.getX());
1779 int x2 = Math.abs(opposite.getY() - mergee.getY());
1780 int distance = (int) Math.sqrt(Math.pow(x1, 2)
1781 + Math.pow(x2, 2));
1782
1783 if (distance < RECTANGLE_TO_POINT_THRESHOLD) {
1784 mergee.removeAllConstraints();
1785 mergee.removeAllLines();
1786 mergee.setThickness(4 * mergee.getThickness());
1787 return mergee.getAllConnected();
1788 } else {
1789
1790 // otherwise, replace the rectangle with a constrained
1791 // line
1792 Item other = null;
1793 // there should be only one line left
1794 for (Line line : mergee.getLines())
1795 if (!merger.contains(line.getOppositeEnd(mergee)))
1796 other = line.getOppositeEnd(mergee);
1797
1798 removeAllLinesExcept(mergee, other);
1799 removeAllLinesExcept(other, mergee);
1800
1801 return mergee.getAllConnected();
1802 }
1803 }
1804 }
1805
1806 List<Item> remain = new ArrayList<Item>();
1807 Item res = null;
1808
1809 for (Item i : merger) {
1810 // check for link merging
1811 if (i instanceof Text
1812 && FrameIO.isValidFrameName((((Text) i).getFirstLine()))
1813 && FrameIO.DoesFrameExist((((Text) i).getFirstLine()))) {
1814 // check that we can actually access the frame this link
1815 // corresponds to
1816
1817 mergee.setLink(((Text) i).getFirstLine());
1818 } else {
1819 // check for attribute merging
1820 if (i instanceof Text && !i.isLineEnd()) {
1821 Text txt = (Text) i;
1822
1823 // if this is not an attribute merge
1824 if (!AttributeUtils.SetAttribute(mergee, txt)) {
1825 // set mouse position for text merges
1826 if (mergee instanceof Text)
1827 ((Text) mergee).insertText(txt.getTextNoList(),
1828 DisplayIO.getMouseX(), DisplayIO
1829 .getMouseY());
1830
1831 else if (mergee instanceof Dot) {
1832 txt.setPosition(mergee.getPosition());
1833 txt.setThickness(mergee.getThickness());
1834 Frame parent = mergee.getParent();
1835 parent.removeItem(mergee);
1836 anchor(txt);
1837 // change the end points of the lines to the text
1838 // item
1839 while (mergee.getLines().size() > 0) {
1840 Line l = mergee.getLines().get(0);
1841 l.replaceEnd(mergee, txt);
1842 }
1843 break;
1844 }
1845
1846 // TODO tidy this up... the text override of this method
1847 // doesnt use the parametres MouseY and MouseX!!
1848 res = mergee.merge(i, DisplayIO.getMouseX(), MouseY);
1849 if (res != null) {
1850 remain.add(res);
1851 }
1852 }
1853 } else {
1854 res = mergee.merge(i, DisplayIO.getMouseY(), MouseY);
1855 if (res != null)
1856 remain.addAll(res.getConnected());
1857
1858 }
1859 }
1860
1861 }
1862
1863 updateCursor();
1864 mergee.getParent().setChanged(true);
1865 ItemUtils.EnclosedCheck(mergee.getParent().getItems());
1866 FrameUtils.Parse(mergee.getParent());
1867
1868 return remain;
1869 }
1870
1871 /**
1872 * Picks up an item on a frame.
1873 *
1874 * @param toGrab
1875 * item to be picked up
1876 * @param removeItem
1877 * true if the item should be removed from the frame
1878 */
1879 public static void pickup(Item toGrab) {
1880 // Brook: If the widget corner is being picked up. Instead refer to
1881 // picking
1882 // up the edge for fixed-sized widgets so it is not so confusing
1883 if (toGrab instanceof WidgetCorner) {
1884 WidgetCorner wc = (WidgetCorner) toGrab;
1885 if (wc.getWidgetSource().isFixedSize()) {
1886 toGrab = toGrab.getConnected().get(1);
1887 }
1888 }
1889
1890 pickup(toGrab.getConnected());
1891 }
1892
1893 public static void pickup(List<Item> toGrab) {
1894 String currentFrame = DisplayIO.getCurrentFrame().getFrameName();
1895 for (Item i : toGrab) {
1896 // Check if it has a relative link if so make it absolute
1897 i.setAbsoluteLink();
1898
1899 // parent may be null
1900 if (i.getParent() != null) {
1901 i.getParent().removeItem(i);
1902 if (currentFrame.equals(i.getParent().getFrameName()))
1903 i.setParent(null);
1904 }
1905 Frame.FreeItems.add(i);
1906
1907 i.setFloating(true);
1908 }
1909
1910 updateCursor();
1911
1912 // if there are multiple items in the list, determine which to use for
1913 // offset calculations
1914 if (toGrab.size() > 1) {
1915 for (Item i : toGrab) {
1916 if (!(i instanceof Line)) {
1917 _offX = DisplayIO.getMouseX() - i.getX() + i.getOffset().x;
1918 _offY = MouseY - i.getY() + i.getOffset().y;
1919
1920 // make the offset item the first item in the list (so
1921 // moving code knows which item to use)
1922 Frame.FreeItems.set(Frame.FreeItems.indexOf(i),
1923 Frame.FreeItems.get(0));
1924 Frame.FreeItems.set(0, i);
1925 break;
1926 }
1927 }
1928
1929 move(Frame.FreeItems);
1930 // otherwise, just use the first item
1931 } else if (toGrab.size() == 1) {
1932 _offX = DisplayIO.getMouseX() - toGrab.get(0).getX()
1933 + toGrab.get(0).getOffset().x;
1934 _offY = MouseY - toGrab.get(0).getY() + toGrab.get(0).getOffset().y;
1935 }
1936
1937 ItemUtils.EnclosedCheck(toGrab);
1938 }
1939
1940 private static Line createLine() {
1941 // create the two endpoints
1942 Dot end = new Dot(DisplayIO.getRealMouseX(), DisplayIO.getMouseY(),
1943 DisplayIO.getCurrentFrame().getNextItemID());
1944 Dot start = new Dot(DisplayIO.getRealMouseX(), DisplayIO.getMouseY(),
1945 DisplayIO.getCurrentFrame().getNextItemID());
1946 // create the Line
1947 Line line = new Line(start, end, DisplayIO.getCurrentFrame()
1948 .getNextItemID());
1949 line.autoArrowheadLength();
1950
1951 // anchor the start
1952 anchor(start);
1953
1954 // attach the line to the cursor
1955 pickup(end);
1956
1957 return line;
1958 }
1959
1960 private static List<Item> copy(Item toCopy) {
1961 return copy(toCopy.getConnected());
1962 }
1963
1964 /**
1965 * Returns a list of copies of the list passed in
1966 *
1967 * @param toCopy
1968 * The list of items to copy
1969 * @return A List of copied Items
1970 */
1971 private static List<Item> copy(List<Item> toCopy) {
1972 return ItemUtils.CopyItems(toCopy);
1973 }
1974
1975 public static void anchor(Item toAnchor) {
1976 toAnchor.anchor();
1977
1978 FrameGraphics.Repaint();
1979 ItemUtils.EnclosedCheck(toAnchor.getParentOrCurrentFrame().getItems());
1980 }
1981
1982 public static void anchor(List<Item> toAnchor) {
1983 for (Item i : toAnchor)
1984 anchor(i);
1985 toAnchor.clear();
1986 }
1987
1988 /*
1989 * private boolean mouseMovedRecently() { Date now = new Date();
1990 *
1991 * return now.getTime() - _lastMouseMovement.getTime() < 150; }
1992 */
1993
1994 public void mouseWheelMoved(MouseWheelEvent arg0) {
1995 // System.out.println(arg0.getClickCount() + " " +
1996 // arg0.getWheelRotation());
1997 int clicks = arg0.getClickCount();
1998
1999 // if a line is being rubber-banded, check for auto straightening
2000 if (Frame.FreeItems.size() == 2) {
2001 if ((Frame.FreeItems.get(0) instanceof Dot && Frame.FreeItems
2002 .get(1) instanceof Line)
2003 || (Frame.FreeItems.get(1) instanceof Dot && Frame.FreeItems
2004 .get(0) instanceof Line)) {
2005
2006 Line line;
2007 if (Frame.FreeItems.get(0) instanceof Line)
2008 line = (Line) Frame.FreeItems.get(0);
2009 else
2010 line = (Line) Frame.FreeItems.get(1);
2011
2012 // User must do multiple clicks to toggle the line
2013 if (clicks == MOUSE_WHEEL_THRESHOLD) {
2014 if (arg0.isShiftDown())
2015 line.toggleArrowHeadRatio(arg0.getWheelRotation());
2016 else
2017 line.toggleArrowHeadLength(arg0.getWheelRotation());
2018 // line.getParent().change();
2019 FrameGraphics.Repaint();
2020 }
2021 }
2022 } else if (arg0.getWheelRotation() != 0 && arg0.isShiftDown()) {
2023
2024 int rotationType = FrameKeyboardActions.SIZE_UP;
2025 if (arg0.getWheelRotation() > 0) {
2026 rotationType = FrameKeyboardActions.SIZE_DOWN;
2027 }
2028
2029 Item ip = FrameUtils.getCurrentItem();
2030 if (ip != null && clicks > 1) {
2031 float size = ip.getSize();
2032 if (ip instanceof Dot || ip instanceof Line)
2033 size = ip.getThickness();
2034 // base the number of clicks on the size of the object
2035 clicks = (int) Math.ceil(size / 20.0 * clicks);
2036 }
2037
2038 FrameKeyboardActions.functionKey(rotationType, clicks);
2039 } else if (clicks == MOUSE_WHEEL_THRESHOLD) {
2040 Item ip = FrameUtils.getCurrentItem();
2041
2042 // if the user is not pointing to any item
2043 if (ip == null) {
2044 FrameGraphics
2045 .DisplayMessage("There are no Items selected on the Frame");
2046 return;
2047 } else {
2048 // check permissions
2049 if (ip.Permission < Item.PERMISSION_FULL) {
2050 FrameGraphics
2051 .DisplayMessage("Insufficient permission to change the size of that item");
2052 return;
2053 }
2054 }
2055
2056 if (ip instanceof Line) {
2057 Line line = (Line) ip;
2058 line.toggleDashed(arg0.getWheelRotation());
2059 line.getParent().change();
2060 FrameGraphics.Repaint();
2061 return;
2062 }
2063 }
2064 }
2065
2066 /**
2067 *
2068 * @return the integer value for the last mouse button clicked.
2069 */
2070 public static int getLastMouseButton() {
2071 if (_lastMouseClick == null)
2072 return MouseEvent.NOBUTTON;
2073
2074 return _lastMouseClick.getButton();
2075 }
2076
2077 public static boolean isDelete(int modifiersEx) {
2078
2079 int onMask = MouseEvent.BUTTON3_DOWN_MASK
2080 | MouseEvent.BUTTON2_DOWN_MASK;
2081 return (modifiersEx & onMask) == onMask;
2082 }
2083
2084 public static boolean isGetAttributes(int modifiersEx) {
2085 int onMask = MouseEvent.BUTTON3_DOWN_MASK
2086 | MouseEvent.BUTTON1_DOWN_MASK;
2087 return (modifiersEx & onMask) == onMask;
2088 }
2089
2090 public static boolean isTwoClickNoOp(int modifiersEx) {
2091 int onMask = MouseEvent.BUTTON2_DOWN_MASK
2092 | MouseEvent.BUTTON1_DOWN_MASK;
2093 return (modifiersEx & onMask) == onMask;
2094 }
2095
2096 public static boolean wasDeleteClicked() {
2097 if (_lastMouseClick == null)
2098 return false;
2099 return isDelete(_lastMouseClickModifiers);
2100 }
2101
2102 public static void setTdfcItem(Item _tdfcItem) {
2103 FrameMouseActions._tdfcItem = _tdfcItem;
2104 }
2105
2106 public static Item getTdfcItem() {
2107 return _tdfcItem;
2108 }
2109}
Note: See TracBrowser for help on using the repository browser.