/* * $Id$ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jdesktop.swingx.action; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; import javax.swing.KeyStroke; /** * Extends the concept of the Action to include toggle or group states. * <p> * SwingX 1.6.3 updates {@code AbstractActionExt} to use new features of {@link Action} that were * added in {@code Java 1.6}. The selection is now managed with {@link Action#SELECTED_KEY}, which * allows the action to correctly configured Swing buttons. The {@link #LARGE_ICON} has also been * changed to correspond to {@link Action#LARGE_ICON_KEY}. * */ public abstract class AbstractActionExt extends AbstractAction implements ItemListener { /** * The key for the large icon * <p> * As of SwingX 1.6.3 is now has the same value as {@link Action#LARGE_ICON_KEY}, which is new to 1.6. */ public static final String LARGE_ICON = Action.LARGE_ICON_KEY; /** * The key for the button group */ public static final String GROUP = "__Group__"; /** * The key for the flag which indicates that this is a state type. */ public static final String IS_STATE = "__State__"; /** * Default constructor, does nothing. */ public AbstractActionExt() { this((String) null); } /** * Copy constructor copies the state. */ public AbstractActionExt(AbstractActionExt action) { Object[] keys = action.getKeys(); for (int i = 0; i < keys.length; i++) { putValue((String)keys[i], action.getValue((String)keys[i])); } this.enabled = action.enabled; // Copy change listeners. PropertyChangeListener[] listeners = action.getPropertyChangeListeners(); for (int i = 0; i < listeners.length; i++) { addPropertyChangeListener(listeners[i]); } } public AbstractActionExt(String name) { super(name); } public AbstractActionExt(String name, Icon icon) { super(name, icon); } /** * Constructs an Action with the label and command * * @param name name of the action usually used as a label * @param command command key of the action */ public AbstractActionExt(String name, String command) { this(name); setActionCommand(command); } /** * @param name display name of the action * @param command the value of the action command key * @param icon icon to display */ public AbstractActionExt(String name, String command, Icon icon) { super(name, icon); setActionCommand(command); } /** * Returns a short description of the action. * * @return the short description or null */ public String getShortDescription() { return (String)getValue(Action.SHORT_DESCRIPTION); } /** * Sets the short description of the action. This will also * set the long description value is it is null. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.SHORT_DESCRIPTION</code> key. * * @param desc the short description; can be <code>null</code>w * @see Action#SHORT_DESCRIPTION * @see Action#putValue */ public void setShortDescription(String desc) { putValue(Action.SHORT_DESCRIPTION, desc); if (desc != null && getLongDescription() == null) { setLongDescription(desc); } } /** * Returns a long description of the action. * * @return the long description or null */ public String getLongDescription() { return (String)getValue(Action.LONG_DESCRIPTION); } /** * Sets the long description of the action. This will also set the * value of the short description if that value is null. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.LONG_DESCRIPTION</code> key. * * @param desc the long description; can be <code>null</code> * @see Action#LONG_DESCRIPTION * @see Action#putValue */ public void setLongDescription(String desc) { putValue(Action.LONG_DESCRIPTION, desc); if (desc != null && getShortDescription() == null) { setShortDescription(desc); } } /** * Returns a small icon which represents the action. * * @return the small icon or null */ public Icon getSmallIcon() { return (Icon)getValue(SMALL_ICON); } /** * Sets the small icon which represents the action. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.SMALL_ICON</code> key. * * @param icon the small icon; can be <code>null</code> * @see Action#SMALL_ICON * @see Action#putValue */ public void setSmallIcon(Icon icon) { putValue(SMALL_ICON, icon); } /** * Returns a large icon which represents the action. * * @return the large icon or null */ public Icon getLargeIcon() { return (Icon)getValue(LARGE_ICON); } /** * Sets the large icon which represents the action. * <p> * This is a convenience method for <code>putValue</code> with the * <code>LARGE_ICON</code> key. * * @param icon the large icon; can be <code>null</code> * @see #LARGE_ICON * @see Action#putValue */ public void setLargeIcon(Icon icon) { putValue(LARGE_ICON, icon); } /** * Sets the name of the action. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.NAME</code> key. * * @param name the name of the action; can be <code>null</code> * @see Action#NAME * @see Action#putValue */ public void setName(String name) { putValue(Action.NAME, name); } /** * Returns the name of the action. * * @return the name of the action or null */ public String getName() { return (String)getValue(Action.NAME); } public void setMnemonic(String mnemonic) { if (mnemonic != null && mnemonic.length() > 0) { putValue(Action.MNEMONIC_KEY, new Integer(mnemonic.charAt(0))); } } /** * Sets the mnemonic key code for the action. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.MNEMONIC_KEY</code> key. * <p> * This method does not validate the value. Please see * {@link javax.swing.AbstractButton#setMnemonic(int)} for details * concerning the value of the mnemonic. * * @param mnemonic an int key code mnemonic or 0 * @see javax.swing.AbstractButton#setMnemonic(int) * @see Action#MNEMONIC_KEY * @see Action#putValue */ public void setMnemonic(int mnemonic) { putValue(Action.MNEMONIC_KEY, new Integer(mnemonic)); } /** * Return the mnemonic key code for the action. * * @return the mnemonic or 0 */ public int getMnemonic() { Integer value = (Integer)getValue(Action.MNEMONIC_KEY); if (value != null) { return value.intValue(); } return '\0'; } /** * Sets the action command key. The action command key * is used to identify the action. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.ACTION_COMMAND_KEY</code> key. * * @param key the action command * @see Action#ACTION_COMMAND_KEY * @see Action#putValue */ public void setActionCommand(String key) { putValue(Action.ACTION_COMMAND_KEY, key); } /** * Returns the action command. * * @return the action command or null */ public String getActionCommand() { return (String) getValue(Action.ACTION_COMMAND_KEY); } /** * Returns the key stroke which represents an accelerator * for the action. * * @return the key stroke or null */ public KeyStroke getAccelerator() { return (KeyStroke)getValue(Action.ACCELERATOR_KEY); } /** * Sets the key stroke which represents an accelerator * for the action. * <p> * This is a convenience method for <code>putValue</code> with the * <code>Action.ACCELERATOR_KEY</code> key. * * @param key the key stroke; can be <code>null</code> * @see Action#ACCELERATOR_KEY * @see Action#putValue */ public void setAccelerator(KeyStroke key) { putValue(Action.ACCELERATOR_KEY, key); } /** * Sets the group identity of the state action. This is used to * identify the action as part of a button group. */ public void setGroup(Object group) { putValue(GROUP, group); } public Object getGroup() { return getValue(GROUP); } /** * Will perform cleanup on the object. * Should be called when finished with the Action. This should be used if * a new action is constructed from the properties of an old action. * The old action properties should be disposed. */ public void dispose() { PropertyChangeListener[] listeners = getPropertyChangeListeners(); for (int i = 0; i < listeners.length; i++) { removePropertyChangeListener(listeners[i]); } } // Properties etc.... /** * Indicates if this action has states. If this method returns * true then the this will send ItemEvents to ItemListeners * when the control constructed with this action in invoked. * * @return true if this can handle states */ public boolean isStateAction() { Boolean state = (Boolean)getValue(IS_STATE); if (state != null) { return state.booleanValue(); } return false; } /** * Set the state property to true. */ public void setStateAction() { setStateAction(true); } /** * Set the state property. * * @param state if true then this action will fire ItemEvents */ public void setStateAction(boolean state) { putValue(IS_STATE, Boolean.valueOf(state)); } /** * @return true if the action is in the selected state */ public boolean isSelected() { Boolean selected = (Boolean) getValue(SELECTED_KEY); if (selected == null) { return false; } return selected.booleanValue(); } /** * Changes the state of the action. This is a convenience method for updating the Action via the * value map. * * @param newValue * true to set the action as selected of the action. * @see Action#SELECTED_KEY */ public void setSelected(boolean newValue) { putValue(SELECTED_KEY, newValue); } @Override public String toString() { StringBuffer buffer = new StringBuffer("["); // RG: Fix for J2SE 5.0; Can't cascade append() calls because // return type in StringBuffer and AbstractStringBuilder are different buffer.append(this.getClass().toString()); buffer.append(":"); try { Object[] keys = getKeys(); for (int i = 0; i < keys.length; i++) { buffer.append(keys[i]); buffer.append('='); buffer.append(getValue( (String) keys[i]).toString()); if (i < keys.length - 1) { buffer.append(','); } } buffer.append(']'); } catch (Exception ex) { // RG: append(char) throws IOException in J2SE 5.0 /** @todo Log it */ } return buffer.toString(); } /** * Callback method as <code>ItemListener</code>. Updates internal state based * on the given ItemEvent. <p> * * Here: synchs selected property if isStateAction(), does nothing otherwise. * * @param e the ItemEvent fired by a ItemSelectable on changing the selected * state. */ @Override public void itemStateChanged(ItemEvent e) { if (isStateAction()) { setSelected(ItemEvent.SELECTED == e.getStateChange()); } } }