001    /*
002     * $Id: ActionManager.java 3197 2009-01-21 17:54:30Z kschaefe $
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.Arrays;
025    import java.util.HashSet;
026    import java.util.Set;
027    
028    import javax.swing.AbstractAction;
029    import javax.swing.Action;
030    import javax.swing.ActionMap;
031    
032    /**
033     * The ActionManager manages sets of <code>javax.swing.Action</code>s for an
034     * application. There are convenience methods for getting and setting the state
035     * of the action.
036     * All of these elements have a unique id tag which is used by the ActionManager
037     * to reference the action. This id maps to the <code>Action.ACTION_COMMAND_KEY</code>
038     * on the Action.
039     * <p>
040     * The ActionManager may be used to conveniently register callback methods
041     * on BoundActions.
042     * <p>
043     * A typical use case of the ActionManager is:
044     * <p>
045     *  <pre>
046     *   ActionManager manager = ActionManager.getInstance();
047     *
048     *   // load Actions
049     *   manager.addAction(action);
050     *
051     *   // Change the state of the action:
052     *   manager.setEnabled("new-action", newState);
053     * </pre>
054     *
055     * The ActionManager also supports Actions that can have a selected state
056     * associated with them. These Actions are typically represented by a
057     * JCheckBox or similar widget. For such actions the registered method is
058     * invoked with an additional parameter indicating the selected state of
059     * the widget. For example, for the callback handler:
060     *<p>
061     * <pre>
062     *  public class Handler {
063     *      public void stateChanged(boolean newState);
064     *   }
065     * </pre>
066     * The registration method would look similar:
067     * <pre>
068     *  manager.registerCallback("select-action", new Handler(), "stateChanged");
069     * </pre>
070     *<p>
071     * The stateChanged method would be invoked as the selected state of
072     * the widget changed. Additionally if you need to change the selected
073     * state of the Action use the ActionManager method <code>setSelected</code>.
074     * <p>
075     * The <code>ActionContainerFactory</code> uses the managed Actions in a
076     * ActionManager to create user interface components. It uses the shared
077     * instance of ActionManager by default. For example, to create a JMenu based on an
078     * action-list id:
079     * <pre>
080     * ActionContainerFactory factory = new ActionContainerFactory();
081     * JMenu file = factory.createMenu(list);
082     * </pre>
083     *
084     * @see ActionContainerFactory
085     * @see TargetableAction
086     * @see BoundAction
087     * @author Mark Davidson
088     * @author Neil Weber
089     */
090    public class ActionManager extends ActionMap {
091    
092        /**
093         * Shared instance of the singleton ActionManager.
094         */
095        private static ActionManager INSTANCE;
096    
097        /**
098         * Creates the action manager. Use this constuctor if the application should
099         * support many ActionManagers. Otherwise, using the getInstance method will
100         * return a singleton.
101         */
102        public ActionManager() {
103        }
104    
105        /**
106         * Return the instance of the ActionManger. If this has not been explicity
107         * set then it will be created.
108         *
109         * @return the ActionManager instance.
110         * @see #setInstance
111         */
112        public static ActionManager getInstance() {
113            if (INSTANCE == null) {
114                INSTANCE = new ActionManager();
115            }
116            return INSTANCE;
117        }
118    
119        /**
120         * Sets the ActionManager instance.
121         */
122        public static void setInstance(ActionManager manager) {
123            INSTANCE = manager;
124        }
125    
126        /**
127         * Returns the ids for all the managed actions.
128         * <p>
129         * An action id is a unique idenitfier which can
130         * be used to retrieve the corrspondng Action from the ActionManager.
131         * This identifier can also
132         * be used to set the properties of the action through the action
133         * manager like setting the state of the enabled or selected flags.
134         *
135         * @return a set which represents all the action ids
136         */
137        public Set<Object> getActionIDs() {
138            Object[] keys = keys();
139            if (keys == null) {
140                return null;
141            }
142    
143            return new HashSet<Object>(Arrays.asList(keys));
144        }
145    
146        public Action addAction(Action action) {
147            return addAction(action.getValue(Action.ACTION_COMMAND_KEY), action);
148        }
149    
150        /**
151         * Adds an action to the ActionManager
152         * @param id value of the action id - which is value of the ACTION_COMMAND_KEY
153         * @param action Action to be managed
154         * @return the action that was added
155         */
156        public Action addAction(Object id, Action action)  {
157            put(id, action);
158            return action;
159        }
160    
161        /**
162         * Retrieves the action corresponding to an action id.
163         *
164         * @param id value of the action id
165         * @return an Action or null if id
166         */
167        public Action getAction(Object id)  {
168            return get(id);
169        }
170    
171        /**
172         * Convenience method for returning the TargetableAction
173         *
174         * @param id value of the action id
175         * @return the TargetableAction referenced by the named id or null
176         */
177        public TargetableAction getTargetableAction(Object id) {
178            Action a = getAction(id);
179            if (a instanceof TargetableAction) {
180                return (TargetableAction)a;
181            }
182            return null;
183        }
184    
185        /**
186         * Convenience method for returning the BoundAction
187         *
188         * @param id value of the action id
189         * @return the TargetableAction referenced by the named id or null
190         */
191        public BoundAction getBoundAction(Object id) {
192            Action a = getAction(id);
193            if (a instanceof BoundAction) {
194                return (BoundAction)a;
195            }
196            return null;
197        }
198    
199        /**
200         * Convenience method for returning the ServerAction
201         *
202         * @param id value of the action id
203         * @return the TargetableAction referenced by the named id or null
204         */
205        public ServerAction getServerAction(Object id) {
206            Action a = getAction(id);
207            if (a instanceof ServerAction) {
208                return (ServerAction)a;
209            }
210            return null;
211        }
212    
213        /**
214         * Convenience method for returning the CompositeAction
215         *
216         * @param id value of the action id
217         * @return the TargetableAction referenced by the named id or null
218         */
219        public CompositeAction getCompositeAction(Object id) {
220            Action a = getAction(id);
221            if (a instanceof CompositeAction) {
222                return (CompositeAction)a;
223            }
224            return null;
225        }
226    
227        /**
228         * Convenience method for returning the StateChangeAction
229         *
230         * @param id value of the action id
231         * @return the StateChangeAction referenced by the named id or null
232         */
233        private AbstractActionExt getStateChangeAction(Object id) {
234            Action a = getAction(id);
235            if (a != null && a instanceof AbstractActionExt) {
236                AbstractActionExt aa = (AbstractActionExt)a;
237                if (aa.isStateAction()) {
238                    return aa;
239                }
240            }
241            return null;
242        }
243    
244        /**
245         * Enables or disables the state of the Action corresponding to the
246         * action id. This method should be used
247         * by application developers to ensure that all components created from an
248         * action remain in synch with respect to their enabled state.
249         *
250         * @param id value of the action id
251         * @param enabled true if the action is to be enabled; otherwise false
252         */
253        public void setEnabled(Object id, boolean enabled) {
254            Action action = getAction(id);
255            if (action != null) {
256                action.setEnabled(enabled);
257            }
258        }
259    
260    
261        /**
262         * Returns the enabled state of the <code>Action</code>. When enabled,
263         * any component associated with this object is active and
264         * able to fire this object's <code>actionPerformed</code> method.
265         *
266         * @param id value of the action id
267         * @return true if this <code>Action</code> is enabled; false if the
268         *         action doesn't exist or disabled.
269         */
270        public boolean isEnabled(Object id) {
271            Action action = getAction(id);
272            if (action != null) {
273                return action.isEnabled();
274            }
275            return false;
276        }
277    
278        /**
279         * Sets the selected state of a toggle action. If the id doesn't
280         * correspond to a toggle action then it will fail silently.
281         *
282         * @param id the value of the action id
283         * @param selected true if the action is to be selected; otherwise false.
284         */
285        public void setSelected(Object id, boolean selected) {
286            AbstractActionExt action = getStateChangeAction(id);
287            if (action != null) {
288                action.setSelected(selected);
289            }
290        }
291    
292        /**
293         * Gets the selected state of a toggle action. If the id doesn't
294         * correspond to a toggle action then it will fail silently.
295         *
296         * @param id the value of the action id
297         * @return true if the action is selected; false if the action
298         *         doesn't exist or is disabled.
299         */
300        public boolean isSelected(Object id) {
301            AbstractActionExt action = getStateChangeAction(id);
302            if (action != null) {
303                return action.isSelected();
304            }
305            return false;
306        }
307    
308        /**
309         * A diagnostic which prints the Attributes of an action
310         * on the printStream
311         */
312        static void printAction(PrintStream stream, Action action) {
313            stream.println("Attributes for " + action.getValue(Action.ACTION_COMMAND_KEY));
314    
315            if (action instanceof AbstractAction) {
316                Object[] keys = ((AbstractAction)action).getKeys();
317    
318                for (int i = 0; i < keys.length; i++) {
319                    stream.println("\tkey: " + keys[i] + "\tvalue: " +
320                                   action.getValue((String)keys[i]));
321                }
322            }
323        }
324    
325        /**
326         * Convenience method to register a callback method on a <code>BoundAction</code>
327         *
328         * @see BoundAction#registerCallback
329         * @param id value of the action id - which is the value of the ACTION_COMMAND_KEY
330         * @param handler the object which will be perform the action
331         * @param method the name of the method on the handler which will be called.
332         */
333        public void registerCallback(Object id, Object handler, String method) {
334            BoundAction action = getBoundAction(id);
335            if (action != null) {
336                action.registerCallback(handler, method);
337            }
338        }
339    
340        //
341        // Convenience methods for determining the type of action.
342        //
343    
344        /**
345         * Determines if the Action corresponding to the action id is a state changed
346         * action (toggle, group type action).
347         *
348         * @param id value of the action id
349         * @return true if the action id represents a multi state action; false otherwise
350         */
351        public boolean isStateAction(Object id) {
352            Action action = getAction(id);
353            if (action != null && action instanceof AbstractActionExt) {
354                return ((AbstractActionExt)action).isStateAction();
355            }
356            return false;
357        }
358    
359        /**
360         * Test to determine if the action is a <code>TargetableAction</code>
361         */
362        public boolean isTargetableAction(Object id) {
363            return (getTargetableAction(id) != null);
364        }
365    
366        /**
367         * Test to determine if the action is a <code>BoundAction</code>
368         */
369        public boolean isBoundAction(Object id) {
370            return (getBoundAction(id) != null);
371        }
372    
373        /**
374         * Test to determine if the action is a <code>BoundAction</code>
375         */
376        public boolean isCompositeAction(Object id) {
377            return (getCompositeAction(id) != null);
378        }
379    
380        /**
381         * Test to determine if the action is a <code>ServerAction</code>
382         */
383        public boolean isServerAction(Object id) {
384            return (getServerAction(id) != null);
385        }
386    }