source: josm/trunk/src/org/openstreetmap/josm/actions/JosmAction.java@ 10428

Last change on this file since 10428 was 10428, checked in by stoecker, 8 years ago

see #9995 - patch mainly by strump - improve HIDPI behaviour

  • Property svn:eol-style set to native
File size: 16.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.KeyEvent;
7import java.util.Collection;
8import java.util.concurrent.CancellationException;
9import java.util.concurrent.ExecutionException;
10import java.util.concurrent.Future;
11
12import javax.swing.AbstractAction;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.data.SelectionChangedListener;
16import org.openstreetmap.josm.data.osm.DataSet;
17import org.openstreetmap.josm.data.osm.OsmPrimitive;
18import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
19import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
20import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
21import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
22import org.openstreetmap.josm.gui.layer.MainLayerManager;
23import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
24import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
25import org.openstreetmap.josm.gui.layer.OsmDataLayer;
26import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
27import org.openstreetmap.josm.tools.Destroyable;
28import org.openstreetmap.josm.tools.ImageProvider;
29import org.openstreetmap.josm.tools.Shortcut;
30
31/**
32 * Base class helper for all Actions in JOSM. Just to make the life easier.
33 *
34 * This action allows you to set up an icon, a tooltip text, a globally registered shortcut, register it in the main toolbar and set up
35 * layer/selection listeners that call {@link #updateEnabledState()} whenever the global context is changed.
36 *
37 * A JosmAction can register a {@link LayerChangeListener} and a {@link SelectionChangedListener}. Upon
38 * a layer change event or a selection change event it invokes {@link #updateEnabledState()}.
39 * Subclasses can override {@link #updateEnabledState()} in order to update the {@link #isEnabled()}-state
40 * of a JosmAction depending on the {@link #getCurrentDataSet()} and the current layers
41 * (see also {@link #getEditLayer()}).
42 *
43 * destroy() from interface Destroyable is called e.g. for MapModes, when the last layer has
44 * been removed and so the mapframe will be destroyed. For other JosmActions, destroy() may never
45 * be called (currently).
46 *
47 * @author imi
48 */
49public abstract class JosmAction extends AbstractAction implements Destroyable {
50
51 protected transient Shortcut sc;
52 private transient LayerChangeAdapter layerChangeAdapter;
53 private transient ActiveLayerChangeAdapter activeLayerChangeAdapter;
54 private transient SelectionChangeAdapter selectionChangeAdapter;
55
56 /**
57 * Constructs a {@code JosmAction}.
58 *
59 * @param name the action's text as displayed on the menu (if it is added to a menu)
60 * @param icon the icon to use
61 * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note
62 * that html is not supported for menu actions on some platforms.
63 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
64 * do want a shortcut, remember you can always register it with group=none, so you
65 * won't be assigned a shortcut unless the user configures one. If you pass null here,
66 * the user CANNOT configure a shortcut for your action.
67 * @param registerInToolbar register this action for the toolbar preferences?
68 * @param toolbarId identifier for the toolbar preferences. The iconName is used, if this parameter is null
69 * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
70 */
71 public JosmAction(String name, ImageProvider icon, String tooltip, Shortcut shortcut, boolean registerInToolbar,
72 String toolbarId, boolean installAdapters) {
73 super(name);
74 if (icon != null)
75 icon.getResource().attachImageIcon(this, true);
76 setHelpId();
77 sc = shortcut;
78 if (sc != null) {
79 Main.registerActionShortcut(this, sc);
80 }
81 setTooltip(tooltip);
82 if (getValue("toolbar") == null) {
83 putValue("toolbar", toolbarId);
84 }
85 if (registerInToolbar && Main.toolbar != null) {
86 Main.toolbar.register(this);
87 }
88 if (installAdapters) {
89 installAdapters();
90 }
91 }
92
93 /**
94 * The new super for all actions.
95 *
96 * Use this super constructor to setup your action.
97 *
98 * @param name the action's text as displayed on the menu (if it is added to a menu)
99 * @param iconName the filename of the icon to use
100 * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note
101 * that html is not supported for menu actions on some platforms.
102 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
103 * do want a shortcut, remember you can always register it with group=none, so you
104 * won't be assigned a shortcut unless the user configures one. If you pass null here,
105 * the user CANNOT configure a shortcut for your action.
106 * @param registerInToolbar register this action for the toolbar preferences?
107 * @param toolbarId identifier for the toolbar preferences. The iconName is used, if this parameter is null
108 * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
109 */
110 public JosmAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean registerInToolbar,
111 String toolbarId, boolean installAdapters) {
112 this(name, iconName == null ? null : new ImageProvider(iconName), tooltip, shortcut, registerInToolbar,
113 toolbarId == null ? iconName : toolbarId, installAdapters);
114 }
115
116 /**
117 * Constructs a new {@code JosmAction}.
118 *
119 * Use this super constructor to setup your action.
120 *
121 * @param name the action's text as displayed on the menu (if it is added to a menu)
122 * @param iconName the filename of the icon to use
123 * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note
124 * that html is not supported for menu actions on some platforms.
125 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
126 * do want a shortcut, remember you can always register it with group=none, so you
127 * won't be assigned a shortcut unless the user configures one. If you pass null here,
128 * the user CANNOT configure a shortcut for your action.
129 * @param registerInToolbar register this action for the toolbar preferences?
130 * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
131 */
132 public JosmAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean registerInToolbar, boolean installAdapters) {
133 this(name, iconName, tooltip, shortcut, registerInToolbar, null, installAdapters);
134 }
135
136 /**
137 * Constructs a new {@code JosmAction}.
138 *
139 * Use this super constructor to setup your action.
140 *
141 * @param name the action's text as displayed on the menu (if it is added to a menu)
142 * @param iconName the filename of the icon to use
143 * @param tooltip a longer description of the action that will be displayed in the tooltip. Please note
144 * that html is not supported for menu actions on some platforms.
145 * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
146 * do want a shortcut, remember you can always register it with group=none, so you
147 * won't be assigned a shortcut unless the user configures one. If you pass null here,
148 * the user CANNOT configure a shortcut for your action.
149 * @param registerInToolbar register this action for the toolbar preferences?
150 */
151 public JosmAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean registerInToolbar) {
152 this(name, iconName, tooltip, shortcut, registerInToolbar, null, true);
153 }
154
155 /**
156 * Constructs a new {@code JosmAction}.
157 */
158 public JosmAction() {
159 this(true);
160 }
161
162 /**
163 * Constructs a new {@code JosmAction}.
164 *
165 * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
166 */
167 public JosmAction(boolean installAdapters) {
168 setHelpId();
169 if (installAdapters) {
170 installAdapters();
171 }
172 }
173
174 /**
175 * Installs the listeners to this action.
176 * <p>
177 * This should either never be called or only called in the constructor of this action.
178 * <p>
179 * All registered adapters should be removed in {@link #destroy()}
180 */
181 protected void installAdapters() {
182 // make this action listen to layer change and selection change events
183 if (listenToLayerChange()) {
184 layerChangeAdapter = new LayerChangeAdapter();
185 activeLayerChangeAdapter = new ActiveLayerChangeAdapter();
186 getLayerManager().addLayerChangeListener(layerChangeAdapter);
187 getLayerManager().addActiveLayerChangeListener(activeLayerChangeAdapter);
188 }
189 if (listenToSelectionChange()) {
190 selectionChangeAdapter = new SelectionChangeAdapter();
191 DataSet.addSelectionListener(selectionChangeAdapter);
192 }
193 initEnabledState();
194 }
195
196 /**
197 * Overwrite this if {@link #updateEnabledState()} should be called when the active / availabe layers change. Default is true.
198 * @return <code>true</code> if a {@link LayerChangeListener} and a {@link ActiveLayerChangeListener} should be registered.
199 * @since 10353
200 */
201 protected boolean listenToLayerChange() {
202 return true;
203 }
204
205 /**
206 * Overwrite this if {@link #updateEnabledState()} should be called when the selection changed. Default is true.
207 * @return <code>true</code> if a {@link SelectionChangedListener} should be registered.
208 * @since 10353
209 */
210 protected boolean listenToSelectionChange() {
211 return true;
212 }
213
214 @Override
215 public void destroy() {
216 if (sc != null) {
217 Main.unregisterActionShortcut(this);
218 }
219 if (layerChangeAdapter != null) {
220 getLayerManager().removeLayerChangeListener(layerChangeAdapter);
221 getLayerManager().removeActiveLayerChangeListener(activeLayerChangeAdapter);
222 }
223 if (selectionChangeAdapter != null) {
224 DataSet.removeSelectionListener(selectionChangeAdapter);
225 }
226 }
227
228 private void setHelpId() {
229 String helpId = "Action/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
230 if (helpId.endsWith("Action")) {
231 helpId = helpId.substring(0, helpId.length()-6);
232 }
233 putValue("help", helpId);
234 }
235
236 /**
237 * Returns the shortcut for this action.
238 * @return the shortcut for this action, or "No shortcut" if none is defined
239 */
240 public Shortcut getShortcut() {
241 if (sc == null) {
242 sc = Shortcut.registerShortcut("core:none", tr("No Shortcut"), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE);
243 // as this shortcut is shared by all action that don't want to have a shortcut,
244 // we shouldn't allow the user to change it...
245 // this is handled by special name "core:none"
246 }
247 return sc;
248 }
249
250 /**
251 * Sets the tooltip text of this action.
252 * @param tooltip The text to display in tooltip. Can be {@code null}
253 */
254 public final void setTooltip(String tooltip) {
255 if (tooltip != null) {
256 putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc));
257 }
258 }
259
260 /**
261 * Gets the layer manager used for this action. Defaults to the main layer manager but you can overwrite this.
262 * <p>
263 * The layer manager must be available when {@link #installAdapters()} is called and must not change.
264 *
265 * @return The layer manager.
266 * @since 10353
267 */
268 public MainLayerManager getLayerManager() {
269 return Main.getLayerManager();
270 }
271
272 /**
273 * Replies the current edit layer
274 *
275 * @return the current edit layer. null, if no edit layer exists
276 * @deprecated Use {@link #getLayerManager()}.getEditLayer() instead. To be removed in end of 2016.
277 */
278 @Deprecated
279 public static OsmDataLayer getEditLayer() {
280 return Main.getLayerManager().getEditLayer();
281 }
282
283 /**
284 * Replies the current dataset.
285 *
286 * @return the current dataset. null, if no current dataset exists
287 * @deprecated Use {@link #getLayerManager()}.getEditDataSet() instead. To be removed in end of 2016.
288 */
289 @Deprecated
290 public static DataSet getCurrentDataSet() {
291 return Main.getLayerManager().getEditDataSet();
292 }
293
294 protected static void waitFuture(final Future<?> future, final PleaseWaitProgressMonitor monitor) {
295 Main.worker.submit(
296 new Runnable() {
297 @Override
298 public void run() {
299 try {
300 future.get();
301 } catch (InterruptedException | ExecutionException | CancellationException e) {
302 Main.error(e);
303 return;
304 }
305 monitor.close();
306 }
307 }
308 );
309 }
310
311 /**
312 * Override in subclasses to init the enabled state of an action when it is
313 * created. Default behaviour is to call {@link #updateEnabledState()}
314 *
315 * @see #updateEnabledState()
316 * @see #updateEnabledState(Collection)
317 */
318 protected void initEnabledState() {
319 updateEnabledState();
320 }
321
322 /**
323 * Override in subclasses to update the enabled state of the action when
324 * something in the JOSM state changes, i.e. when a layer is removed or added.
325 *
326 * See {@link #updateEnabledState(Collection)} to respond to changes in the collection
327 * of selected primitives.
328 *
329 * Default behavior is empty.
330 *
331 * @see #updateEnabledState(Collection)
332 * @see #initEnabledState()
333 * @see #listenToLayerChange()
334 */
335 protected void updateEnabledState() {
336 }
337
338 /**
339 * Override in subclasses to update the enabled state of the action if the
340 * collection of selected primitives changes. This method is called with the
341 * new selection.
342 *
343 * @param selection the collection of selected primitives; may be empty, but not null
344 *
345 * @see #updateEnabledState()
346 * @see #initEnabledState()
347 * @see #listenToSelectionChange()
348 */
349 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
350 }
351
352 /**
353 * Updates enabled state according to primitives currently selected in edit data set, if any.
354 * Can be called in {@link #updateEnabledState()} implementations.
355 * @since 10409
356 */
357 protected final void updateEnabledStateOnCurrentSelection() {
358 DataSet ds = getLayerManager().getEditDataSet();
359 if (ds == null) {
360 setEnabled(false);
361 } else {
362 updateEnabledState(ds.getSelected());
363 }
364 }
365
366 /**
367 * Adapter for layer change events. Runs updateEnabledState() whenever the active layer changed.
368 */
369 protected class LayerChangeAdapter implements LayerChangeListener {
370 @Override
371 public void layerAdded(LayerAddEvent e) {
372 updateEnabledState();
373 }
374
375 @Override
376 public void layerRemoving(LayerRemoveEvent e) {
377 updateEnabledState();
378 }
379
380 @Override
381 public void layerOrderChanged(LayerOrderChangeEvent e) {
382 updateEnabledState();
383 }
384
385 @Override
386 public String toString() {
387 return "LayerChangeAdapter [" + JosmAction.this.toString() + ']';
388 }
389 }
390
391 /**
392 * Adapter for layer change events. Runs updateEnabledState() whenever the active layer changed.
393 */
394 protected class ActiveLayerChangeAdapter implements ActiveLayerChangeListener {
395 @Override
396 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
397 updateEnabledState();
398 }
399
400 @Override
401 public String toString() {
402 return "ActiveLayerChangeAdapter [" + JosmAction.this.toString() + ']';
403 }
404 }
405
406 /**
407 * Adapter for selection change events. Runs updateEnabledState() whenever the selection changed.
408 */
409 protected class SelectionChangeAdapter implements SelectionChangedListener {
410 @Override
411 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
412 updateEnabledState(newSelection);
413 }
414
415 @Override
416 public String toString() {
417 return "SelectionChangeAdapter [" + JosmAction.this.toString() + ']';
418 }
419 }
420}
Note: See TracBrowser for help on using the repository browser.