Changeset 4327 in josm for trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
- Timestamp:
- 2011-08-21T13:12:53+02:00 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
r4191 r4327 6 6 import static org.openstreetmap.josm.tools.I18n.trn; 7 7 8 import java.awt.AWTEvent; 8 9 import java.awt.Cursor; 9 10 import java.awt.Point; 10 11 import java.awt.Rectangle; 12 import java.awt.Toolkit; 13 import java.awt.event.AWTEventListener; 11 14 import java.awt.event.ActionEvent; 12 15 import java.awt.event.InputEvent; … … 16 19 import java.util.Collection; 17 20 import java.util.Collections; 21 import java.util.HashSet; 18 22 import java.util.Iterator; 19 23 import java.util.LinkedList; 24 import java.util.Set; 20 25 21 26 import javax.swing.JOptionPane; … … 60 65 * @author imi 61 66 */ 62 public class SelectAction extends MapMode implements SelectionEnded { 67 public class SelectAction extends MapMode implements AWTEventListener, SelectionEnded { 68 // "select" means the selection rectangle and "move" means either dragging 69 // or select if no mouse movement occurs (i.e. just clicking) 63 70 enum Mode { move, rotate, scale, select } 64 71 72 // contains all possible cases the cursor can be in the SelectAction except the 73 // the move pointer (latter is a system one and not an image) 74 private enum SelectActionCursor { 75 rect("normal", "selection"), 76 rect_add("normal", "select_add"), 77 rect_rm("normal", "select_remove"), 78 way("normal", "select_way"), 79 way_add("normal", "select_way_add"), 80 way_rm("normal", "select_way_remove"), 81 node("normal", "select_node"), 82 node_add("normal", "select_node_add"), 83 node_rm("normal", "select_node_remove"), 84 virtual_node("normal", "addnode"), 85 scale("scale", null), 86 rotate("rotate", null); 87 88 private final Cursor c; 89 private SelectActionCursor(String main, String sub) { 90 c = ImageProvider.getCursor(main, sub); 91 } 92 public Cursor cursor() { 93 return c; 94 } 95 } 96 97 // Cache previous mouse event (needed when only the modifier keys are 98 // pressed but the mouse isn't moved) 99 private MouseEvent oldEvent = null; 100 65 101 private Mode mode = null; 66 102 private SelectionManager selectionManager; 67 103 private boolean cancelDrawMode = false; 104 private boolean drawTargetHighlight; 68 105 private boolean didMouseDrag = false; 69 106 /** … … 94 131 private int initialMoveThreshold; 95 132 private boolean initialMoveThresholdExceeded = false; 133 134 /** 135 * elements that have been highlighted in the previous iteration. Used 136 * to remove the highlight from them again as otherwise the whole data 137 * set would have to be checked. 138 */ 139 private Set<OsmPrimitive> oldHighlights = new HashSet<OsmPrimitive>(); 96 140 97 141 /** … … 109 153 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay", 200); 110 154 initialMoveThreshold = Main.pref.getInteger("edit.initial-move-threshold", 5); 155 drawTargetHighlight = Main.pref.getBoolean("draw.target-highlight", true); 156 // This is required to update the cursors when ctrl/shift/alt is pressed 157 try { 158 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK); 159 } catch (SecurityException ex) { 160 System.out.println(ex); 161 } 111 162 } 112 163 … … 127 178 mv.removeMouseMotionListener(this); 128 179 mv.setVirtualNodesEnabled(false); 180 removeHighlighting(); 181 } 182 183 /** 184 * works out which cursor should be displayed for most of SelectAction's 185 * features. The only exception is the "move" cursor when actually dragging 186 * primitives. 187 * @param nearbyStuff primitives near the cursor 188 * @return the cursor that should be displayed 189 */ 190 private Cursor getCursor(Collection<OsmPrimitive> nearbyStuff) { 191 String c = "rect"; 192 switch(mode) { 193 case move: 194 if(virtualNode != null) { 195 c = "virtual_node"; 196 break; 197 } 198 199 // nearbyStuff cannot be empty as otherwise we would be in 200 // Move.select and not Move.move 201 OsmPrimitive osm = nearbyStuff.iterator().next(); 202 203 c = (osm instanceof Node) ? "node" : c; 204 c = (osm instanceof Way) ? "way" : c; 205 206 if(shift) { 207 c += "_add"; 208 } else if(ctrl) { 209 c += osm.isSelected() ? "_rm" : "_add"; 210 } 211 break; 212 case rotate: 213 c = "rotate"; 214 break; 215 case scale: 216 c = "scale"; 217 break; 218 case select: 219 c = "rect" + (shift ? "_add" : (ctrl ? "_rm" : "")); 220 break; 221 } 222 return SelectActionCursor.valueOf(c).cursor(); 223 } 224 225 /** 226 * Removes all existing highlights. 227 * @return true if a repaint is required 228 */ 229 private boolean removeHighlighting() { 230 boolean needsRepaint = false; 231 DataSet ds = getCurrentDataSet(); 232 if(ds != null && !ds.getHighlightedVirtualNodes().isEmpty()) { 233 needsRepaint = true; 234 ds.clearHighlightedVirtualNodes(); 235 } 236 if(oldHighlights.isEmpty()) 237 return needsRepaint; 238 239 for(OsmPrimitive prim : oldHighlights) { 240 prim.setHighlighted(false); 241 } 242 oldHighlights = new HashSet<OsmPrimitive>(); 243 return true; 244 } 245 246 /** 247 * handles adding highlights and updating the cursor for the given mouse event. 248 * @param MouseEvent which should be used as base for the feedback 249 * @return true if repaint is required 250 */ 251 private boolean giveUserFeedback(MouseEvent e) { 252 return giveUserFeedback(e, e.getModifiers()); 253 } 254 255 /** 256 * handles adding highlights and updating the cursor for the given mouse event. 257 * @param MouseEvent which should be used as base for the feedback 258 * @param define custom keyboard modifiers if the ones from MouseEvent are outdated or similar 259 * @return true if repaint is required 260 */ 261 private boolean giveUserFeedback(MouseEvent e, int modifiers) { 262 boolean needsRepaint = false; 263 264 Collection<OsmPrimitive> c = MapView.asColl( 265 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, true)); 266 267 updateKeyModifiers(modifiers); 268 determineMapMode(!c.isEmpty()); 269 270 if(drawTargetHighlight) { 271 needsRepaint = removeHighlighting(); 272 } 273 274 virtualWays.clear(); 275 virtualNode = null; 276 if(mode == Mode.move && setupVirtual(e)) { 277 DataSet ds = getCurrentDataSet(); 278 if (ds != null) { 279 ds.setHighlightedVirtualNodes(virtualWays); 280 } 281 mv.setNewCursor(SelectActionCursor.virtual_node.cursor(), this); 282 // don't highlight anything else if a virtual node will be 283 return true; 284 } 285 286 mv.setNewCursor(getCursor(c), this); 287 288 // return early if there can't be any highlights 289 if(!drawTargetHighlight || mode != Mode.move || c.isEmpty()) 290 return needsRepaint; 291 292 for(OsmPrimitive x : c) { 293 // only highlight primitives that will change the selection 294 // when clicked. I.e. don't highlight selected elements unless 295 // we are in toggle mode. 296 if(ctrl || !x.isSelected()) { 297 x.setHighlighted(true); 298 oldHighlights.add(x); 299 } 300 } 301 return needsRepaint || !oldHighlights.isEmpty(); 302 } 303 304 /** 305 * This is called whenever the keyboard modifier status changes 306 */ 307 public void eventDispatched(AWTEvent e) { 308 if(oldEvent == null) 309 return; 310 // We don't have a mouse event, so we pass the old mouse event but the 311 // new modifiers. 312 giveUserFeedback(oldEvent, ((InputEvent) e).getModifiers()); 129 313 } 130 314 … … 264 448 public void mouseMoved(MouseEvent e) { 265 449 // Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired. 266 //267 450 if ((Main.platform instanceof PlatformHookOsx) && (mode == Mode.rotate || mode == Mode.scale)) { 268 451 mouseDragged(e); 269 } 270 } 452 return; 453 } 454 oldEvent = e; 455 if(giveUserFeedback(e)) { 456 mv.repaint(); 457 } 458 } 459 460 @Override 461 public void mouseExited(MouseEvent e) { 462 if(removeHighlighting()) { 463 mv.repaint(); 464 } 465 } 466 271 467 private Node virtualNode = null; 272 468 private Collection<WaySegment> virtualWays = new LinkedList<WaySegment>(); … … 339 535 Point p = e.getPoint(); 340 536 boolean waitForMouseUp = Main.pref.getBoolean("mappaint.select.waits-for-mouse-up", false); 341 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;342 boolean alt = ((e.getModifiers() & (ActionEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK)) != 0|| Main.pref.getBoolean("selectaction.cycles.multiple.matches", false));537 updateKeyModifiers(e); 538 alt = alt || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false); 343 539 344 540 if (!alt) { … … 389 585 390 586 /** 587 * sets the mapmode according to key modifiers and if there are any 588 * selectables nearby. Everything has to be pre-determined for this 589 * function; its main purpose is to centralize what the modifiers do. 590 * @param nearSelectables 591 */ 592 private void determineMapMode(boolean hasSelectionNearby) { 593 if (shift && ctrl) { 594 mode = Mode.rotate; 595 } else if (alt && ctrl) { 596 mode = Mode.scale; 597 } else if (hasSelectionNearby) { 598 mode = Mode.move; 599 } else { 600 mode = Mode.select; 601 } 602 } 603 604 /** 391 605 * Look, whether any object is selected. If not, select the nearest node. 392 606 * If there are no nodes in the dataset, do nothing. … … 400 614 public void mousePressed(MouseEvent e) { 401 615 // return early 402 if (!mv.isActiveLayerVisible() || !(Boolean) this.getValue("active") || e.getButton() != MouseEvent.BUTTON1) { 403 return; 404 } 616 if (!mv.isActiveLayerVisible() || !(Boolean) this.getValue("active") || e.getButton() != MouseEvent.BUTTON1) 617 return; 405 618 406 619 // request focus in order to enable the expected keyboard shortcuts 407 620 mv.requestFocus(); 408 621 409 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 410 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 411 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 622 // update which modifiers are pressed (shift, alt, ctrl) 623 updateKeyModifiers(e); 412 624 413 625 // We don't want to change to draw tool if the user tries to (de)select … … 422 634 mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, true)); 423 635 424 if (shift && ctrl) { 425 mode = Mode.rotate; 426 636 determineMapMode(!c.isEmpty()); 637 switch(mode) { 638 case rotate: 639 case scale: 427 640 if (getCurrentDataSet().getSelected().isEmpty()) { 428 641 getCurrentDataSet().setSelected(c); … … 432 645 // Mode.move redraws when mouseDragged is called 433 646 // Mode.rotate redraws here 434 mv.setNewCursor(ImageProvider.getCursor("rotate", null), this);435 mv.repaint();436 } else if (alt && ctrl) {437 mode = Mode.scale;438 439 if (getCurrentDataSet().getSelected().isEmpty()) {440 getCurrentDataSet().setSelected(c);441 }442 443 // Mode.select redraws when selectPrims is called444 // Mode.move redraws when mouseDragged is called445 647 // Mode.scale redraws here 446 mv.setNewCursor(ImageProvider.getCursor("scale", null), this); 447 mv.repaint(); 448 } else if (!c.isEmpty()) { 449 mode = Mode.move; 450 648 break; 649 case move: 451 650 if (!cancelDrawMode && c.iterator().next() instanceof Way) { 452 651 setupVirtual(e); … … 454 653 455 654 selectPrims(cycleSetup(c, e), e, false, false); 456 } else {457 mode = Mode.select;458 655 break; 656 case select: 657 default: 459 658 selectionManager.register(mv); 460 659 selectionManager.mousePressed(e); 461 } 462 660 break; 661 } 662 giveUserFeedback(e); 663 mv.repaint(); 463 664 updateStatusLine(); 464 665 } … … 466 667 @Override 467 668 public void mouseReleased(MouseEvent e) { 468 if (!mv.isActiveLayerVisible()) { 469 return; 470 } 669 if (!mv.isActiveLayerVisible()) 670 return; 471 671 472 672 startingDraggingPos = null; 473 673 474 mv.setNewCursor(cursor, this);475 674 if (mode == Mode.select) { 476 675 selectionManager.unregister(mv); … … 489 688 virtualNode = null; 490 689 491 // do nothing if the click was to short to be recognized as a drag, 690 // do nothing if the click was to short too be recognized as a drag, 492 691 // but the release position is farther than 10px away from the press position 493 if (lastMousePos.distanceSq(e.getPoint()) < 100) { 692 if (lastMousePos == null || lastMousePos.distanceSq(e.getPoint()) < 100) { 494 693 selectPrims(cyclePrims(cycleList, e), e, true, false); 495 694 … … 500 699 // click and switch back to SelectMode 501 700 Main.worker.execute(new Runnable() { 502 503 701 public void run() { 504 702 Main.map.selectDrawTool(true); … … 539 737 } 540 738 541 // I don't see why we need this.542 //updateStatusLine();543 739 mode = null; 740 giveUserFeedback(e); 544 741 updateStatusLine(); 545 742 } 546 743 547 744 public void selectionEnded(Rectangle r, MouseEvent e) { 548 boolean alt = (e.getModifiersEx() & (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK)) != 0;745 updateKeyModifiers(e); 549 746 selectPrims(selectionManager.getObjectsInRectangle(r, alt), e, true, true); 550 747 } … … 554 751 * selection cycle given by <code>prims</code>. 555 752 * @param prims the primitives that form the selection cycle 556 * @param shift whether shift is pressed 557 * @param ctrl whether ctrl is pressed 753 * @param mouse event 558 754 * @return the next element of cycle list <code>prims</code>. 559 755 */ … … 562 758 563 759 if (prims.size() > 1) { 564 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 565 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 760 updateKeyModifiers(e); 566 761 567 762 DataSet ds = getCurrentDataSet(); … … 630 825 631 826 private void selectPrims(Collection<OsmPrimitive> prims, MouseEvent e, boolean released, boolean area) { 632 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 633 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 827 updateKeyModifiers(e); 634 828 DataSet ds = getCurrentDataSet(); 635 829 636 830 // not allowed together: do not change dataset selection, return early 637 if ((shift && ctrl) || (ctrl && !released) || (!virtualWays.isEmpty())) { 638 return; 639 } 831 if ((shift && ctrl) || (ctrl && !released) || (!virtualWays.isEmpty())) 832 return; 640 833 641 834 if (!released) { … … 666 859 @Override 667 860 public String getModeHelpText() { 668 if (mode == Mode.select) {861 if (mode == Mode.select) 669 862 return tr("Release the mouse button to select the objects in the rectangle."); 670 }else if (mode == Mode.move){863 else if (mode == Mode.move) 671 864 return tr("Release the mouse button to stop moving. Ctrl to merge with nearest node."); 672 }else if (mode == Mode.rotate){865 else if (mode == Mode.rotate) 673 866 return tr("Release the mouse button to stop rotating."); 674 }else if (mode == Mode.scale){867 else if (mode == Mode.scale) 675 868 return tr("Release the mouse button to stop scaling."); 676 } else {869 else 677 870 return tr("Move objects by dragging; Shift to add to selection (Ctrl to toggle); Shift-Ctrl to rotate selected; Alt-Ctrl to scale selected; or change selection"); 678 }679 871 } 680 872
Note:
See TracChangeset
for help on using the changeset viewer.
