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

Last change on this file since 10711 was 10601, checked in by Don-vip, 8 years ago

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

  • Property svn:eol-style set to native
File size: 16.8 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 try {
297 future.get();
298 } catch (InterruptedException | ExecutionException | CancellationException e) {
299 Main.error(e);
300 return;
301 }
302 monitor.close();
303 });
304 }
305
306 /**
307 * Override in subclasses to init the enabled state of an action when it is
308 * created. Default behaviour is to call {@link #updateEnabledState()}
309 *
310 * @see #updateEnabledState()
311 * @see #updateEnabledState(Collection)
312 */
313 protected void initEnabledState() {
314 updateEnabledState();
315 }
316
317 /**
318 * Override in subclasses to update the enabled state of the action when
319 * something in the JOSM state changes, i.e. when a layer is removed or added.
320 *
321 * See {@link #updateEnabledState(Collection)} to respond to changes in the collection
322 * of selected primitives.
323 *
324 * Default behavior is empty.
325 *
326 * @see #updateEnabledState(Collection)
327 * @see #initEnabledState()
328 * @see #listenToLayerChange()
329 */
330 protected void updateEnabledState() {
331 }
332
333 /**
334 * Override in subclasses to update the enabled state of the action if the
335 * collection of selected primitives changes. This method is called with the
336 * new selection.
337 *
338 * @param selection the collection of selected primitives; may be empty, but not null
339 *
340 * @see #updateEnabledState()
341 * @see #initEnabledState()
342 * @see #listenToSelectionChange()
343 */
344 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
345 }
346
347 /**
348 * Updates enabled state according to primitives currently selected in edit data set, if any.
349 * Can be called in {@link #updateEnabledState()} implementations.
350 * @since 10409
351 */
352 protected final void updateEnabledStateOnCurrentSelection() {
353 DataSet ds = getLayerManager().getEditDataSet();
354 if (ds == null) {
355 setEnabled(false);
356 } else {
357 updateEnabledState(ds.getSelected());
358 }
359 }
360
361 /**
362 * Adapter for layer change events. Runs updateEnabledState() whenever the active layer changed.
363 */
364 protected class LayerChangeAdapter implements LayerChangeListener {
365 @Override
366 public void layerAdded(LayerAddEvent e) {
367 updateEnabledState();
368 }
369
370 @Override
371 public void layerRemoving(LayerRemoveEvent e) {
372 updateEnabledState();
373 }
374
375 @Override
376 public void layerOrderChanged(LayerOrderChangeEvent e) {
377 updateEnabledState();
378 }
379
380 @Override
381 public String toString() {
382 return "LayerChangeAdapter [" + JosmAction.this.toString() + ']';
383 }
384 }
385
386 /**
387 * Adapter for layer change events. Runs updateEnabledState() whenever the active layer changed.
388 */
389 protected class ActiveLayerChangeAdapter implements ActiveLayerChangeListener {
390 @Override
391 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
392 updateEnabledState();
393 }
394
395 @Override
396 public String toString() {
397 return "ActiveLayerChangeAdapter [" + JosmAction.this.toString() + ']';
398 }
399 }
400
401 /**
402 * Adapter for selection change events. Runs updateEnabledState() whenever the selection changed.
403 */
404 protected class SelectionChangeAdapter implements SelectionChangedListener {
405 @Override
406 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
407 updateEnabledState(newSelection);
408 }
409
410 @Override
411 public String toString() {
412 return "SelectionChangeAdapter [" + JosmAction.this.toString() + ']';
413 }
414 }
415}
Note: See TracBrowser for help on using the repository browser.