How to Make the NetBeans Platform Sensitive to Customers
In your NetBeans Platform application, you may often find yourself in the situation where you need to create actions that are sensitive to their context. A simple case in point is shown below. We have a customer application with an action invoked from the toolbar, as well as from a menu item in the main menubar and from a node in an explorer view. In the screenshot below, the action is invoked from the button with the angry face (a typical customer expression) in the toolbar, from the "Customer Details" menu item on the node in the view window, as well as from a menu item in the File menu (which you can't see below). In this case, there is a Customer object on which the action can be invoked...
...while in this situation, the UI for invoking the action is disabled (as you can see, by looking at the button in the toolbar, which is disabled, and notice that other actions are enabled in this context, since those actions relate to the editor, which is the window that is currently selected) because here we no longer have a Customer object in the context of the application, since the cursor is currently in the editor window, instead of in the view window that exposes the Customer object from the currently selected node:
So the question in this article is how to create an action such as the above. The good news is that it's really easy to do so, but you do need to be aware of the steps you need to take, which is why I am writing this article.
Take the following steps:
- In the New Action wizard, in the module where you want to create your action, specify that you want to create a contextually aware action that should be sensitive to Customer objects, which is part of the model in my application:

Note: We (i.e., the NetBeans team) need to change the strings in the dialog above (i.e., in the NetBeans IDE source code), since in both cases an ActionListener will be created. I.e., when you select the "Conditionally Enabled" radiobutton, you will not get a CookieAction. Instead, you will get an ActionListener that is registered in the layer such that it is injected with context-sensitivity to Customer objects, as will be seen below. - Click Next and then specify that you want to create a menu item and a toolbar button:

- Click Next again and choose an icon on disk, as well as specifying a class name prefix and a display name:

Tip: Read this cool tip about icons and NetBeans Platform applications! - When you complete the above wizard, you will see you have a plain old ActionListener class, which is great news since this means you can port your own ActionListeners from your own application to the NetBeans Platform without needing to rewrite them in any way. In other words, the NetBeans Platform handles ActionListeners natively and does not require you to use some special NetBeans API for creating actions. Here's the ActionListener created from the above, which has access to the current Customer object (pretty handy!):
package org.shop.ui;
import demo.Customer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public final class CustomerDetailsAction implements ActionListener {
private final Customer context;
public CustomerDetailsAction(Customer context) {
this.context = context;
}
public void actionPerformed(ActionEvent ev) {
// TODO use context
}
}Meanwhile, your layer.xml file has the following entries, created by the above wizard, turning your humble ActionListener into a context-sensitive action that is sensitive to Customer objects:
<folder name="Actions">
<folder name="Build">
<file name="org-shop-ui-CustomerDetailsAction.instance">
<attr name="delegate" methodvalue="org.openide.awt.Actions.inject"/>
<attr name="displayName" bundlevalue="org.shop.ui.Bundle#CTL_CustomerDetailsAction"/>
<attr name="iconBase" stringvalue="org/shop/ui/customer.png"/>
<attr name="injectable" stringvalue="org.shop.ui.CustomerDetailsAction"/>
<attr name="instanceCreate" methodvalue="org.openide.awt.Actions.context"/>
<attr name="noIconInMenu" boolvalue="false"/>
<attr name="selectionType" stringvalue="EXACTLY_ONE"/>
<attr name="type" stringvalue="demo.Customer"/>
</file>
</folder>
</folder>
<folder name="Menu">
<folder name="File">
<file name="org-shop-ui-CustomerDetailsAction.shadow">
<attr name="originalFile" stringvalue="Actions/Build/org-shop-ui-CustomerDetailsAction.instance"/>
<attr name="position" intvalue="1300"/>
</file>
</folder>
</folder>
<folder name="Toolbars">
<folder name="File">
<file name="org-shop-ui-CustomerDetailsAction.shadow">
<attr name="originalFile" stringvalue="Actions/Build/org-shop-ui-CustomerDetailsAction.instance"/>
</file>
</folder>
</folder>Note: I tweaked line 11 above. By default, the type is set to "Customer", while it actually needs to be the fully-qualified name of the Customer class, which is "demo.Customer", since the Customer object is found in a package called "demo". Maybe in the New Action wizard, one should type the fully-qualified name, rather than just the name of the domain class. Need to check that. The "type" element in the layer determines the context available to the ActionListener, i.e., the currently available Customer object.
By the way, above, in line 10, we are ensuring that the action will be disabled if more than one node is selected (thanks to choosing "User Selects One Node" in the first page of the New Action wizard), as can be seen here:
- Finally, let's add the action as a contextual menu item on our node. In this case, I know I have two actions in the "Actions/Build" folder, one from the current module, and the other from another one. You could also get all the actions within a particular folder, rather than specific ones, or you could get actions from different folders. Up to you.
private class CustomerNode extends AbstractNode {
public CustomerNode(Customer c) {
super(Children.LEAF, Lookups.singleton(c));
setDisplayName(c.getName());
setShortDescription(c.getCity());
}
@Override
public Action[] getActions(boolean context) {
return new Action[]{
Utilities.actionsForPath("Actions/Build/").get(0),
Utilities.actionsForPath("Actions/Build/").get(1),
};
}
}
And that's really all. You can use your plain old ActionListener class. The downside is you have a bunch of tags in the layer file to deal with, though (aside from the small tweak) it was all generated for you. Looking forward to an annotation for actions, so that my ActionListener can simply be decorated with an annotation that will create the necessary layer entries when the module is compiled.
Nevertheless, you now have a context-sensitive action for customer objects.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Miguel Garcia-lopez replied on Thu, 2010/03/25 - 4:56pm
One question: what about if selection type (see line 10 in XML layer excerpt) is not EXACTLY_ONE, but ALL, SOME or ANY? I mean, what should the code (class constructor, probably?) look like when using the different CookieAction.MODE_* selection modes?
Should the constructor take an array (Collection, maybe?) of Customer objects, for example?
Thanks as usual!
Geertjan Wielenga replied on Thu, 2010/03/25 - 6:42pm
Great question.
If you select "User May Select Multiple Nodes" in the first panel of the New Action wizard, the EXACTLY_ONE in the layer is ANY instead, with this being generated as the ActionListener:
Don't know about ALL and SOME though. Will find out (or in the meantime you can read the NetBeans API javadoc for details)!
Miguel Garcia-lopez replied on Fri, 2010/03/26 - 3:51am
Fabrizio Giudici replied on Fri, 2010/03/26 - 6:29am
Fabrizio Giudici replied on Fri, 2010/03/26 - 9:24am
Geertjan Wielenga replied on Sat, 2010/03/27 - 4:41pm
Fabrizio Giudici replied on Mon, 2010/03/29 - 5:13am
in response to: geertjan
Peter Kirkham replied on Mon, 2010/03/29 - 10:10am
Sirisha Gvls replied on Wed, 2010/06/09 - 4:49am
Hi Geertjan,
We are using netbeans platform 6.8. Previously we used filesystem api to display the nodes hierarchy. Right now we moved the data into database and showing the hierarcy. This is working fine. For these nodes and leafs we have to attach actions like Add, edit, copy,paste, cut,delete, rename and the same actions should update the nodes details in the database.
Structure [parent Node]
|
-- Version
|
---- Node1 [Root Node]
|
----Multiple Nodes and leafs [ReportStructrureNode]
|
---- Node2
|
----Multipe Nodes and leafs
Attached the actions by using the following code:
public Action[] getActions(boolean context) {
Action[] result = new Action[] { new AddAction(), new EditAction()};
return result;
}
We tried one approach for these action events with the following code:
private final class EditAction extends AbstractAction implements ActionListener{
public EditAction() {
putValue(Action.NAME, "Edit");
}
public void actionPerformed(ActionEvent e) {
int response = JOptionPane.showConfirmDialog(null, "Are you sure to want to edit "+ reportStructureItem.getNodeName() + "?");
ReportStructureItem obj =(ReportStructureItem)getLookup().lookup(ReportStructureItem.class);
editNode(obj);
EditCookie cookie = (EditCookie) getCookie(EditCookie.class);
}
}
Actual Result what we have to show is when ever we click on "edit" action it has to get the xml content from the database and show in the report panel. The following code "editNode" will fetch the xml content from the database.
But for me edit is showing in the disabled form. Even i updated in the layer.xml.
// Edit the node
public void editNode(ReportStructureItem obj) {
ReportStructureAdapter reportStructureAdapter = new ReportStructureAdapter();
try {
reportStructureAdapter.editItem(obj);
} catch (DBException ex) {
System.out.println("ex = " + ex.getMessage());
}
}
Please do guide me how i would have to show the xml content in the report panel when i click on edit action.
Thanks
-Sirisha
Sirisha Gvls replied on Wed, 2010/06/09 - 4:49am
Hi Geertjan,
We are using netbeans platform 6.8. Previously we used filesystem api to display the nodes hierarchy. Right now we moved the data into database and showing the hierarcy. This is working fine. For these nodes and leafs we have to attach actions like Add, edit, copy,paste, cut,delete, rename and the same actions should update the nodes details in the database.
Structure [parent Node]
|
-- Version
|
---- Node1 [Root Node]
|
----Multiple Nodes and leafs [ReportStructrureNode]
|
---- Node2
|
----Multipe Nodes and leafs
Attached the actions by using the following code:
public Action[] getActions(boolean context) {
Action[] result = new Action[] { new AddAction(), new EditAction()};
return result;
}
We tried one approach for these action events with the following code:
private final class EditAction extends AbstractAction implements ActionListener{
public EditAction() {
putValue(Action.NAME, "Edit");
}
public void actionPerformed(ActionEvent e) {
int response = JOptionPane.showConfirmDialog(null, "Are you sure to want to edit "+ reportStructureItem.getNodeName() + "?");
ReportStructureItem obj =(ReportStructureItem)getLookup().lookup(ReportStructureItem.class);
editNode(obj);
EditCookie cookie = (EditCookie) getCookie(EditCookie.class);
}
}
Actual Result what we have to show is when ever we click on "edit" action it has to get the xml content from the database and show in the report panel. And if i do any modifications in the xml, it has to save in the database. The following code "editNode" will fetch the xml content from the database.
But for me edit is showing in the disabled form. Even i updated in the layer.xml.
// Edit the node
public void editNode(ReportStructureItem obj) {
ReportStructureAdapter reportStructureAdapter = new ReportStructureAdapter();
try {
reportStructureAdapter.editItem(obj);
} catch (DBException ex) {
System.out.println("ex = " + ex.getMessage());
}
}
Please do guide me how i would have to show the xml content in the report panel when i click on edit action.
Thanks
-Sirisha
Nicholas Dunn replied on Thu, 2010/12/09 - 3:09pm
Angga Euy replied on Tue, 2011/10/18 - 6:51am
I like the article you wrote here; it is very informative and useful for the internet users like me. I will come back to read more blog posts on your website and I have bookmarked your website as well
Thank You
Kitchenaid Blender
Angga Ruy replied on Thu, 2011/12/01 - 4:54am
Finally I found the good blog to read on your website. Thanks for sharing the good contents and I have enjoyed reading it.
Thank you
Calphalon outlet - Canvas Backpack
Carla Brian replied on Mon, 2012/05/07 - 9:39am