In Chapter 12, “Adding Key Bindings,” we were first introduced to commands as a way to add key bindings. The introduction was brief on purpose as actions were still being introduced and not covered in depth as we just did in Chapter 17. One thing that wasn’t mentioned was that the actions and commands frameworks are two different frameworks that accomplish the same thing: contributing bits of functionality to the Workbench. Another thing that wasn’t mentioned was that the actions framework has been around since the very early release of Eclipse and has aged considerably. The actions framework outgrew its initial use cases as the Eclipse platform expanded into new territories. Furthermore, there are many weaknesses in the API, but the main weakness is that the UI and handling of an action are coupled. So an effort was made in Eclipse 3.1 to provide a modern alternative to actions with the introduction of the commands framework. As of Eclipse 3.3, the commands API stabilized and offered a viable alternative to the actions framework.
This chapter is split into two parts. The first explains commands, and the second part upgrades Hyperbola to use commands instead of actions. Specifically in this chapter we’ll show you how to
• Understand the problems surrounding the actions framework
• Use commands to provide menu contributions
• Decide between using commands and actions
• Replace certain actions in Hyperbola with commands
The deficiencies of the actions API were identified a while ago and documented in Bug 36968. In Chapter 17 some of the complexities involved in using actions as a Workbench contribution mechanism should have become evident. To understand the basics of the problem, it’s best to look at the IAction
interface:
From the IAction
interface, it’s evident that many aspects of an action are tightly coupled that shouldn’t be. The user interface bits of the action (text and image) are coupled to the actual implementation of the action (run). This coupling makes the action framework less flexible, for example, if you wanted to provide different implementations of the same action depending on the context. How can the action’s enablement be dynamically computed based on some external properties or workflow? What happens if you need to maintain several customizable keyboard accelerator codes? Furthermore, the existing action extension points—org.eclipse.ui.actionSets
, org.eclipse.ui.popupMenus
, org.eclipse.ui.editorActions
, and org.eclipse.ui.viewActions
—specify both their placement and visibility criteria.
The limitations of actions become apparent if you use them in a large, complex application. Because of these limitations, there was a need for a new framework. This new framework needed to unify the concept of contributions across the Workbench and also have a clean separation between behavior and presentation. The commands framework is the incarnation of this new framework.
As of Eclipse 3.5, the actions framework isn’t deprecated. It may be deprecated in a future release of Eclipse, but that’s doubtful given its widespread usage. In this book we cover both the actions and commands frameworks because both frameworks are used widely.
In essence, a command represents the abstraction between presentation and behavior. A command itself doesn’t represent the presentation or the behavior. In concrete terms, a command binds one or more menu contributions (presentation) with one or more handlers (behavior) as shown in Figure 18-1.
In Chapter 12, “Adding Key Bindings,” we created our first command in Hyperbola that represented the notion of adding a contact to Hyperbola. This was required in order to take advantage of the key binding support in Eclipse via the org.eclipse.ui.bindings
extension point. To understand how to define a command, let’s look at an example:
The first step was to define a command category which is a grouping mechanism for commands. A category has an associated name and description. The next step was to define a command with a unique identifier of org.eclipsercp.hyperbola.addContact
. The optional categoryId
defines the ID for the category to which this command applies. The name
and description
fields represent human-readable text. Note that there is no reference to a specific implementation, key binding, or menu contribution.
To observe the execution of commands in the commands framework, you can register an IExecutionListener
via the ICommandService
. Note that the listener is merely an observer of commands so it can’t veto or modify them.
In Chapter 17, “Actions,” we discussed the four action-related extension points: org.eclipse.ui.actionSets
, org.eclipse.ui.popupMenus
, org.eclipse.ui.editorActions
, and org.eclipse.ui.viewActions
. In the commands framework, these extension points have been replaced by the org.eclipse.ui.menus
extension point. The org.eclipse.ui.menus
extension point is used to define placement and visibility of a command. Specifically, the locationURI
attribute dictates the placement of the command. The visibleWhen
child element specifies the visibility of the command.
Hyperbola already has a named top-level menu. In the ActionBarAdvisor
method fillMenu()
, the Hyperbola menu is created as a MenuManager
using the snippet below. The first parameter to the constructor is the menu name, as shown in the UI, and the second, hyperbola
, is the ID of the menu.
org.eclipsercp.hyperbola/ApplicationActionBarAdvisor
MenuManager hyperbolaMenu = new MenuManager("&Hyperbola", "hyperbola");
To place the Hyperbola menu contribution in a declarative fashion using the org.eclipse.ui.menus
extension is straightforward via this example:
The menuContribution
element specifies the placement of the menu via the locationURI
attribute. The locationURI
has a special syntax and can be broken down into three parts as shown in Figure 18-2: scheme, identifier, and arguments.
The scheme of the locationURI
identifies the type of menu contribution. The valid types are
menu
—the main application menu or a view pull-down menu
popup
—a pop-up (context) menu in a view or editor
toolbar
—the main application toolbar or a toolbar within a view
The identifier of the locationURI
specifies the unique identifier of the contribution. In the preceding example we added the Hyperbola menu item to the Eclipse main menu using the org.eclipse.ui.main.menu
identifier. A list of common locationURI
identifiers is provided here:
org.eclipse.ui.main.menu
—the main menu
org.eclipse.ui.main.toolbar
—the main toolbar
org.eclipse.ui.views.ProblemView
—the Problems view
org.eclipse.ui.views.ContentOutline
—the Outline view
org.eclipse.ui.popup.any
—any context menu
The final aspect of the locationURI
is the arguments. Arguments represent the specific location within a given menu, pop-up, or toolbar where a contribution lives. The arguments are composed of the placement of the contribution, which can be before or after, an equals sign (“=
”), and the identifier of some item in the menu, pop-up, or toolbar. The identifier additions can be used to indicate that the contribution placement should happen in the default location within the given menu, pop-up, or toolbar.
If you’re having trouble finding the proper locationURI
identifier for your contribution, consider using the Plug-in Spy (Alt+Shift+F1). Invoke the Plug-in Spy over an editor or view you’re interested in contributing to and the Plug-in Spy will present you with identifiers you can use. If you’re interested in menus, the Menu Spy (Alt+Shift+F2) will give you relevant information related to menu contribution identifiers.
In Hyperbola the Add Contact command is placed in two locations, the main menu and the toolbar. To place the Add Contact command in the main menu, we simply need to add a new menu element under the Hyperbola menu element as shown here:
To add the Add Contact command to the toolbar, we need to specify a new locationURI
that represents the toolbar location org.eclipse.ui.main.toolbar
as shown here:
In Hyperbola we created and registered standard Workbench actions like Exit with the ActionFactory
class in the makeActions(IWorkbenchWindow window)
method as demonstrated in the following snippet:
We registered these actions by using the ActionBarAdvisor.register(IAction)
method as demonstrated below. Under the covers, this took care of things like registering the standard action’s respective key binding. In terms of presentation, we placed the actions in the ActionBarAdvisor
method fillMenu()
.
The commands framework requires a different approach. For example, if we look at the Exit action, we can replicate the behavior and placement using the org.eclipse.ui.menus
extension point. To place the Exit action in the proper location, we add a menu child element relative to the Hyperbola menu we defined earlier. To represent the proper exit behavior, we can reuse the existing Exit command identifier provided by the Workbench, org.eclipse.ui.file.exit
, as shown in this snippet:
This pattern can be applied to the other standard Workbench actions like Preferences and Help. To find existing command identifiers, we recommend that you take a look at the IWorkbenchActionConstants
and IIDEActionConstants
classes. An even easier way to find existing command identifiers is to use the extension editor and click the Browse... button to bring up a command identifier selection dialog as shown in Figure 18-3.
The visibility of contributions needs to be controlled. For example, imagine if all plug-in developers contributed top-level menu items without any regard for when they should be visible? The user interface would get cluttered quickly. In Hyperbola the Add Contact command should be visible only when a RosterGroup
entry is selected. To accomplish this, we need to add a visibleWhen
child element to the respective Add Contact menu element.
In the visibleWhen
expression in this snippet, the selection variable
evaluates its child element, or
. The or
element expresses that the object must be an instance of RosterGroup
via the instanceof
element.
The behavior of a command is specified via a handler, specifically via the org.eclipse.ui.handlers
extension point. Using the org.eclipse.ui.handlers
extension point, you can associate one or more classes that implement IHandler
and represent the behavior of your command.
To programmatically associate a handler with a command, you can use the IHandlerService
which is obtained from a view site. To programmatically define or execute a command, you can obtain the ICommandService
.
In Hyperbola we can refactor the Add Contact action into a command handler by making AddContactAction
extend AbstractHandler
instead of Action
as shown in the following snippet:
The main differences between the old and new AddContactAction
is that the logic that was previously in the run()
method is now in the execute(ExecutionEvent event)
method which is required by extending AbstractHandler
. Furthermore, since the Workbench window is no longer passed in as part of the constructor, we have to use the HandlerUtil
class to obtain the current Workbench window and selection. When working with handlers, we recommend using the HandlerUtil
class for things like acquiring the active editor and current selection.
Now that we have defined a proper handler, the next step is to make the commands framework aware of our handler by using the org.eclipse.ui.handlers
extension point as shown here:
The handler element acts as the glue between a defined command and the behavior. The class attribute defines a class that implements IHandler
or extends AbstractHandler
. The commandId
attribute represents an identifier that is used to reference an existing command.
If you have an existing action, you can use the ActionHandler
class to convert it into an instance of an IHandler
.
Restricting the execution of a handler is done in a similar fashion to the way we restricted menu contributions via visibleWhen
expressions, discussed in Section 18.3.3. Within the context of handlers, we have the option of enabledWhen
and activeWhen
expressions. We recommend always specifying an activeWhen
expression to avoid unnecessary plug-in loading. For a handler to be loaded, the command must be selected and the enabledWhen
and activeWhen
expressions must be satisfied.
In this chapter you learned about the existing problems of the actions framework and how the commands framework solves those problems. We even modified existing actions in Hyperbola to take advantage of the commands framework. When you are starting an application from scratch, it’s highly recommended that you use the commands framework instead of the actions framework.
The Eclipse wiki contains a breadth of information regarding the commands framework:
• http://wiki.eclipse.org/Platform_Command_Framework
• http://wiki.eclipse.org/Command_Core_Expressions
• http://wiki.eclipse.org/Menu_Contributions
The Eclipse Platform team has a useful example illustrating different aspects of the commands framework. It’s available in CVS under the org.eclipse.ui.examples
name:
• http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.examples.contributions/