How to Get Hold of an Action Instance

The NetBeans Platform manages the creation of actions at run time. When you create a new action with the wizard (File > New File… > Module Development > Action) everything gets generated that is needed for the Platform to load the action and call your actionPerformed() method when required.

However, the question does sometimes arise whether it is possible to get hold of the action that is created. And there is indeed a way. Whether this is desirable in general, or the the best way to solve a particular problem, is a debate for another day. It does, however, illustrate an interesting way in which the Platform can be extended. And so I thought it worth documenting here. 🙂

Note that the generated actions discussed below are Always Enabled actions.

With NetBeans 7.1, a class implementing ActionListener is generated with some annotations on it. At compile time, the annotations are used to generate a layer file where the action is registered. (With earlier versions of NetBeans, such as 6.9.1, actions used to be specified directly in the module’s layer file. And the wizard generated the layer file entries instead of the annotations.)

The interesting attribute that is not visible in the annotations (but still in the generated layer file) is the attribute called instanceCreate. It has as default value the method org.openide.awt.Actions.alwaysEnabled. This means that the method that is called to create the action is alwaysEnabled in the class org.openide.awt.Actions. It is possible to replace this method with our own.

Assume that a singleton class called Controller exists, that is interested in the action instance. Now create a new class that will be containing the custom version of the method:

public class ActionCreator
{
    public static Action alwaysEnabled(java.util.Map TheMap)
    {
        javax.swing.Action newAction = 
                org.openide.awt.Actions.alwaysEnabled(
                (java.awt.event.ActionListener) TheMap.get("delegate"),
                (String) TheMap.get("displayName"),
                (String) TheMap.get("iconBase"),
                (Boolean) TheMap.get("noIconInMenu"));
        Controller.instance().setAction(newAction);
        return newAction;
    }
}

And add to the layer file (create the file if it does not yet exist, and remember to change the paths as applicable):

<folder name="Actions">
    <folder name="Edit">
        <file name="za-co-pellissier-example-TestAction.instance">
            <attr name="instanceCreate" methodvalue=
                "za.co.pellissier.example.ActionCreator.alwaysEnabled"/>
            <attr name="noIconInMenu" boolvalue="false"/>
        </file>
    </folder>
</folder>

That is it. Now the controller will have an instance of that particular action, and it can do with it whatever it wants.

Note that the same instance will be registered on the menu as well as the toolbars (if you choose to put it in both places in the wizard). So for example disabling it will disable the action in both locations.

Edit: If you have trouble disabling the action that you now have a reference to in the above code, create a new class extending AbstractAction and instantiate that instead of calling the alwaysEnabled method to create an action for you.

2 thoughts on “How to Get Hold of an Action Instance”

  1. Hallo,

    thanks for blogging this great idea.

    It works for me in some cases but not in combination with enable/disable the action (tested with NB 7.4 patch 3).

    At the end of your blog you already give the hint that this maybe not work and as an alternative you can create the action an other way.

    Can you give me further details how to set all the needed properties into the self created Action? Especially the “delegate” property value?

    best regards
    Oliver

  2. Hi,
    ok, I have answered my own question. Enable/disable works fine if I create an Action as follow:

    private static class MyAbstractAction extends AbstractAction {
    private final java.awt.event.ActionListener actionListener;
    public MyAbstractAction(java.awt.event.ActionListener actionListener,
    String displayName, String iconBase, boolean notIconInMenu){
    this.actionListener = actionListener;
    putValue(“iconBase”, iconBase);
    putValue(“displayName”, displayName);
    putValue(“noIconInMenu”,notIconInMenu);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
    actionListener.actionPerformed(e);
    }
    }

    best regards
    Oliver

Leave a Reply to Oliver Rettig Cancel reply