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            map.setZoom(map.getZoom() - e.getWheelRotation(), e.getPoint());
086        }
087    }
088
089    public boolean isMovementEnabled() {
090        return movementEnabled;
091    }
092
093    /**
094     * Enables or disables that the map pane can be moved using the mouse.
095     *
096     * @param movementEnabled {@code true} to allow the map pane to be moved using the mouse
097     */
098    public void setMovementEnabled(boolean movementEnabled) {
099        this.movementEnabled = movementEnabled;
100    }
101
102    public int getMovementMouseButton() {
103        return movementMouseButton;
104    }
105
106    /**
107     * Sets the mouse button that is used for moving the map. Possible values are:
108     * <ul>
109     * <li>{@link MouseEvent#BUTTON1} (left mouse button)</li>
110     * <li>{@link MouseEvent#BUTTON2} (middle mouse button)</li>
111     * <li>{@link MouseEvent#BUTTON3} (right mouse button)</li>
112     * </ul>
113     *
114     * @param movementMouseButton the mouse button that is used for moving the map
115     */
116    public void setMovementMouseButton(int movementMouseButton) {
117        this.movementMouseButton = movementMouseButton;
118        switch (movementMouseButton) {
119            case MouseEvent.BUTTON1:
120                movementMouseButtonMask = MouseEvent.BUTTON1_DOWN_MASK;
121                break;
122            case MouseEvent.BUTTON2:
123                movementMouseButtonMask = MouseEvent.BUTTON2_DOWN_MASK;
124                break;
125            case MouseEvent.BUTTON3:
126                movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK;
127                break;
128            default:
129                throw new RuntimeException("Unsupported button");
130        }
131    }
132
133    public boolean isWheelZoomEnabled() {
134        return wheelZoomEnabled;
135    }
136
137    public void setWheelZoomEnabled(boolean wheelZoomEnabled) {
138        this.wheelZoomEnabled = wheelZoomEnabled;
139    }
140
141    public boolean isDoubleClickZoomEnabled() {
142        return doubleClickZoomEnabled;
143    }
144
145    public void setDoubleClickZoomEnabled(boolean doubleClickZoomEnabled) {
146        this.doubleClickZoomEnabled = doubleClickZoomEnabled;
147    }
148
149    @Override
150    public void mouseEntered(MouseEvent e) {
151    }
152
153    @Override
154    public void mouseExited(MouseEvent e) {
155    }
156
157    @Override
158    public void mouseMoved(MouseEvent e) {
159        // Mac OSX simulates with  ctrl + mouse 1  the second mouse button hence no dragging events get fired.
160        //
161        if (isPlatformOsx()) {
162            if (!movementEnabled || !isMoving)
163                return;
164            // Is only the selected mouse button pressed?
165            if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) {
166                Point p = e.getPoint();
167                if (lastDragPoint != null) {
168                    int diffx = lastDragPoint.x - p.x;
169                    int diffy = lastDragPoint.y - p.y;
170                    map.moveMap(diffx, diffy);
171                }
172                lastDragPoint = p;
173            }
174
175        }
176
177    }
178
179    /**
180     * Replies true if we are currently running on OSX
181     *
182     * @return true if we are currently running on OSX
183     */
184    public static boolean isPlatformOsx() {
185        String os = System.getProperty("os.name");
186        return os != null && os.toLowerCase().startsWith("mac os x");
187    }
188}