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