40 | | public class DeleteAction extends MapMode { |
| 49 | |
| 50 | /** |
| 51 | * This class contains stubs for highlighting affected primitives when affected. |
| 52 | * However, way segments can be deleted as well, but cannot be highlighted |
| 53 | * alone. If the highlight feature for this delete action is to be implemented |
| 54 | * properly, highlighting way segments must be possible first. --xeen, 2009-09-02 |
| 55 | */ |
| 56 | public class DeleteAction extends MapMode implements AWTEventListener { |
| 57 | //private boolean drawTargetHighlight; |
| 58 | private boolean drawTargetCursor; |
| 59 | //private Collection<? extends OsmPrimitive> oldPrims = null; |
| 60 | |
| 61 | // Cache previous mouse event (needed when only the modifier keys are |
| 62 | // pressed but the mouse isn't moved) |
| 63 | private MouseEvent oldEvent = null; |
| 64 | |
| 65 | private enum Cursors { |
| 66 | none, |
| 67 | node, |
| 68 | segment, |
| 69 | way_node_only, |
| 70 | way_normal, |
| 71 | way_only; |
| 72 | |
| 73 | private Cursor c = null; |
| 74 | // This loads and caches the cursor for each |
| 75 | public Cursor cursor() { |
| 76 | if(c == null) { |
| 77 | String nm = "delete_" + this.name().toLowerCase(); |
| 78 | // "None" has no special icon |
| 79 | nm = nm.equals("delete_none") ? "delete" : nm; |
| 80 | this.c = ImageProvider.getCursor("normal", nm); |
| 81 | } |
| 82 | return c; |
| 83 | } |
| 84 | } |
| 85 | private Cursors currCursor = Cursors.none; |
| 155 | * Listen to mouse move to be able to update the cursor (and highlights) |
| 156 | * @param MouseEvent The mouse event that has been captured |
| 157 | */ |
| 158 | @Override public void mouseMoved(MouseEvent e) { |
| 159 | oldEvent = e; |
| 160 | updateCursor(e, e.getModifiers()); |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * This function handles all work related to updating the cursor and |
| 165 | * highlights. For now, only the cursor is enabled because highlighting |
| 166 | * requires WaySegment to be highlightable. |
| 167 | * |
| 168 | * Normally the mouse event also contains the modifiers. However, when the |
| 169 | * mouse is not moved and only modifier keys are pressed, no mouse event |
| 170 | * occurs. We can use AWTEvent to catch those but still lack a proper |
| 171 | * mouseevent. Instead we copy the previous event and only update the |
| 172 | * modifiers. |
| 173 | * |
| 174 | * @param MouseEvent |
| 175 | * @parm int modifiers |
| 176 | */ |
| 177 | private void updateCursor(MouseEvent e, int modifiers) { |
| 178 | if(!Main.map.mapView.isActiveLayerVisible() || e == null) |
| 179 | return; |
| 180 | |
| 181 | // Clean old highlights |
| 182 | //cleanOldHighlights(); |
| 183 | |
| 184 | Command c = buildDeleteCommands(e, modifiers, true); |
| 185 | if(c == null) { |
| 186 | setCursor(Cursors.none); |
| 187 | return; |
| 188 | } |
| 189 | |
| 190 | Collection<OsmPrimitive> prims = new HashSet<OsmPrimitive>(); |
| 191 | Collection<OsmPrimitive> mods = new HashSet<OsmPrimitive>(); |
| 192 | c.fillModifiedData(mods, prims, prims); |
| 193 | |
| 194 | if(prims.size() == 0 && mods.size() == 0) { |
| 195 | // Default-Cursor |
| 196 | setCursor(Cursors.none); |
| 197 | return; |
| 198 | } |
| 199 | |
| 200 | // There are no deleted parts if solely a way segment is deleted |
| 201 | // This is no the case when actually deleting only a segment but that |
| 202 | // segment happens to be the whole way. This is an acceptable error |
| 203 | // though |
| 204 | if(prims.size() == 0) { |
| 205 | setCursor(Cursors.segment); |
| 206 | } else if(prims.size() == 1 && prims.toArray()[0] instanceof Node) { |
| 207 | setCursor(Cursors.node); |
| 208 | } else if(prims.size() == 1 && prims.toArray()[0] instanceof Way) { |
| 209 | setCursor(Cursors.way_only); |
| 210 | } else { |
| 211 | // Decide between non-accel click where "useless" nodes are deleted |
| 212 | // and ctrl-click where nodes and ways are deleted |
| 213 | boolean ctrl = (modifiers & ActionEvent.CTRL_MASK) != 0; |
| 214 | if(ctrl) { |
| 215 | setCursor(Cursors.way_node_only); |
| 216 | } else { |
| 217 | setCursor(Cursors.way_normal); |
| 218 | } |
| 219 | |
| 220 | } |
| 221 | |
| 222 | // Needs to implement WaySegment highlight first |
| 223 | /*if(drawTargetHighlight) { |
| 224 | // Add new highlights |
| 225 | for(OsmPrimitive p : prims) { |
| 226 | p.highlighted = true; |
| 227 | } |
| 228 | oldPrims = prims; |
| 229 | }*/ |
| 230 | |
| 231 | // We only need to repaint if the highlights changed |
| 232 | //Main.map.mapView.repaint(); |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * Small helper function that cleans old highlights |
| 237 | */ |
| 238 | /*private void cleanOldHighlights() { |
| 239 | if(oldPrims == null) |
| 240 | return; |
| 241 | for(OsmPrimitive p: oldPrims) { |
| 242 | p.highlighted = false; |
| 243 | } |
| 244 | }*/ |
| 245 | |
| 246 | /** |
109 | | boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; |
110 | | boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; |
111 | | boolean alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0; |
112 | | |
113 | | OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint()); |
114 | | Command c = null; |
115 | | if (sel == null) { |
116 | | WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint()); |
117 | | if (ws != null) { |
118 | | if (shift) { |
119 | | c = DeleteCommand.deleteWaySegment(getEditLayer(),ws); |
120 | | } else if (ctrl) { |
121 | | c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way)); |
122 | | } else { |
123 | | c = DeleteCommand.delete(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way), !alt); |
124 | | } |
125 | | } |
126 | | } else if (ctrl) { |
127 | | c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton(sel)); |
128 | | } else { |
129 | | c = DeleteCommand.delete(getEditLayer(),Collections.singleton(sel), !alt); |
130 | | } |
| 260 | Command c = buildDeleteCommands(e, e.getModifiers(), false); |
| 305 | |
| 306 | /** |
| 307 | * This function takes any mouse event argument and builds the list of elements |
| 308 | * that should be deleted but does not actually delete them. |
| 309 | * @param e MouseEvent from which modifiers and position are taken |
| 310 | * @param int modifiers For explanation: @see updateCursor |
| 311 | * @param Simulate Set to true if the user should be bugged with additional |
| 312 | * dialogs |
| 313 | * @return |
| 314 | */ |
| 315 | private Command buildDeleteCommands(MouseEvent e, int modifiers, boolean simulate) { |
| 316 | // Note: CTRL is the only modifier that is checked in MouseMove, don't |
| 317 | // forget updating it there |
| 318 | boolean ctrl = (modifiers & ActionEvent.CTRL_MASK) != 0; |
| 319 | boolean shift = (modifiers & ActionEvent.SHIFT_MASK) != 0; |
| 320 | boolean alt = (modifiers & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0; |
| 321 | |
| 322 | OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint()); |
| 323 | Command c = null; |
| 324 | if (sel == null) { |
| 325 | WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint()); |
| 326 | if (ws != null) { |
| 327 | if (shift) { |
| 328 | c = DeleteCommand.deleteWaySegment(getEditLayer(),ws); |
| 329 | } else if (ctrl) { |
| 330 | c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way),true); |
| 331 | } else { |
| 332 | c = DeleteCommand.delete(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way), !alt, simulate); |
| 333 | } |
| 334 | } |
| 335 | } else if (ctrl) { |
| 336 | c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton(sel)); |
| 337 | } else { |
| 338 | c = DeleteCommand.delete(getEditLayer(),Collections.singleton(sel), !alt, simulate); |
| 339 | } |
| 340 | |
| 341 | return c; |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * This function sets the given cursor in a safe way. This implementation |
| 346 | * differs from the on in DrawAction (it is favorable, too). |
| 347 | * FIXME: Update DrawAction to use this "setCursor-style" and move function |
| 348 | * to MapMode. |
| 349 | * @param c |
| 350 | */ |
| 351 | private void setCursor(final Cursors c) { |
| 352 | if(currCursor.equals(c) || (!drawTargetCursor && currCursor.equals(Cursors.none))) |
| 353 | return; |
| 354 | try { |
| 355 | // We invoke this to prevent strange things from happening |
| 356 | EventQueue.invokeLater(new Runnable() { |
| 357 | public void run() { |
| 358 | // Don't change cursor when mode has changed already |
| 359 | if(!(Main.map.mapMode instanceof DeleteAction)) |
| 360 | return; |
| 361 | |
| 362 | Main.map.mapView.setCursor(c.cursor()); |
| 363 | //System.out.println("Set cursor to: " + c.name()); |
| 364 | } |
| 365 | }); |
| 366 | currCursor = c; |
| 367 | } catch(Exception e) {} |
| 368 | } |
| 369 | |
| 370 | /** |
| 371 | * This is required to update the cursors when ctrl/shift/alt is pressed |
| 372 | */ |
| 373 | public void eventDispatched(AWTEvent e) { |
| 374 | // We don't have a mouse event, so we pass the old mouse event but the |
| 375 | // new modifiers. |
| 376 | updateCursor(oldEvent, ((InputEvent)e).getModifiers()); |
| 377 | } |