Eclipse Search

Loading

Mar 8, 2009

Commands Part 6: Toggle & Radio menu contributions

In the previous parts of the series, we saw how Commands contribute to push style menu items. But commands allow you to contribute menu items with 'toggle' and 'radio' style as well. Let us see how to contribute the familiar "Format" menu thru Commands:

image

 

First we will look at the toggle style menu contribution. Commands can have states associated with it. A state id known by its id and it can have any value, which is stored in a subclass of org.eclipse.core.commands.State. To know whether a command is checked or not, the Command Framework looks for a state with the id 'org.eclipse.ui.commands.toggleState'. Lets specify the default checked state of Bold to true and Italic to false:

<command
      defaultHandler="com.eclipse_tips.commandstate.BoldHandler"
      id="com.eclipse-tips.commandState.boldCommand"
      name="Bold">
   <state
         class="org.eclipse.ui.handlers.RegistryToggleState:true"
         id="org.eclipse.ui.commands.toggleState">
   </state>
</command>
<command
      defaultHandler="com.eclipse_tips.commandstate.ItalicHandler"
      id="com.eclipse-tips.commandState.italicCommand"
      name="Italic">
   <state
         class="org.eclipse.ui.handlers.RegistryToggleState:false"
         id="org.eclipse.ui.commands.toggleState">
   </state>
</command>

The Framework expects a Boolean value from the state. It doesn't care about which class that holds the state. So why should we use the RegistryToggleState instead of our own state class? Two reasons:

  1. It implements IExecutableExtension. So you can specify the default values in the plugin.xml as I've done above.
  2. It extends PersistentState, so it can remember the value between Eclipse sessions. So if the user had checked/unchecked the menu, restarts Eclipse, he will get the same state as he left before - all this, you get without even your plugin being loaded.

So how should the handler work for this command?

public Object execute(ExecutionEvent event) throws ExecutionException {
     Command command = event.getCommand();
     boolean oldValue = HandlerUtil.toggleCommandState(command);
     // use the old value and perform the operation

    return null;
}

Basically, its the Handler's responsibility to update the Command's state. The HandlerUtil has a convenient method which toggles the state and gives you the old value of the state. You can use the value to perform the operation.

The radio state is also similar, which expects the state with a predefined id. Since the same command is contributed for various option, its should be a parameterized command. The id of the parameter which denotes the radio state is also predefined:

<command
      defaultHandler="com.eclipse_tips.commandstate.AlignHandler"
      id="com.eclipse-tips.commandState.alignCommand"
      name="Align Command">
   <commandParameter
         id="org.eclipse.ui.commands.radioStateParameter"
         name="State"
         optional="false">
   </commandParameter>
   <state
         class="org.eclipse.ui.handlers.RadioState:left"
         id="org.eclipse.ui.commands.radioState">
   </state>
</command>

Just like toggle state, the state must have the id and the class could be anything that stores the value as String. The RadioState class provides initializing from plugin.xml and also persists the value across sessions.

The menu contribution would specify the parameter value:

<menuContribution
      locationURI="menu:org.eclipse.ui.main.menu?after=additions">
   <menu
         label="Format">
      ... other contributions here
      <command
            commandId="com.eclipse-tips.commandState.alignCommand"
            label="Align Left"
            style="radio">
         <parameter
               name="org.eclipse.ui.commands.radioStateParameter"
               value="left">
         </parameter>
      </command>
      <command
            commandId="com.eclipse-tips.commandState.alignCommand"
            label="Align Center"
            style="radio">
         <parameter
               name="org.eclipse.ui.commands.radioStateParameter"
               value="center">
         </parameter>
      </command>
      <command
            commandId="com.eclipse-tips.commandState.alignCommand"
            label="Align Right"
            style="radio">
         <parameter
               name="org.eclipse.ui.commands.radioStateParameter"
               value="right">
         </parameter>
      </command>
   </menu>
</menuContribution>

The Command Framework understands this parameter and sets the UI contributions according to this value. Again its the handler's job to set the correct state from the command's parameter during execution. You have helper methods in the HandlerUtil to perform this:

