001 /*
002 * $Id: ActionManager.java,v 1.4 2005/10/13 08:59:55 kleopatra Exp $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021 package org.jdesktop.swingx.action;
022
023 import java.io.PrintStream;
024 import java.util.HashMap;
025 import java.util.Map;
026 import java.util.Set;
027
028 import javax.swing.AbstractAction;
029 import javax.swing.Action;
030
031 /**
032 * The ActionManager manages sets of <code>javax.swing.Action</code>s for an
033 * application. There are convenience methods for getting and setting the state
034 * of the action.
035 * All of these elements have a unique id tag which is used by the ActionManager
036 * to reference the action. This id maps to the <code>Action.ACTION_COMMAND_KEY</code>
037 * on the Action.
038 * <p>
039 * The ActionManager may be used to conveniently register callback methods
040 * on BoundActions.
041 * <p>
042 * A typical use case of the ActionManager is:
043 * <p>
044 * <pre>
045 * ActionManager manager = ActionManager.getInstance();
046 *
047 * // load Actions
048 * manager.addAction(action);
049 *
050 * // Change the state of the action:
051 * manager.setEnabled("new-action", newState);
052 * </pre>
053 *
054 * The ActionManager also supports Actions that can have a selected state
055 * associated with them. These Actions are typically represented by a
056 * JCheckBox or similar widget. For such actions the registered method is
057 * invoked with an additional parameter indicating the selected state of
058 * the widget. For example, for the callback handler:
059 *<p>
060 * <pre>
061 * public class Handler {
062 * public void stateChanged(boolean newState);
063 * }
064 * </pre>
065 * The registration method would look similar:
066 * <pre>
067 * manager.registerCallback("select-action", new Handler(), "stateChanged");
068 * </pre>
069 *<p>
070 * The stateChanged method would be invoked as the selected state of
071 * the widget changed. Additionally if you need to change the selected
072 * state of the Action use the ActionManager method <code>setSelected</code>.
073 * <p>
074 * The <code>ActionContainerFactory</code> uses the managed Actions in the
075 * ActionManager to create
076 * user interface components. For example, to create a JMenu based on an
077 * action-list id:
078 * <pre>
079 * JMenu file = manager.getFactory().createMenu(list);
080 * </pre>
081 *
082 * @see ActionContainerFactory
083 * @see TargetableAction
084 * @see BoundAction
085 * @author Mark Davidson
086 */
087 public class ActionManager {
088
089 // Internal data structures which manage the actions.
090
091 // key: value of ID_ATTR, value instanceof AbstractAction
092 private Map actionMap;
093
094 // Container factory instance for this ActionManager
095 private ActionContainerFactory factory;
096
097 /**
098 * Shared instance of the singleton ActionManager.
099 */
100 private static ActionManager INSTANCE;
101
102 // To enable debugging:
103 // Pass -Ddebug=true to the vm on start up.
104 // or
105 // set System.setProperty("debug", "true"); before constructing this Object
106
107 private static boolean DEBUG = false;
108
109 /**
110 * Creates the action manager. Use this constuctor if the application should
111 * support many ActionManagers. Otherwise, using the getInstance method will
112 * return a singleton.
113 */
114 public ActionManager() {
115 }
116
117 /**
118 * Return the Action Container Factory associated with this ActionManager
119 * instance. Will always return a factory instance.
120 */
121 public ActionContainerFactory getFactory() {
122 if (factory == null) {
123 factory = new ActionContainerFactory(this);
124 }
125 return factory;
126 }
127
128 /**
129 * This method should be used to associate a subclassed ActionContainerFactory
130 * with this ActionManager.
131 */
132 public void setFactory(ActionContainerFactory factory) {
133 this.factory = factory;
134 }
135
136 /**
137 * Return the instance of the ActionManger. If this has not been explicity
138 * set then it will be created.
139 *
140 * @return the ActionManager instance.
141 * @see #setInstance
142 */
143 public static ActionManager getInstance() {
144 if (INSTANCE == null) {
145 INSTANCE = new ActionManager();
146 }
147 return INSTANCE;
148 }
149
150 /**
151 * Sets the ActionManager instance.
152 */
153 public static void setInstance(ActionManager manager) {
154 INSTANCE = manager;
155 }
156
157 /**
158 * Returns the ids for all the managed actions.
159 * <p>
160 * An action id is a unique idenitfier which can
161 * be used to retrieve the corrspondng Action from the ActionManager.
162 * This identifier can also
163 * be used to set the properties of the action through the action
164 * manager like setting the state of the enabled or selected flags.
165 *
166 * @return a set which represents all the action ids
167 */
168 public Set getActionIDs() {
169 if (actionMap == null) {
170 return null;
171 }
172 return actionMap.keySet();
173 }
174
175 public Action addAction(Action action) {
176 return addAction(action.getValue(Action.ACTION_COMMAND_KEY), action);
177 }
178
179 /**
180 * Adds an action to the ActionManager
181 * @param id value of the action id - which is value of the ACTION_COMMAND_KEY
182 * @param action Action to be managed
183 * @return the action that was added
184 */
185 public Action addAction(Object id, Action action) {
186 if (actionMap == null) {
187 actionMap = new HashMap();
188 }
189 actionMap.put(id, action);
190
191 return action;
192 }
193
194 /**
195 * Retrieves the action corresponding to an action id.
196 *
197 * @param id value of the action id
198 * @return an Action or null if id
199 */
200 public Action getAction(Object id) {
201 if (actionMap != null) {
202 return (Action)actionMap.get(id);
203 }
204 return null;
205 }
206
207 /**
208 * Convenience method for returning the TargetableAction
209 *
210 * @param id value of the action id
211 * @return the TargetableAction referenced by the named id or null
212 */
213 public TargetableAction getTargetableAction(Object id) {
214 Action a = getAction(id);
215 if (a instanceof TargetableAction) {
216 return (TargetableAction)a;
217 }
218 return null;
219 }
220
221 /**
222 * Convenience method for returning the BoundAction
223 *
224 * @param id value of the action id
225 * @return the TargetableAction referenced by the named id or null
226 */
227 public BoundAction getBoundAction(Object id) {
228 Action a = getAction(id);
229 if (a instanceof BoundAction) {
230 return (BoundAction)a;
231 }
232 return null;
233 }
234
235 /**
236 * Convenience method for returning the ServerAction
237 *
238 * @param id value of the action id
239 * @return the TargetableAction referenced by the named id or null
240 */
241 public ServerAction getServerAction(Object id) {
242 Action a = getAction(id);
243 if (a instanceof ServerAction) {
244 return (ServerAction)a;
245 }
246 return null;
247 }
248
249 /**
250 * Convenience method for returning the CompositeAction
251 *
252 * @param id value of the action id
253 * @return the TargetableAction referenced by the named id or null
254 */
255 public CompositeAction getCompositeAction(Object id) {
256 Action a = getAction(id);
257 if (a instanceof CompositeAction) {
258 return (CompositeAction)a;
259 }
260 return null;
261 }
262
263 /**
264 * Convenience method for returning the StateChangeAction
265 *
266 * @param id value of the action id
267 * @return the StateChangeAction referenced by the named id or null
268 */
269 private AbstractActionExt getStateChangeAction(Object id) {
270 Action a = getAction(id);
271 if (a != null && a instanceof AbstractActionExt) {
272 AbstractActionExt aa = (AbstractActionExt)a;
273 if (aa.isStateAction()) {
274 return aa;
275 }
276 }
277 return null;
278 }
279
280 /**
281 * Enables or disables the state of the Action corresponding to the
282 * action id. This method should be used
283 * by application developers to ensure that all components created from an
284 * action remain in synch with respect to their enabled state.
285 *
286 * @param id value of the action id
287 * @param enabled true if the action is to be enabled; otherwise false
288 */
289 public void setEnabled(Object id, boolean enabled) {
290 Action action = getAction(id);
291 if (action != null) {
292 action.setEnabled(enabled);
293 }
294 }
295
296
297 /**
298 * Returns the enabled state of the <code>Action</code>. When enabled,
299 * any component associated with this object is active and
300 * able to fire this object's <code>actionPerformed</code> method.
301 *
302 * @param id value of the action id
303 * @return true if this <code>Action</code> is enabled; false if the
304 * action doesn't exist or disabled.
305 */
306 public boolean isEnabled(Object id) {
307 Action action = getAction(id);
308 if (action != null) {
309 return action.isEnabled();
310 }
311 return false;
312 }
313
314 /**
315 * Sets the selected state of a toggle action. If the id doesn't
316 * correspond to a toggle action then it will fail silently.
317 *
318 * @param id the value of the action id
319 * @param selected true if the action is to be selected; otherwise false.
320 */
321 public void setSelected(Object id, boolean selected) {
322 AbstractActionExt action = getStateChangeAction(id);
323 if (action != null) {
324 action.setSelected(selected);
325 }
326 }
327
328 /**
329 * Gets the selected state of a toggle action. If the id doesn't
330 * correspond to a toggle action then it will fail silently.
331 *
332 * @param id the value of the action id
333 * @return true if the action is selected; false if the action
334 * doesn't exist or is disabled.
335 */
336 public boolean isSelected(Object id) {
337 AbstractActionExt action = getStateChangeAction(id);
338 if (action != null) {
339 return action.isSelected();
340 }
341 return false;
342 }
343
344 /**
345 * A diagnostic which prints the Attributes of an action
346 * on the printStream
347 */
348 static void printAction(PrintStream stream, Action action) {
349 stream.println("Attributes for " + action.getValue(Action.ACTION_COMMAND_KEY));
350
351 if (action instanceof AbstractAction) {
352 Object[] keys = ((AbstractAction)action).getKeys();
353
354 for (int i = 0; i < keys.length; i++) {
355 stream.println("\tkey: " + keys[i] + "\tvalue: " +
356 action.getValue((String)keys[i]));
357 }
358 }
359 }
360
361 /**
362 * Convenience method to register a callback method on a <code>BoundAction</code>
363 *
364 * @see BoundAction#registerCallback
365 * @param id value of the action id - which is the value of the ACTION_COMMAND_KEY
366 * @param handler the object which will be perform the action
367 * @param method the name of the method on the handler which will be called.
368 */
369 public void registerCallback(Object id, Object handler, String method) {
370 BoundAction action = getBoundAction(id);
371 if (action != null) {
372 action.registerCallback(handler, method);
373 }
374 }
375
376 //
377 // Convenience methods for determining the type of action.
378 //
379
380 /**
381 * Determines if the Action corresponding to the action id is a state changed
382 * action (toggle, group type action).
383 *
384 * @param id value of the action id
385 * @return true if the action id represents a multi state action; false otherwise
386 */
387 public boolean isStateAction(Object id) {
388 Action action = getAction(id);
389 if (action != null && action instanceof AbstractActionExt) {
390 return ((AbstractActionExt)action).isStateAction();
391 }
392 return false;
393 }
394
395 /**
396 * Test to determine if the action is a <code>TargetableAction</code>
397 */
398 public boolean isTargetableAction(Object id) {
399 return (getTargetableAction(id) != null);
400 }
401
402 /**
403 * Test to determine if the action is a <code>BoundAction</code>
404 */
405 public boolean isBoundAction(Object id) {
406 return (getBoundAction(id) != null);
407 }
408
409 /**
410 * Test to determine if the action is a <code>BoundAction</code>
411 */
412 public boolean isCompositeAction(Object id) {
413 return (getCompositeAction(id) != null);
414 }
415
416 /**
417 * Test to determine if the action is a <code>ServerAction</code>
418 */
419 public boolean isServerAction(Object id) {
420 return (getServerAction(id) != null);
421 }
422 }