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

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

Yellow highlighting for deleting text ranges wasnt working.

Fixed a couple things for the left and right mouse button shortcut for formatting

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