9. Action!

It is not fair to ask of others what you are unwilling to do yourself.

If you know Swing you already know Actions and AbstractActions so I won't talk about them here (if you don't know about Swing actions you can see this tutorial). Just let me say that the NetBeans Platform has lots of cool types of actions, ready to use, but we'll cover only some of the more important topics here.

Figure 7. A PopupMenu showing a list of actions

A PopupMenu showing a list of actions

For instance, you have the ContextAwareAction, an action that is automagically enabled or disabled depending on the contents of the "current context" (usually what the user has selected). This special type of action is very useful in big applications, when you want to enable or disable components (menus, menu items, buttons, etc.) automatically depending on the current selection. We won't be covering ContextAwareActions here, though (because we don't cover the "current selection" either.

9.1. Actions and nodes

All NetBeans Nodes (remember that nodes are responsible for the visualization of one or more entities) have a set of Actions. These actions are usually presented to the user via a popup menu when the user right-clicks on a node, and may have a key binding, an icon, a title and a short description, for instance.

The exact actions that a Node returns may depend on the context (the "global selection") or on the node itself. You decide which actions you want to return for each Node, by overriding the getActions(boolean) method. In our little example application we define a "ReloadAction" for out "QueryNode", this action will be reponsible for refreshing the tweets of the query entity, like so [2]:

  @Override
  public Action[] getActions(boolean context) {
    // A list of actions for this node
    return new Action[]{new ReloadAction(getLookup())};
  }
  

The NetBeans machinery will build a popup menu for you automagically, so when the user right clicks on a node each action will be included in a popup item in the popup menu. Of course you specify the text of the popup item within the constructor of the Action itself, like so:

    public ReloadAction( Lookup abilities )
    {
      ...
      putValue( AbstractAction.NAME, "Reload");
      putValue( AbstractAction.SHORT_DESCRIPTION, "Reloads this object");
      ...
    }
    

What we do here is to return a single "ReloadAction", and we build this action using the set of abilities of our "QueryNode", as returned by the "getLookup()" method. Let's see why!

9.2. Actions and abilities

One of the easier ways to create actions is to use the Node's (or entity's) abilities that you already keep in the Lookup of your node (entity). So, for instance, if you have an "Openable" ability with an "open()" method then creating a "OpenAction" is a piece of cake: you just invoke the "open()" method and you're all set. Or if you have a "ReloadableNode" ability with a "reloadChildren()" method then creating a "ReloadAction" is very easy, right?

In fact you may think of "Actions" as visual representations of abilities, and you can relate them both naturally. A "Saveable" ability with a "save" method is naturallly related to a "SaveAction". And, in our case, a "NodeReloadable" ability (as we defined earlier) is naturally related to our "ReloadAction".

What we do in our Action is simple: we just pass it a list of abilities (a "Lookup" object) and we seek for the "ReloadableNode" ability in the list, if the ability is in there then we'll invoke the "reloadChildren" when the actionPerformed in invoked.

Note that the ReloadAction is not coupled to the exact type of node, as it depends only on a standard set of abilities (a "Lookup" object). This allows us to decouple Nodes from Actions, as Actions depend on the abilities we included in the Lookup and not on the exact type of node. This is a very important and a extremely powerful idiom.

The code is as follows:

public final class ReloadAction 
extends AbstractAction
{
  private ReloadableNode reloadableNode;

  /**
   * Constructor from Lookup
   * @param lookup the set of abilities (of a node, an entity or whatever).
   */
  public ReloadAction( Lookup lookup )
  {
    // Get the ReloadableNode ability
    reloadableNode = lookup.lookup( ReloadableNode.class );
    // Add some Action specific parameters (we could add an icon too)
    putValue( AbstractAction.NAME, "Reload");
    putValue( AbstractAction.SHORT_DESCRIPTION, "Reloads this object");
  }
  // Continued below...

See? We pass the constructor of our ReloadAction a list of abilities (a Lookup) and we extract the "ReloadableNode" ability and keep it in a member variable. Now when the user presses the button or selects a popup item the "actionPerformed" method will be invoked, and...

  // ... continued from above
  public void actionPerformed(ActionEvent e) {
    // If the "ReloadableNode" ability is non empty we just invoke
    // the "reloadChildren" method to refresh the list of children.
    if( reloadableNode!= null )
      try {
      reloadableNode.reloadChildren();
    } catch (Exception ex) {
      Exceptions.printStackTrace(ex);
    }
  }
}

... we just invoke the "reloadChildren" method if the ability is in there.

Oh, come on, it's much simpler than it seems!

9.3. Summary

In this section we've reviewed some topics of the Actions API. We've seen that Nodes have Actions and that Actions can react to a global selection context or can be coupled to the abilities (of nodes or entities). Basically Actions are visual representations (with a display name, icon or short description) of the abilities of the capabilities of nodes and entities.

On the next section we'll summarize what we've explained in these series of articles, and we'll finish this introduction. Comments are, of course, welcome!



[2] The NetBeans' AbstractNode base class already returns a default array of actions for all nodes. You may want to include these in your list of actions (by getting the list using "super.getActions(boolean)". We haven't done so in this simple example, though.


blog comments powered by Disqus