public Object execute(ExecutionEvent event) throws ExecutionException {
    if(HandlerUtil.matchesRadioState(event))
        return null; // we are already in the updated state - do nothing
    String currentState = event.getParameter(RadioState.PARAMETER_ID);
    // perform task for current state
    if(currentState.equals("left"))
    // perform left alignment
    else if(currentState.equals("center"))
    // perform center alignment
    // and so on ...

    // and finally update the current state
    HandlerUtil.updateRadioState(event.getCommand(), currentState);

    return null;
}

Bonus:

Q: I want the initial value always loaded from the plugin.xml, the state should not be remembered between sessions. Should I write my own State class?

A: Not necessary. You can specify both the default value of the state and whether to persist or not in the plugin.xml itself:

<command
      defaultHandler="com.eclipse_tips.commandstate.AlignHandler"
      id="com.eclipse-tips.commandState.alignCommand"
      name="Align Command">
   <commandParameter
         id="org.eclipse.ui.commands.radioStateParameter"
         name="State"
         optional="false">
   </commandParameter>
   <state
         id="org.eclipse.ui.commands.radioState">
      <class
            class="org.eclipse.ui.handlers.RadioState">
         <parameter
               name="default"
               value="left">
         </parameter>
         <parameter
               name="persisted"
               value="false">
         </parameter>
      </class>
   </state>
</command>

This applies for toggle state as well.

Q: I want to know the state of the command elsewhere in the code. How do I get it?

A: Just use Command.getState(<state id>).getValue(). The state ids are available as constants at RegistryToggleState.STATE_ID and RadioState.STATE_ID

My patch for this feature has just been checked in and should be available from 3.5 M6 onwards. If you are curious, pick up a recent Nightly build and play with this.

