Tag Archives: commands

Executing commands programmatically

Executing commands (e.g. from a SWT listener) is a pretty handy thing. You don’t have to care about, where the handler is registered, you simply execute the command via the ICommandService, the delegation is provided by the framework.


...
ICommandService commandService = (ICommandService)getViewSite().getService(ICommandService.class);
try {
commandService.getCommand("com.foo.the.command").executeWithChecks(new ExecutionEvent());
}
catch (Exception exception) {
logger.error(exception.getMessage(), exception);
}
...

There is just one thing about the snippet above that is bad: The way the ExecutionEvent is created. Using the default constructor results in hard time for a handler, as the HandlerUtil class cannot be used due to the missing context information. A better way is to pass on some parameters to the ExecutionEvent to be created. The most important one is the last parameter, the application context object. It can be obtained via the IEvaluationService.


...
ICommandService commandService = (ICommandService)getViewSite().getService(ICommandService.class);
IEvaluationService evaluationService = (IEvaluationService)getViewSite().getService(IEvaluationService.class);
try {
Command theCommand = commandService.getCommand("com.foo.the.command");
theCommand.executeWithChecks(new ExecutionEvent(theCommand, new HashMap(), control, evaluationService.getCurrentState()));
}
catch (Exception exception) {
logger.error(exception.getMessage(), exception);
}
...

This command invokation can be processed by any handler using the HandlerUtil class.

Programmatic command contributions

Have you ever faced the task of programmatically adding commands to a given menu? It is not that difficult – when you finally found out ;).
First of all, you need a target menu, whether it is declared or programmatically created does not matter. What you actually need is the menu’s location URI. You may the create an AbstractContributionFactory with the menu’s location URI:

...
AbstractContributionFactory contribFactory = new AbstractContributionFactory ("menu:com.foo.targetmenu?after=targetGroup", null) {
@Override
public void createContributionItems(IServiceLocator locator, IContributionRoot additions) {
// add the contribution using it's source
additions.addContributionItem(createContributionItem(contribSource), null);
}
IMenuService menuService = (IMenuService)getViewSite().getService(IMenuService.class);
menuService.addContributionFactory(locator, contribFactory);
...

The items are added to the IContributionRoot within the factory’s createContributionItems() method. The factory is then registered at the IMenuService and your contributions show up.
For creating an IContributionItem for a given command, you may use the CommandContributionItem class, which takes an CommandContributionItemParameter as constructor parameter. A CommandContributionItemParameter basicly contains all the required data for a command contribution known from the org.eclipse.ui.menus extension point.


private IContributionItem createContributionItem(IServiceLocator locator, Object contribSource) {
CommandContributionItemParameter contributionItemParameter = new CommandContributionItemParameter(locator, null, "com.foo.the.command.id", SWT.PUSH);
// pimp the item using the contribSource
...
return new CommandContributionItem(contributionItemParameter);
}

There you go.

Eclipse Commands: Cut, Copy and Paste

I came around the requirement to enable the default cut, copy and paste function as menu entries within the main menubar. When adding the commands org.eclipse.ui.edit.cut, org.eclipse.ui.edit.copy and org.eclipse.ui.edit.paste to my menubar using the org.eclipse.ui.menus extension point. Unfortunately, these menu entries where disabled all the time. This was pretty comprehensible, as there was no active handler available. So I did register an appropriate handler (working like the Eclipse WidgetMethodHandler) using the IHandlerService. It takes the currently focussed element and checks whether this Control has the necessary cut(), copy() or paste() method to invoke and calls the required method on execution. So far, so good, my menu entries were now enabled – at least sometimes?!? The reason for that was the isHandled() method of my handler which returns true only if the method is available on the currently focussed element.

/** {@inheritDoc} */
@Override
public boolean isHandled() {
return isTargetMethodAvailable(Display.getCurrent().getFocusControl());
}

As the enablement state is determined only on the context part’s (IWorkbenchPart) activation, the state is only requested once for the default focus Control of the part. So the state of the commands stays the same for the whole part activation. Additionally, a ‘no active handler available’ exception is thrown, if the menu entry is selected with a focussed Control not supporting the method. Bad luck.

So, what to do now? I ended up registering a focus Listener for all elements of the part to keep the command state in sync with the handler state.


// create a listener for tracking the focus
final Listener listener = new Listener() {
/** {@inheritDoc} */
@Override
public void handleEvent(Event event) {
ICommandService commandService = (ICommandService)workbenchPart.getSite().getService(ICommandService.class);
commandService.refreshElements(commandId, null);
}
};

The refreshElements() method calls the updateElement() method of active handlers for the given command ID implementing the IElementUpdater interface. So I let my handler implement that interface, the interface method simply fired an HandlerEvent stating that the enabled state changed.

/** {@inheritDoc} */
@Override
public void updateElement(UIElement element, Map parameters) {
fireHandlerChanged(new HandlerEvent(this, true, false));
}

The only thing left to do was to implement the isEnabled() method which is used to determine the commands enablement state.

/** {@inheritDoc} */
@Override
public boolean isEnabled() {
return isTargetMethodAvailable(Display.getCurrent().getFocusControl());
}

There you are, it works!