001// License: GPL. For details, see Readme.txt file.
002package org.openstreetmap.gui.jmapviewer;
003
004import java.awt.Point;
005import java.awt.event.MouseEvent;
006import java.awt.event.MouseListener;
007import java.awt.event.MouseMotionListener;
008import java.awt.event.MouseWheelEvent;
009import java.awt.event.MouseWheelListener;
010import java.util.Locale;
011
012/**
013 * Default map controller which implements map moving by pressing the right
014 * mouse button and zooming by double click or by mouse wheel.
015 *
016 * @author Jan Peter Stotz
017 *
018 */
019public class DefaultMapController extends JMapController implements MouseListener, MouseMotionListener,
020MouseWheelListener {
021
022    private static final int MOUSE_BUTTONS_MASK = MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK
023    | MouseEvent.BUTTON2_DOWN_MASK;
024
025    private static final int MAC_MOUSE_BUTTON3_MASK = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
026
027    private Point lastDragPoint;
028
029    private boolean isMoving;
030
031    private boolean movementEnabled = true;
032
033    private int movementMouseButton = MouseEvent.BUTTON3;
034    private int movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK;
035
036    private boolean wheelZoomEnabled = true;
037    private boolean doubleClickZoomEnabled = true;
038
039    /**
040     * Constructs a new {@code DefaultMapController}.
041     * @param map map panel
042     */
043    public DefaultMapController(JMapViewer map) {
044        super(map);
045    }
046
047    @Override
048    public void mouseDragged(MouseEvent e) {
049        if (!movementEnabled || !isMoving)
050            return;
051        // Is only the selected mouse button pressed?
052        if ((e.getModifiersEx() & MOUSE_BUTTONS_MASK) == movementMouseButtonMask
053                || (isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) {
054            Point p = e.getPoint();
055            if (lastDragPoint != null) {
056                int diffx = lastDragPoint.x - p.x;
057                int diffy = lastDragPoint.y - p.y;
058                map.moveMap(diffx, diffy);
059            }
060            lastDragPoint = p;
061        }
062    }
063
064    @Override
065    public void mouseClicked(MouseEvent e) {
066        if (doubleClickZoomEnabled && e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
067            map.zoomIn(e.getPoint());
068        }
069    }
070
071    @Override
072    public void mousePressed(MouseEvent e) {
073        if (e.getButton() == movementMouseButton || (isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) {
074            lastDragPoint = null;
075            isMoving = true;
076        }
077    }
078
079    @Override
080    public void mouseReleased(MouseEvent e) {
081        if (e.getButton() == movementMouseButton || (isPlatformOsx() && e.getButton() == MouseEvent.BUTTON1)) {
082            lastDragPoint = null;
083            isMoving = false;
084        }
085    }
086
087    @Override
088    public void mouseWheelMoved(MouseWheelEvent e) {
089        if (wheelZoomEnabled) {
090            int rotation = JMapViewer.zoomReverseWheel ? -e.getWheelRotation() : e.getWheelRotation();
091            map.setZoom(map.getZoom() - rotation, e.getPoint());
092        }
093    }
094
095    /**
096     * Determines if the map pane is allowed to be moved using the mouse
097     * @return {@code true} to allow the map pane to be moved using the mouse
098     */
099    public boolean isMovementEnabled() {
100        return movementEnabled;
101    }
102
103    /**
104     * Enables or disables that the map pane can be moved using the mouse.
105     *
106     * @param movementEnabled {@code true} to allow the map pane to be moved using the mouse
107     */
108    public void setMovementEnabled(boolean movementEnabled) {
109        this.movementEnabled = movementEnabled;
110    }
111
112    public int getMovementMouseButton() {
113        return movementMouseButton;
114    }
115
116    /**
117     * Sets the mouse button that is used for moving the map. Possible values are:
118     * <ul>
119     * <li>{@link MouseEvent#BUTTON1} (left mouse button)</li>
120     * <li>{@link MouseEvent#BUTTON2} (middle mouse button)</li>
121     * <li>{@link MouseEvent#BUTTON3} (right mouse button)</li>
122     * </ul>
123     *
124     * @param movementMouseButton the mouse button that is used for moving the map
125     */
126    public void setMovementMouseButton(int movementMouseButton) {
127        this.movementMouseButton = movementMouseButton;
128        switch (movementMouseButton) {
129            case MouseEvent.BUTTON1:
130                movementMouseButtonMask = MouseEvent.BUTTON1_DOWN_MASK;
131                break;
132            case MouseEvent.BUTTON2:
133                movementMouseButtonMask = MouseEvent.BUTTON2_DOWN_MASK;
134                break;
135            case MouseEvent.BUTTON3:
136                movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK;
137                break;
138            default:
139                throw new RuntimeException("Unsupported button");
140        }
141    }
142
143    public boolean isWheelZoomEnabled() {
144        return wheelZoomEnabled;
145    }
146
147    public void setWheelZoomEnabled(boolean wheelZoomEnabled) {
148        this.wheelZoomEnabled = wheelZoomEnabled;
149    }
150
151    public boolean isDoubleClickZoomEnabled() {
152        return doubleClickZoomEnabled;
153    }
154
155    public void setDoubleClickZoomEnabled(boolean doubleClickZoomEnabled) {
156        this.doubleClickZoomEnabled = doubleClickZoomEnabled;
157    }
158
159    @Override
160    public void mouseEntered(MouseEvent e) {
161        // do nothing
162    }
163
164    @Override
165    public void mouseExited(MouseEvent e) {
166        // do nothing
167    }
168
169    @Override
170    public void mouseMoved(MouseEvent e) {
171        // Mac OSX simulates with  ctrl + mouse 1  the second mouse button hence no dragging events get fired.
172        //
173        if (isPlatformOsx()) {
174            if (!movementEnabled || !isMoving)
175                return;
176            // Is only the selected mouse button pressed?
177            if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) {
178                Point p = e.getPoint();
179                if (lastDragPoint != null) {
180                    int diffx = lastDragPoint.x - p.x;
181                    int diffy = lastDragPoint.y - p.y;
182                    map.moveMap(diffx, diffy);
183                }
184                lastDragPoint = p;
185            }
186        }
187    }
188
189    /**
190     * Replies true if we are currently running on OSX
191     *
192     * @return true if we are currently running on OSX
193     */
194    public static boolean isPlatformOsx() {
195        String os = System.getProperty("os.name");
196        return os != null && os.toLowerCase(Locale.ENGLISH).startsWith("mac os x");
197    }
198}