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).
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.
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, allSystemAction
classes run theiractionPerformed
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 methodSystemAction.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 (orvar/log/messages.log
file) - if you forget to override this method on some action, you will be warned when it is run.
To make this possible,
The typical usage pattern for these is along the lines of:
FindAction
is a public API; modules providing implementations
affect its behavior by setting its action delegate into .
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.
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 beJMenuItem
orJMenu
instances.
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.
CookieAction
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.
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.
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.
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:
Nodes may attach actions by overriding
Node.getActions(...)
or
Node.getPreferredAction()
.
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.
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.
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:
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
.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.
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.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.
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.
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.
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>