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

Last change on this file since 676 was 676, checked in by jts21, 10 years ago

Switch to using a link instead of an action for navigation buttons, also add a setting for whether a link should be added to history (to enable navigation buttons that don't generate history)

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