Changeset 2026 in josm for trunk/src/org


Ignore:
Timestamp:
2009-09-02T21:37:18+02:00 (15 years ago)
Author:
Gubaer
Message:

applied #2163: patch by xeen: for delete-command mouse-icons

Location:
trunk/src/org/openstreetmap/josm
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java

    r1935 r2026  
    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;
     
    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    /**
     
    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
     
    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
     
    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.
     
    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;
     260        Command c = buildDeleteCommands(e, e.getModifiers(), false);
     261        if (c != null) {
     262            Main.main.undoRedo.add(c);
     263        }
     264
     265        getCurrentDataSet().setSelected();
     266        Main.map.mapView.repaint();
     267    }
     268
     269    @Override public String getModeHelpText() {
     270        return tr("Click to delete. Shift: delete way segment. Alt: don't delete unused nodes when deleting a way. Ctrl: delete referring objects.");
     271    }
     272
     273    @Override public boolean layerIsSupported(Layer l) {
     274        return l instanceof OsmDataLayer;
     275    }
     276
     277    @Override
     278    protected void updateEnabledState() {
     279        setEnabled(Main.map != null && Main.map.mapView != null && Main.map.mapView.isActiveLayerDrawable());
     280    }
     281
     282    /**
     283     * Deletes the relation in the context of the given layer. Also notifies
     284     * {@see RelationDialogManager} and {@see OsmDataLayer#fireDataChange()} events.
     285     *
     286     * @param layer the layer in whose context the relation is deleted. Must not be null.
     287     * @param toDelete  the relation to be deleted. Must  not be null.
     288     * @exception IllegalArgumentException thrown if layer is null
     289     * @exception IllegalArgumentException thrown if toDelete is nul
     290     */
     291    public static void deleteRelation(OsmDataLayer layer, Relation toDelete) {
     292        if (layer == null)
     293            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
     294        if (toDelete == null)
     295            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "toDelete"));
     296
     297        Command cmd = DeleteCommand.delete(layer, Collections.singleton(toDelete));
     298        if (cmd != null) {
     299            // cmd can be null if the user cancels dialogs DialogCommand displays
     300            Main.main.undoRedo.add(cmd);
     301            RelationDialogManager.getRelationDialogManager().close(layer, toDelete);
     302            layer.fireDataChange();
     303        }
     304    }
     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;
    112321
    113322        OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint());
     
    119328                    c = DeleteCommand.deleteWaySegment(getEditLayer(),ws);
    120329                } else if (ctrl) {
    121                     c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way));
     330                    c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way),true);
    122331                } else {
    123                     c = DeleteCommand.delete(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way), !alt);
     332                    c = DeleteCommand.delete(getEditLayer(),Collections.singleton((OsmPrimitive)ws.way), !alt, simulate);
    124333                }
    125334            }
     
    127336            c = DeleteCommand.deleteWithReferences(getEditLayer(),Collections.singleton(sel));
    128337        } else {
    129             c = DeleteCommand.delete(getEditLayer(),Collections.singleton(sel), !alt);
    130         }
    131         if (c != null) {
    132             Main.main.undoRedo.add(c);
    133         }
    134 
    135         getCurrentDataSet().setSelected();
    136         Main.map.mapView.repaint();
    137     }
    138 
    139     @Override public String getModeHelpText() {
    140         return tr("Click to delete. Shift: delete way segment. Alt: don't delete unused nodes when deleting a way. Ctrl: delete referring objects.");
    141     }
    142 
    143     @Override public boolean layerIsSupported(Layer l) {
    144         return l instanceof OsmDataLayer;
    145     }
    146 
    147     @Override
    148     protected void updateEnabledState() {
    149         setEnabled(Main.map != null && Main.map.mapView != null && Main.map.mapView.isActiveLayerDrawable());
    150     }
    151 
    152     /**
    153      * Deletes the relation in the context of the given layer. Also notifies
    154      * {@see RelationDialogManager} and {@see OsmDataLayer#fireDataChange()} events.
    155      *
    156      * @param layer the layer in whose context the relation is deleted. Must not be null.
    157      * @param toDelete  the relation to be deleted. Must  not be null.
    158      * @exception IllegalArgumentException thrown if layer is null
    159      * @exception IllegalArgumentException thrown if toDelete is nul
    160      */
    161     public static void deleteRelation(OsmDataLayer layer, Relation toDelete) {
    162         if (layer == null)
    163             throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
    164         if (toDelete == null)
    165             throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "toDelete"));
    166         if (toDelete == null)
    167             return;
    168         Command cmd = DeleteCommand.delete(layer, Collections.singleton(toDelete));
    169         if (cmd != null) {
    170             // cmd can be null if the user cancels dialogs DialogCommand displays
    171             //
    172             Main.main.undoRedo.add(cmd);
    173             RelationDialogManager.getRelationDialogManager().close(layer, toDelete);
    174             layer.fireDataChange();
    175         }
     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());
    176377    }
    177378}
  • trunk/src/org/openstreetmap/josm/command/DeleteCommand.java

    r2025 r2026  
    4343 */
    4444public class DeleteCommand extends Command {
    45 
    4645    /**
    4746     * The primitives that get deleted.
     
    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) {
     
    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()) {
     
    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
     
    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;
     
    260277        }
    261278
    262         if (!checkAndConfirmOutlyingDeletes(layer,primitivesToDelete))
     279        if (!checkAndConfirmOutlyingDeletes(layer,primitivesToDelete) && !simulate)
    263280            return null;
    264281
     
    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) {
     
    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);
Note: See TracChangeset for help on using the changeset viewer.