All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.globalmentor.swing.ProxyAction Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 1996-2009 GlobalMentor, Inc. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.globalmentor.swing;

import java.awt.event.*;
import java.beans.*;
import javax.swing.*;

/**
 * Action that stands as a proxy for another action. This action will change its properties to reflect the proxied action, and will activate the proxied action
 * when activated.
 * 

* Some code based upon {@link AbstractButton} *

* @author Garret Wilson */ public class ProxyAction extends AbstractAction implements PropertyChangeListener { /** The action being proxied, or null if no action is being proxied. */ private Action proxiedAction = null; //TODO maybe later make this a weak reference so that if it goes away, we remove ourselves as a listener or something //TODO can we ever get garbage collected? won't the proxied action still be referencing us? /** * @return The action being proxied, or null if no action is being proxied. */ public Action getProxiedAction() { return proxiedAction; } /** The listener we use to find out when the proxied action is changing. */ //TODO del if not needed private PropertyChangeListener actionPropertyChangeListener; /** Whether action properties should be updated when the proxied action changes. */ //TODO fix private boolean shouldUpdateProperties=true; /** * Defines a proxy action object with a default description string and default icon. */ public ProxyAction() { super(); //construct the parent setProxiedAction(null); //show that we don't have a proxied action, yet (this will correctly disable the action) setEnabled(true); //TODO testing } /** * Defines a proxy action object with the specified description string and a default icon. * @param name The name description of the action. */ public ProxyAction(final String name) { super(name); //construct the parent setProxiedAction(null); //show that we don't have a proxied action, yet (this will correctly disable the action) } /** * Defines a proxy action object with the specified description string and the specified icon. * @param name The name description of the action. * @param icon The icon to represent the action. */ public ProxyAction(final String name, final Icon icon) { super(name, icon); //construct the parent setProxiedAction(null); //show that we don't have a proxied action, yet (this will correctly disable the action) } /** * Defines a proxy action object initialized with a proxied action. * @param action The action to proxy. * @see #setProxiedAction */ public ProxyAction(final Action action) { super(); //construct the parent setProxiedAction(action); //set the action being proxied } /** * Sets the proxying Action properties according to values from the proxied Action instance. Be default all properties are updated. * //TODO fix *

* If the Action passed in is null, the following things will occur: *

    *
  • the text is set to null, *
  • the icon is set to null, *
  • enabled is set to true, *
  • the tooltip text is set to null *
*

* @param action The Action from which to get the properties, or null. * @see Action * @see #setProxiedAction */ protected void configurePropertiesFromAction(final Action action) { //TODO Del Log.trace("configuring properties from actions: ", action); //TODO del if(action != null) { //if there is an action if(action instanceof AbstractAction) { //if the action is an abstract action, we can iterate its properties final Object[] keys = ((AbstractAction)action).getKeys(); //get the keys to the action's properties if(keys != null) { //if there are keys for(int i = keys.length - 1; i >= 0; --i) { //look at each key final Object key = keys[i]; //get the key if(key instanceof String) { //if the key is a string final String keyString = (String)key; //cast the key to a string putValue(keyString, action.getValue(keyString)); //put the key's value in our action } } } } else { //if the given action is a normal action, update the values that we know about //TODO don't change the name to work around a Java bug that adds the text to buttons even if they shouldn't be changed //TODO fix; see above comment putValue(Action.NAME, action.getValue(Action.NAME)); putValue(NAME, action.getValue(NAME)); //TODO see if this works in JDK 1.4.1 putValue(SMALL_ICON, action.getValue(SMALL_ICON)); putValue(SHORT_DESCRIPTION, action.getValue(SHORT_DESCRIPTION)); putValue(LONG_DESCRIPTION, action.getValue(LONG_DESCRIPTION)); /*TODO fix; with a proxied action with a mnemonic key of Integer('s'), this traps Alt+F4 if(action.getValue(Action.MNEMONIC_KEY)!=null) //TODO testing; why is this needed, and not for the others? putValue(Action.MNEMONIC_KEY, action.getValue(Action.MNEMONIC_KEY)); */ //TODO fix; this gives strange results sometimes putValue(Action.ACTION_COMMAND_KEY, action.getValue(Action.ACTION_COMMAND_KEY)); //TODO fix; this gives strange results sometimes putValue(Action.ACCELERATOR_KEY, action.getValue(Action.ACCELERATOR_KEY)); putValue(ACCELERATOR_KEY, action.getValue(ACCELERATOR_KEY)); //TODO is this fixed in JDK1.4.x? } setEnabled(action.isEnabled()); //update the enabled property (which isn't stored using a normal key/value pair, as are the other properties) } else { //if there is no action //TODO del Log.trace("no action; disabling"); //TODO del //TODO fix the properties appropriately setEnabled(false); //disable this action } } /** * Sets the proxied Action to receive action events and to notify this action of any property changes. The new Action replaces any * previously set Action but does not affect ActionListeners independently added with addActionListener. If the * Action is already a registered ActionListener for the button, it is not re-registered. *

* A side-effect of setting the Action is that the ActionEvent source's properties are immediately set from the values in the * Action (performed by the method configurePropertiesFromAction) and subsequently updated as the Action's properties * change (via a PropertyChangeListener created by the method createActionPropertyChangeListener). *

* @param action The Action to be proxied, or null if proxying should be discontinued. * @see AbstractAction * @see #getProxiedAction * @see #configurePropertiesFromAction * @see #createActionPropertyChangeListener * @beaninfo bound: true attribute: visualUpdate false description: The Action instance connected with this ActionEvent source */ public void setProxiedAction(final Action action) { final Action oldAction = getProxiedAction(); //see what action we had before if(proxiedAction == null || !proxiedAction.equals(action)) { //if we didn't have an action or if they want a new action proxiedAction = action; //update the action if(oldAction != null) { //if we had an action before //TODO del removeActionListener(oldAction); //don't listen for the old action anymore oldAction.removePropertyChangeListener(this); //don't listen for the old action anymore //TODO del if not needed actionPropertyChangeListener=null; //we no longer need the listener for the old action } configurePropertiesFromAction(proxiedAction); //now that we have a new action, configure our properties from it if(proxiedAction != null) { //if we now have an action /*TODO do we need this? // Don't add if it is already a listener if (!isListener(ActionListener.class, action)) { addActionListener(action); } */ proxiedAction.addPropertyChangeListener(this); //add ourselves as a listener to the property changes of the proxied action /*TODO del if not needed //create a listener to find out when the proxied action changes properties actionPropertyChangeListener=createActionPropertyChangeListener(action); proxiedAction.addPropertyChangeListener(actionPropertyChangeListener); //add the listener to the proxied action */ } } firePropertyChange("proxiedAction", oldAction, proxiedAction); //show that our proxied action has changed TODO use a constant here } /** * Called whenever a bound property in the proxied action is changed. * @param propertyChangeEvent A PropertyChangeEvent object describing the event source and the property that has changed. */ public void propertyChange(final PropertyChangeEvent propertyChangeEvent) { final String propertyName = propertyChangeEvent.getPropertyName(); //get the property name being changed //TODO del Log.trace("proxied property changed: ", propertyName); //TODO del when fixed enabled/disabled //TODO del Log.trace("new value: ", propertyChangeEvent.getNewValue()); //TODO del when fixed enabled/disabled /*TODO fix for when/if we have a weak reference to the proxied action if(proxyAction==null) { //if we no longer have a proxy action (its weak reference has been garbage collected) final Action proxiedAction=(Action)e.getSource(); //get the action being proxied proxiedAction.removePropertyChangeListener(this); //remove ourselves as a listener from the action being proxied } else { //if we still have a proxy action */ //TODO del Log.trace("property being changed: ", propertyName); //TODO del /*TODO del; not needed; "enabled" property changes apparently property propogate if("enabled".equals(propertyName)) { //if the enabled property is changing, we'll have to call the method manually, apparently } else */ if(Components.ENABLED_PROPERTY.equals(propertyName)) { //if the enabled state is changing, we must change the actual property (which will change the property value), rather than just changing the underlying value or the actual property won't change setEnabled(((Boolean)propertyChangeEvent.getNewValue()).booleanValue()); //TODO testing } else if(!"proxiedAction".equals(propertyName)) { //if the proxied action is itself proxied, don't respond when it changes its proxied action TODO use constant here if(!NAME.equals(propertyName)) //TODO don't change the name to work around a Java bug that adds the text to buttons even if they shouldn't be changed TODO check with JDK 1.4.x putValue(propertyName, propertyChangeEvent.getNewValue()); //unconditionally update the value in the proxied action } } /** * Called when the action should be performed. Forwards the event on to the proxied action. A descendant class can override this event for special processing * before and/or after the proxied action receives the event. * @param actionEvent The event causing the action. */ public void actionPerformed(final ActionEvent actionEvent) { if(proxiedAction != null) //if we have a proxied action proxiedAction.actionPerformed(actionEvent); //forward the action } /** * Factory method which creates the PropertyChangeListener used to update the ActionEvent source as properties change on its * Action instance. The default property change listener simply updates all properties. *

* Note that PropertyChangeListeners should avoid holding strong references to the ActionEvent source, as this may hinder garbage * collection of the ActionEvent source and all components in its containment hierarchy. * @param proxiedAction The new action the property changes of which should be monitored. * @see Action * @see #setProxiedAction */ /*TODO del when not needed protected PropertyChangeListener createActionPropertyChangeListener(final Action proxiedAction) { return new ProxyActionPropertyChangeListener(this, proxiedAction); //create a listener and return it } private static class ProxiedActionPropertyChangeListener extends AbstractActionPropertyChangeListener { ProxiedActionPropertyChangeListener(final ProxyAction proxyAction, final Action proxiedAction) { super(proxyAction, proxiedAction); } public void propertyChange(PropertyChangeEvent e) { final String propertyName=e.getPropertyName(); //get the property name being changed final ProxyAction proxyAction=(ProxyAction)getTarget(); //get the proxy action we represent if(proxyAction==null) { //if we no longer have a proxy action (its weak reference has been garbage collected) final Action proxiedAction=(Action)e.getSource(); //get the action being proxied proxiedAction.removePropertyChangeListener(this); //remove ourselves as a listener from the action being proxied } else { //if we still have a proxy action proxyAction.setProperty(propertyName, e.getNewValue()); //unconditionally update the value in the proxied action } } } */ }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy