Ticket #11629: 0003-Fixed-state-errors-in-selection-rect-lasso-and-made-.patch

File 0003-Fixed-state-errors-in-selection-rect-lasso-and-made-.patch, 17.0 KB (added by michael2402, 10 years ago)
  • src/org/openstreetmap/josm/actions/mapmode/SelectAction.java

    From 6f8b8dad2291ebbdafcdb66359ca2bdbdf03c59a Mon Sep 17 00:00:00 2001
    From: Michael Zangl <michael.zangl@student.kit.edu>
    Date: Wed, 1 Jul 2015 14:22:16 +0200
    Subject: [PATCH 3/8] Fixed state errors in selection (rect/lasso) and made
     them draw on a temporary layer.
    
    ---
     .../josm/actions/mapmode/SelectAction.java         |   4 +
     .../openstreetmap/josm/gui/SelectionManager.java   | 217 ++++++++++++++-------
     2 files changed, 152 insertions(+), 69 deletions(-)
    
    diff --git a/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java b/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
    index 70ad05a..36cfdb7 100644
    a b public class SelectAction extends MapMode implements ModifierListener, KeyPressR  
    585585        mouseReleaseTime = System.currentTimeMillis();
    586586
    587587        if (mode == Mode.SELECT) {
     588            if (e.getButton() != MouseEvent.BUTTON1) {
     589                return;
     590            }
     591            selectionManager.endSelecting(e);
    588592            selectionManager.unregister(mv);
    589593
    590594            // Select Draw Tool if no selection has been made
  • src/org/openstreetmap/josm/gui/SelectionManager.java

    diff --git a/src/org/openstreetmap/josm/gui/SelectionManager.java b/src/org/openstreetmap/josm/gui/SelectionManager.java
    index f50ed98..4a7703a 100644
    a b  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui;
    33
    4 import java.awt.Component;
     4import java.awt.Color;
     5import java.awt.Graphics2D;
    56import java.awt.Point;
    67import java.awt.Polygon;
    78import java.awt.Rectangle;
    import java.util.LinkedList;  
    1617
    1718import org.openstreetmap.josm.Main;
    1819import org.openstreetmap.josm.actions.SelectByInternalPointAction;
     20import org.openstreetmap.josm.data.Bounds;
    1921import org.openstreetmap.josm.data.osm.Node;
    2022import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2123import org.openstreetmap.josm.data.osm.Way;
     24import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
     25import org.openstreetmap.josm.gui.layer.MapViewPaintable;
     26import org.openstreetmap.josm.tools.Utils;
    2227
    2328/**
    24  * Manages the selection of a rectangle. Listening to left and right mouse button
     29 * Manages the selection of a rectangle or a lasso loop. Listening to left and right mouse button
    2530 * presses and to mouse motions and draw the rectangle accordingly.
    2631 *
    2732 * Left mouse button selects a rectangle from the press until release. Pressing
    28  * right mouse button while left is still pressed enable the rectangle to move
     33 * right mouse button while left is still pressed enable the selection area to move
    2934 * around. Releasing the left button fires an action event to the listener given
    3035 * at constructor, except if the right is still pressed, which just remove the
    3136 * selection rectangle and does nothing.
    3237 *
     38 * It is possible to switch between lasso selection and rectangle selection by using {@link #setLassoMode(boolean)}.
     39 *
    3340 * The point where the left mouse button was pressed and the current mouse
    3441 * position are two opposite corners of the selection rectangle.
    3542 *
    36  * It is possible to specify an aspect ratio (width per height) which the
     43 * For rectangle mode, it is possible to specify an aspect ratio (width per height) which the
    3744 * selection rectangle always must have. In this case, the selection rectangle
    3845 * will be the largest window with this aspect ratio, where the position the left
    3946 * mouse button was pressed and the corner of the current mouse position are at
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    5562    public interface SelectionEnded {
    5663        /**
    5764         * Called, when the left mouse button was released.
    58          * @param r The rectangle that is currently the selection.
     65         * @param r The rectangle that encloses the current selection.
    5966         * @param e The mouse event.
    6067         * @see InputEvent#getModifiersEx()
     68         * @see SelectionManager#getSelectedObjects(boolean)
    6169         */
    6270        void selectionEnded(Rectangle r, MouseEvent e);
    6371
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    7482         */
    7583        void removePropertyChangeListener(PropertyChangeListener listener);
    7684    }
     85
     86    /**
     87     * This draws the selection hint (rectangle or lasso polygon) on the screen.
     88     *
     89     * @author Michael Zangl
     90     */
     91    private class SelectionHintLayer implements MapViewPaintable {
     92        @Override
     93        public void paint(Graphics2D g, MapView mv, Bounds bbox) {
     94            if (mousePos == null || mousePosStart == null || mousePos == mousePosStart)
     95                return;
     96            Color color = Utils.complement(PaintColors.getBackgroundColor());
     97            g.setColor(color);
     98            if (lassoMode) {
     99                g.drawPolygon(lasso);
     100
     101                g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 8));
     102                g.fillPolygon(lasso);
     103            } else {
     104                Rectangle paintRect = getSelectionRectangle();
     105                g.drawRect(paintRect.x, paintRect.y, paintRect.width, paintRect.height);
     106            }
     107        }
     108    }
     109
    77110    /**
    78111     * The listener that receives the events after left mouse button is released.
    79112     */
    80113    private final SelectionEnded selectionEndedListener;
    81114    /**
    82115     * Position of the map when the mouse button was pressed.
    83      * If this is not <code>null</code>, a rectangle is drawn on screen.
     116     * If this is not <code>null</code>, a rectangle/lasso line is drawn on screen.
     117     * If this is <code>null</code>, no selection is active.
    84118     */
    85119    private Point mousePosStart;
    86120    /**
    87      * Position of the map when the selection rectangle was last drawn.
     121     * The last position of the mouse while the mouse button was pressed.
    88122     */
    89123    private Point mousePos;
    90124    /**
    91      * The Component, the selection rectangle is drawn onto.
     125     * The Component that provides us with OSM data and the aspect is taken from.
    92126     */
    93127    private final NavigatableComponent nc;
    94128    /**
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    97131     */
    98132    private boolean aspectRatio;
    99133
     134    /**
     135     * <code>true</code> if we should paint a lasso instead of a rectangle.
     136     */
    100137    private boolean lassoMode;
     138    /**
     139     * The polygon to store the selection outline if {@link #lassoMode} is used.
     140     */
    101141    private Polygon lasso = new Polygon();
    102142
    103143    /**
     144     * The result of the last selection.
     145     */
     146    private Polygon selectionResult = new Polygon();
     147
     148    private final SelectionHintLayer selectionHintLayer = new SelectionHintLayer();
     149
     150    /**
    104151     * Create a new SelectionManager.
    105152     *
    106153     * @param selectionEndedListener The action listener that receives the event when
    107154     *      the left button is released.
    108155     * @param aspectRatio If true, the selection window must obtain the aspect
    109156     *      ratio of the drawComponent.
    110      * @param navComp The component, the rectangle is drawn onto.
     157     * @param navComp The component that provides us with OSM data and the aspect is taken from.
    111158     */
    112159    public SelectionManager(SelectionEnded selectionEndedListener, boolean aspectRatio, NavigatableComponent navComp) {
    113160        this.selectionEndedListener = selectionEndedListener;
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    116163    }
    117164
    118165    /**
    119      * Register itself at the given event source.
     166     * Register itself at the given event source and add a hint layer.
    120167     * @param eventSource The emitter of the mouse events.
    121168     * @param lassoMode {@code true} to enable lasso mode, {@code false} to disable it.
    122169     */
    123     public void register(NavigatableComponent eventSource, boolean lassoMode) {
     170    public void register(MapView eventSource, boolean lassoMode) {
    124171       this.lassoMode = lassoMode;
    125172        eventSource.addMouseListener(this);
    126173        eventSource.addMouseMotionListener(this);
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    128175        eventSource.addPropertyChangeListener("scale", new PropertyChangeListener() {
    129176            @Override
    130177            public void propertyChange(PropertyChangeEvent evt) {
    131                 if (mousePosStart != null) {
    132                     paintRect();
    133                     mousePos = mousePosStart = null;
    134                 }
     178                abortSelecting();
    135179            }
    136180        });
     181        eventSource.addTemporaryLayer(selectionHintLayer);
    137182    }
    138183    /**
    139      * Unregister itself from the given event source. If a selection rectangle is
    140      * shown, hide it first.
     184     * Unregister itself from the given event source and hide the selection hint layer.
    141185     *
    142186     * @param eventSource The emitter of the mouse events.
    143187     */
    144     public void unregister(Component eventSource) {
     188    public void unregister(MapView eventSource) {
     189        abortSelecting();
     190        eventSource.removeTemporaryLayer(selectionHintLayer);
    145191        eventSource.removeMouseListener(this);
    146192        eventSource.removeMouseMotionListener(this);
    147193        selectionEndedListener.removePropertyChangeListener(this);
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    175221            if (mousePosStart == null) {
    176222                mousePosStart = mousePos = e.getPoint();
    177223            }
    178             if (!lassoMode) {
    179                 paintRect();
    180             }
     224            selectionAreaChanged();
    181225        }
    182226
    183227        if (buttonPressed == MouseEvent.BUTTON1_DOWN_MASK) {
    184228            mousePos = e.getPoint();
    185             if (lassoMode) {
    186                 paintLasso();
    187             } else {
    188                 paintRect();
    189             }
     229            addLassoPoint(e.getPoint());
     230            selectionAreaChanged();
    190231        } else if (buttonPressed == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) {
    191             mousePosStart.x += e.getX()-mousePos.x;
    192             mousePosStart.y += e.getY()-mousePos.y;
     232            moveSelection(e.getX()-mousePos.x, e.getY()-mousePos.y);
    193233            mousePos = e.getPoint();
    194             paintRect();
     234            selectionAreaChanged();
    195235        }
    196236    }
    197237
    198238    /**
     239     * Moves the current selection by some pixels.
     240     * @param dx How much to move it in x direction.
     241     * @param dy How much to move it in y direction.
     242     */
     243    private void moveSelection(int dx, int dy) {
     244        mousePosStart.x += dx;
     245        mousePosStart.y += dy;
     246        lasso.translate(dx, dy);
     247    }
     248
     249    /**
    199250     * Check the state of the keys and buttons and set the selection accordingly.
    200251     */
    201252    @Override
    202253    public void mouseReleased(MouseEvent e) {
    203         if (e.getButton() != MouseEvent.BUTTON1)
    204             return;
    205         if (mousePos == null || mousePosStart == null)
    206             return; // injected release from outside
    207         // disable the selection rect
    208         Rectangle r;
    209         if (!lassoMode) {
    210             nc.requestClearRect();
    211             r = getSelectionRectangle();
    212 
    213             lasso = rectToPolygon(r);
    214         } else {
    215             nc.requestClearPoly();
    216             lasso.addPoint(mousePos.x, mousePos.y);
    217             r = lasso.getBounds();
    218         }
    219         mousePosStart = null;
    220         mousePos = null;
    221 
    222         if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0) {
    223             selectionEndedListener.selectionEnded(r, e);
     254        if (e.getButton() == MouseEvent.BUTTON1) {
     255            endSelecting(e);
    224256        }
    225257    }
    226258
    227259    /**
    228      * Draws a selection rectangle on screen.
     260     * Ends the selection of the current area. This simulates a release of mouse button 1.
     261     * @param e A mouse event that caused this. Needed for backward compatibility.
    229262     */
    230     private void paintRect() {
    231         if (mousePos == null || mousePosStart == null || mousePos == mousePosStart)
    232             return;
    233         nc.requestPaintRect(getSelectionRectangle());
     263    public void endSelecting(MouseEvent e) {
     264        mousePos = e.getPoint();
     265        if (lassoMode) {
     266            addLassoPoint(e.getPoint());
     267        }
     268
     269        // Left mouse was released while right is still pressed.
     270        boolean rightMouseStillPressed = (e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0;
     271
     272        if (!rightMouseStillPressed) {
     273            selectingDone(e);
     274        }
     275        abortSelecting();
    234276    }
    235277
    236     private void paintLasso() {
    237         if (mousePos == null || mousePosStart == null || mousePos == mousePosStart) {
     278    private void addLassoPoint(Point point) {
     279        if (isNoSelection()) {
    238280            return;
    239281        }
    240         lasso.addPoint(mousePos.x, mousePos.y);
    241         nc.requestPaintPoly(lasso);
     282        lasso.addPoint(point.x, point.y);
     283    }
     284
     285    private boolean isNoSelection() {
     286        return mousePos == null || mousePosStart == null || mousePos == mousePosStart;
    242287    }
    243288
    244289    /**
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    286331     */
    287332    @Override
    288333    public void propertyChange(PropertyChangeEvent evt) {
    289         if ("active".equals(evt.getPropertyName()) && !(Boolean) evt.getNewValue() && mousePosStart != null) {
    290             paintRect();
    291             mousePosStart = null;
    292             mousePos = null;
     334        if ("active".equals(evt.getPropertyName()) && !(Boolean)evt.getNewValue()) {
     335            abortSelecting();
    293336        }
    294337    }
    295338
    296339    /**
    297      * Return a list of all objects in the selection, respecting the different
     340     * Stores the  current selection and stores the result in {@link #selectionResult} to  be retrieved by {@link #getSelectedObjects(boolean)} later.
     341     * @param e The mouse event that caused the selection to be finished.
     342     */
     343    private void selectingDone(MouseEvent e) {
     344        if (isNoSelection()) {
     345            // Nothing selected.
     346            return;
     347        }
     348        Rectangle r;
     349        if (lassoMode) {
     350            r = lasso.getBounds();
     351
     352            selectionResult = new Polygon(lasso.xpoints, lasso.ypoints, lasso.npoints);
     353        } else {
     354            r = getSelectionRectangle();
     355
     356            selectionResult = rectToPolygon(r);
     357        }
     358        selectionEndedListener.selectionEnded(r, e);
     359    }
     360
     361    private void abortSelecting() {
     362        if (mousePosStart != null) {
     363            mousePos = mousePosStart = null;
     364            lasso.reset();
     365            selectionAreaChanged();
     366        }
     367    }
     368
     369    private void selectionAreaChanged() {
     370        // Trigger a redraw of the map view.
     371        // A nicer way would be to provide change events for the temporary layer.
     372        Main.map.mapView.repaint();
     373    }
     374
     375    /**
     376     * Return a list of all objects in the active/last selection, respecting the different
    298377     * modifier.
    299378     *
    300379     * @param alt Whether the alt key was pressed, which means select all
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    307386
    308387        // whether user only clicked, not dragged.
    309388        boolean clicked = false;
    310         Rectangle bounding = lasso.getBounds();
     389        Rectangle bounding = selectionResult.getBounds();
    311390        if (bounding.height <= 2 && bounding.width <= 2) {
    312391            clicked = true;
    313392        }
    314393
    315394        if (clicked) {
    316             Point center = new Point(lasso.xpoints[0], lasso.ypoints[0]);
     395            Point center = new Point(selectionResult.xpoints[0], selectionResult.ypoints[0]);
    317396            OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive.isSelectablePredicate, false);
    318397            if (osm != null) {
    319398                selection.add(osm);
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    321400        } else {
    322401            // nodes
    323402            for (Node n : nc.getCurrentDataSet().getNodes()) {
    324                 if (n.isSelectable() && lasso.contains(nc.getPoint2D(n))) {
     403                if (n.isSelectable() && selectionResult.contains(nc.getPoint2D(n))) {
    325404                    selection.add(n);
    326405                }
    327406            }
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    333412                }
    334413                if (alt) {
    335414                    for (Node n : w.getNodes()) {
    336                         if (!n.isIncomplete() && lasso.contains(nc.getPoint2D(n))) {
     415                        if (!n.isIncomplete() && selectionResult.contains(nc.getPoint2D(n))) {
    337416                            selection.add(w);
    338417                            break;
    339418                        }
    public class SelectionManager implements MouseListener, MouseMotionListener, Pro  
    341420                } else {
    342421                    boolean allIn = true;
    343422                    for (Node n : w.getNodes()) {
    344                         if (!n.isIncomplete() && !lasso.contains(nc.getPoint(n))) {
     423                        if (!n.isIncomplete() && !selectionResult.contains(nc.getPoint(n))) {
    345424                            allIn = false;
    346425                            break;
    347426                        }