jdk/src/share/classes/javax/swing/AbstractAction.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 package javax.swing;
       
    26 
       
    27 import java.awt.*;
       
    28 import java.awt.event.*;
       
    29 import java.beans.*;
       
    30 import java.util.Hashtable;
       
    31 import java.util.Enumeration;
       
    32 import java.io.Serializable;
       
    33 import java.io.IOException;
       
    34 import java.io.ObjectInputStream;
       
    35 import java.io.ObjectOutputStream;
       
    36 import java.security.AccessController;
       
    37 import javax.swing.event.SwingPropertyChangeSupport;
       
    38 import sun.security.action.GetPropertyAction;
       
    39 
       
    40 /**
       
    41  * This class provides default implementations for the JFC <code>Action</code>
       
    42  * interface. Standard behaviors like the get and set methods for
       
    43  * <code>Action</code> object properties (icon, text, and enabled) are defined
       
    44  * here. The developer need only subclass this abstract class and
       
    45  * define the <code>actionPerformed</code> method.
       
    46  * <p>
       
    47  * <strong>Warning:</strong>
       
    48  * Serialized objects of this class will not be compatible with
       
    49  * future Swing releases. The current serialization support is
       
    50  * appropriate for short term storage or RMI between applications running
       
    51  * the same version of Swing.  As of 1.4, support for long term storage
       
    52  * of all JavaBeans<sup><font size="-2">TM</font></sup>
       
    53  * has been added to the <code>java.beans</code> package.
       
    54  * Please see {@link java.beans.XMLEncoder}.
       
    55  *
       
    56  * @author Georges Saab
       
    57  * @see Action
       
    58  */
       
    59 public abstract class AbstractAction implements Action, Cloneable, Serializable
       
    60 {
       
    61     /**
       
    62      * Whether or not actions should reconfigure all properties on null.
       
    63      */
       
    64     private static Boolean RECONFIGURE_ON_NULL;
       
    65 
       
    66     /**
       
    67      * Specifies whether action is enabled; the default is true.
       
    68      */
       
    69     protected boolean enabled = true;
       
    70 
       
    71 
       
    72     /**
       
    73      * Contains the array of key bindings.
       
    74      */
       
    75     private transient ArrayTable arrayTable;
       
    76 
       
    77     /**
       
    78      * Whether or not to reconfigure all action properties from the
       
    79      * specified event.
       
    80      */
       
    81     static boolean shouldReconfigure(PropertyChangeEvent e) {
       
    82         if (e.getPropertyName() == null) {
       
    83             synchronized(AbstractAction.class) {
       
    84                 if (RECONFIGURE_ON_NULL == null) {
       
    85                     RECONFIGURE_ON_NULL = Boolean.valueOf(
       
    86                         AccessController.doPrivileged(new GetPropertyAction(
       
    87                         "swing.actions.reconfigureOnNull", "false")));
       
    88                 }
       
    89                 return RECONFIGURE_ON_NULL;
       
    90             }
       
    91         }
       
    92         return false;
       
    93     }
       
    94 
       
    95     /**
       
    96      * Sets the enabled state of a component from an Action.
       
    97      *
       
    98      * @param c the Component to set the enabled state on
       
    99      * @param a the Action to set the enabled state from, may be null
       
   100      */
       
   101     static void setEnabledFromAction(JComponent c, Action a) {
       
   102         c.setEnabled((a != null) ? a.isEnabled() : true);
       
   103     }
       
   104 
       
   105     /**
       
   106      * Sets the tooltip text of a component from an Action.
       
   107      *
       
   108      * @param c the Component to set the tooltip text on
       
   109      * @param a the Action to set the tooltip text from, may be null
       
   110      */
       
   111     static void setToolTipTextFromAction(JComponent c, Action a) {
       
   112         c.setToolTipText(a != null ?
       
   113                          (String)a.getValue(Action.SHORT_DESCRIPTION) : null);
       
   114     }
       
   115 
       
   116     static boolean hasSelectedKey(Action a) {
       
   117         return (a != null && a.getValue(Action.SELECTED_KEY) != null);
       
   118     }
       
   119 
       
   120     static boolean isSelected(Action a) {
       
   121         return Boolean.TRUE.equals(a.getValue(Action.SELECTED_KEY));
       
   122     }
       
   123 
       
   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",
       
   237                                Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
       
   238         }
       
   239     }
       
   240 
       
   241 
       
   242     /**
       
   243      * Returns an array of <code>Object</code>s which are keys for
       
   244      * which values have been set for this <code>AbstractAction</code>,
       
   245      * or <code>null</code> if no keys have values set.
       
   246      * @return an array of key objects, or <code>null</code> if no
       
   247      *                  keys have values set
       
   248      * @since 1.3
       
   249      */
       
   250     public Object[] getKeys() {
       
   251         if (arrayTable == null) {
       
   252             return null;
       
   253         }
       
   254         Object[] keys = new Object[arrayTable.size()];
       
   255         arrayTable.getKeys(keys);
       
   256         return keys;
       
   257     }
       
   258 
       
   259     /**
       
   260      * If any <code>PropertyChangeListeners</code> have been registered, the
       
   261      * <code>changeSupport</code> field describes them.
       
   262      */
       
   263     protected SwingPropertyChangeSupport changeSupport;
       
   264 
       
   265     /**
       
   266      * Supports reporting bound property changes.  This method can be called
       
   267      * when a bound property has changed and it will send the appropriate
       
   268      * <code>PropertyChangeEvent</code> to any registered
       
   269      * <code>PropertyChangeListeners</code>.
       
   270      */
       
   271     protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
       
   272         if (changeSupport == null ||
       
   273             (oldValue != null && newValue != null && oldValue.equals(newValue))) {
       
   274             return;
       
   275         }
       
   276         changeSupport.firePropertyChange(propertyName, oldValue, newValue);
       
   277     }
       
   278 
       
   279 
       
   280     /**
       
   281      * Adds a <code>PropertyChangeListener</code> to the listener list.
       
   282      * The listener is registered for all properties.
       
   283      * <p>
       
   284      * A <code>PropertyChangeEvent</code> will get fired in response to setting
       
   285      * a bound property, e.g. <code>setFont</code>, <code>setBackground</code>,
       
   286      * or <code>setForeground</code>.
       
   287      * Note that if the current component is inheriting its foreground,
       
   288      * background, or font from its container, then no event will be
       
   289      * fired in response to a change in the inherited property.
       
   290      *
       
   291      * @param listener  The <code>PropertyChangeListener</code> to be added
       
   292      *
       
   293      * @see Action#addPropertyChangeListener
       
   294      */
       
   295     public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
       
   296         if (changeSupport == null) {
       
   297             changeSupport = new SwingPropertyChangeSupport(this);
       
   298         }
       
   299         changeSupport.addPropertyChangeListener(listener);
       
   300     }
       
   301 
       
   302 
       
   303     /**
       
   304      * Removes a <code>PropertyChangeListener</code> from the listener list.
       
   305      * This removes a <code>PropertyChangeListener</code> that was registered
       
   306      * for all properties.
       
   307      *
       
   308      * @param listener  the <code>PropertyChangeListener</code> to be removed
       
   309      *
       
   310      * @see Action#removePropertyChangeListener
       
   311      */
       
   312     public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
       
   313         if (changeSupport == null) {
       
   314             return;
       
   315         }
       
   316         changeSupport.removePropertyChangeListener(listener);
       
   317     }
       
   318 
       
   319 
       
   320     /**
       
   321      * Returns an array of all the <code>PropertyChangeListener</code>s added
       
   322      * to this AbstractAction with addPropertyChangeListener().
       
   323      *
       
   324      * @return all of the <code>PropertyChangeListener</code>s added or an empty
       
   325      *         array if no listeners have been added
       
   326      * @since 1.4
       
   327      */
       
   328     public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
       
   329         if (changeSupport == null) {
       
   330             return new PropertyChangeListener[0];
       
   331         }
       
   332         return changeSupport.getPropertyChangeListeners();
       
   333     }
       
   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) throws ClassNotFoundException,
       
   361         IOException {
       
   362         s.defaultReadObject();
       
   363         for (int counter = s.readInt() - 1; counter >= 0; counter--) {
       
   364             putValue((String)s.readObject(), s.readObject());
       
   365         }
       
   366     }
       
   367 }