001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions.mapmode;
003
004import java.awt.Cursor;
005import java.awt.event.ActionEvent;
006import java.awt.event.InputEvent;
007import java.awt.event.MouseEvent;
008import java.awt.event.MouseListener;
009import java.awt.event.MouseMotionListener;
010
011import org.openstreetmap.josm.Main;
012import org.openstreetmap.josm.actions.JosmAction;
013import org.openstreetmap.josm.gui.MapFrame;
014import org.openstreetmap.josm.gui.layer.Layer;
015import org.openstreetmap.josm.tools.ImageProvider;
016import org.openstreetmap.josm.tools.Shortcut;
017import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
018import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
019
020/**
021 * A class implementing MapMode is able to be selected as an mode for map editing.
022 * As example scrolling the map is a MapMode, connecting Nodes to new Ways is another.
023 *
024 * MapModes should register/deregister all necessary listeners on the map's view control.
025 */
026public abstract class MapMode extends JosmAction implements MouseListener, MouseMotionListener, PreferenceChangedListener {
027    protected final Cursor cursor;
028    protected boolean ctrl;
029    protected boolean alt;
030    protected boolean shift;
031
032    /**
033     * Constructor for mapmodes without a menu
034     * @param name the action's text
035     * @param iconName icon filename in {@code mapmode} directory
036     * @param tooltip  a longer description of the action that will be displayed in the tooltip.
037     * @param shortcut a ready-created shortcut object or null if you don't want a shortcut.
038     * @param mapFrame unused but kept for plugin compatibility. Can be {@code null}
039     * @param cursor cursor displayed when map mode is active
040     */
041    public MapMode(String name, String iconName, String tooltip, Shortcut shortcut, MapFrame mapFrame, Cursor cursor) {
042        super(name, "mapmode/"+iconName, tooltip, shortcut, false);
043        this.cursor = cursor;
044        putValue("active", Boolean.FALSE);
045    }
046
047    /**
048     * Constructor for mapmodes with a menu (no shortcut will be registered)
049     * @param name the action's text
050     * @param iconName icon filename in {@code mapmode} directory
051     * @param tooltip  a longer description of the action that will be displayed in the tooltip.
052     * @param mapFrame unused but kept for plugin compatibility. Can be {@code null}
053     * @param cursor cursor displayed when map mode is active
054     */
055    public MapMode(String name, String iconName, String tooltip, MapFrame mapFrame, Cursor cursor) {
056        putValue(NAME, name);
057        new ImageProvider("mapmode", iconName).getResource().attachImageIcon(this);
058        putValue(SHORT_DESCRIPTION, tooltip);
059        this.cursor = cursor;
060    }
061
062    /**
063     * Makes this map mode active.
064     */
065    public void enterMode() {
066        putValue("active", Boolean.TRUE);
067        Main.pref.addPreferenceChangeListener(this);
068        readPreferences();
069        Main.map.mapView.setNewCursor(cursor, this);
070        updateStatusLine();
071    }
072
073    /**
074     * Makes this map mode inactive.
075     */
076    public void exitMode() {
077        putValue("active", Boolean.FALSE);
078        Main.pref.removePreferenceChangeListener(this);
079        Main.map.mapView.resetCursor(this);
080    }
081
082    protected void updateStatusLine() {
083        Main.map.statusLine.setHelpText(getModeHelpText());
084        Main.map.statusLine.repaint();
085    }
086
087    public String getModeHelpText() {
088        return "";
089    }
090
091    protected void readPreferences() {}
092
093    /**
094     * Call selectMapMode(this) on the parent mapFrame.
095     */
096    @Override
097    public void actionPerformed(ActionEvent e) {
098        if (Main.isDisplayingMapView()) {
099            Main.map.selectMapMode(this);
100        }
101    }
102
103    /**
104     * Determines if layer {@code l} is supported by this map mode.
105     * By default, all tools will work with all layers.
106     * Can be overwritten to require a special type of layer
107     * @param l layer
108     * @return {@code true} if the layer is supported by this map mode
109     */
110    public boolean layerIsSupported(Layer l) {
111        return l != null;
112    }
113
114    protected void updateKeyModifiers(InputEvent e) {
115        updateKeyModifiers(e.getModifiers());
116    }
117
118    protected void updateKeyModifiers(MouseEvent e) {
119        updateKeyModifiers(e.getModifiers());
120    }
121
122    protected void updateKeyModifiers(int modifiers) {
123        ctrl = (modifiers & ActionEvent.CTRL_MASK) != 0;
124        alt = (modifiers & (ActionEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK)) != 0;
125        shift = (modifiers & ActionEvent.SHIFT_MASK) != 0;
126    }
127
128    protected void requestFocusInMapView() {
129        if (isEnabled()) {
130            // request focus in order to enable the expected keyboard shortcuts (see #8710)
131            Main.map.mapView.requestFocus();
132        }
133    }
134
135    @Override
136    public void mouseReleased(MouseEvent e) {
137        requestFocusInMapView();
138    }
139
140    @Override
141    public void mouseExited(MouseEvent e) {
142        // Do nothing
143    }
144
145    @Override
146    public void mousePressed(MouseEvent e) {
147        requestFocusInMapView();
148    }
149
150    @Override
151    public void mouseClicked(MouseEvent e) {
152        // Do nothing
153    }
154
155    @Override
156    public void mouseEntered(MouseEvent e) {
157        // Do nothing
158    }
159
160    @Override
161    public void mouseMoved(MouseEvent e) {
162        // Do nothing
163    }
164
165    @Override
166    public void mouseDragged(MouseEvent e) {
167        // Do nothing
168    }
169
170    @Override
171    public void preferenceChanged(PreferenceChangeEvent e) {
172        readPreferences();
173    }
174}