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 }