Document describing implemented changes in action system
$Revision: 1.6 $
Changes: available in
CVS
Status: issue
27868 Dependency
Tree: of
issue
27868
Note: Some of the links are broken (describing
new API), they will start to work, when the branch action_27868 is merged
into trunk.
Abstract
This document describes implemented API changes, implementations
and usage of the new action API enhancements.
First part
1. Popup menu creation
describes change of building popup in NB. This is only related change for
those developers who are just using the Actions API for building
the popups, i.e. they are not action "providers".
The rest of this document describes the changes to the standard API, and
explains its implementations, and their usage.
At the end
5. Summary of Action
API changes it summarizes the up-to-date changes comparing to
old API.
Content
1. Popup menu creation
1.1 Old scenario
1.2 Scenario describing the idea
1.3 Scenario describing the new API usage
2. Context
3. Context Aware Actions
3.1 Callback Actions
3.2 Node Actions
3.3 Other SystemAction subclasses which implement
ContextAwareAction
4. Move from SystemAction to Action
5. Summary of Action API changes
1. Popup menu creation
Here is described typical scenario of popup menu creation and new ones describing
the idea and API in new way.
1.1 Old scenario
Folowing way was created popup menu in NB in old-fashion:
...
// There is TopComponent tc, and was clicked at
its point Point.
// 1) Retrieve actions for that top compnent
SystemAction[] sa = retrieveActionsSomehow();
// 2) Create popup.
JPopupMenu popup = createPopup(sa);
// 3) Show popup
popup.show(tc, point);
...
1.2 Scenario describing the idea
This scenarion describes the idea how the popup should be created when the
changes introduced.
...
// There is TopComponent tc, and was clicked at
its point Point.
// 1) Retrieve actions for that top compnent
SystemAction[] sa = retrieveActionsSomehow();
// 1a) Get context used for actions in current
popup
Lookup context = retrieveContextSomehow();
// Note: this is just imaginary method.
// 1b) The actions which are ContextAwareActionS
replace by its context aware instances:
Action[] aa = changeActionsToContextAwareInstancesSomehow(sa, context); // Note: this is just imaginary method.
// 2) Create popup.
JPopupMenu popup = createPopup(aa);
// 3) Show popup
popup.show(tc, point);
...
1.3 Scenario describing the new API
usage
Last scenarion shows new way of popup creation in NB with context
aware actions. Note that all the work is done behind the scenes in
Utilities.actionsToPopup(Action[],
Lookup context) method (btw. also TopComponent.getLookup()
method).
Using the new API for the steps 1a) and 1b) (the steps 1b) and 2) merged)
looks typically like this:
...
// There is TopComponent tc, and was clicked at
its point Point.
// 1) Retrieve actions for that top compnent
Action[] aa = retrieveActions(); // Note: Here
could be used the Action[] array.
// 1a) Get context used for actions in current
popup
Lookup context = tc.getLookup();
// 1b) + 2) The actions which are ContextAwareActionS
replace by its context aware instances and create popup
JPopup popup = Utilities.actionsToPopup(aa, context);
// 3) Show popup
popup.show(tc, point);
...
Important Note: This new scenario is
a MUST-HAVE when creating popup menu in NB,
otherwise the popup would consist of global action instances only, which
in case they impelement
ContextAwareAction
interface, may not for correctly for the provided
context.
2. Context
The one most important idea is a notion of context, which is used when
building popup menu from actions. The context describes environment according
the certain actions (
ContextAwareActionS)
work, i.e. change their enablement status and perform themself.
Comparing to the old implementation where all actions were singletons,
i.e. they are all were used in "global-context". I.e. each node actions listens
on global activated nodes changes, each callback actions is set/unset/reset
its performer, depending in which popup is currently used.
In new implementation for actions of type
ContextAwareAction
are created instances which are valid for the specified context, and afterwards
upon popup hiding and garbaging, the instance is also garbaged.
E.g. when creating popup in Explorer, which certain node is selected, each
such context aware action instance works accordingly to that node, i.e. each
such node action instance doesn't need to listen on global activated node
changes, it can retrieve the selected node from the context directly (
see below), also each such callback action instance
doesn't has to be watched by to set/reset/unset its action performer, it
can retrieve the
"performer-like" action
directly from the context -> TopComponent action map (
see below).
The context is described by
Lookup
instance. The Lookup is typically retrieved from
TopComponent.getLookup()
method. The default implementation of that method returns context,
Lookup
which lookups activated nodes of that
TopComponent
and
ActionMap
delegate of that
TopComponent.
The individual components which the
Lookup
consist of is described later.
3. Context Aware Actions
The second important idea is
ContextAwareAction.
It is kind of
Action
which creates context aware instances, which have to be used in that context
and are valid for that context only. I.e when creating the popup menu in
certain context it is necessary to retrieve for each context aware action
its context aware instance. The context aware intstance is retrieved via
the
createContextAwareInstance
method.
Current actions API enhancements:
CallbackSystemAction,
NodeAction
and certain other
SystemAction
subclasses implement
ContextAwareAction
interface.
Next there is described how the standard system actions works in new way:
3.1 Callback Actions
In old time CallbackSystemAction
works with ActionPerformerS,
which are set/unset/reset for that action via its setActionPerformer
method.
New way is that the action
performer is not used and it is replaced
by action
which is retrieved from action
map which is provided by the context working in.
Let's call it "performer-like" action
for purpose of this document.
The retrieval of that action is done by provided context
aware instance
of the callback action, which works whithin the specific context. This way
it is achieved that the performer is context depedent thus it doesn't need
to be changed (set/unset/reset) for the old singleton callback action instance
The performer is usually same for the specified context.
How the context aware action instance finds the "action-performe-like"
action from the ActionMap?
There is a contract between context provider (Lookup.Provider)
which puts the action map in and each callback action instance to agree on
a key which is used to find the "performer-like"
action instance (it plays the old ActionPerformer
role) from the action
map. The key can be retrieved from callback
action subclasses following way:
...
// E.g. for delete action
CallbackSystemAction globalDeleteAction = (CallbackSystemAction)SystemAction.get(DeleteAction.class);
String deleteActionKey = globalDeleteAction.getActionMapKey();
...
Each callback action is supposed to override getActionMapKey()
method to provide its key. Default implementation returns the class name.
Then the key is used when the action works in specific context that way, it finds ActionMap
provided by that context and tries to get the "performer-like"
action using its key.
How to put the context aware instance into context action map?
If working with default scenario(see above),
the action
map is delegating to the TopComponent
action map, i.e. when in put in TopComponent's
tc action
map:
...
// Put the "performer-like" action into TopComponent's
(tc) ActionMap
tc.getActionMap ().put (deleteActionKey, performerLikeDeleteAction);
..
Then when the context got from that TopComponent
is used for popup creation, the performerLikeDeleteAction
is used in the popup as its action
performer.
The advantage of this aproach is that there isn't possible any clash,
which happens when working with old callback
action singletons, i.e. the ActionPerformerS
could be easily replaced by another ones and that could lead to incosistent
state of that action whitin certain popup (context). In new impl for each
specific context is used specific context
aware instance of callback
action which can maintain more easily the performer (via the "performer-like" action) within the context.
Important Note: When for the callback
action isn't found the "performer-like"
action by provided key (via getActionMapKey()
method, it falls back to old behaviour working with ActionPerformerS.
3.2 Node Actions
Old Node
actions (and thus also Cookie
actions which are subclasses) worked the way they listened in global
node changes, and has updating their state accordingly.
The new way is to create the context aware instsances of each Node action,
and that context aware instance is working according nodes provided by the
context. This way is possible to save the overhead, and various problems
with global nodes changes. I.e. When creating popup in Explorer, the node
(or cookie) context aware action is working according the selected node in
the explorer.
How are the nodes put into the context?
The default implementation of TopComponentS
context retrieved by TopCompoent.getLookup()
provides as nodes the activated nodes, which are returned by TopComponent.getActivatedNodes()
method.
3.3 Other SystemAction subclasses which implement
ContextAwareAction
There are also some other action subclasses which implement ContextAwareAction
interface, those are e.g. ToolsAction,
PropertiesAction,
PasteAction
etc. Those implementations are specific to each one, and could be examined
by code review, but for most cases the implementations are irrelevant for
their usage.
4. Move from SystemAction to Action
There is created possibility to use direct
Action
implelementations instead of
SystemAction
Therefore were new API enhancements in
Node,
AbstractNode
and
NodeOp
classes introduced, and some old API is deprecated.
See the table.
Important Note: There is currently no support for creating
Action
type subclasses, i.e. action couterparts for
CallbackSystemAction,
NodeAction
etc. It will be subject of later changes. Therefore currently the developers
aren't yet encouraged to move from the
SystemAction
subclasses to
Action
types yet (but it is fully possible for direct
SystemAction
subclasses, where no additional API support is required).
5. Summary of Action API changes
The table shows the changes to old API, their replacements, and enhancements
by new API's (also some problems coping with especially the lack of good
names for methods in node API).
Old API
|
Desciption of old API change
|
New API enancement/replacement
|
Description of new API change
|
|
|
ContextAwareAction interface
and its Action
cretateContextAwareInstance() method
|
Provides context
aware actions, it means that the action creates context aware
action instances for each specified context.
|
|
|
TopComponent.getLookup()
method
|
Provide default implementation of context. The
default context provides lookup for nodes,
which delegates
to TopComponent.getActivatedNodes()
method and for action map
which delegatest to TopComponent.getActionMap().
|
|
|
JPopupMenu
Utilities.actionsToPopup(Action[], Lookup context) method
|
Utility method whic builds popup from action array,
it retrieves the context
aware action instances for the specified context, and builds the
popup menu.
|
|
|
JPopupMenu
Utilities.actionsToPopup(Action[], Componet) method
|
Utility helper method. There are
certaing cases there isn't direct reference to TopComponent,
thus to its context (getLookup()
method), or could be used form more general scenerion.
This method finds the context following way, it traverses the components
up
to the component hierarchy behinning from the specified component and
searches for first Lookup.Provider
instance (which is typically TopComponent)
if found the provider,
its Lookup
instance is used as the context, if not there is created
Lookup
which delegates to composite action map (which consists of all action
maps
of the searched component hierarchy), and that instance
is used as context. Then the Utilities.actionsToPopup(Action[],
Lookup context) is called.
|
|
|
CallbackSystemAction implements ContextAwareAction
|
See above.
|
|
|
Object
CallbackSystemAction.getActionMapKey() method
|
This method returns the key which is then used to
find the action which performs the callback. See
above.
|
|
|
NodeAction implements ContextAwareAction
|
See above.
|
|
|
Some direct SystemAction
subclasses implement ContextAwareAction
|
See above.
|
SystemAction[]
Node.getActions()
|
deprecated
|
Action[]
Node.getActions(false)
|
See above.
|
SystemAction[]
Node.getContextActions
|
deprecated
|
Action[]
Node.getActions(true)
|
See above.
|
SystemAction
Node.getDefaultAction()
|
deprecated, and changed from abstract to return
null as default impl.
|
Action
Node.getPreferredAction()
|
See above.
|
AbstractNode.setDefaultAction(SystemAction)
|
deprecated
|
AbstractNode.setPreferredAction(Action)
|
See above.
|
SystemAction[]
AbstractNode.createActions()
|
it is not deprecated yet, but looses its effect
in new usage
|
N/A yet, it is needed to figure out some way later.
|
See above.
|
SystemAction[]
NodeOp.getDefaultActions()
|
deprecated
|
No replacement, its useless now.
|
See above.
|
|
|
Action[]
NodeOp.findActions(Node[])
|
Utility method for retrieving actions for node
array.
|
static
Actions.connect(AbstractButton, SystemAction)
|
deprecated
|
static
Actions.connect(AbstractButton, Action)
|
Replaces the older method working with SystemAction.
|
static
Actions.connect(JMenuItem, SystemAction, boolean)
|
deprecated
|
static
Actions.connect(JMenuItem, SystemAction, boolean) method
|
Replaces the older method working with SystemAction.
|
Send comments and patches to
openide-dev@netbeans.org or attach
them to issue
27868.