/* * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing; import java.awt.*;import java.awt.event.*;import java.beans.*;import java.util.Hashtable;import java.util.Enumeration;import java.io.Serializable;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.security.AccessController;import javax.swing.event.SwingPropertyChangeSupport;import sun.security.action.GetPropertyAction; /** * This class provides default implementations for the JFC <code>Action</code> * interface. Standard behaviors like the get and set methods for * <code>Action</code> object properties (icon, text, and enabled) are defined * here. The developer need only subclass this abstract class and * define the <code>actionPerformed</code> method. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author Georges Saab * @see Action */public abstract class AbstractAction implements Action, Cloneable, Serializable{ /** * Whether or not actions should reconfigure all properties on null. */ private static Boolean RECONFIGURE_ON_NULL; /** * Specifies whether action is enabled; the default is true. */ protected boolean enabled = true; /** * Contains the array of key bindings. */ private transient ArrayTable arrayTable; /** * Whether or not to reconfigure all action properties from the * specified event. */ static boolean shouldReconfigure(PropertyChangeEvent e) { if (e.getPropertyName() == null) { synchronized(AbstractAction.class) { if (RECONFIGURE_ON_NULL == null) { RECONFIGURE_ON_NULL = Boolean.valueOf( AccessController.doPrivileged(new GetPropertyAction( "swing.actions.reconfigureOnNull", "false"))); } return RECONFIGURE_ON_NULL; } } return false; } /** * Sets the enabled state of a component from an Action. * * @param c the Component to set the enabled state on * @param a the Action to set the enabled state from, may be null */ static void setEnabledFromAction(JComponent c, Action a) { c.setEnabled((a != null) ? a.isEnabled() : true); } /** * Sets the tooltip text of a component from an Action. * * @param c the Component to set the tooltip text on * @param a the Action to set the tooltip text from, may be null */ static void setToolTipTextFromAction(JComponent c, Action a) { c.setToolTipText(a != null ? (String)a.getValue(Action.SHORT_DESCRIPTION) : null); } static boolean hasSelectedKey(Action a) { return (a != null && a.getValue(Action.SELECTED_KEY) != null); } static boolean isSelected(Action a) { return Boolean.TRUE.equals(a.getValue(Action.SELECTED_KEY)); } /** * Creates an {@code Action}. */ public AbstractAction() { } /** * Creates an {@code Action} with the specified name. * * @param name the name ({@code Action.NAME}) for the action; a * value of {@code null} is ignored */ public AbstractAction(String name) { putValue(Action.NAME, name); } /** * Creates an {@code Action} with the specified name and small icon. * * @param name the name ({@code Action.NAME}) for the action; a * value of {@code null} is ignored * @param icon the small icon ({@code Action.SMALL_ICON}) for the action; a * value of {@code null} is ignored */ public AbstractAction(String name, Icon icon) { this(name); putValue(Action.SMALL_ICON, icon); } /** * Gets the <code>Object</code> associated with the specified key. * * @param key a string containing the specified <code>key</code> * @return the binding <code>Object</code> stored with this key; if there * are no keys, it will return <code>null</code> * @see Action#getValue */ public Object getValue(String key) { if (key == "enabled") { return enabled; } if (arrayTable == null) { return null; } return arrayTable.get(key); } /** * Sets the <code>Value</code> associated with the specified key. * * @param key the <code>String</code> that identifies the stored object * @param newValue the <code>Object</code> to store using this key * @see Action#putValue */ public void putValue(String key, Object newValue) { Object oldValue = null; if (key == "enabled") { // Treat putValue("enabled") the same way as a call to setEnabled. // If we don't do this it means the two may get out of sync, and a // bogus property change notification would be sent. // // To avoid dependencies between putValue & setEnabled this // directly changes enabled. If we instead called setEnabled // to change enabled, it would be possible for stack // overflow in the case where a developer implemented setEnabled // in terms of putValue. if (newValue == null || !(newValue instanceof Boolean)) { newValue = false; } oldValue = enabled; enabled = (Boolean)newValue; } else { if (arrayTable == null) { arrayTable = new ArrayTable(); } if (arrayTable.containsKey(key)) oldValue = arrayTable.get(key); // Remove the entry for key if newValue is null // else put in the newValue for key. if (newValue == null) { arrayTable.remove(key); } else { arrayTable.put(key,newValue); } } firePropertyChange(key, oldValue, newValue); } /** * Returns true if the action is enabled. * * @return true if the action is enabled, false otherwise * @see Action#isEnabled */ public boolean isEnabled() { return enabled; } /** * Sets whether the {@code Action} is enabled. The default is {@code true}. * * @param newValue {@code true} to enable the action, {@code false} to * disable it * @see Action#setEnabled */ public void setEnabled(boolean newValue) { boolean oldValue = this.enabled; if (oldValue != newValue) { this.enabled = newValue; firePropertyChange("enabled", Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } } /** * Returns an array of <code>Object</code>s which are keys for * which values have been set for this <code>AbstractAction</code>, * or <code>null</code> if no keys have values set. * @return an array of key objects, or <code>null</code> if no * keys have values set * @since 1.3 */ public Object[] getKeys() { if (arrayTable == null) { return null; } Object[] keys = new Object[arrayTable.size()]; arrayTable.getKeys(keys); return keys; } /** * If any <code>PropertyChangeListeners</code> have been registered, the * <code>changeSupport</code> field describes them. */ protected SwingPropertyChangeSupport changeSupport; /** * Supports reporting bound property changes. This method can be called * when a bound property has changed and it will send the appropriate * <code>PropertyChangeEvent</code> to any registered * <code>PropertyChangeListeners</code>. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (changeSupport == null || (oldValue != null && newValue != null && oldValue.equals(newValue))) { return; } changeSupport.firePropertyChange(propertyName, oldValue, newValue); } /** * Adds a <code>PropertyChangeListener</code> to the listener list. * The listener is registered for all properties. * <p> * A <code>PropertyChangeEvent</code> will get fired in response to setting * a bound property, e.g. <code>setFont</code>, <code>setBackground</code>, * or <code>setForeground</code>. * Note that if the current component is inheriting its foreground, * background, or font from its container, then no event will be * fired in response to a change in the inherited property. * * @param listener The <code>PropertyChangeListener</code> to be added * * @see Action#addPropertyChangeListener */ public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { if (changeSupport == null) { changeSupport = new SwingPropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Removes a <code>PropertyChangeListener</code> from the listener list. * This removes a <code>PropertyChangeListener</code> that was registered * for all properties. * * @param listener the <code>PropertyChangeListener</code> to be removed * * @see Action#removePropertyChangeListener */ public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { if (changeSupport == null) { return; } changeSupport.removePropertyChangeListener(listener); } /** * Returns an array of all the <code>PropertyChangeListener</code>s added * to this AbstractAction with addPropertyChangeListener(). * * @return all of the <code>PropertyChangeListener</code>s added or an empty * array if no listeners have been added * @since 1.4 */ public synchronized PropertyChangeListener[] getPropertyChangeListeners() { if (changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(); } /** * Clones the abstract action. This gives the clone * its own copy of the key/value list, * which is not handled for you by <code>Object.clone()</code>. **/ protected Object clone() throws CloneNotSupportedException { AbstractAction newAction = (AbstractAction)super.clone(); synchronized(this) { if (arrayTable != null) { newAction.arrayTable = (ArrayTable)arrayTable.clone(); } } return newAction; } private void writeObject(ObjectOutputStream s) throws IOException { // Store the default fields s.defaultWriteObject(); // And the keys ArrayTable.writeArrayTable(s, arrayTable); } private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); for (int counter = s.readInt() - 1; counter >= 0; counter--) { putValue((String)s.readObject(), s.readObject()); } }}
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.AccessController;
import javax.swing.event.SwingPropertyChangeSupport;
import sun.security.action.GetPropertyAction;
/**
* This class provides default implementations for the JFC <code>Action</code>
* interface. Standard behaviors like the get and set methods for
* <code>Action</code> object properties (icon, text, and enabled) are defined
* here. The developer need only subclass this abstract class and
* define the <code>actionPerformed</code> method.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans<sup><font size="-2">TM</font></sup>
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Georges Saab
* @see Action
*/
public abstract class AbstractAction implements Action, Cloneable, Serializable
{
/**
* Whether or not actions should reconfigure all properties on null.
*/
private static Boolean RECONFIGURE_ON_NULL;
/**
* Specifies whether action is enabled; the default is true.
*/
protected boolean enabled = true;
/**
* Contains the array of key bindings.
*/
private transient ArrayTable arrayTable;
/**
* Whether or not to reconfigure all action properties from the
* specified event.
*/
static boolean shouldReconfigure(PropertyChangeEvent e) {
if (e.getPropertyName() == null) {
synchronized(AbstractAction.class) {
if (RECONFIGURE_ON_NULL == null) {
RECONFIGURE_ON_NULL = Boolean.valueOf(
AccessController.doPrivileged(new GetPropertyAction(
"swing.actions.reconfigureOnNull", "false")));
}
return RECONFIGURE_ON_NULL;
}
}
return false;
}
/**
* Sets the enabled state of a component from an Action.
*
* @param c the Component to set the enabled state on
* @param a the Action to set the enabled state from, may be null
*/
static void setEnabledFromAction(JComponent c, Action a) {
c.setEnabled((a != null) ? a.isEnabled() : true);
}
/**
* Sets the tooltip text of a component from an Action.
*
* @param c the Component to set the tooltip text on
* @param a the Action to set the tooltip text from, may be null
*/
static void setToolTipTextFromAction(JComponent c, Action a) {
c.setToolTipText(a != null ?
(String)a.getValue(Action.SHORT_DESCRIPTION) : null);
}
static boolean hasSelectedKey(Action a) {
return (a != null && a.getValue(Action.SELECTED_KEY) != null);
}
static boolean isSelected(Action a) {
return Boolean.TRUE.equals(a.getValue(Action.SELECTED_KEY));
}
/**
* Creates an {@code Action}.
*/
public AbstractAction() {
}
/**
* Creates an {@code Action} with the specified name.
*
* @param name the name ({@code Action.NAME}) for the action; a
* value of {@code null} is ignored
*/
public AbstractAction(String name) {
putValue(Action.NAME, name);
}
/**
* Creates an {@code Action} with the specified name and small icon.
*
* @param name the name ({@code Action.NAME}) for the action; a
* value of {@code null} is ignored
* @param icon the small icon ({@code Action.SMALL_ICON}) for the action; a
* value of {@code null} is ignored
*/
public AbstractAction(String name, Icon icon) {
this(name);
putValue(Action.SMALL_ICON, icon);
}
/**
* Gets the <code>Object</code> associated with the specified key.
*
* @param key a string containing the specified <code>key</code>
* @return the binding <code>Object</code> stored with this key; if there
* are no keys, it will return <code>null</code>
* @see Action#getValue
*/
public Object getValue(String key) {
if (key == "enabled") {
return enabled;
}
if (arrayTable == null) {
return null;
}
return arrayTable.get(key);
}
/**
* Sets the <code>Value</code> associated with the specified key.
*
* @param key the <code>String</code> that identifies the stored object
* @param newValue the <code>Object</code> to store using this key
* @see Action#putValue
*/
public void putValue(String key, Object newValue) {
Object oldValue = null;
if (key == "enabled") {
// Treat putValue("enabled") the same way as a call to setEnabled.
// If we don't do this it means the two may get out of sync, and a
// bogus property change notification would be sent.
//
// To avoid dependencies between putValue & setEnabled this
// directly changes enabled. If we instead called setEnabled
// to change enabled, it would be possible for stack
// overflow in the case where a developer implemented setEnabled
// in terms of putValue.
if (newValue == null || !(newValue instanceof Boolean)) {
newValue = false;
}
oldValue = enabled;
enabled = (Boolean)newValue;
} else {
if (arrayTable == null) {
arrayTable = new ArrayTable();
}
if (arrayTable.containsKey(key))
oldValue = arrayTable.get(key);
// Remove the entry for key if newValue is null
// else put in the newValue for key.
if (newValue == null) {
arrayTable.remove(key);
} else {
arrayTable.put(key,newValue);
}
}
firePropertyChange(key, oldValue, newValue);
}
/**
* Returns true if the action is enabled.
*
* @return true if the action is enabled, false otherwise
* @see Action#isEnabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Sets whether the {@code Action} is enabled. The default is {@code true}.
*
* @param newValue {@code true} to enable the action, {@code false} to
* disable it
* @see Action#setEnabled
*/
public void setEnabled(boolean newValue) {
boolean oldValue = this.enabled;
if (oldValue != newValue) {
this.enabled = newValue;
firePropertyChange("enabled",
Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
}
/**
* Returns an array of <code>Object</code>s which are keys for
* which values have been set for this <code>AbstractAction</code>,
* or <code>null</code> if no keys have values set.
* @return an array of key objects, or <code>null</code> if no
* keys have values set
* @since 1.3
*/
public Object[] getKeys() {
if (arrayTable == null) {
return null;
}
Object[] keys = new Object[arrayTable.size()];
arrayTable.getKeys(keys);
return keys;
}
/**
* If any <code>PropertyChangeListeners</code> have been registered, the
* <code>changeSupport</code> field describes them.
*/
protected SwingPropertyChangeSupport changeSupport;
/**
* Supports reporting bound property changes. This method can be called
* when a bound property has changed and it will send the appropriate
* <code>PropertyChangeEvent</code> to any registered
* <code>PropertyChangeListeners</code>.
*/
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
if (changeSupport == null ||
(oldValue != null && newValue != null && oldValue.equals(newValue))) {
return;
}
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Adds a <code>PropertyChangeListener</code> to the listener list.
* The listener is registered for all properties.
* <p>
* A <code>PropertyChangeEvent</code> will get fired in response to setting
* a bound property, e.g. <code>setFont</code>, <code>setBackground</code>,
* or <code>setForeground</code>.
* Note that if the current component is inheriting its foreground,
* background, or font from its container, then no event will be
* fired in response to a change in the inherited property.
*
* @param listener The <code>PropertyChangeListener</code> to be added
*
* @see Action#addPropertyChangeListener
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
changeSupport = new SwingPropertyChangeSupport(this);
}
changeSupport.addPropertyChangeListener(listener);
}
/**
* Removes a <code>PropertyChangeListener</code> from the listener list.
* This removes a <code>PropertyChangeListener</code> that was registered
* for all properties.
*
* @param listener the <code>PropertyChangeListener</code> to be removed
*
* @see Action#removePropertyChangeListener
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
return;
}
changeSupport.removePropertyChangeListener(listener);
}
/**
* Returns an array of all the <code>PropertyChangeListener</code>s added
* to this AbstractAction with addPropertyChangeListener().
*
* @return all of the <code>PropertyChangeListener</code>s added or an empty
* array if no listeners have been added
* @since 1.4
*/
public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
if (changeSupport == null) {
return new PropertyChangeListener[0];
}
return changeSupport.getPropertyChangeListeners();
}
/**
* Clones the abstract action. This gives the clone
* its own copy of the key/value list,
* which is not handled for you by <code>Object.clone()</code>.
**/
protected Object clone() throws CloneNotSupportedException {
AbstractAction newAction = (AbstractAction)super.clone();
synchronized(this) {
if (arrayTable != null) {
newAction.arrayTable = (ArrayTable)arrayTable.clone();
}
}
return newAction;
}
private void writeObject(ObjectOutputStream s) throws IOException {
// Store the default fields
s.defaultWriteObject();
// And the keys
ArrayTable.writeArrayTable(s, arrayTable);
}
private void readObject(ObjectInputStream s) throws ClassNotFoundException,
IOException {
s.defaultReadObject();
for (int counter = s.readInt() - 1; counter >= 0; counter--) {
putValue((String)s.readObject(), s.readObject());
}
}
}