@Retention(value=SOURCE) @Target(value=FIELD) public @interface ActionState
ActionRegistration.enabledOn()
and ActionRegistration.checkedOn()
to control action's enabled or checked state. The annotation
can be only applied on context actions, which have a single parameter constructor,
which accept the model object - see Actions.context(java.lang.Class, boolean, boolean, org.openide.util.ContextAwareAction, java.lang.String, java.lang.String, java.lang.String, boolean)
.
When used as ActionRegistration.checkedOn()
value, the annotated action will change
to toggle on/off action, represented by a checkbox (menu) or a toggle button (toolbar).
The action state will track the model property specified by this
annotation. Toggle actions become enabled when the model object is
found in the Lookup, and checked (or toggled on) when the model property
is set to a defined value (usually true
)
The ActionState.type()
specifies type which is searched for in the Lookup
and
if an instance is found, it is used as the model object. If the ActionState.type()
is not set,
the the type inferred from Action's
constructor (see ActionRegistration
) will be used to find the model.
The ActionState.property()
specifies bean property whose value should be used to
determine checked state. The obtained value is compared using ActionState.checkedValue()
as follows:
Boolean.TRUE
or the ActionState.checkedValue()
,
if present.
#checkValue
is ActionState.NULL_VALUE
, the action is checked if and only if
the value is null
.
#checkValue
is ActionState.NON_NULL_VALUE
, the action is checked if and only if
the value is not null
.
Enum.name
is compared to #checkValue
Collection
or Map
, state evaluates to the Collection#isEmpty
or
Map#isEmpty
, respectively.
Number
, state will be true if value evaluates to a positive integer.
false
(unchecked) otherwise.
If ActionState.type()
is set to Action
.class, the annotated element must
be an Action
subclass. Action.getValue(java.lang.String)
will be used to determine
the state. The Action delegate will not be instantiated eagerly, but only
after the necessary context type becomes available in Lookup.
This support minimizes from premature code loading for custom action implementations.
Important note: if your Action implements ContextAwareAction
,
or one of the Presenter
interfaces, it is eager and will be loaded immediately !
Changes to the model object will be tracked using event listener pattern. The annotation-supplied
delegate attempts to PropertyChangeListener
and ChangeListener
automatically; \
other listener interfaces must be specified using ActionState.listenOn()
value. Finally, ActionState.listenOnMethod()
specifies which listener method will trigger
state update; by default, all listener method calls will update the action state.
The ActionState
annotation may be also used as a value of ActionRegistration.enabledOn()
and causes the annotated Action to be enabled not only on presence of object of the context type,
but also based on the model property. The property, enable value and listener is specified the
same way as for "checked" state. See the above text.
If a completely custom behaviour is desired, the system can finally delegate Action.isEnabled()
and
getValue
(Action.SELECTED_KEY
) to the action implementation itself: use ActionState.useActionInstance()
value.
Here are several examples of @ActionState
usage:
To define action, which enables on modified DataObjects do the following
registration:
@ActionID(category = "Example", id = "example.SaveAction")
@ActionRegistration(displayName = "Save modified",
enabledOn = @ActionState(property = "modified")
)
public class ExampleAction implements ActionListener {
public ExampleAction(DataObject d) {
// ...
}
public void actionPerformed(ActionEvent e) {
// ...
}
}
The action will be instantiated and run only after:
modified
property becomes true
enum SelectionMode {
Rectangular,
normal
}
@ActionID(category = "Example", id = "example.RectSelection")
@ActionRegistration(displayName = "Toggle rectangular selection", checkedOn = @ActionState(
property = "selectionMode", checkedValue = "Rectangular", listenOn = EditorStateListener.class)
)
public class RectangularSelectionAction implements ActionListener {
public RectangularSelectionAction(EditorInterface editor) {
// ...
}
@Override
public void actionPerformed(ActionEvent e) {
}
}
The action enables when EditorInterface
appears in the action Lookup. Then,
its state will be derived from EditorInterface.selectionMode
property. Since
there's a custom listener interface for this value, it must be specified using ActionState.listenOn()
.
Finally, if the action needs to perform its own special magic to check enable state, we
hand over final control to the action, but the annotation-introduced wrappers will still
create action instance for a new model object, attach and detach listeners on it and ensure
that UI will not be strongly referenced from the model for proper garbage collection:
@ActionID(category = "Example", id = "example.SelectPrevious")
@ActionRegistration(displayName = "Selects previous item", checkedOn = @ActionState(
listenOn = ListSelectionListener.class, useActionInstance = true)
)
public class SelectPreviousAction extends AbstractAction {
private final ListSelectionModel model;
public SelectPreviousAction(ListSelectionModel model) {
this.model = model;
}
@Override
public boolean isEnabled() {
return model.getAnchorSelectionIndex() > 0;
}
@Override
public void actionPerformed(ActionEvent e) {
}
}
The system will do the necessary bookkeeping, but the action decides using its
Action.isEnabled()
implementation.Modifier and Type | Fields and Description |
---|---|
static String |
NON_NULL_VALUE
|
static String |
NULL_VALUE
|
Modifier and Type | Optional Element and Description |
---|---|
String |
checkedValue
The value which makes the action checked.
|
Class |
listenOn
Custom listener interface to monitor for changes.
|
String |
listenOnMethod
Allows to pick one listener method, which will trigger action state update.
|
String |
property
Property name whose value represents the state.
|
Class<?> |
type
The type which the action will look for in its context.
|
boolean |
useActionInstance
If true, the target system will delegate to the action instance itself.
|
public static final String NULL_VALUE
public static final String NON_NULL_VALUE
public abstract Class<?> type
Object.class
(the default) the context object will be used and the property will be read
from the context object. Only applicable if the action accepts single object.
Action.class
: the ActionState.property()
action value will be used,
as obtained by Action.getValue(java.lang.String)
.
@ActionState
is used in ActionRegistration.enabledOn()
, the
type
can be left unspecified, defaulting to the context type for the action.public abstract String property
ActionState.type()
class; read-only properties are
supported. If the target class supports attaching
PropertyChangeListener
or ChangeListener
, the action will
attach a listener (PropertyChangeListener
takes precedence) and will fire
appropriate state events when the property property changes.
In the case that checked state is delegated to Action
, the property
default is different depending on the context the annotation is used:
ActionRegistration.enabledOn()
, the property defaults to "enabled"
@ActionState
directly annotates to element}, the property defaults to Action.SELECTED_KEY
.
Action
, Action.getValue(java.lang.String)
is also used
to obtain the value.
ActionState.useActionInstance()
for Actions,
in the case where ActionState.type()
.ActionState.property()
is used to specify necessary guard condition,
ActionState.useActionInstance()
is necessary to perform custom check.public abstract String checkedValue
"true"
, "false"
to represent boolean or Boolean values
Enum.name()
ActionState.NULL_VALUE
to indicate null
value
ActionState.NON_NULL_VALUE
to indicate any non-null value
Object.toString()
public abstract Class listenOn
PropertyChangeListener
or ChangeListener
will be
auto-detected from ActionState.type()
class.
All listener methods will cause the system to re-evaluate enable and on/off
(if applicable) state for the action, unless ActionState.listenOnMethod()
is
also used.public abstract String listenOnMethod
ActionState.listenOn()
value.public abstract boolean useActionInstance
ActionState.type()
becomes available and the guard ActionState.property()
has the appropriate value
.
After that, the system will delegate to Action.isEnabled()
for enablement, or
to getValue
(Action.SELECTED_KEY
) for on/off state of the action.
The annotated element must implement Action
interface in order to use
this value.