Ticket #2163: delete_icons.patch

File delete_icons.patch, 16.8 KB (added by xeen, 3 years ago)

The patch.

  • src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java

     
    33 
    44import static org.openstreetmap.josm.tools.I18n.tr; 
    55 
     6import java.awt.AWTEvent; 
     7import java.awt.Cursor; 
     8import java.awt.EventQueue; 
     9import java.awt.Toolkit; 
     10import java.awt.event.AWTEventListener; 
    611import java.awt.event.ActionEvent; 
    712import java.awt.event.InputEvent; 
    813import java.awt.event.KeyEvent; 
    914import java.awt.event.MouseEvent; 
     15import java.util.Collection; 
    1016import java.util.Collections; 
     17import java.util.HashSet; 
    1118 
    1219import org.openstreetmap.josm.Main; 
    1320import org.openstreetmap.josm.command.Command; 
    1421import org.openstreetmap.josm.command.DeleteCommand; 
     22import org.openstreetmap.josm.data.osm.Node; 
    1523import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    1624import org.openstreetmap.josm.data.osm.Relation; 
     25import org.openstreetmap.josm.data.osm.Way; 
    1726import org.openstreetmap.josm.data.osm.WaySegment; 
    1827import org.openstreetmap.josm.gui.MapFrame; 
    1928import org.openstreetmap.josm.gui.dialogs.relation.RelationDialogManager; 
     
    3746 * 
    3847 * @author imi 
    3948 */ 
    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 */ 
     56public 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; 
    4186 
    4287    /** 
    4388     * Construct a new DeleteAction. Mnemonic is the delete - key. 
     
    56101        super.enterMode(); 
    57102        if (!isEnabled()) 
    58103            return; 
     104        //drawTargetHighlight = Main.pref.getBoolean("draw.target-highlight", true); 
     105        drawTargetCursor = Main.pref.getBoolean("draw.target-cursor", true); 
     106 
    59107        Main.map.mapView.addMouseListener(this); 
     108        Main.map.mapView.addMouseMotionListener(this); 
     109        // This is required to update the cursors when ctrl/shift/alt is pressed 
     110        try { 
     111            Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK); 
     112        } catch (SecurityException ex) {} 
     113 
     114        currCursor = Cursors.none; 
    60115    } 
    61116 
    62117    @Override public void exitMode() { 
    63118        super.exitMode(); 
    64119        Main.map.mapView.removeMouseListener(this); 
     120        Main.map.mapView.removeMouseMotionListener(this); 
     121        try { 
     122            Toolkit.getDefaultToolkit().removeAWTEventListener(this); 
     123        } catch (SecurityException ex) {} 
    65124    } 
    66125 
    67126 
     
    93152    } 
    94153 
    95154    /** 
     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    /** 
    96247     * If user clicked with the left button, delete the nearest object. 
    97248     * position. 
    98249     */ 
     
    106257        // 
    107258        Main.map.mapView.requestFocus(); 
    108259 
    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); 
    131261        if (c != null) { 
    132262            Main.main.undoRedo.add(c); 
    133263        } 
     
    163293            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer")); 
    164294        if (toDelete == null) 
    165295            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "toDelete")); 
    166         if (toDelete == null) 
    167             return; 
     296 
    168297        Command cmd = DeleteCommand.delete(layer, Collections.singleton(toDelete)); 
    169298        if (cmd != null) { 
    170299            // cmd can be null if the user cancels dialogs DialogCommand displays 
    171             // 
    172300            Main.main.undoRedo.add(cmd); 
    173301            RelationDialogManager.getRelationDialogManager().close(layer, toDelete); 
    174302            layer.fireDataChange(); 
    175303        } 
    176304    } 
     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    } 
    177378} 
  • src/org/openstreetmap/josm/command/DeleteCommand.java

     
    4242 * @author imi 
    4343 */ 
    4444public class DeleteCommand extends Command { 
    45  
    4645    /** 
    4746     * The primitives that get deleted. 
    4847     */ 
     
    156155     * 
    157156     * If a way is deleted, only the way and no nodes are deleted. 
    158157     * 
     158     * @param layer 
    159159     * @param selection The list of all object to be deleted. 
     160     * @param simulate  Set to true if the user should not be bugged with additional dialogs 
    160161     * @return command A command to perform the deletions, or null of there is nothing to delete. 
    161162     */ 
    162     public static Command deleteWithReferences(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection) { 
     163    public static Command deleteWithReferences(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection, boolean simulate) { 
    163164        CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(layer.data); 
    164165        for (OsmPrimitive osm : selection) { 
    165166            osm.visit(v); 
     
    167168        v.data.addAll(selection); 
    168169        if (v.data.isEmpty()) 
    169170            return null; 
    170         if (!checkAndConfirmOutlyingDeletes(layer,v.data)) 
     171        if (!checkAndConfirmOutlyingDeletes(layer,v.data) && !simulate) 
    171172            return null; 
    172173        return new DeleteCommand(layer,v.data); 
    173174    } 
    174175 
    175     private static int testRelation(Relation ref, OsmPrimitive osm) { 
     176    public static Command deleteWithReferences(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection) { 
     177        return deleteWithReferences(layer, selection, false); 
     178    } 
     179 
     180    private static int testRelation(Relation ref, OsmPrimitive osm, boolean simulate) { 
     181        // If this delete action is simulated, do not bug the user with dialogs 
     182        // and assume the relations should be deleted 
     183        if(simulate) 
     184            return 1; 
     185 
    176186        String role = new String(); 
    177187        for (RelationMember m : ref.getMembers()) { 
    178188            if (m.getMember() == osm) { 
     
    194204    } 
    195205 
    196206    public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection) { 
    197         return delete(layer, selection, true); 
     207        return delete(layer, selection, true, false); 
    198208    } 
    199209 
    200210    /** 
     
    242252     * @param layer the {@see OsmDataLayer} in whose context a primitive the primitives are deleted 
    243253     * @param selection The objects to delete. 
    244254     * @param alsoDeleteNodesInWay <code>true</code> if nodes should be deleted as well 
     255     * @param simulate Set to true if the user should not be bugged with additional questions 
    245256     * @return command a command to perform the deletions, or null if there is nothing to delete. 
    246257     */ 
    247     public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection, boolean alsoDeleteNodesInWay) { 
     258    public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection, 
     259            boolean alsoDeleteNodesInWay) { 
     260        return delete(layer, selection, alsoDeleteNodesInWay, false); 
     261    } 
     262 
     263    public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection, 
     264            boolean alsoDeleteNodesInWay, boolean simulate) { 
    248265        if (selection.isEmpty()) 
    249266            return null; 
    250267 
     
    259276            primitivesToDelete.addAll(nodesToDelete); 
    260277        } 
    261278 
    262         if (!checkAndConfirmOutlyingDeletes(layer,primitivesToDelete)) 
     279        if (!checkAndConfirmOutlyingDeletes(layer,primitivesToDelete) && !simulate) 
    263280            return null; 
    264281 
    265282        for (OsmPrimitive osm : primitivesToDelete) { 
     
    272289                if (ref instanceof Way) { 
    273290                    waysToBeChanged.add((Way) ref); 
    274291                } else if (ref instanceof Relation) { 
    275                     if (testRelation((Relation) ref, osm) == 1) { 
     292                    if (testRelation((Relation) ref, osm, simulate) == 1) { 
    276293                        Collection<OsmPrimitive> relset = relationsToBeChanged.get(ref); 
    277294                        if (relset == null) { 
    278295                            relset = new HashSet<OsmPrimitive>(); 
     
    313330                            } 
    314331                        } 
    315332                        if (!found) { 
    316                             if (testRelation((Relation) ref, w) == 1) { 
     333                            if (testRelation((Relation) ref, w, simulate) == 1) { 
    317334                                relset.add(w); 
    318335                                relationsToBeChanged.put(ref, relset); 
    319336                            } else