views:

1893

answers:

3

I'm currently struggling with the following Eclipse RCP commands:

  • org.eclipse.ui.edit.cut
  • org.eclipse.ui.edit.copy
  • org.eclipse.ui.edit.paste

I'm using them as command contributions in the toolbar, but the UIElements (toolbar items) are not updated when the 'handled' state of those commands changes.

For testing I used a polling mechanism to verify that the state of those commands really changes depending on the currently focussed element, and I found out that the handler remains the same but the handler's 'handled' state changes properly, causing the commands 'handled' state to also change properly.

The only problem is, that neither one of those state changes causes a notification (neither on the command's ICommandListener, nor on the handler's IHandlerListener), so the UIElements won't get updated.

Here's some testing code to observe the states of a Command:

ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);

final String commandId="org.eclipse.ui.edit.copy";
Command command = commandService.getCommand(commandId);
command.addCommandListener(new ICommandListener() {

    public void commandChanged (CommandEvent commandEvent) {
        System.out.println(">> Command changed: " + commandId);
    }
});

Am I missing something, or is this an bug in the cut/copy/paste handler implementations? Any insights?

EDIT: The commands are enabled all the time, and the handler is never exchanged, only the handler's 'handled' state (and thus also the commmand's 'handled' state) changes depending on which ui element has the focus. There is however no notification when this state changes. This results in the toolbar buttons always being enabled, and pressing them will cause a org.eclipse.core.commands.NotHandledException: There is no handler to execute for command.

A: 

Your problem is that you need to register a handler for anything that is not a text because Eclipse needs to know how to copy the currently selected "something" to the clipboard. That's what a handler does. This article in the Eclipse wiki will get you started how to create and register a handler.

Aaron Digulla
It's the 'handled' state that I'm interested in.When the focus is not on a text field, all three commands are enabled, but not handled (handler is set but not handled)When the command is invoked by pressing a toolbar button then, it will result in a "org.eclipse.core.commands.NotHandledException: There is no handler to execute for command"
Peter Walser
Ah ... now it makes sense. I've updated my answer.
Aaron Digulla
I'm perfectly ok when a command isn't handled temporarily (for example when the focus changes from a text field to a button, then there's nothing to cut/copy/paste), but in that case I'd want to get notified so I can disable the toobar item for as long as the command isn't handled.I could of course register a dummy handler for said commands so at least no exception is thrown when trying to execute them, but I'd rather have the toolbar/menu items disabled.
Peter Walser
That's odd. In this case, the command framework should already disable the buttons for you (that's what the conditions are for).
Aaron Digulla
A: 

I could be wrong, but the source of the problem is that the handler is always enabled.

See Platform Plug-in Developer Guide > Programmer's Guide > Plugging into the workbench > Basic workbench extension points using commands > Handlers.

The <activeWhen/> expressions in the plugin.xml and programmatic core expressions are used to help determine the scope of a handlers activation. For example, a specific window, a specific Shell, an active part type or active part.

   <extension
         point="org.eclipse.ui.handlers">
      ...
      <handler
            class="org.eclipse.ui.examples.contributions.view.SwapInfoHandler"
            commandId="org.eclipse.ui.examples.contributions.view.swap">
         <activeWhen>
            <reference
                  definitionId="org.eclipse.ui.examples.contributions.view.inView">
            </reference>
         </activeWhen>
         <enabledWhen>
            <count
                  value="2">
            </count>
         </enabledWhen>
      </handler>
      ...
eed3si9n
It's valid for a handler to be enabled but not 'handled'.The 'activeWhen' condition is optional, when not specified the handler will be active all the time.The problem is still that changes in the handler are not notified, and the specific handler for cut/copy/paste cannot be observed.
Peter Walser
+1  A: 

The handler which is registered for the cut/copy/paste commands is org.eclipse.ui.internal.handlers.WidgetMethodHandler. This handler checks if a given method is declared on the current display's focus control. When executed, that handler will invoke the method using reflection.

Snippet from WidgetMethodHandler:

 public final boolean isHandled() {
     return getMethodToExecute() != null;
 }

The getMethodToExecute() will locate the current focus control using Display.getCurrent().getFocusControl(), and then check if the given trigger method is declared on it.

Widgets such as org.eclipse.swt.widgets.Text have cut(), copy() and paste() methods, so when the focus is on such a widget, the handler will return 'true' for isHandled().

This handler is however not aware when the current focus control changes (I think there isn't even a way to observe this on the Display), and thus can't notify about changes on its dynamic 'isHandled' state.

This results in the cut/copy/paste commands being fine for popup menus, but they're quite problematic when used in toolbars, as their UI elements can't be updated properly when the handler does no notifications.

This leaves me with either not using those commands in the toolbar, or having a polling mechansim to update the ui elements (which is also bad and error prone). :-(

Peter Walser