Actions API

Overview

# Javadoc

For installation of global, always-enabled actions, you do not need to use any special interfaces - just create a `javax.swing.Action` and register it. Common, global actions such as Cut or Copy are to be found in the `org.openide.actions` package. The base classes used to create new actions reside in `org.openide.util.actions` (you need these only if you want to affect a global action's behavior or provide a context-sensitive action which is disabled or enabled depending on what is selected).

# Contents

- Using Standard Swing Actions
- Introduction to NetBeans' Actions
  - Callback Actions
  - Context-sensitive Actions
  - Presenters
- Using Standard Actions Found in NetBeans APIs
  - Attaching to an Existing Object
- Installation and Maintenance
  - Menu Installation
  - Toolbar Installation
  - The Global Actions Pool
  - Installing Keyboard Shortcuts

# Actions API

The Actions API allows users to interact with module code, by invoking actions via menus, toolbars or keyboard shortcuts.

## Using Standard Swing Actions

For uncomplicated cases (such as adding an always-enabled action to the main menu or the context menu of a `Node`), it is not necessary to use any of the Actions API classes at all - just register an instance of the action class into the appropriate folder of the System Filesystem and it will appear. Similarly, any Swing action can be returned from `Node.getActions()`; it is not necessary to subclass one of NetBeans' action classes unless there is specific functionality that only it can provide.

Actions are typically either presented in popup menus, or otherwise attached to a component such as a window, node, data object, or filesystem, or installed globally in the main menu or toolbars.

## Introduction to NetBeans' Action classes

For historical reasons, the action subclasses available in NetBeans' APIs are subclasses of `SystemAction`. It is no longer required that you use `SystemAction` - for most purposes, generic Swing `Action` subclasses will work just fine. For example, one benefit of using `SystemAction` is automatic handling of mnemonics - just place an `&` character before the letter that should be the mnemonic, in the display name - for example, the defined display name for the Copy action is `&Copy` - that is why `C` is the mnemonic for it. However you can get the same effect with any action using `Mnemonics.setLocalizedText`.

If you do use NetBeans' `SystemAction` or its subclasses, one important thing to note is that `SystemAction` objects should be singletons, meaning that any instance of the class is interchangeable, and all useful state should be static. For this reason, actions are commonly specified by class name only.

> Important: For historical reasons, by default, all `SystemAction` classes run their `actionPerformed` method off the AWT event dispatch thread. For backward compatibility this remains the default. Unless you really want your action run in a background thread, you should override the method `SystemAction.asynchronous()` to return false. Failing to do so can cause your action to appear to run slower than it really does, not to mention introducing nondeterminacy. Watch your console (or `var/log/messages.log` file) - if you forget to override this method on some action, you will be warned when it is run.

### Callback Actions

Some global actions should perform a different function depending on which component has focus. For example, Ctrl-F runs a generic "Find" action. In the editor, this searches text, but in the Explorer it searches files, and so on.

To make this possible, `FindAction` is a public API; modules providing implementations affect its behavior by setting its action delegate into .

The typical usage pattern for these is along the lines of:

    ActionMap actionMap = topComponent.getActionMap();
    CallbackSystemAction a = (CallbackSystemAction)SystemAction.get(SomeAction.class);
    actionMap.put (a.getActionMapKey(), new MyAction())

or directly

    actionMap.put("the-key", new MyAction());

if the key has been made publicly known and the action is not accessible.

### Context-sensitive actions

Often you will want to have actions be enabled or disabled based on what is selected. Historically this was based on the activated `Node`; in more recent versions of NetBeans, this is abstracted into an instance of `Lookup` (which may be the result of the selected `Node`'s `getLookup()` method, or a merge of several selected `Node`s' `Lookup`s). This `Lookup` instance is found via `Utilities.actionsGlobalContext`. So typically, an action will be enabled or disabled depending on the presence or absence of a given object in the global action context - which is typically the `Lookup` of the selected `Node`(s) for whichever `TopComponent` has focus.

`CookieAction` is a base class for context-sensitive actions in NetBeans. Its name comes from the historic `Node.Cookie` interface, which in earlier versions of NetBeans was a marker interface for objects returned by `Node.getCookie()`. A `CookieAction` may be sensitive to any object that implements `Node.Cookie`. If you want an action sensitive to some other kind of interface in node lookup, you cannot use it.

Typical usage is to subclass `CookieAction`, providing an array of one or more classes to be returned from `getCookieClasses()`. If the global action context (typically the `Lookup` of the selected `Node`s in whichever component has focus) returns non-null for all of the classes returned by `getCookieClasses()`, that action should be enabled. Override the `mode()` method to determine the algorithm used for enablement - if all selected `Node`s must contain the requested classes in their `Lookup` or if the presence of the classes on any of the selected `Node`s is enough, or if the action should be disabled if more than one `Node` is selected.

### Presenters

Sometimes an action needs to provide a custom component to be used in menus or toolbars. For example, the memory meter (enabled in development builds of NetBeans) provides a custom component that shows the amount of memory NetBeans is using, which, when clicked, calls `System.gc()`.

`Presenter` is an empty interface which contains sub-interfaces `Presenter.Menu`, `Presenter.Toolbar` and `Presenter.Popup`. Any action can implement one or more of these interfaces to provide a custom component which will be used in toolbars, popup menus or the main menu.

The most typical use of the `Presenter`s is to return a `JMenu` instead of a `JMenuItem`, to create a submenu.

> Important: Using ad-hoc Swing components in menus is not recommended, as this interacts badly with the screen menu bar on the Apple Macintosh, which expects all menu contents to be `JMenuItem` or `JMenu` instances.

## Using `CallbackSystemAction`

`CallbackSystemAction` makes it possible for a single action to have multiple implementations, depending on what is selected. A `CallbackSystemAction` has an `ActionPerformer` that actually does the work when the user invokes the action. The `ActionPerformer` can be set programmatically, for example, when a component receives focus.

`CallbackSystemAction` may be subclassed. First decide what, if any, state the action needs; if it does need some, this should be stored in the class itself, rather than in the instance, as it should be a singleton. Conventionally, `SystemAction.getValue(...)` and `SystemAction.putValue(...)` are used for storage.

`SystemAction.getName()`, `SystemAction.getHelpCtx()`, and `SystemAction.iconResource()` should all be overridden to provide basic information about how to display the action in its presenters.

Note that you may include an ampersand in the name before a letter to indicate a desired mnemonic position. E.g. `My Act&ion` should use the `i` as a mnemonic. The default menu and popup presenters honor these mnemonics.

`CallbackSystemAction.setSurviveFocusChange(...)` might be called in the `initialize` method if it needs to be changed.

That's it for creating the action class itself. Now another implementation class should provide the performers for it, e.g.:

    // Get the action:
    MyCallbackAction action = (MyCallbackAction) SystemAction.get(MyCallbackAction.class);
    // Some subsystem, changes in which should affect the action:
    FooSystem fooSys;
    // Attach a listener for the action's benefit:
    fooSys.addWidgetListener(new WidgetListener() {
        public void widgetAdded(final WidgetEvent ev) {
            // Enable it, and tell it what to act on.
            action.setActionPerformer(new ActionPerformer() {
                public void performAction(SystemAction ignore) {
                    ev.getWidget().doMyActionStuff();
                }
            });
        }
        public void widgetRemoved(WidgetEvent ev) {
            // Now disable it.
            action.setActionPerformer(null);
        }
    });

A newer style, used for actions such as Next Error and Previous Error, is not to use `CallbackSystemAction` at all; instead, a `TopComponent` need only register an action implementation in its `ActionMap` with a specified key, such as `jumpPrev` or `jumpNext`. When a component with such a binding has focus (or was recently focussed), the global command will use that component's implementation of the action.

## Using `CookieAction`

Using `CookieAction` is fairly easy, as it assumes that the objects providing the cookies have already done most of the hard work in providing cookie supports and determining which objects should contain the cookies. Basic implementation of the action is similar to that for `CallbackSystemAction`, but now a few more methods should be implemented, for example:

    public class MyScanAction extends CookieAction {
        // help context, display name, icon...
        public String getName() {
            return "Scan Things";
        }
        public HelpCtx getHelpCtx() {
            // Update with real help when ready:
            return HelpCtx.DEFAULT_HELP;
        }
        public Class[] cookieClasses() {
            // Which cookies is this action sensitive to?
            return new Class[] {MyScanCookie.class};
        }
        public int mode() {
            // At least some of the selected nodes must have this cookie.
            return MODE_ANY;
        }
        public void performAction(Node[] selectedNodes) {
            MyScanContext ctxt = new MyScanContext();
            for (int i = 0; i < selectedNodes.length; i++) {
                MyScanCookie cookie = (MyScanCookie) selectedNodes[i].getCookie(MyScanCookie.class);
                if (cookie != null)
                    ctxt.addScannable(cookie);
            }
            ctxt.scanAway();
        }
    }

Now this action may be installed into a toolbar, for example, and it will be enabled automatically whenever the node selection includes at least one "scannable" object.

### Installation

Global installation of an action may be accomplished using the Modules API, by adding it to the `Actions` folder of the system filesystem, and optionally creating links in the `Toolbars` or `MenuBar` folders.

Actions only present in the popup menu of specific Nodes need no installation. See below.

Though not part of the Open APIs, it bears mentioning here that the NetBeans `editor` module also has its own action creation and installation system. These actions are not based on `SystemAction`; there are based directly on the Swing `Action` class, and are written and registered quite differently, more along the lines of the named actions used in editor kits in the Swing Text API. Using XML layers, modules which use the `editor`-specific APIs may install them into appropriate editor kits (content types). Actions may be bound to keyboard shortcuts active only in the editor window; added to the editor toolbar; or added to the editor's context menu.

## Using Standard Actions Found in NetBeans' APIs

Many of the commonly-used actions available in the IDE are located in the `org.openide.actions` package. Typically you don't subclass one of these actions - you simply find the default instance (via `SystemAction.findAction()`and use it.

### Attaching to an Existing Object

An action that does not have global scope and sensitivity should usually be explicitly attached to components that can use it. For example, `DataLoader.actionsContext()` may be used to provide context-menu actions appropriate to all `DataObject`s created by that loader, e.g.:

    public class MyDataLoader extends DataLoader {
        public MyDataLoader() {
            super("com.me.MyDataObject");
        }
        protected void actionsContext() {
            return "Loaders/text/x-my-file-type/Actions";
        }
    }

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
    <filesystem>
        <folder name="Loaders">
            <folder name="text">
                <folder name="x-my-file-type">
                    <folder name="Actions">
                        <file name="org-openide-actions-OpenAction.instance"/>
                        <attr name="org-openide-actions-OpenAction.instance/org-openide-actions-FileSystemAction.instance"
                            boolvalue="true"/>
                        <file name="org-openide-actions-FileSystemAction.instance"/>
                        <attr name="org-openide-actions-FileSystemAction.instance/sep-1.instance"boolvalue="true"/>
                        <file name="sep-1.instance">
                            <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        </file>
                        <attr name="sep-1.instance/org-openide-actions-CutAction.instance" boolvalue="true"/>
                        <file name="org-openide-actions-CutAction.instance"/>
                        <attr name="org-openide-actions-CutAction.instance/org-openide-actions-CopyAction.instance"
                            boolvalue="true"/>
                        <file name="org-openide-actions-CopyAction.instance"/>
                        <attr name="org-openide-actions-CopyAction.instance/sep-2.instance" boolvalue="true"/>
                        <file name="sep-2.instance">
                            <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        </file>
                        <attr name="sep-2.instance/org-openide-actions-DeleteAction.instance" boolvalue="true"/>
                        <file name="org-openide-actions-DeleteAction.instance"/>
                        <attr name="org-openide-actions-DeleteAction.instance/org-openide-actions-RenameAction.instance"
                            boolvalue="true"/>
                        <file name="org-openide-actions-RenameAction.instance"/>
                        <attr name="org-openide-actions-RenameAction.instance/sep-3.instance" boolvalue="true"/>
                        <file name="sep-3.instance">
                            <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        </file>
                        <attr name="sep-3.instance/org-openide-actions-SaveAsTemplateAction.instance" boolvalue="true"/>
                        <file name="org-openide-actions-SaveAsTemplateAction.instance"/>
                        <attr name="org-openide-actions-SaveAsTemplateAction.instance/sep-4.instance" boolvalue="true"/>
                        <file name="sep-4.instance">
                            <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        </file>
                        <attr name="sep-4.instance/org-openide-actions-ToolsAction.instance" boolvalue="true"/>
                        <file name="org-openide-actions-ToolsAction.instance"/>
                        <attr name="org-openide-actions-ToolsAction.instance/org-openide-actions-PropertiesAction.instance"
                            boolvalue="true"/>
                        <file name="org-openide-actions-PropertiesAction.instance"/>
                    </folder>
                </folder>
            </folder>
        </folder>
    </filesystem>

This set of actions will then provide the context menu for the `DataObject`s belonging to this loader. It is the responsibility of the loader author in this case to make sure that all of the provided actions make sense for the object, and will run the correct code - for example, providing `OpenAction` means that the object will have to provide a usable `OpenCookie`.

Some other ways that actions may be attached:

1. Nodes may attach actions by overriding `Node.getActions(...)` or `Node.getPreferredAction()`.

2. `TopComponent`s (dockable, window-like tabs) may attach actions by overriding `TopComponent.getActions()`. For example, Editor windows provide a few right-click actions in the Editor tab, such as Save and Close.

## Installation and Maintenance

Except for programmatic uses (overriding `Node.getActions()` and the like), actions are installed via module XML layer files - by placing a JavaBean "instance" of a given action class into a folder in the system filesystem which represents a menu or a toolbar. For details see Working with Instances.

### Menu installation

The main menu in NetBeans is constructed from a set of folders in the system filesystem. There is a folder called `MenuBar`. Each child folder of it is one menu; child folders may contain additional child folders to represent submenus.

Typical installation of an action into a menu looks like this:

    <filesystem>
        <folder name="Menu">
            <folder name="Build">
                <!-- After Set Arguments... -->
                <attr name=
    "org-netbeans-core-actions-SetArgumentsAction.instance/com-me-MyAction.instance"
                      boolvalue="true"/>
                <file name="com-me-MyAction.instance"/>
                <!-- ...and before Execute, with an intervening separator. -->
                <attr name=
    "com-me-MyAction.instance/sep-after-my-action.instance"
                      boolvalue="true"/>
                <file name="sep-after-my-action.instance">
                    <attr name="instanceClass"
                          stringvalue="javax.swing.JSeparator"/>
                </file>
                <attr name=
    "sep-after-my-action.instance/org-openide-actions-ExecuteAction.instance"
                      boolvalue="true"/>
            </folder>
        </folder>
    </filesystem>

You could also, if you wished, provide an object whose instance was of type `JMenu`, as an item in the top-level `MenuBar` folder if you had very special requirements that demanded complete control over the menu at all times.

Within a subfolder representing a menu, there are four kinds of instances which you may provide to create items in the menu:

1. An implementation of `javax.swing.Action` - by far the simplest technique, and often all that is needed - provide a file that names the action class required, i.e. `com-foo-mymodule-MyAction.instance`.
2. An actual `JMenuItem`, which will be inserted as-is; you are responsible for its appearance and behavior. This uses the same syntax as above, the only difference being that it constructs a JMenu, not an Action.
3. An action which implements `Presenter.Menu` In this case, the returned `JMenuItem` will be used in the menu. Also note that bookmarks (`*.url` files) as created by the User Utilities module provide a cookie implementing this interface and so are useful things to place in menus.
4. An instance of `JSeparator`, to separate items in the menu.

Please see the class Javadoc for `MenuBar` for a precise list of what kinds of instances are permitted and how they will be treated.

### Toolbar installation

Installing items into toolbars can be done in a similar fashion to that available for menu installation, but with more options for customized behavior.

The normal way to customize the Main Window's toolbars is to add an item or two to one of the existing toolbars in the Main Window. To do that, simply add your action to the appropriate subfolder of the `Toolbars` folder in the System Filesystem. Remember to specify an icon for the action. There are options to entirely replace a toolbar with your choice of components; read about advanced Toolbar configuration here.

### The Global Actions Pool

The `Actions/` folder in the system filesystem contains a list of standard actions, as instances in the same style as is used for menus and toolbars. This is what is shown when a user browses Tools | Options | IDE Configuration | Look and Feel | Actions. Any global action you install should be installed into this folder, regardless of whether it is used in a menu or toolbar.

This folder makes up the global actions pool. NetBeans' UI allows users to customize menus and toolbars - meaning that a user can delete your action, effectively making it inaccessible. The actions pool provides a way to restore an action that has been deleted, so any global action should be found here as well as in whatever toolbar or menu it is displayed in.

### Installing Keyboard Shortcuts

The way to install keyboard shortcuts is to make an instance of the action in question, and place it in the `Shortcuts/` folder. The file name will give the keyboard sequence, named according to the method `Utilities.keyToString(KeyStroke)`. Such instances will be used to create the global keymap used as a fallback for all components. (Per-component keymaps can override these bindings.)

Typically, you create an instance of an `Action` in the Actions Pool, by defining it in one or another subfolder of `Actions` in your module's layer. Then you link to it using `.shadow` files from the various other places it may be used (shadow files are similar to Unix symlinks - see `DataShadow` for more details).

A typical action installation looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
    <filesystem>
        <folder name="Actions" >
            <folder name="Edit">
                <file name="com-mymodule-MyEditAction.instance"/>
            </folder>
        </folder>
        <folder name="Menu">
            <folder name="Edit">
                <file name="com-mymodule-MyEditAction.shadow">
                    <attr name="originalFile" value="Actions/Edit/com-mymodule-MyEditAction.instance"/>
                </file>
            </folder>
        </folder>
        <folder name="Toolbars">
            <folder name="Edit">
                <file name="com-mymodule-MyEditAction.shadow">
                    <attr name="originalFile" value="Actions/Edit/com-mymodule-MyEditAction.instance"/>
                </file>
            </folder>
        </folder>
        <folder name="Shortcuts">
            <file name="S-A-Left.shadow">
                <attr name="originalFile" value="Actions/Edit/com-mymodule-MyEditAction.instance"/>
            </file>
        </folder>
    </filesystem>

@FOOTER@