47 comments:

  1. Is the entire 'bonus' section what you have recently patched? The reason is, I'm trying to specify the default value, but my plugin.xml warns me that I should not be nesting 'parameter' in 'state'.

    Maybe I'm just doing it wrong? :(

    ReplyDelete
  2. Ah, figured out my problem. Didn't note the class tag... :(

    My bad.

    ReplyDelete
  3. The state default mentioned in the Bonus section won't work properly till this bug is fixed:

    https://bugs.eclipse.org/bugs/show_bug.cgi?id=267408

    ReplyDelete
  4. Hi,
    I seek in the org.eclipse.ui.handlers.HandlerUtil
    class and the following functions don't seem to exist in Eclipse 3.3, 3.4 and 3.5.

    a) toggleCommandState
    b) matchesRadioState
    c) updateRadioState

    Where should I find these functions?

    Thanks

    ReplyDelete
  5. @Anonymous,
    They are introduced in 3.5 M6. Which build of 3.5 you are using?

    ReplyDelete
  6. Thanks Prakash,

    I don't know exactly the version of Eclipse documentation I consulted since it was only wrote release 3.5 in the doc of the HanderUtil class.

    Vincent

    ReplyDelete
  7. Hi Prakash,

    Is there any way to create a command/action without any UI, with only key bindings? Like I want to set some predefined values with selection of some combination of keys?

    Thanks in advance.

    ReplyDelete
  8. @Asha,
    That is very well possible. Create a command and assign a keybinding and a handler. Its up to the handler to show the UI or just to perform the execution by itself. So your handler can simply execute the task. Think of Copy/Paste commands that doesn't show any UI and perform the task at the key stroke

    ReplyDelete
  9. Hi Prakash,

    Yeah, just now I implemented the same :-)!
    I doubted it's possible or not thinking that there would be some conflict of contexts... but I just left the contextId blank. :-)!
    Thank you for your reply.

    ReplyDelete
  10. what about Authentication and Authorization with RCP?

    ReplyDelete
  11. For Eclipse 3.4 you need to have this state in order to graphically show the selected option:

    < state id="STYLE" class="org.eclipse.jface.commands.RadioState" />

    There is no need for the handler to do anything special.

    Now investigating how to set the initial value shown.

    ReplyDelete
  12. @David Pérez,

    Check this: http://wiki.eclipse.org/Menu_Contributions/Radio_Button_Command

    ReplyDelete
  13. Hello,

    I have a simple question - is it possible to persist a toggle state of menu command between workbench sessions in Eclipse 3.4? I've tried the solutions from this post but it seems they don't work.

    Thanks in advance.

    ReplyDelete
  14. @Blekit,
    Nope. this feature was implemented in 3.5

    ReplyDelete
  15. Just how broken are radio group commands in PRE-3.5M6 (3.4.1 in my case)?!

    Using STYLE & RadioState gets as far as switching the check indicator, however using RegistryRadioState to enable persistence doesn't work - just how is a boolean supposed to save the state of which menu is selected?!! Guess I have to work around by persisting myself in the handler.

    ReplyDelete
  16. @Justin,
    Check this link for how it used to work in 3.4: http://wiki.eclipse.org/Menu_Contributions/Radio_Button_Command

    ReplyDelete
  17. When using RegistryToggleState, which registry is the state persisted in?

    What I really need to know is how does other client code listen to state changes?

    ReplyDelete
  18. @DaSH,
    It doesn't store in any registry. Just in a file under the .metadata of your workspace.

    Check the HandlerProxy and AbstractHandler code. You will find more info

    ReplyDelete
  19. @Prakash
    Hmm, ok, I think I was confused about how RegistryToggleState works. Anyway, what I'm trying to figure out now is, is it possible to set the initial state in the UI of a command via code? My command is displayed in a view toolbar and has the toggle style. I store the state of the command in my plugin preference store. I can't do it in my handler because the handler isn't created until the toolbar item is first selected (although this isn't the case for the main application toolbar, it works in that case).

    ReplyDelete
  20. @Prakash
    Just wanted to follow up on my previous question about getting the initial state into the UI, I finally found "http://wiki.eclipse.org/Menu_Contributions/Radio_Button_Command#Initializing_the_Handler" which is exactly what I was looking for (I just have a toggle button instead of radio buttons). Thanks for you assistance!

    ReplyDelete
  21. I have a question about the command framework. I am confused about the capabilities, but here's what I'm trying to do (on eclipse 3.4)
    I tried the org.eclipse.ui.menus extension point, with not a lot of success.


    I want to contribte an action in a view's dropdown menu. I want the action's visibility to be dynamic, so I want to be able to determine whether or not to show the action right before the menu is opened, every time. Is this possible? Either programmatically or from xml?
    The "visibleWhen" and "enabledWhen" tags only seem to provide system property testers.

    ReplyDelete
  22. @anonymous,

    you can contribute your own property testers/source providers. Thats the easiest way to achieve what you want. Check this one: http://blog.eclipse-tips.com/2009/02/commands-part-5-authentication-in-rcp.html

    ReplyDelete
  23. I have a following requirement:
    I want a compound toggle button in toolbar which also have a drop down radio selection. For example, look into MS word 'Text highlight color' menu. You can toggle the menu & at the same time you can select a color from the dropdown.
    Please tell me is it possible in eclipse?
    Thanks.

    ReplyDelete
  24. @amey,
    I haven't come across any implementations in eclipse. You should try the forum, someone else might have done that already. Its doesn't sound like an impossible thing

    ReplyDelete
  25. How can I change button's text (eg. from Enable to Disable) after its state changes?

    ReplyDelete
  26. I have implemented a toggle command now myself. However the whole feature is somehow incomplete. What if the toggle should enable/disable another toolbar for instance, or some command. How would one toggle the enablement of commands?
    I guess that would be only possible using contexts.

    So activating the context is possible, however after the workbench restore somehow one need to be notified about the toggle state and re-activate the context.

    Any other ideas how to solve such problems of enable/disable other contributions with the toggle?

    ReplyDelete
  27. @Philipp,
    I've replied your query in the newsgroup as well. In essence:

    command1's handler.execute(){

    //do stuff

    HandlerUtil.toggleCommandState(command2);

    }

    You can use ICommandService.refreshElements() to update all the UI contributions for command2.

    ReplyDelete
  28. Can Anyone please help me? How to disable or enable Toolbar icons in the application through JAVA coding?

    My application is built based on Eclipse RCP framework. some of toolbar icons should be disabled/deactivated. when i clicked on "OK" button which is there in UI panel in my application.

    ReplyDelete
  29. Hi. I`m trying to have a command related to a preference (it`s defined by me, i want to have control over it from other places) I could manage updating the preference through the handler of the command, but i don`t know how to:
    1- Initialize the command state using the preference value
    2- Update the state when the preference is modified from other place (like preference page)

    Any idea?
    Thanks!

    ReplyDelete
  30. Hello.

    How can I make to the state of toggleCommand doesn't remember between sesions??

    Thanks

    Jose

    ReplyDelete
  31. @Anonymous,
    Use the (parameter name="persisted" value="false") as said above

    ReplyDelete
  32. Excusame, last post wasn't good.

    I make this:












    But the state is remember still

    Thanks ans I'm sorry

    ReplyDelete
  33. Why don't my code see?

    ReplyDelete
  34. No idea. Probably you have posted xml code which isn't valid in HTML

    ReplyDelete
  35. Ok

    This is my code (but the state is remember still)

    (command
    category="Favorites"
    categoryId="com.quaityeclipse.favorites.commands.category"
    description="Open tha Favorites view if it is not already visible"
    id="com.quaityeclipse.favorites.commands.openView"
    name="Open Favorites View">

    (state

    class="org.eclipse.ui.handlers.RegistryToggleState:false"
    id="org.eclipse.ui.commands.toggleState">
    ( / state )

    (parameter

    name="org.eclipse.ui.commands.radioStateParameter"
    value="center">

    ( / parameter )


    ( / command)

    It isn't xml now.

    Thanks a lot and sorry for the error

    ReplyDelete
  36. Thanks very much for the great article! I have 5 commands in a menu contribution that are all marked as radio and configured using a single parameterized command as you explained in your article. However, I'd like to put a separator after the second one. When I do this, the menu items are broken into two radio groups, which is not the behavior I'm looking for.

    In other words, when I put the separator in after the second command, if I click the first menu item, then click the 5th menu item, they both end up with a check next to them. The only way to clear the check next to the 5th menu item is to click on the 3rd or 4th menu item.

    Thanks in advance!

    ReplyDelete
  37. Best regards friends.
    Is there some way to execute command defined in plugin.xml programmatically.
    I am not interested in below
    handlerService = (IHandlerService ) PlatformUI.getWorkbench().getService(IHandlerService.class);

    as it does not work with E4 @inject

    An exact piece of code shall be highly appreciated.

    regards
    Parvez Ahmad Hakim
    RCP Developer Srinagar Kashmir India

    ReplyDelete
  38. hi,

    suppose I have a radio menu as presented above. in addition I have a some random command and it's representation as a toolbar button. is there any way to enable/disable this button (its handler) based on the selected in the radio menu?
    I am sure there must be a way to do this via the "enabledWhen" property of the handler, but I can't figure out how to properly reference the state of the radio menu.

    Any help would be greatly appreciated!
    cheers,
    Lena

    ReplyDelete
  39. Hii

    I am using the command framework(org.eclipse.ui.menus) to create a new command and add it to the main toolbar (editor toolbar)in my application. But the problem is with the placement of the toggle command !!!

    I have an editor Action bar contributor which adds actions to the editor toolbar. I am trying to add my command to the end of the editor action bar without much success !!

    please help me out !!!

    ReplyDelete
  40. Hi Prakash ,

    Thanks for this series of article, I still haven't found a way to reproduce old action behavior like :

    - What if I have an editor and a view opened, with both a menu contribution pointing to the same command.
    - When I click on the menu in the editor, the menu in the view is updated, which did not occurred when I was using actions.

    How can I manage a "local" behavior within my part?

    Regards,
    Nicolas Guyomar

    ReplyDelete
  41. No. With commands there is nothing like a "local" contribution. At least not in 3.x. You need to switch to e4 for that.

    ReplyDelete
  42. Ok, thanks for putting an end to my investigation

    ReplyDelete
  43. Hi Prakash

    I like to activate a context based on the type of the object.
    Can i do it via .xml ? How can i achieve this ?

    I have seen examples in plugin.xml where you run the PropertyTester and based on menu items get enabled disabled. I want soemthign similar for context purpose.

    Thanks..
    Usha

    ReplyDelete
  44. When I have multiple instances of the same view, the toggle state seems to be shared among view instances. Is there a way to toggle the button on only one view?

    Thanks,

    Jay

    ReplyDelete
    Replies
    1. Did you find the answer to this question? How you ensure that the command toggle is per instance of the view?

      Delete
  45. Prakash,
    I want to remove the action sets defined in plugin.xml programatically while opening a perspective/view. I have tried various options like defining an ActionBarAdvisor, calling it in IStartUp class (earlyStartUp() method), using ActionBarRegistry to unregister an entry. But none of these options are working for me.

    Please help...

    Pankaj

    ReplyDelete