001/*
002 * $Id: ActionManager.java 3972 2011-03-17 20:31:58Z 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 */
021package org.jdesktop.swingx.action;
022
023import java.io.PrintStream;
024import java.util.Arrays;
025import java.util.HashSet;
026import java.util.Set;
027
028import javax.swing.AbstractAction;
029import javax.swing.Action;
030import 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 */
090public 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}