001    /*
002     * $Id: AbstractActionExt.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    
022    package org.jdesktop.swingx.action;
023    
024    import java.awt.event.ItemEvent;
025    import java.awt.event.ItemListener;
026    import java.beans.PropertyChangeListener;
027    
028    import javax.swing.AbstractAction;
029    import javax.swing.Action;
030    import javax.swing.Icon;
031    import javax.swing.KeyStroke;
032    
033    /**
034     * Extends the concept of the Action to include toggle or group states.
035     *
036     */
037    public abstract class AbstractActionExt extends AbstractAction
038        implements ItemListener {
039    
040        /**
041         * The key for the large icon
042         */
043        public static final String LARGE_ICON = "__LargeIcon__";
044    
045        /**
046         * The key for the button group
047         */
048        public static final String GROUP = "__Group__";
049    
050        /**
051         * The key for the flag which indicates that this is a state type.
052         */
053        public static final String IS_STATE = "__State__";
054    
055        /**
056         * Specified whether the action is selected; the default is false
057         */
058        private boolean selected = false;
059    
060        /**
061         * Default constructor, does nothing.
062         *
063         */
064        public AbstractActionExt() {
065            // default constructor
066        }
067        /**
068         * Copy constructor copies the state.
069         */
070        public AbstractActionExt(AbstractActionExt action) {
071            Object[] keys = action.getKeys();
072            for (int i = 0; i < keys.length; i++) {
073                putValue((String)keys[i], action.getValue((String)keys[i]));
074            }
075            this.selected = action.selected;
076            this.enabled = action.enabled;
077    
078            // Copy change listeners.
079            PropertyChangeListener[] listeners = action.getPropertyChangeListeners();
080            for (int i = 0; i < listeners.length; i++) {
081                addPropertyChangeListener(listeners[i]);
082            }
083        }
084    
085        public AbstractActionExt(String name) {
086            super(name);
087        }
088    
089        public AbstractActionExt(String name, Icon icon) {
090            super(name, icon);
091        }
092    
093        /**
094         * Constructs an Action with the label and command
095         *
096         * @param name name of the action usually used as a label
097         * @param command command key of the action
098         */
099        public AbstractActionExt(String name, String command) {
100            this(name);
101            setActionCommand(command);
102        }
103    
104        /**
105         * @param name display name of the action
106         * @param command the value of the action command key
107         * @param icon icon to display
108         */
109        public AbstractActionExt(String name, String command, Icon icon) {
110            super(name, icon);
111            setActionCommand(command);
112        }
113        /**
114         * Returns a short description of the action.
115         *
116         * @return the short description or null
117         */
118        public String getShortDescription()  {
119            return (String)getValue(Action.SHORT_DESCRIPTION);
120        }
121    
122        /**
123         * Sets the short description of the action. This will also
124         * set the long description value is it is null.
125         * <p>
126         * This is a convenience method for <code>putValue</code> with the
127         * <code>Action.SHORT_DESCRIPTION</code> key.
128         *
129         * @param desc the short description; can be <code>null</code>w
130         * @see Action#SHORT_DESCRIPTION
131         * @see Action#putValue
132         */
133        public void setShortDescription(String desc) {
134            putValue(Action.SHORT_DESCRIPTION, desc);
135            if (desc != null && getLongDescription() == null) {
136                setLongDescription(desc);
137            }
138        }
139    
140        /**
141         * Returns a long description of the action.
142         *
143         * @return the long description or null
144         */
145        public String getLongDescription()  {
146            return (String)getValue(Action.LONG_DESCRIPTION);
147        }
148    
149        /**
150         * Sets the long description of the action. This will also set the
151         * value of the short description if that value is null.
152         * <p>
153         * This is a convenience method for <code>putValue</code> with the
154         * <code>Action.LONG_DESCRIPTION</code> key.
155         *
156         * @param desc the long description; can be <code>null</code>
157         * @see Action#LONG_DESCRIPTION
158         * @see Action#putValue
159         */
160        public void setLongDescription(String desc) {
161            putValue(Action.LONG_DESCRIPTION, desc);
162            if (desc != null && getShortDescription() == null) {
163                setShortDescription(desc);
164            }
165        }
166    
167        /**
168         * Returns a small icon which represents the action.
169         *
170         * @return the small icon or null
171         */
172        public Icon getSmallIcon() {
173            return (Icon)getValue(SMALL_ICON);
174        }
175    
176        /**
177         * Sets the small icon which represents the action.
178         * <p>
179         * This is a convenience method for <code>putValue</code> with the
180         * <code>Action.SMALL_ICON</code> key.
181         *
182         * @param icon the small icon; can be <code>null</code>
183         * @see Action#SMALL_ICON
184         * @see Action#putValue
185         */
186        public void setSmallIcon(Icon icon) {
187            putValue(SMALL_ICON, icon);
188        }
189    
190        /**
191         * Returns a large icon which represents the action.
192         *
193         * @return the large icon or null
194         */
195        public Icon getLargeIcon() {
196            return (Icon)getValue(LARGE_ICON);
197        }
198    
199        /**
200         * Sets the large icon which represents the action.
201         * <p>
202         * This is a convenience method for <code>putValue</code> with the
203         * <code>LARGE_ICON</code> key.
204         *
205         * @param icon the large icon; can be <code>null</code>
206         * @see #LARGE_ICON
207         * @see Action#putValue
208         */
209        public void setLargeIcon(Icon icon) {
210            putValue(LARGE_ICON, icon);
211        }
212    
213        /**
214         * Sets the name of the action.
215         * <p>
216         * This is a convenience method for <code>putValue</code> with the
217         * <code>Action.NAME</code> key.
218         *
219         * @param name the name of the action; can be <code>null</code>
220         * @see Action#NAME
221         * @see Action#putValue
222         */
223        public void setName(String name) {
224            putValue(Action.NAME, name);
225        }
226    
227        /**
228         * Returns the name of the action.
229         *
230         * @return the name of the action or null
231         */
232        public String getName() {
233            return (String)getValue(Action.NAME);
234        }
235    
236        public void setMnemonic(String mnemonic) {
237            if (mnemonic != null && mnemonic.length() > 0) {
238                putValue(Action.MNEMONIC_KEY, new Integer(mnemonic.charAt(0)));
239            }
240        }
241    
242        /**
243         * Sets the mnemonic key code for the action.
244         * <p>
245         * This is a convenience method for <code>putValue</code> with the
246         * <code>Action.MNEMONIC_KEY</code> key.
247         * <p>
248         * This method does not validate the value. Please see
249         * {@link javax.swing.AbstractButton#setMnemonic(int)} for details
250         * concerning the value of the mnemonic.
251         *
252         * @param mnemonic an int key code mnemonic or 0
253         * @see javax.swing.AbstractButton#setMnemonic(int)
254         * @see Action#MNEMONIC_KEY
255         * @see Action#putValue
256         */
257        public void setMnemonic(int mnemonic) {
258            putValue(Action.MNEMONIC_KEY, new Integer(mnemonic));
259        }
260    
261        /**
262         * Return the mnemonic key code for the action.
263         *
264         * @return the mnemonic or 0
265         */
266        public int getMnemonic() {
267            Integer value = (Integer)getValue(Action.MNEMONIC_KEY);
268            if (value != null) {
269                return value.intValue();
270            }
271            return '\0';
272        }
273    
274        /**
275         * Sets the action command key. The action command key
276         * is used to identify the action.
277         * <p>
278         * This is a convenience method for <code>putValue</code> with the
279         * <code>Action.ACTION_COMMAND_KEY</code> key.
280         *
281         * @param key the action command
282         * @see Action#ACTION_COMMAND_KEY
283         * @see Action#putValue
284         */
285        public void setActionCommand(Object key) {
286            putValue(Action.ACTION_COMMAND_KEY, key);
287        }
288    
289        /**
290         * Returns the action command.
291         *
292         * @return the action command or null
293         */
294        public Object getActionCommand() {
295            return getValue(Action.ACTION_COMMAND_KEY);
296        }
297    
298        /**
299         * Returns the key stroke which represents an accelerator
300         * for the action.
301         *
302         * @return the key stroke or null
303         */
304        public KeyStroke getAccelerator() {
305            return (KeyStroke)getValue(Action.ACCELERATOR_KEY);
306        }
307    
308        /**
309         * Sets the key stroke which represents an accelerator
310         * for the action.
311         * <p>
312         * This is a convenience method for <code>putValue</code> with the
313         * <code>Action.ACCELERATOR_KEY</code> key.
314         *
315         * @param key the key stroke; can be <code>null</code>
316         * @see Action#ACCELERATOR_KEY
317         * @see Action#putValue
318         */
319        public void setAccelerator(KeyStroke key) {
320            putValue(Action.ACCELERATOR_KEY, key);
321        }
322    
323        /**
324         * Sets the group identity of the state action. This is used to
325         * identify the action as part of a button group.
326         */
327        public void setGroup(Object group) {
328            putValue(GROUP, group);
329        }
330    
331        public Object getGroup() {
332            return getValue(GROUP);
333        }
334    
335        /**
336         * Will perform cleanup on the object.
337         * Should be called when finished with the Action. This should be used if
338         * a new action is constructed from the properties of an old action.
339         * The old action properties should be disposed.
340         */
341        public void dispose() {
342            PropertyChangeListener[] listeners = getPropertyChangeListeners();
343            for (int i = 0; i < listeners.length; i++) {
344                removePropertyChangeListener(listeners[i]);
345            }
346        }
347    
348        // Properties etc....
349    
350        /**
351         * Indicates if this action has states. If this method returns
352         * true then the this will send ItemEvents to ItemListeners
353         * when the control constructed with this action in invoked.
354         *
355         * @return true if this can handle states
356         */
357        public boolean isStateAction() {
358            Boolean state = (Boolean)getValue(IS_STATE);
359            if (state != null) {
360                return state.booleanValue();
361            }
362            return false;
363        }
364    
365        /**
366         * Set the state property to true.
367         */
368        public void setStateAction() {
369            setStateAction(true);
370        }
371    
372        /**
373         * Set the state property.
374         *
375         * @param state if true then this action will fire ItemEvents
376         */
377        public void setStateAction(boolean state) {
378            putValue(IS_STATE, Boolean.valueOf(state));
379        }
380    
381        /**
382         * @return true if the action is in the selected state
383         */
384        public boolean isSelected()  {
385            return selected;
386        }
387    
388        /**
389         * Changes the state of the action
390         * @param newValue true to set the action as selected of the action.
391         */
392        public synchronized void setSelected(boolean newValue) {
393            boolean oldValue = this.selected;
394            if (oldValue != newValue) {
395                this.selected = newValue;
396                firePropertyChange("selected", Boolean.valueOf(oldValue),
397                                   Boolean.valueOf(newValue));
398            }
399        }
400    
401        @Override
402        public String toString() {
403            StringBuffer buffer = new StringBuffer("[");
404            // RG: Fix for J2SE 5.0; Can't cascade append() calls because
405            // return type in StringBuffer and AbstractStringBuilder are different
406            buffer.append(this.getClass().toString());
407            buffer.append(":");
408            try {
409                Object[] keys = getKeys();
410                for (int i = 0; i < keys.length; i++) {
411                    buffer.append(keys[i]);
412                    buffer.append('=');
413                    buffer.append(getValue( (String) keys[i]).toString());
414                    if (i < keys.length - 1) {
415                        buffer.append(',');
416                    }
417                }
418                buffer.append(']');
419            }
420            catch (Exception ex) {  // RG: append(char) throws IOException in J2SE 5.0
421                /** @todo Log it */
422            }
423            return buffer.toString();
424        }
425    
426        // /**
427        // * @inheritDoc
428        // * Default to no-op
429        // */
430        // public void itemStateChanged(ItemEvent e) {
431        // }
432        
433        /**
434         * Callback method as <code>ItemListener</code>. Updates internal state based
435         * on the given ItemEvent. <p>
436         * 
437         * Here: synchs selected property if isStateAction(), does nothing otherwise.
438         * 
439         * @param e the ItemEvent fired by a ItemSelectable on changing the selected 
440         *    state.
441         */
442        public void itemStateChanged(ItemEvent e) {
443            if (isStateAction()) {
444                setSelected(ItemEvent.SELECTED == e.getStateChange());
445            }
446        }
447    
448    
449    }