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 javax.swing.Action; 012 013import org.openstreetmap.josm.actions.JosmAction; 014import org.openstreetmap.josm.gui.MainApplication; 015import org.openstreetmap.josm.gui.MapFrame; 016import org.openstreetmap.josm.gui.layer.Layer; 017import org.openstreetmap.josm.gui.layer.OsmDataLayer; 018import org.openstreetmap.josm.spi.preferences.Config; 019import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent; 020import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener; 021import org.openstreetmap.josm.tools.ImageProvider; 022import org.openstreetmap.josm.tools.Logging; 023import org.openstreetmap.josm.tools.Shortcut; 024 025/** 026 * A class implementing MapMode is able to be selected as an mode for map editing. 027 * As example scrolling the map is a MapMode, connecting Nodes to new Ways is another. 028 * 029 * MapModes should register/deregister all necessary listeners on the map's view control. 030 */ 031public abstract class MapMode extends JosmAction implements MouseListener, MouseMotionListener, PreferenceChangedListener { 032 protected final Cursor cursor; 033 protected boolean ctrl; 034 protected boolean alt; 035 protected boolean shift; 036 037 /** 038 * Constructor for mapmodes without a menu 039 * @param name the action's text 040 * @param iconName icon filename in {@code mapmode} directory 041 * @param tooltip a longer description of the action that will be displayed in the tooltip. 042 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. 043 * @param cursor cursor displayed when map mode is active 044 * @since 11713 045 */ 046 protected MapMode(String name, String iconName, String tooltip, Shortcut shortcut, Cursor cursor) { 047 super(name, "mapmode/"+iconName, tooltip, shortcut, false); 048 this.cursor = cursor; 049 putValue("active", Boolean.FALSE); 050 } 051 052 /** 053 * Constructor for mapmodes with a menu (no shortcut will be registered) 054 * @param name the action's text 055 * @param iconName icon filename in {@code mapmode} directory 056 * @param tooltip a longer description of the action that will be displayed in the tooltip. 057 * @param cursor cursor displayed when map mode is active 058 * @since 11713 059 */ 060 protected MapMode(String name, String iconName, String tooltip, Cursor cursor) { 061 putValue(NAME, name); 062 new ImageProvider("mapmode", iconName).getResource().attachImageIcon(this); 063 putValue(SHORT_DESCRIPTION, tooltip); 064 this.cursor = cursor; 065 } 066 067 /** 068 * Makes this map mode active. 069 */ 070 public void enterMode() { 071 Logging.debug("Entering map mode: {0}", getValue(Action.NAME)); 072 putValue("active", Boolean.TRUE); 073 Config.getPref().addPreferenceChangeListener(this); 074 readPreferences(); 075 MainApplication.getMap().mapView.setNewCursor(cursor, this); 076 MainApplication.getMap().statusLine.setAutoLength(true); 077 updateStatusLine(); 078 } 079 080 /** 081 * Makes this map mode inactive. 082 */ 083 public void exitMode() { 084 Logging.debug("Exiting map mode: {0}", getValue(Action.NAME)); 085 putValue("active", Boolean.FALSE); 086 Config.getPref().removePreferenceChangeListener(this); 087 MainApplication.getMap().mapView.resetCursor(this); 088 MainApplication.getMap().statusLine.setAutoLength(true); 089 } 090 091 protected void updateStatusLine() { 092 MapFrame map = MainApplication.getMap(); 093 if (map != null && map.statusLine != null) { 094 map.statusLine.setHelpText(getModeHelpText()); 095 map.statusLine.repaint(); 096 } 097 } 098 099 /** 100 * Returns a short translated help message describing how this map mode can be used, to be displayed in status line. 101 * @return a short translated help message describing how this map mode can be used 102 */ 103 public String getModeHelpText() { 104 return ""; 105 } 106 107 protected void readPreferences() {} 108 109 /** 110 * Call selectMapMode(this) on the parent mapFrame. 111 */ 112 @Override 113 public void actionPerformed(ActionEvent e) { 114 if (MainApplication.isDisplayingMapView()) { 115 MainApplication.getMap().selectMapMode(this); 116 } 117 } 118 119 /** 120 * Determines if layer {@code l} is supported by this map mode. 121 * By default, all tools will work with all layers. 122 * Can be overwritten to require a special type of layer 123 * @param l layer 124 * @return {@code true} if the layer is supported by this map mode 125 */ 126 public boolean layerIsSupported(Layer l) { 127 return l != null; 128 } 129 130 /** 131 * Update internal ctrl, alt, shift mask from given input event. 132 * @param e input event 133 */ 134 protected void updateKeyModifiers(InputEvent e) { 135 updateKeyModifiersEx(e.getModifiersEx()); 136 } 137 138 /** 139 * Update internal ctrl, alt, shift mask from given mouse event. 140 * @param e mouse event 141 */ 142 protected void updateKeyModifiers(MouseEvent e) { 143 updateKeyModifiersEx(e.getModifiersEx()); 144 } 145 146 /** 147 * Update internal ctrl, alt, shift mask from given action event. 148 * @param e action event 149 * @since 12526 150 */ 151 protected void updateKeyModifiers(ActionEvent e) { 152 // ActionEvent does not have a getModifiersEx() method like other events :( 153 updateKeyModifiersEx(mapOldModifiers(e.getModifiers())); 154 } 155 156 /** 157 * Update internal ctrl, alt, shift mask from given extended modifiers mask. 158 * @param modifiers event extended modifiers mask 159 * @since 12517 160 */ 161 protected void updateKeyModifiersEx(int modifiers) { 162 ctrl = (modifiers & InputEvent.CTRL_DOWN_MASK) != 0; 163 alt = (modifiers & (InputEvent.ALT_DOWN_MASK | InputEvent.ALT_GRAPH_DOWN_MASK)) != 0; 164 shift = (modifiers & InputEvent.SHIFT_DOWN_MASK) != 0; 165 } 166 167 /** 168 * Map old (pre jdk 1.4) modifiers to extended modifiers (only for Ctrl, Alt, Shift). 169 * @param modifiers old modifiers 170 * @return extended modifiers 171 */ 172 @SuppressWarnings("deprecation") 173 private static int mapOldModifiers(int modifiers) { 174 if ((modifiers & InputEvent.CTRL_MASK) != 0) { 175 modifiers |= InputEvent.CTRL_DOWN_MASK; 176 } 177 if ((modifiers & InputEvent.ALT_MASK) != 0) { 178 modifiers |= InputEvent.ALT_DOWN_MASK; 179 } 180 if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 181 modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK; 182 } 183 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 184 modifiers |= InputEvent.SHIFT_DOWN_MASK; 185 } 186 187 return modifiers; 188 } 189 190 protected void requestFocusInMapView() { 191 if (isEnabled()) { 192 // request focus in order to enable the expected keyboard shortcuts (see #8710) 193 MainApplication.getMap().mapView.requestFocus(); 194 } 195 } 196 197 @Override 198 public void mouseReleased(MouseEvent e) { 199 requestFocusInMapView(); 200 } 201 202 @Override 203 public void mouseExited(MouseEvent e) { 204 // Do nothing 205 } 206 207 @Override 208 public void mousePressed(MouseEvent e) { 209 requestFocusInMapView(); 210 } 211 212 @Override 213 public void mouseClicked(MouseEvent e) { 214 // Do nothing 215 } 216 217 @Override 218 public void mouseEntered(MouseEvent e) { 219 // Do nothing 220 } 221 222 @Override 223 public void mouseMoved(MouseEvent e) { 224 // Do nothing 225 } 226 227 @Override 228 public void mouseDragged(MouseEvent e) { 229 // Do nothing 230 } 231 232 @Override 233 public void preferenceChanged(PreferenceChangeEvent e) { 234 readPreferences(); 235 } 236 237 /** 238 * Determines if the given layer is a data layer that can be modified. 239 * Useful for {@link #layerIsSupported(Layer)} implementations. 240 * @param l layer 241 * @return {@code true} if the given layer is a data layer that can be modified 242 * @since 13434 243 */ 244 protected boolean isEditableDataLayer(Layer l) { 245 return l instanceof OsmDataLayer && !((OsmDataLayer) l).isLocked(); 246 } 247}