001 /*
002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import java.awt.*;
028 import java.awt.event.*;
029 import java.beans.*;
030 import java.util.Hashtable;
031 import java.util.Enumeration;
032 import java.io.Serializable;
033 import java.io.IOException;
034 import java.io.ObjectInputStream;
035 import java.io.ObjectOutputStream;
036 import java.security.AccessController;
037 import javax.swing.event.SwingPropertyChangeSupport;
038 import sun.security.action.GetPropertyAction;
039
040 /**
041 * This class provides default implementations for the JFC <code>Action</code>
042 * interface. Standard behaviors like the get and set methods for
043 * <code>Action</code> object properties (icon, text, and enabled) are defined
044 * here. The developer need only subclass this abstract class and
045 * define the <code>actionPerformed</code> method.
046 * <p>
047 * <strong>Warning:</strong>
048 * Serialized objects of this class will not be compatible with
049 * future Swing releases. The current serialization support is
050 * appropriate for short term storage or RMI between applications running
051 * the same version of Swing. As of 1.4, support for long term storage
052 * of all JavaBeans<sup><font size="-2">TM</font></sup>
053 * has been added to the <code>java.beans</code> package.
054 * Please see {@link java.beans.XMLEncoder}.
055 *
056 * @version 1.61 05/05/07
057 * @author Georges Saab
058 * @see Action
059 */
060 public abstract class AbstractAction implements Action, Cloneable,
061 Serializable {
062 /**
063 * Whether or not actions should reconfigure all properties on null.
064 */
065 private static Boolean RECONFIGURE_ON_NULL;
066
067 /**
068 * Specifies whether action is enabled; the default is true.
069 */
070 protected boolean enabled = true;
071
072 /**
073 * Contains the array of key bindings.
074 */
075 private transient ArrayTable arrayTable;
076
077 /**
078 * Whether or not to reconfigure all action properties from the
079 * specified event.
080 */
081 static boolean shouldReconfigure(PropertyChangeEvent e) {
082 if (e.getPropertyName() == null) {
083 synchronized (AbstractAction.class) {
084 if (RECONFIGURE_ON_NULL == null) {
085 RECONFIGURE_ON_NULL = Boolean
086 .valueOf(AccessController
087 .doPrivileged(new GetPropertyAction(
088 "swing.actions.reconfigureOnNull",
089 "false")));
090 }
091 return RECONFIGURE_ON_NULL;
092 }
093 }
094 return false;
095 }
096
097 /**
098 * Sets the enabled state of a component from an Action.
099 *
100 * @param c the Component to set the enabled state on
101 * @param a the Action to set the enabled state from, may be null
102 */
103 static void setEnabledFromAction(JComponent c, Action a) {
104 c.setEnabled((a != null) ? a.isEnabled() : true);
105 }
106
107 /**
108 * Sets the tooltip text of a component from an Action.
109 *
110 * @param c the Component to set the tooltip text on
111 * @param a the Action to set the tooltip text from, may be null
112 */
113 static void setToolTipTextFromAction(JComponent c, Action a) {
114 c.setToolTipText(a != null ? (String) a
115 .getValue(Action.SHORT_DESCRIPTION) : null);
116 }
117
118 static boolean hasSelectedKey(Action a) {
119 return (a != null && a.getValue(Action.SELECTED_KEY) != null);
120 }
121
122 static boolean isSelected(Action a) {
123 return Boolean.TRUE.equals(a.getValue(Action.SELECTED_KEY));
124 }
125
126 /**
127 * Creates an {@code Action}.
128 */
129 public AbstractAction() {
130 }
131
132 /**
133 * Creates an {@code Action} with the specified name.
134 *
135 * @param name the name ({@code Action.NAME}) for the action; a
136 * value of {@code null} is ignored
137 */
138 public AbstractAction(String name) {
139 putValue(Action.NAME, name);
140 }
141
142 /**
143 * Creates an {@code Action} with the specified name and small icon.
144 *
145 * @param name the name ({@code Action.NAME}) for the action; a
146 * value of {@code null} is ignored
147 * @param icon the small icon ({@code Action.SMALL_ICON}) for the action; a
148 * value of {@code null} is ignored
149 */
150 public AbstractAction(String name, Icon icon) {
151 this (name);
152 putValue(Action.SMALL_ICON, icon);
153 }
154
155 /**
156 * Gets the <code>Object</code> associated with the specified key.
157 *
158 * @param key a string containing the specified <code>key</code>
159 * @return the binding <code>Object</code> stored with this key; if there
160 * are no keys, it will return <code>null</code>
161 * @see Action#getValue
162 */
163 public Object getValue(String key) {
164 if (key == "enabled") {
165 return enabled;
166 }
167 if (arrayTable == null) {
168 return null;
169 }
170 return arrayTable.get(key);
171 }
172
173 /**
174 * Sets the <code>Value</code> associated with the specified key.
175 *
176 * @param key the <code>String</code> that identifies the stored object
177 * @param newValue the <code>Object</code> to store using this key
178 * @see Action#putValue
179 */
180 public void putValue(String key, Object newValue) {
181 Object oldValue = null;
182 if (key == "enabled") {
183 // Treat putValue("enabled") the same way as a call to setEnabled.
184 // If we don't do this it means the two may get out of sync, and a
185 // bogus property change notification would be sent.
186 //
187 // To avoid dependencies between putValue & setEnabled this
188 // directly changes enabled. If we instead called setEnabled
189 // to change enabled, it would be possible for stack
190 // overflow in the case where a developer implemented setEnabled
191 // in terms of putValue.
192 if (newValue == null || !(newValue instanceof Boolean)) {
193 newValue = false;
194 }
195 oldValue = enabled;
196 enabled = (Boolean) newValue;
197 } else {
198 if (arrayTable == null) {
199 arrayTable = new ArrayTable();
200 }
201 if (arrayTable.containsKey(key))
202 oldValue = arrayTable.get(key);
203 // Remove the entry for key if newValue is null
204 // else put in the newValue for key.
205 if (newValue == null) {
206 arrayTable.remove(key);
207 } else {
208 arrayTable.put(key, newValue);
209 }
210 }
211 firePropertyChange(key, oldValue, newValue);
212 }
213
214 /**
215 * Returns true if the action is enabled.
216 *
217 * @return true if the action is enabled, false otherwise
218 * @see Action#isEnabled
219 */
220 public boolean isEnabled() {
221 return enabled;
222 }
223
224 /**
225 * Sets whether the {@code Action} is enabled. The default is {@code true}.
226 *
227 * @param newValue {@code true} to enable the action, {@code false} to
228 * disable it
229 * @see Action#setEnabled
230 */
231 public void setEnabled(boolean newValue) {
232 boolean oldValue = this .enabled;
233
234 if (oldValue != newValue) {
235 this .enabled = newValue;
236 firePropertyChange("enabled", Boolean.valueOf(oldValue),
237 Boolean.valueOf(newValue));
238 }
239 }
240
241 /**
242 * Returns an array of <code>Object</code>s which are keys for
243 * which values have been set for this <code>AbstractAction</code>,
244 * or <code>null</code> if no keys have values set.
245 * @return an array of key objects, or <code>null</code> if no
246 * keys have values set
247 * @since 1.3
248 */
249 public Object[] getKeys() {
250 if (arrayTable == null) {
251 return null;
252 }
253 Object[] keys = new Object[arrayTable.size()];
254 arrayTable.getKeys(keys);
255 return keys;
256 }
257
258 /**
259 * If any <code>PropertyChangeListeners</code> have been registered, the
260 * <code>changeSupport</code> field describes them.
261 */
262 protected SwingPropertyChangeSupport changeSupport;
263
264 /**
265 * Supports reporting bound property changes. This method can be called
266 * when a bound property has changed and it will send the appropriate
267 * <code>PropertyChangeEvent</code> to any registered
268 * <code>PropertyChangeListeners</code>.
269 */
270 protected void firePropertyChange(String propertyName,
271 Object oldValue, Object newValue) {
272 if (changeSupport == null
273 || (oldValue != null && newValue != null && oldValue
274 .equals(newValue))) {
275 return;
276 }
277 changeSupport.firePropertyChange(propertyName, oldValue,
278 newValue);
279 }
280
281 /**
282 * Adds a <code>PropertyChangeListener</code> to the listener list.
283 * The listener is registered for all properties.
284 * <p>
285 * A <code>PropertyChangeEvent</code> will get fired in response to setting
286 * a bound property, e.g. <code>setFont</code>, <code>setBackground</code>,
287 * or <code>setForeground</code>.
288 * Note that if the current component is inheriting its foreground,
289 * background, or font from its container, then no event will be
290 * fired in response to a change in the inherited property.
291 *
292 * @param listener The <code>PropertyChangeListener</code> to be added
293 *
294 * @see Action#addPropertyChangeListener
295 */
296 public synchronized void addPropertyChangeListener(
297 PropertyChangeListener listener) {
298 if (changeSupport == null) {
299 changeSupport = new SwingPropertyChangeSupport(this );
300 }
301 changeSupport.addPropertyChangeListener(listener);
302 }
303
304 /**
305 * Removes a <code>PropertyChangeListener</code> from the listener list.
306 * This removes a <code>PropertyChangeListener</code> that was registered
307 * for all properties.
308 *
309 * @param listener the <code>PropertyChangeListener</code> to be removed
310 *
311 * @see Action#removePropertyChangeListener
312 */
313 public synchronized void removePropertyChangeListener(
314 PropertyChangeListener listener) {
315 if (changeSupport == null) {
316 return;
317 }
318 changeSupport.removePropertyChangeListener(listener);
319 }
320
321 /**
322 * Returns an array of all the <code>PropertyChangeListener</code>s added
323 * to this AbstractAction with addPropertyChangeListener().
324 *
325 * @return all of the <code>PropertyChangeListener</code>s added or an empty
326 * array if no listeners have been added
327 * @since 1.4
328 */
329 public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
330 if (changeSupport == null) {
331 return new PropertyChangeListener[0];
332 }
333 return changeSupport.getPropertyChangeListeners();
334 }
335
336 /**
337 * Clones the abstract action. This gives the clone
338 * its own copy of the key/value list,
339 * which is not handled for you by <code>Object.clone()</code>.
340 **/
341
342 protected Object clone() throws CloneNotSupportedException {
343 AbstractAction newAction = (AbstractAction) super .clone();
344 synchronized (this ) {
345 if (arrayTable != null) {
346 newAction.arrayTable = (ArrayTable) arrayTable.clone();
347 }
348 }
349 return newAction;
350 }
351
352 private void writeObject(ObjectOutputStream s) throws IOException {
353 // Store the default fields
354 s.defaultWriteObject();
355
356 // And the keys
357 ArrayTable.writeArrayTable(s, arrayTable);
358 }
359
360 private void readObject(ObjectInputStream s)
361 throws ClassNotFoundException, IOException {
362 s.defaultReadObject();
363 for (int counter = s.readInt() - 1; counter >= 0; counter--) {
364 putValue((String) s.readObject(), s.readObject());
365 }
366 }
367 }
|