Document describing implemented changes in action system
$Revision: 1.2 $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 creation1.1 Old scenario2. Context
1.2 Scenario describing the idea
1.3 Scenario describing the new API usage
3. Context Aware Actions
3.1 Callback Actions4. Move from SystemAction to Action
3.2 Node Actions
3.3 Other SystemAction subclasses which implement ContextAwareAction
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.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.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);
...
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.