org.jdesktop.application.ApplicationAction Maven / Gradle / Ivy
Show all versions of tink-app Show documentation
/*
* Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.application;
import org.jdesktop.application.Task.InputBlocker;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
/**
* The {@link javax.swing.Action} class used to implement the
* @Action annotation. This class is typically not
* instantiated directly, it's created as a side effect of constructing
* an ApplicationActionMap:
*
* public class MyActions {
* @Action public void anAction() { } // an @Action named "anAction"
* }
* ApplicationContext ac = ApplicationContext.getInstance();
* ActionMap actionMap = ac.getActionMap(new MyActions());
* myButton.setAction(actionMap.get("anAction"));
*
*
*
* When an ApplicationAction is constructed, it initializes all of its
* properties from the specified ResourceMap. Resource names
* must match the {@code @Action's} name, which is the name of the
* corresponding method, or the value of the optional {@code @Action} name
* parameter. To initialize the text and shortDescription properties
* of the action named "anAction" in the previous example, one
* would define two resources:
*
* anAction.Action.text = Button/Menu/etc label text for anAction
* anAction.Action.shortDescription = Tooltip text for anAction
*
*
*
* A complete description of the mapping between resources and Action
* properties can be found in the ApplicationAction {@link
* #ApplicationAction constructor} documentation.
*
*
* An ApplicationAction's enabled and selected
* properties can be delegated to boolean properties of the
* Actions class, by specifying the corresponding property names.
* This can be done with the {@code @Action} annotation, e.g.:
*
* public class MyActions {
* @Action(enabledProperty = "anActionEnabled")
* public void anAction() { }
* public boolean isAnActionEnabled() {
* // will fire PropertyChange when anActionEnabled changes
* return anActionEnabled;
* }
* }
*
* If the MyActions class supports PropertyChange events, then then
* ApplicationAction will track the state of the specified property
* ("anActionEnabled" in this case) with a PropertyChangeListener.
*
*
* ApplicationActions can automatically block the GUI while the
* actionPerformed method is running, depending on the value of
* block annotation parameter. For example, if the value of block is
* Task.BlockingScope.ACTION, then the action will be disabled while
* the actionPerformed method runs.
*
*
* An ApplicationAction can have a proxy Action, i.e.
* another Action that provides the actionPerformed method,
* the enabled/selected properties, and values for the Action's long
* and short descriptions. If the proxy property is set, this
* ApplicationAction tracks all of the aforementioned properties, and
* the actionPerformed method just calls the proxy's
* actionPerformed method. If a proxySource is
* specified, then it becomes the source of the ActionEvent that's
* passed to the proxy actionPerformed method. Proxy action
* dispatching is as simple as this:
*
* public void actionPerformed(ActionEvent actionEvent) {
* javax.swing.Action proxy = getProxy();
* if (proxy != null) {
* actionEvent.setSource(getProxySource());
* proxy.actionPerformed(actionEvent);
* }
* // ....
* }
*
*
*
* @author Hans Muller ([email protected])
* @see ApplicationContext#getActionMap(Object)
* @see ResourceMap
*/
public class ApplicationAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ApplicationAction.class.getName());
private final ApplicationActionMap appAM;
private final ResourceMap resourceMap;
private final String actionName; // see getName()
private final Method actionMethod; // The @Action method
private final String enabledProperty; // names a bound appAM.getActionsClass() property
private final Method isEnabledMethod; // Method object for is/getEnabledProperty
private final Method setEnabledMethod; // Method object for setEnabledProperty
private final String selectedProperty; // names a bound appAM.getActionsClass() property
private final Method isSelectedMethod; // Method object for is/getSelectedProperty
private final Method setSelectedMethod; // Method object for setSelectedProperty
private final Task.BlockingScope block;
private javax.swing.Action proxy = null;
private Object proxySource = null;
private PropertyChangeListener proxyPCL = null;
/**
* Construct an ApplicationAction that implements an @Action.
*
*
* If a {@code ResourceMap} is provided, then all of the
* {@link javax.swing.Action Action} properties are initialized
* with the values of resources whose key begins with {@code baseName}.
* ResourceMap keys are created by appending an @Action resource
* name, like "Action.shortDescription" to the @Action's baseName
* For example, Given an @Action defined like this:
*
* @Action void actionBaseName() { }
*
*
* Then the shortDescription resource key would be
* actionBaseName.Action.shortDescription
, as in:
*
* actionBaseName.Action.shortDescription = Do perform some action
*
*
*
* The complete set of @Action resources is:
*
* Action.icon
* Action.text
* Action.shortDescription
* Action.longDescription
* Action.smallIcon
* Action.largeIcon
* Action.command
* Action.accelerator
* Action.mnemonic
* Action.displayedMnemonicIndex
*
*
*
* A few the resources are handled specially:
*
* - Action.text
* Used to initialize the Action properties with keys
* Action.NAME, Action.MNEMONIC_KEY and
* Action.DISPLAYED_MNEMONIC_INDEX.
* If the resources's value contains an "&" or an "_" it's
* assumed to mark the following character as the mnemonic.
* If Action.mnemonic/Action.displayedMnemonic resources are
* also defined (an odd case), they'll override the mnemonic
* specfied with the Action.text marker character.
*
* - Action.icon
* Used to initialize both ACTION.SMALL_ICON,LARGE_ICON. If
* Action.smallIcon or Action.largeIcon resources are also defined
* they'll override the value defined for Action.icon.
*
* - Action.displayedMnemonicIndexKey
* The corresponding javax.swing.Action constant is only defined in Java SE 6.
* We'll set the Action property in Java SE 5 too.
*
*
* @param appAM the ApplicationActionMap this action is being constructed for.
* @param resourceMap initial Action properties are loaded from this ResourceMap.
* @param baseName the name of the @Action
* @param actionMethod unless a proxy is specified, actionPerformed calls this method.
* @param enabledProperty name of the enabled property.
* @param selectedProperty name of the selected property.
* @param block how much of the GUI to block while this action executes.
*
* @see #getName
* @see ApplicationActionMap#getActionsClass
* @see ApplicationActionMap#getActionsObject
*/
public ApplicationAction(ApplicationActionMap appAM,
ResourceMap resourceMap,
String baseName,
Method actionMethod,
String enabledProperty,
String selectedProperty,
Task.BlockingScope block) {
if (appAM == null) {
throw new IllegalArgumentException("null appAM");
}
if (baseName == null) {
throw new IllegalArgumentException("null baseName");
}
this.appAM = appAM;
this.resourceMap = resourceMap;
this.actionName = baseName;
this.actionMethod = actionMethod;
this.enabledProperty = enabledProperty;
this.selectedProperty = selectedProperty;
this.block = block;
/* If enabledProperty is specified, lookup up the is/set methods and
* verify that the former exists.
*/
if (enabledProperty != null) {
setEnabledMethod = propertySetMethod(enabledProperty, boolean.class);
isEnabledMethod = propertyGetMethod(enabledProperty);
if (isEnabledMethod == null) {
throw newNoSuchPropertyException(enabledProperty);
}
}
else {
this.isEnabledMethod = null;
this.setEnabledMethod = null;
}
/* If selectedProperty is specified, lookup up the is/set methods and
* verify that the former exists.
*/
if (selectedProperty != null) {
setSelectedMethod = propertySetMethod(selectedProperty, boolean.class);
isSelectedMethod = propertyGetMethod(selectedProperty);
if (isSelectedMethod == null) {
throw newNoSuchPropertyException(selectedProperty);
}
super.putValue(SELECTED_KEY, Boolean.FALSE);
}
else {
this.isSelectedMethod = null;
this.setSelectedMethod = null;
}
if (resourceMap != null) {
initActionProperties(resourceMap, baseName);
}
}
/* Shorter convenience constructor used to create ProxyActions,
* see ApplicationActionMap.addProxyAction().
*/
ApplicationAction(ApplicationActionMap appAM, ResourceMap resourceMap, String actionName) {
this(appAM, resourceMap, actionName, null, null, null, Task.BlockingScope.NONE);
}
private IllegalArgumentException newNoSuchPropertyException(String propertyName) {
String actionsClassName = appAM.getActionsClass().getName();
String msg = String.format("no property named %s in %s", propertyName, actionsClassName);
return new IllegalArgumentException(msg);
}
/**
* The name of the {@code @Action} enabledProperty
* whose value is returned by {@link #isEnabled isEnabled},
* or null.
*
* @return the name of the enabledProperty or null.
* @see #isEnabled
*/
String getEnabledProperty() {
return enabledProperty;
}
/**
* The name of the {@code @Action} selectedProperty whose value is
* returned by {@link #isSelected isSelected}, or null.
*
* @return the name of the selectedProperty or null.
* @see #isSelected
*/
String getSelectedProperty() {
return selectedProperty;
}
/**
* Return the proxy for this action or null.
*
* @return the value of the proxy property.
* @see #setProxy
* @see #setProxySource
* @see #actionPerformed
*/
public javax.swing.Action getProxy() {
return proxy;
}
/**
* Set the proxy for this action. If the proxy is non-null then
* we delegate/track the following:
*
* - actionPerformed
* Our actionPerformed method calls the delegate's after
* the ActionEvent source to be the value of getProxySource
*
* - shortDescription
* If the proxy's shortDescription, i.e. the value for key
* {@link javax.swing.Action#SHORT_DESCRIPTION SHORT_DESCRIPTION} is not null,
* then set this action's shortDescription. Most Swing components use
* the shortDescription to initialize their tooltip.
*
* - longDescription
* If the proxy's longDescription, i.e. the value for key
* {@link javax.swing.Action#LONG_DESCRIPTION LONG_DESCRIPTION} is not null,
* then set this action's longDescription.
*
*
* @see #setProxy
* @see #setProxySource
* @see #actionPerformed
*/
public void setProxy(javax.swing.Action proxy) {
javax.swing.Action oldProxy = this.proxy;
this.proxy = proxy;
if (oldProxy != null) {
oldProxy.removePropertyChangeListener(proxyPCL);
proxyPCL = null;
}
if (this.proxy != null) {
updateProxyProperties();
proxyPCL = new ProxyPCL();
proxy.addPropertyChangeListener(proxyPCL);
}
else if (oldProxy != null) {
setEnabled(false);
setSelected(false);
}
firePropertyChange("proxy", oldProxy, this.proxy);
}
/**
* Return the value that becomes the ActionEvent source before
* the ActionEvent is passed along to the proxy Action.
*
* @return the value of the proxySource property.
* @see #getProxy
* @see #setProxySource
* @see ActionEvent#getSource
*/
public Object getProxySource() {
return proxySource;
}
/**
* Set the value that becomes the ActionEvent source before
* the ActionEvent is passed along to the proxy Action.
*
* @param source the ActionEvent source/
* @see #getProxy
* @see #getProxySource
* @see ActionEvent#setSource
*/
public void setProxySource(Object source) {
Object oldValue = this.proxySource;
this.proxySource = source;
firePropertyChange("proxySource", oldValue, this.proxySource);
}
private void maybePutDescriptionValue(String key, javax.swing.Action proxy) {
Object s = proxy.getValue(key);
if (s instanceof String) {
putValue(key, (String)s);
}
}
private void updateProxyProperties() {
javax.swing.Action proxy = getProxy();
if (proxy != null) {
setEnabled(proxy.isEnabled());
Object s = proxy.getValue(SELECTED_KEY);
setSelected((s instanceof Boolean) && ((Boolean)s).booleanValue());
maybePutDescriptionValue(javax.swing.Action.SHORT_DESCRIPTION, proxy);
maybePutDescriptionValue(javax.swing.Action.LONG_DESCRIPTION, proxy);
}
}
/* This PCL is added to the proxy action, i.e. getProxy(). We
* track the following properties of the proxy action we're bound to:
* enabled, selected, longDescription, shortDescription. We only
* mirror the description properties if they're non-null.
*/
private class ProxyPCL implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if ((propertyName == null) ||
"enabled".equals(propertyName) ||
"selected".equals(propertyName) ||
javax.swing.Action.SHORT_DESCRIPTION.equals(propertyName) ||
javax.swing.Action.LONG_DESCRIPTION.equals(propertyName)) {
updateProxyProperties();
}
}
}
/* The corresponding javax.swing.Action constants are only
* defined in Mustang (1.6), see
* http://download.java.net/jdk6/docs/api/javax/swing/Action.html
*/
private static final String SELECTED_KEY = "SwingSelectedKey";
private static final String DISPLAYED_MNEMONIC_INDEX_KEY = "SwingDisplayedMnemonicIndexKey";
private static final String LARGE_ICON_KEY = "SwingLargeIconKey";
/* Init all of the javax.swing.Action properties for the @Action
* named actionName.
*/
private void initActionProperties(ResourceMap resourceMap, String baseName) {
boolean iconOrNameSpecified = false; // true if Action's icon/name properties set
String typedName = null;
// Action.text => Action.NAME,MNEMONIC_KEY,DISPLAYED_MNEMONIC_INDEX_KEY
String text = resourceMap.getString(baseName + ".Action.text");
if (text != null) {
MnemonicText.configure(this, text);
iconOrNameSpecified = true;
}
// Action.mnemonic => Action.MNEMONIC_KEY
Integer mnemonic = resourceMap.getKeyCode(baseName + ".Action.mnemonic");
if (mnemonic != null) {
putValue(javax.swing.Action.MNEMONIC_KEY, mnemonic);
}
// Action.mnemonic => Action.DISPLAYED_MNEMONIC_INDEX_KEY
Integer index = resourceMap.getInteger(baseName + ".Action.displayedMnemonicIndex");
if (index != null) {
putValue(DISPLAYED_MNEMONIC_INDEX_KEY, index);
}
// Action.accelerator => Action.ACCELERATOR_KEY
KeyStroke key = resourceMap.getKeyStroke(baseName + ".Action.accelerator");
if (key != null) {
putValue(javax.swing.Action.ACCELERATOR_KEY, key);
}
// Action.icon => Action.SMALL_ICON,LARGE_ICON_KEY
Icon icon = resourceMap.getIcon(baseName + ".Action.icon");
if (icon != null) {
putValue(javax.swing.Action.SMALL_ICON, icon);
putValue(LARGE_ICON_KEY, icon);
iconOrNameSpecified = true;
}
// Action.smallIcon => Action.SMALL_ICON
Icon smallIcon = resourceMap.getIcon(baseName + ".Action.smallIcon");
if (smallIcon != null) {
putValue(javax.swing.Action.SMALL_ICON, smallIcon);
iconOrNameSpecified = true;
}
// Action.largeIcon => Action.LARGE_ICON
Icon largeIcon = resourceMap.getIcon(baseName + ".Action.largeIcon");
if (largeIcon != null) {
putValue(LARGE_ICON_KEY, largeIcon);
iconOrNameSpecified = true;
}
// Action.shortDescription => Action.SHORT_DESCRIPTION
putValue(javax.swing.Action.SHORT_DESCRIPTION,
resourceMap.getString(baseName + ".Action.shortDescription"));
// Action.longDescription => Action.LONG_DESCRIPTION
putValue(javax.swing.Action.LONG_DESCRIPTION,
resourceMap.getString(baseName + ".Action.longDescription"));
// Action.command => Action.ACTION_COMMAND_KEY
putValue(javax.swing.Action.ACTION_COMMAND_KEY,
resourceMap.getString(baseName + ".Action.command"));
// If no visual was defined for this Action, i.e. no text
// and no icon, then we default Action.NAME
if (!iconOrNameSpecified) {
putValue(javax.swing.Action.NAME, actionName);
}
}
private String propertyMethodName(String prefix, String propertyName) {
return prefix + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
}
private Method propertyGetMethod(String propertyName) {
String[] getMethodNames = {
propertyMethodName("is", propertyName),
propertyMethodName("get", propertyName)
};
Class actionsClass = appAM.getActionsClass();
for (String name : getMethodNames) {
try {
return actionsClass.getMethod(name);
}
catch(NoSuchMethodException ignore) { }
}
return null;
}
private Method propertySetMethod(String propertyName, Class type) {
Class actionsClass = appAM.getActionsClass();
try {
return actionsClass.getMethod(propertyMethodName("set", propertyName), type);
}
catch(NoSuchMethodException ignore) {
return null;
}
}
/**
*
* The name of this Action. This string begins with the name
* the corresponding @Action method (unless the name
* @Action parameter was specified).
*
*
* This name is used as a prefix to look up action resources,
* and the ApplicationContext Framework uses it as the key for this
* Action in ApplicationActionMaps.
*
*
* Note: this property should not confused with the {@link
* javax.swing.Action#NAME Action.NAME} key. That key is actually
* used to initialize the text properties of Swing
* components, which is why we call the corresponding
* ApplicationAction resource "Action.text", as in:
*
* myCloseButton.Action.text = Close
*
*
*
* @return the (read-only) value of the name property
*/
public String getName() {
return actionName;
}
/**
* The resourceMap for this Action.
*
* @return the (read-only) value of the resourceMap property
*/
public ResourceMap getResourceMap() {
return resourceMap;
}
/**
*
* Provides parameter values to @Action methods. By default, parameter
* values are selected based exclusively on their type:
*
*
* Parameter Type
* Parameter Value
*
*
* ActionEvent
* actionEvent
*
*
* javax.swing.Action
* this ApplicationAction object
*
*
* ActionMap
* the ActionMap that contains this Action
*
*
* ResourceMap
* the ResourceMap of the the ActionMap that contains this Action
*
*
* ApplicationContext
* the value of ApplicationContext.getInstance()
*
*
*
*
* ApplicationAction subclasses may also select values based on
* the value of the Action.Parameter annotation, which is
* passed along as the pKey argument to this method:
*
* @Action public void doAction(@Action.Parameter("myKey") String myParameter) {
* // The value of myParameter is computed by:
* // getActionArgument(String.class, "myKey", actionEvent)
* }
*
*
*
* If pType and pKey aren't recognized, this method
* calls {@link #actionFailed} with an IllegalArgumentException.
*
*
* @param pType parameter type
* @param pKey the value of the @Action.Parameter annotation
* @param actionEvent the ActionEvent that trigged this Action
*/
protected Object getActionArgument(Class pType, String pKey, ActionEvent actionEvent) {
Object argument = null;
if (pType == ActionEvent.class) {
argument = actionEvent;
}
else if (pType == javax.swing.Action.class) {
argument = this;
}
else if (pType == ActionMap.class) {
argument = appAM;
}
else if (pType == ResourceMap.class) {
argument = resourceMap;
}
else if (pType == ApplicationContext.class) {
argument = appAM.getContext();
}
else if (pType == Application.class) {
argument = appAM.getContext().getApplication();
}
else {
Exception e = new IllegalArgumentException("unrecognized @Action method parameter");
actionFailed(actionEvent, e);
}
return argument;
}
private Task.InputBlocker createInputBlocker(Task task, ActionEvent event) {
Object target = event.getSource();
if (block == Task.BlockingScope.ACTION) {
target = this;
}
return new DefaultInputBlocker(task, block, target, this);
}
private void noProxyActionPerformed(ActionEvent actionEvent) {
Object taskObject = null;
/* Create the arguments array for actionMethod by
* calling getActionArgument() for each parameter.
*/
Annotation[][] allPAnnotations = actionMethod.getParameterAnnotations();
Class>[] pTypes = actionMethod.getParameterTypes();
Object[] arguments = new Object[pTypes.length];
for(int i = 0; i < pTypes.length; i++) {
String pKey = null;
for(Annotation pAnnotation : allPAnnotations[i]) {
if (pAnnotation instanceof Action.Parameter) {
pKey = ((Action.Parameter)pAnnotation).value();
break;
}
}
arguments[i] = getActionArgument(pTypes[i], pKey, actionEvent);
}
/* Call target.actionMethod(arguments). If the return value
* is a Task, then execute it.
*/
try {
Object target = appAM.getActionsObject();
taskObject = actionMethod.invoke(target, arguments);
}
catch (Exception e) {
actionFailed(actionEvent, e);
}
if (taskObject instanceof Task) {
Task task = (Task)taskObject;
if (task.getInputBlocker() == null) {
task.setInputBlocker(createInputBlocker(task, actionEvent));
}
ApplicationContext ctx = appAM.getContext();
ctx.getTaskService().execute(task);
}
}
/**
* This method implements this Action's behavior.
*
* If there's a proxy Action then call its actionPerformed
* method. Otherwise, call the @Action method with parameter
* values provided by {@code getActionArgument()}. If anything goes wrong
* call {@code actionFailed()}.
*
* @param actionEvent @{inheritDoc}
* @see #setProxy
* @see #getActionArgument
* @see Task
*/
public void actionPerformed(ActionEvent actionEvent) {
javax.swing.Action proxy = getProxy();
if (proxy != null) {
actionEvent.setSource(getProxySource());
proxy.actionPerformed(actionEvent);
}
else if (actionMethod != null) {
noProxyActionPerformed(actionEvent);
}
}
/**
* If the proxy action is null and {@code enabledProperty} was
* specified, then return the value of the enabled property's
* is/get method applied to our ApplicationActionMap's
* {@code actionsObject}.
* Otherwise return the value of this Action's enabled property.
*
* @return {@inheritDoc}
* @see #setProxy
* @see #setEnabled
* @see ApplicationActionMap#getActionsObject
*/
@Override public boolean isEnabled() {
if ((getProxy() != null) || (isEnabledMethod == null)) {
return super.isEnabled();
}
else {
try {
Object b = isEnabledMethod.invoke(appAM.getActionsObject());
return (Boolean)b;
}
catch (Exception e) {
throw newInvokeError(isEnabledMethod, e);
}
}
}
/**
* If the proxy action is null and {@code enabledProperty} was
* specified, then set the value of the enabled property by
* invoking the corresponding {@code set} method on our
* ApplicationActionMap's {@code actionsObject}.
* Otherwise set the value of this Action's enabled property.
*
* @param enabled {@inheritDoc}
* @see #setProxy
* @see #isEnabled
* @see ApplicationActionMap#getActionsObject
*/
@Override public void setEnabled(boolean enabled) {
if ((getProxy() != null) || (setEnabledMethod == null)) {
super.setEnabled(enabled);
}
else {
try {
setEnabledMethod.invoke(appAM.getActionsObject(), enabled);
}
catch (Exception e) {
throw newInvokeError(setEnabledMethod, e, enabled);
}
}
}
/**
* If the proxy action is null and {@code selectedProperty} was
* specified, then return the value of the selected property's
* is/get method applied to our ApplicationActionMap's {@code actionsObject}.
* Otherwise return the value of this Action's enabled property.
*
* @return true if this Action's JToggleButton is selected
* @see #setProxy
* @see #setSelected
* @see ApplicationActionMap#getActionsObject
*/
public boolean isSelected() {
if ((getProxy() != null) || (isSelectedMethod == null)) {
Object v = getValue(SELECTED_KEY);
return (v instanceof Boolean) ? ((Boolean)v).booleanValue() : false;
}
else {
try {
Object b = isSelectedMethod.invoke(appAM.getActionsObject());
return (Boolean)b;
}
catch (Exception e) {
throw newInvokeError(isSelectedMethod, e);
}
}
}
/**
* If the proxy action is null and {@code selectedProperty} was
* specified, then set the value of the selected property by
* invoking the corresponding {@code set} method on our
* ApplicationActionMap's {@code actionsObject}.
* Otherwise set the value of this Action's selected property.
*
* @param selected this Action's JToggleButton's value
* @see #setProxy
* @see #isSelected
* @see ApplicationActionMap#getActionsObject
*/
public void setSelected(boolean selected) {
if ((getProxy() != null) || (setSelectedMethod == null)) {
super.putValue(SELECTED_KEY, Boolean.valueOf(selected));
}
else {
try {
super.putValue(SELECTED_KEY, Boolean.valueOf(selected));
if (selected != isSelected()) {
setSelectedMethod.invoke(appAM.getActionsObject(), selected);
}
}
catch (Exception e) {
throw newInvokeError(setSelectedMethod, e, selected);
}
}
}
/**
* Keeps the {@code @Action selectedProperty} in sync when
* the value of {@code key} is {@code Action.SELECTED_KEY}.
*
* @param key {@inheritDoc}
* @param value {@inheritDoc}
*/
public void putValue(String key, Object value) {
if (SELECTED_KEY.equals(key) && (value instanceof Boolean)) {
setSelected((Boolean)value);
}
else {
super.putValue(key, value);
}
}
/* Throw an Error because invoking Method m on the actionsObject,
* with the specified arguments, failed.
*/
private Error newInvokeError(Method m, Exception e, Object... args) {
String argsString = (args.length == 0) ? "" : args[0].toString();
for(int i = 1; i < args.length; i++) {
argsString += ", " + args[i];
}
String actionsClassName = appAM.getActionsObject().getClass().getName();
String msg = String.format("%s.%s(%s) failed", actionsClassName, m, argsString);
return new Error(msg, e);
}
/* Forward the @Action class's PropertyChangeEvent e to this
* Action's PropertyChangeListeners using actionPropertyName instead
* the original @Action class's property name. This method is used
* by ApplicationActionMap#ActionsPCL to forward @Action
* enabledProperty and selectedProperty changes.
*/
void forwardPropertyChangeEvent(PropertyChangeEvent e, String actionPropertyName) {
if ("selected".equals(actionPropertyName) && (e.getNewValue() instanceof Boolean)) {
putValue(SELECTED_KEY, (Boolean)e.getNewValue());
}
firePropertyChange(actionPropertyName, e.getOldValue(), e.getNewValue());
}
/* Log enough output for a developer to figure out
* what went wrong.
*/
private void actionFailed(ActionEvent actionEvent, Exception e) {
// TBD Log an error
// e.printStackTrace();
throw new Error(e);
}
/**
* Returns a string representation of this
* ApplicationAction that should be useful for debugging.
* If the action is enabled it's name is enclosed by parentheses;
* if it's selected then a "+" appears after the name. If the
* action will appear with a text label, then that's included too.
* If the action has a proxy, then we append the string for
* the proxy action.
*
* @return A string representation of this ApplicationAction
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName());
sb.append(" ");
boolean enabled = isEnabled();
if (!enabled) { sb.append("("); }
sb.append(getName());
Object selectedValue = getValue(SELECTED_KEY);
if (selectedValue instanceof Boolean) {
if (((Boolean)selectedValue).booleanValue()) {
sb.append("+");
}
}
if (!enabled) { sb.append(")"); }
Object nameValue = getValue(javax.swing.Action.NAME); // [getName()].Action.text
if (nameValue instanceof String) {
sb.append(" \"");
sb.append((String)nameValue);
sb.append("\"");
}
proxy = getProxy();
if (proxy != null) {
sb.append(" Proxy for: ");
sb.append(proxy.toString());
}
return sb.toString();
}
}