Skip to main content
Jump to: navigation, search

Menu Contributions

Revision as of 20:29, 22 October 2006 by Pwebster.ca.ibm.com (Talk | contribs) (Menus API Option 2)

Placement examples that try to descript the old to new way.



Add ProblemView menus

Add the Problems view menus. The problems view has one toolbar action and in the view menu, 3 actions and 2 dynamic submenus. It also has a dynamic menu and another bunch of actions in its context menu.

Commands

First define commands that are specific to the view. Since these are view commands, we can specify a default handler ... we're unlikely to replace it.

<extension
    point="org.eclipse.ui.commands">
  <category
      id="org.eclipse.ui.views.problems"
      name="Problems View">
  </category>
  <command
      categoryId="org.eclipse.ui.views.problems"
      defaultHandler="org.eclipse.ui.views.markers.internal.TableSortHandler"
      description="Change the problem sort order"
      id="org.eclipse.ui.views.problems.sorting"
      name="Sorting...">
  </command>
  <-- the view preference command would probably be defined once
      with the other preference contributions -->
  <command
      categoryId="org.eclipse.ui.views.problems"
      defaultHandler="org.eclipse.ui.preferences.ViewPreferencesHandler"
      description="Open the view preferences"
      id="org.eclipse.ui.preferences.viewPreferences"
      name="Preferences">
    <commandParameter
        id="org.eclipse.ui.preferences.viewPreferences.markerEnablementName"
        name="Marker Enablement Preference"
        optional="false"/>
    <commandParameter
        id="org.eclipse.ui.preferences.viewPreferences.markerLimitName"
        name="Marker Limit Preference"
        optional="false"/>
  </command>
  <command
      categoryId="org.eclipse.ui.views.problems"
      defaultHandler="org.eclipse.ui.views.markers.internal.FiltersHandler"
      description="Open the filters dialog"
      id="org.eclipse.ui.views.problems.configureFilters"
      name="Configure Filters...">
  </command>
  <command
      categoryId="org.eclipse.ui.views.problems"
      defaultHandler="org.eclipse.ui.views.markers.internal.OpenMarkerHandler"
      description="Go to the current marker"
      id="org.eclipse.ui.views.problems.goTo"
      name="Go To"/>
</extension>

Handlers

We can also use a number of global commands, like copy, paste, delete, quick fix, and properties. For these, we just need to define our handlers. We need to add them with <activeWhen/> clauses to restrict them to being active when the view is active.

<extension
    point="org.eclipse.ui.handlers">
  <handler
      commandId="org.eclipse.ui.edit.copy"
      class="org.eclipse.ui.views.markers.internal.CopyMarkerHandler">
    <enabledWhen>
      <not>
        <count value="0"/>
      </not>
    </enabledWhen>
    <activeWhen>
      <with variable="activePartId">
        <equals value="org.eclipse.ui.views.ProblemView"/>
      </with>
    </activeWhen>
  </handler>
  <handler
      commandId="org.eclipse.ui.edit.paste"
      class="org.eclipse.ui.views.markers.internal.PasteMarkerHandler">
    <enabledWhen>
      <not>
        <count value="0"/>
      </not>
    </enabledWhen>
    <activeWhen>
      <with variable="activePartId">
        <equals value="org.eclipse.ui.views.ProblemView"/>
      </with>
    </activeWhen>
  </handler>      
  <handler
      commandId="org.eclipse.ui.edit.delete"
      class="org.eclipse.ui.views.markers.internal.RemoveMarkerHandler">
    <enabledWhen>
      <not>
        <count value="0"/>
      </not>
    </enabledWhen>
    <activeWhen>
      <with variable="activePartId">
        <equals value="org.eclipse.ui.views.ProblemView"/>
      </with>
    </activeWhen>
  </handler>      
  <handler
      commandId="org.eclipse.ui.edit.selectAll"
      class="org.eclipse.ui.views.markers.internal.SelectAllMarkersHandler">
    <enabledWhen>
      <not>
        <count value="0"/>
      </not>
    </enabledWhen>
    <activeWhen>
      <with variable="activePartId">
        <equals value="org.eclipse.ui.views.ProblemView"/>
      </with>
    </activeWhen>
  </handler>
  <handler
      commandId="org.eclipse.jdt.ui.edit.text.java.correction.assist.proposals"
      class="org.eclipse.ui.views.markers.internal.ResolveMarkerHandler">
    <enabledWhen>
      <not>
        <count value="0"/>
      </not>
    </enabledWhen>
    <activeWhen>
      <with variable="activePartId">
        <equals value="org.eclipse.ui.views.ProblemView"/>
      </with>
    </activeWhen>
  </handler>      
  <handler
      commandId="org.eclipse.ui.file.properties"
      class="org.eclipse.ui.views.markers.internal.ProblemPropertiesHandler">
    <enabledWhen>
      <not>
        <count value="0"/>
      </not>
    </enabledWhen>
    <activeWhen>
      <with variable="activePartId">
        <equals value="org.eclipse.ui.views.ProblemView"/>
      </with>
    </activeWhen>
  </handler>
</extension>

Or we can programmatically activate them through the IHandlerService which we would retrieve from the ProblemView site.

IHandlerService handlerServ = (IHandlerService)getSite().getService(IHandlerService.class);
copy = new CopyMarkerHandler();
handlerServ.activateHandler("org.eclipse.ui.edit.copy", copy);

Using the ProblemView site to access the IHandlerService handles the <activeWhen/> clause for us, and our programmatic handler would manage its own enablement state.

Expression Sidebar

You can see that the <activeWhen/>, <enabledWhen/>, and probably the <visibleWhen/> are likely to be replicated over and over again. A possible option is some kind of expression template markup ... either in its own extension or supported by our UI extensions that can use core expressions.

Here's an example of using expression templates in its own extension point.

<extension
    point="org.eclipse.core.expression.templates">
  <expression id="isPartActive">
    <parameter id="partId"/>
    <with variable="activePartId">
      <equals value="$partId"/>
    </with>
  </expression>
  <expression id="isActionSetActive">
    <parameter id="actionSetId"/>
    <with variable="activeContexts">
      <iterator operator="or">
        <equals value="$actionSetId"/>
      </iterator>
    </with>
  </expression>
  <expression id="isContextActive">
    <parameter id="contextId"/>
    <with variable="activeContexts">
      <iterator operator="or">
        <equals value="$contextId"/>
      </iterator>
    </with>
  </expression>
  <expression id="isSelectionAvailable">
    <not>
      <count value="0"/>
    </not>
  </expression>
</extension>

This could be used to simplify the handler definitions:

<extension
    point="org.eclipse.ui.handlers">
  <handler
      commandId="org.eclipse.ui.edit.copy"
      class="org.eclipse.ui.views.markers.internal.CopyMarkerHandler">
    <enabledWhen>
      <evaluate ref="isSelectionAvailable"/>
    </enabledWhen>
    <activeWhen>
      <evaluate ref="isPartActive">
        <parameter id="partId" value="org.eclipse.ui.views.ProblemView"/>
      </evaluate>
    </activeWhen>
  </handler>
</extension>

If we allow recursive template definitions, that would allow you to specify the concrete expression once and then reference it throughout your view.

<extension
    point="org.eclipse.core.expression.templates">
  <expression id="isProblemViewActive">
      <evaluate ref="isPartActive">
        <parameter id="partId" value="org.eclipse.ui.views.ProblemView"/>
      </evaluate>
  </expression>
</extension>
<extension
    point="org.eclipse.ui.handlers">
  <handler
      commandId="org.eclipse.ui.edit.copy"
      class="org.eclipse.ui.views.markers.internal.CopyMarkerHandler">
    <enabledWhen>
      <evaluate ref="isSelectionAvailable"/>
    </enabledWhen>
    <activeWhen>
      <evaluate ref="isProblemViewActive"/>
    </activeWhen>
  </handler>
</extension>

This reduces the handler definition even more.

Menus

Then we would define the ProblemView menu structures. We are using 3 roots: the view menu, the view toolbar, and the view context menu. This is an example of an "in-place" menu definition. The XML hierarchy mirrors the menu hierarchy.


<extension point="org.eclipse.ui.menus">
  <menuRoot location="menu://org.eclipse.ui.views.ProblemView">
    <item id="org.eclipse.ui.views.problems.sorting.item" 
          commandId="org.eclipse.ui.views.problems.sorting"
          mnemonic="S"
          tooltip="Change the Sort order"/>
    <menu id="org.eclipse.ui.views.problems.groupBy.menu"
          label="Group By"
          mnemonic="G">
      <dynamic class="org.eclipse.ui.views.markers.internal.GroupByMenu"/>
    </menu>
    <separator id="group.filter" visible="true"/>
    <menu id="org.eclipse.ui.views.problems.filters.menu"
          label="Filters"
          mnemonic="F">
      <dynamic class="org.eclipse.ui.views.markers.internal.FilterMenu"/>
    </menu>
    <item id="org.eclipse.ui.views.problems.configureFilters.item"
          commandId="org.eclipse.ui.views.problems.configureFilters"
          mnemonic="C"
          icon="$nl$/elcl16/filter_ps.gif"
          tooltip="Configure the filters to be applied to this view"/>
    <item id="org.eclipse.ui.preferences.viewPreferences.item"
          commandId="org.eclipse.ui.preferences.viewPreferences"
          mnemonic="P"
          tooltip="">
      <parameter id="org.eclipse.ui.preferences.viewPreferences.markerEnablementName" 
                 value="LIMIT_PROBLEMS"/>
      <parameter id="org.eclipse.ui.preferences.viewPreferences.markerLimitName" 
                 value="PROBLEMS_LIMIT"/>
    </item>
  </menuRoot>
  <menuRoot location="toolbar://org.eclipse.ui.views.ProblemView">
    <item id="org.eclipse.ui.views.problems.configureFilters.toolitem"
          commandId="org.eclipse.ui.views.problems.configureFilters"
          icon="$nl$/elcl16/filter_ps.gif"
          tooltip="Configure the filters to be applied to this view"/>
  </menuRoot>
  <menuRoot location="popup://org.eclipse.ui.views.ProblemView">
    <item id="org.eclipse.ui.views.problems.goTo.item"
          commandId="org.eclipse.ui.views.problems.goTo"
          mnemonic="G"
          icon="$nl$/elcl16/gotoobj_tsk.gif"
          disabledIcon="$nl$/dlcl16/gotoobj_tsk.gif"
          tooltip="Go to the marker location"/>
    <separator id="group.showIn" visible="true"/>
    <menu id="org.eclipse.ui.views.problems.showIn.menu"
          label="Show In"
          mnemonic="w">
      <dynamic class="org.eclipse.ui.actions.ShowInContributions"/>
    </menu>
    <separator id="group.edit" visible="true"/>
    <item id="org.eclipse.ui.views.problems.copy.item"
          mnemonic="C"
          commandId="org.eclipse.ui.edit.copy"
          icon="$nl$/icons/full/etool16/copy_edit.gif"
          disabledIcon="$nl$/icons/full/dtool16/copy_edit.gif"/>
    <item id="org.eclipse.ui.views.problems.paste.item"
          commandId="org.eclipse.ui.edit.paste"
          mnemonic="P"
          icon="$nl$/icons/full/etool16/paste_edit.gif"
          disabledIcon="$nl$/icons/full/dtool16/paste_edit.gif"/>
    <item id="org.eclipse.ui.views.problems.delete.item"
          commandId="org.eclipse.ui.edit.delete"
          mnemonic="D"
          icon="$nl$/icons/full/etool16/delete_edit.gif"
          disabledIcon="$nl$/icons/full/dtool16/delete_edit.gif">
      <visibleWhen>
        <not>
          <with variable="activePartId">
            <equals value="org.eclipse.ui.views.ProblemView"/>
          </with>
        </not>
      </visibleWhen>
    </item>
    <item id="org.eclipse.ui.views.problems.selectAll.item"
          commandId="org.eclipse.ui.edit.selectAll"
          mnemonic="A"/>
    <separator id="group.resolve" visible="true"/>
    <item id="org.eclipse.ui.views.problems.resolveMarker.item"
          commandId="org.eclipse.jdt.ui.edit.text.java.correction.assist.proposals"
          mnemonic="Q"
          icon="$nl$/icons/full/elcl16/smartmode_co.gif"
          disabledIcon="$nl$/icons/full/dlcl16/smartmode_co.gif"/>
    <separator id="additions" visible="true"/>
    <separator id="group.properties" visible="true"/>
    <item id="org.eclipse.ui.views.problems.properties.item"
          commandId="org.eclipse.ui.file.properties"
          mnemonic="r"/>
  </menuRoot>
</extension>

Some constraints on the system:

  1. Identifiers (id) for <menu/> and <item/> elements must be globally unique.
  2. <separator/> ids only have to be unique within that menu level.
  3. You can provide an <item/> label attribute. If none is provided, it will take the command name.
  4. In this design the item contains most of the same rendering information that <action/> did.
  5. <menu/> and <item/> can have <visibleWhen/> clauses. If a menu's <visibleWhen/> evaluates to false, we will never ask the items contained in that menu.


Menu - JSR198

There is a JSR describing how IDEs can contribute menus. Below is a sample for 2 items:

  • org.eclipse.ui.views.problems.sorting.item from menu://org.eclipse.ui.views.ProblemView
  • org.eclipse.ui.views.problems.resolveMarker.item from popup://org.eclipse.ui.views.ProblemView
<menu-hook>
  <actions>
    <action id="org.eclipse.ui.views.problems.sorting.item">
      <label>Sorting...</label>
      <mnemonic>S</mnemonic>
      <tooltip>Change the Sort order</tooltip>
      <invoke-class>org.eclipse.ui.views.problems.sorting</invoke-class>
    </action>
    <action id="org.eclipse.ui.views.problems.resolveMarker.item">
      <label>Quick Fix</label>
      <mnemonic>Q</mnemonic>
      <iconpath>$nl$/icons/full/elcl16/smartmode_co.gif</iconpath>
      <invoke-class>org.eclipse.jdt.ui.edit.text.java.correction.assist.proposals</invoke-class>
      <update-class>org.eclipse.jdt.ui.edit.text.java.correction.assist.proposals</update-class>
    </action>
  </actions>
  <menus>
    <menubar id="org.eclipse.ui.views.ProblemView">
      <menu id="org.eclipse.ui.views.ProblemView">
        <section id="problem.view.section">
          <item action-ref="org.eclipse.ui.views.problems.sorting.item" />
          <menu id="org.eclipse.ui.views.problems.groupBy.menu">
            <label>Group By</label>
            <mnemonic>G</mnemonic>
          </menu>
        </section>
      </menu>
    </menubar>
    <popup id="org.eclipse.ui.views.ProblemView">
      <section id="group.resolve">
        <item action-ref="org.eclipse.ui.views.problems.resolveMarker.item" />
      </section>
    </popup>
  </menus>
</menu-hook>

Some thoughts:

  • the actions can only specify one icon
  • the actions can't *quite* link to our commands
  • the menus can't specify dynamic submenus

Menu - XUL

With Mozilla everywhere, there is the probability eclipse will include xulrunner. Menu definitions that are consistent with XUL look like:

 <keyset>
   <key id="paste-key" modifiers="accel" key="V" />
 </keyset>
 <menubar id="org.eclipse.ui.views.ProblemView">
   <menupopup id="org.eclipse.ui.views.ProblemView">
     <menuitem id="org.eclipse.ui.views.problems.sorting.item"
               accesskey="S"
               key="paste-key"
               label="Sorting..."
               oncommand="invokeCommand('org.eclipse.ui.views.problems.sorting')" />
     <menu id="org.eclipse.ui.views.problems.groupBy.menu"
           label="Group By"
           accesskey="G">
       <menupopup id="groupby.popup">
       </menupopup>
     </menu>
   </menupopup>
 </menubar>

XUL supports everything as a flavour of a DOM, and javascripting can drive your buttons to perform commands. I suspect the scripting would allow you to dynamically update menus (dynamic menus) on popup, depending on what events the DOM would report to you.

Menus API

We can contribute menus through the IMenuService API.

The above example can be done for the view menus:

   public void createProblemsViewMenu() {
       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       IMenuCollection menuRoot = menuServ.getMenuRoot(
               "menu://org.eclipse.ui.views.ProblemView", //$NON-NLS-1$
               null);

       // add a menu item
       Item sort = new Item(SORTING_CMD);
       sort.setMnemonic("S"); //$NON-NLS-1$
       sort.setTooltip("Change the Sort order"); //$NON-NLS-1$
       menuRoot.add(SORTING_ITEM, sort, null);

       // add a dynamic submenu
       Menu groupByMenu = new Menu(GROUPBY_MENU, "Group By"); //$NON-NLS-1$
       groupByMenu.setMnemonic("B"); //$NON-NLS-1$
       groupByMenu.setDynamicMenu(new GroupByDynamicMenu());
       menuRoot.add(groupByMenu, null);

       menuRoot.add(new Separator("group.filter", true)); //$NON-NLS-1$

       // add a sub menu
       Menu filtersMenu = new Menu(FILTERS_MENU, "Filters"); //$NON-NLS-1$
       filtersMenu.setMnemonic("F"); //$NON-NLS-1$
       filtersMenu.setDynamicMenu(new FiltersDynamicMenu());
       menuRoot.add(filtersMenu, null);

       // add items to the sub menu
       Item fitem = new Item(CONFIGURE_FILTER_CMD);
       fitem.setTooltip("Configure the filters to be applied to this view"); //$NON-NLS-1$
       fitem.setMnemonic("C"); //$NON-NLS-1$
       fitem.setImage(getSharedImage("elcl16/filter_ps.gif")); //$NON-NLS-1$
       menuRoot.add(CONFIGURE_FILTER_ITEM, fitem, null);

       fitem = new Item(getProblemPreferencesCommand());
       fitem.setTooltip("Open the preferences"); //$NON-NLS-1$
       fitem.setMnemonic("P"); //$NON-NLS-1$
       menuRoot.add(PREFERENCES_ITEM, fitem, null);

       // place the contribution back into the menu service.
       IMenuContribution viewMenu = menuServ.contribute(menuRoot);

       // and possibly much later
       menuServ.remove(viewMenu);
   }

You ask for a menu root, specifying a root item (like menu://org.eclipse.ui.views.ProblemView) and potentially a location modifier (after some id).

You can add Menus, Separators, Items, and Widgets to an IMenuCollection (and a Menu is an IMenuCollection). Once you've update your collection, contribute it back to the menu service.


Menus API Option 2

In option 1, the IMenuCollection manages a set of menu updates and works on the menu hierarchy, but is a distinct object. In option 2, you are returned your menu and just add items to it.

   public void createProblemsViewMenuOption2() {
       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       Menu viewMenu = menuServ.getMenu("menu://org.eclipse.ui.views.ProblemView", //$NON-NLS-1$
               null);

       // add a menu item
       Item sort = new Item(SORTING_CMD);
       sort.setMnemonic("S"); //$NON-NLS-1$
       sort.setTooltip("Change the Sort order"); //$NON-NLS-1$
       viewMenu.add(SORTING_ITEM, sort, null);

       // add a dynamic submenu
       Menu groupByMenu = new Menu(GROUPBY_MENU, "Group By"); //$NON-NLS-1$
       groupByMenu.setMnemonic("B"); //$NON-NLS-1$
       groupByMenu.setDynamicMenu(new GroupByDynamicMenu());
       viewMenu.add(groupByMenu, null);

       viewMenu.add(new Separator("group.filter", true)); //$NON-NLS-1$

       // add a sub menu
       Menu filtersMenu = new Menu(FILTERS_MENU, "Filters"); //$NON-NLS-1$
       filtersMenu.setMnemonic("F"); //$NON-NLS-1$
       filtersMenu.setDynamicMenu(new FiltersDynamicMenu());
       viewMenu.add(filtersMenu, null);

       // add items to the sub menu
       Item fitem = new Item(CONFIGURE_FILTER_CMD);
       fitem.setTooltip("Configure the filters to be applied to this view"); //$NON-NLS-1$
       fitem.setMnemonic("C"); //$NON-NLS-1$
       fitem.setImage(getSharedImage("elcl16/filter_ps.gif")); //$NON-NLS-1$
       viewMenu.add(CONFIGURE_FILTER_ITEM, fitem, null);

       fitem = new Item(getProblemPreferencesCommand());
       fitem.setTooltip("Open the preferences"); //$NON-NLS-1$
       fitem.setMnemonic("P"); //$NON-NLS-1$
       viewMenu.add(PREFERENCES_ITEM, fitem, null);

       // update the menu changes
       viewMenu.update();
   }

The update() at the end tells the menu service to contribute your changes to the menu rendering items (IContributionItems).

Add Toggle Mark Occurrences to main toolbar

We can provide the Toggle Mark Occurrences toolbar button. It's normally contributed through an actionSet as a retargettable action, and the Java and Class File editors *EditorActionBarContributors provide the implementation ToggleMarkOccurrencesAction through IActionBars#setGlobalActionHandler(*).

Commands

First define the toggle mark occurrences command. Pretty straight forward, although it needs a "STYLE" state since it can be toggled. To allow handlers to update the label for the menu/toolbar items, we also add the "NAME" state.

 <extension point="org.eclipse.ui.commands">
   <command categoryId="org.eclipse.jdt.ui.category.source"
            description="Toggles mark occurrences in Java editors"
            id="org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences"
            name="Toggle Mark Occurrences">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
     <state id="STYLE" class="org.eclipse.jface.commands.ToggleState:true" />
   </command>
 </extension>

Handlers

This command doesn't have a default handler, as it only applies to specific editors that are provided. So we would provide the handler for the java editor.

 <extension point="org.eclipse.ui.handlers">
   <handler commandId="org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences"
            class="org.eclipse.jdt.internal.ui.javaeditor.ToggleMarkOccurrencesHandler">
     <activeWhen>
       <with variable="activePartId">
         <or>
           <equals value="org.eclipse.jdt.ui.CompilationUnitEditor" />
           <equals value="org.eclipse.jdt.ui.ClassFileEditor" />
         </or>
       </with>
     </activeWhen>
   </handler>
 </extension>

We're active for both the Java editor and the Class File editor. There is also the option to programmatically install the handler.

AndExpression expr = new AndExperssion();
expr.add(new ActivePartIdExpression("org.eclipse.jdt.ui.CompilationUnitEditor"));
expr.add(new ActivePartIdExpression("org.eclipse.jdt.ui.ClassFileEditor"));
IHandlerService handlerServ = (IHandlerService)getSite().getWorkbenchWindow().getService(IHandlerService.class);
toggleOccurrencesHandler = new ToggleMarkOccurrencesHandler();
handlerServ.activateHandler("org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences", toggleOccurrencesHandler, expr, true);

Since the same handler is valid for both editors, we install it with a specific expression and don't tie the activation to the part site.

Menus

 <extension point="org.eclipse.ui.menus">
   <menuRoot location="toolbar:org.eclipse.ui.edit.text.actionSet.presentation"
             after="Presentation">
     <item id="org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences.item"
           commandId="org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences"
           icon="$nl$/icons/full/etool16/mark_occurrences.gif"
           disabledIcon="$nl$/icons/full/dtool16/mark_occurrences.gif"
           helpContextId="toggle_mark_occurrences_action_context">
       <visibleWhen>
         <with variable="activeContexts">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.text.java.actionSet.presentation" />
           </iterator>
         </with>
       </visibleWhen>
     </item>
   </menuRoot>
 </extension>

Here we are adding ourselves to the workbench text presentation toolbar, which has already been contributed. This item is also tied to an actionSet.

Menus API

The above XML can be done using the menus API:

   public void createToggleMarkOccurrences() {
       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       IMenuCollection menuRoot = menuServ.getMenuRoot(
               "toolbar:org.eclipse.ui.edit.text.actionSet.presentation", //$NON-NLS-1$
               "Presentation"); //$NON-NLS-1$

       Item toggleItem = new Item(TOGGLE_MARK_OCCURRENCES_CMD);
       toggleItem.setHelpContextId("toggle_mark_occurrences_action_context"); //$NON-NLS-1$
       menuRoot.add(TOGGLE_MARK_OCCURRENCES_ITEM, toggleItem,
               new ActiveContextExpression(JAVA_PRESENTATION_ACTIONSET_ID));

       menuServ.contribute(menuRoot);
   }

This asks for a menu root in the org.eclipse.ui.edit.text.actionSet.presentation toolbar after the Presentation id.

It's contributed with a visibleWhen clause ActiveContextExpression(JAVA_PRESENTATION_ACTIONSET_ID), so it will be visible when the actionSet is active.

Menus API Option 2

You can add your item directly to the toolbar, and update it when you are done.

   public void createToggleMarkOccurrencesOption2() {
       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       Menu menu = menuServ.getMenu(
               "toolbar:org.eclipse.ui.edit.text.actionSet.presentation", //$NON-NLS-1$
               "Presentation"); //$NON-NLS-1$

       Item toggleItem = new Item(TOGGLE_MARK_OCCURRENCES_CMD);
       toggleItem.setHelpContextId("toggle_mark_occurrences_action_context"); //$NON-NLS-1$
       IMenuContribution toggleContribution = menu.add(
               TOGGLE_MARK_OCCURRENCES_ITEM, toggleItem,
               new ActiveContextExpression(JAVA_PRESENTATION_ACTIONSET_ID));

       menu.update();

       // at some later point
       menu.remove(toggleContribution);
   }

Each add(*) returns an IMenuContribution, which you can use to remove that item. You cannot remove items that you didn't add yourself. An application can hide any item at the product level using the override mechanism.

Possible Java Search Menu Example

The java search menu items are added through a Java Search action set. They have code that enables/disables the action set depending on the active editor.

ActionSet context

If this is still to go in an actionSet, then we would define the actionSet context.

 <extension point="org.eclipse.ui.contexts">
   <context description="%JavaSearchActionSet.description"
            id="org.eclipse.jdt.ui.SearchActionSet"
            name="%JavaSearchActionSet.label"
            parentId="org.eclipse.ui.contexts.actionSet">
   </context>
 </extension>

Commands

Also, a number of the items were retargetable actions that allow label updates. The current pattern would mean adding the NAME state to a lot of the commands.

 <extension point="org.eclipse.ui.commands">
   <command name="%ActionDefinition.readAccessInworkspace.name"
            description="%ActionDefinition.readAccessInWorkspace.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.workspace">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.readAccessInProject.name"
            description="%ActionDefinition.readAccessInProject.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.project">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.readAccessInHierarchy.name"
            description="%ActionDefinition.readAccessInHierarchy.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.hierarchy">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.readAccessInWorkingSet.name"
            description="%ActionDefinition.readAccessInWorkingSet.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.working.set">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.writeAccessInWorkspace.name"
            description="%ActionDefinition.writeAccessInWorkspace.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.workspace">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.writeAccessInProject.name"
            description="%ActionDefinition.writeAccessInProject.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.project">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.writeAccessInHierarchy.name"
            description="%ActionDefinition.writeAccessInHierarchy.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.hierarchy">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
   <command name="%ActionDefinition.writeAccessInWorkingSet.name"
            description="%ActionDefinition.writeAccessInWorkingSet.description"
            categoryId="org.eclipse.search.ui.category.search"
            id="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.working.set">
     <state id="NAME" class="org.eclipse.jface.menus.TextState" />
   </command>
 </extension>

Menus

We'll assume that the Search menu is globally defined elsewhere by the org.eclipse.search plugin.

 <extension point="org.eclipse.ui.menus">
   <menuRoot location="menu://org.eclipse.ui.main.menu" after="navigate">
     <menu label="%searchMenu.label" mnemonic="a" id="org.eclipse.search.menu">
       <separator name="internalDialogGroup" visible="false" />
       <separator name="dialogGroup" visible="false" />
       <separator name="fileSearchContextMenuActionsGroup" visible="true" />
       <separator name="contextMenuActionsGroup" visible="true" />
       <separator name="occurencesActionsGroup" visible="true" />
       <separator name="extraSearchGroup" visible="true" />
     </menu>
   </menuRoot>
 </extension>

Then the JDT plugin would contribute the menu items to search, where the menuRoot location specifies the starting point for adding the menus. For groups of actions like the Write Access or Read Access shown here, they can just be specified in order. The <visibleWhen/> clauses must be specified on the items contributed if they want to belong to the actionSet, but if the contribute items are contain in a contributed menu, it can just be specified on the <menu/> element. We also use the relative URI notation, since the menu id org.eclipse.search.menu must be unique.

 <extension point="org.eclipse.ui.menus">
   <menuRoot location="menu:org.eclipse.search.menu" after="dialogGroup">
     <item id="org.eclipse.jdt.ui.actions.OpenJavaSearchPageAction"
           commandId="org.eclipse.jdt.internal.ui.search.openJavaSearchPage"
           label="%openJavaSearchPageAction.label"
           mnemonic="J"
           icon="$nl$/icons/full/obj16/jsearch_obj.gif"
           helpContextId="java_search_action_context">
       <visibleWhen>
         <with variable="activeContexts">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </item>
   </menuRoot>
   <menuRoot location="menu:org.eclipse.search.menu"
             after="contextMenuActionsGroup">
     <menu id="readAccessSubMenu"
           label="%readAccessSubMenu.label"
           mnemonic="R">
       <separator name="group1" visible="false" />
       <item id="org.eclipse.jdt.ui.actions.ReadAccessInWorkspace"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.workspace"
             label="%InWorkspace.label"
             mnemonic="W">
       </item>
       <item id="org.eclipse.jdt.ui.actions.ReadAccessInProject"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.project"
             label="%InProject.label"
             mnemonic="P">
       </item>
       <item id="org.eclipse.jdt.ui.actions.ReadAccessInHierarchy"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.hierarchy"
             label="%InHierarchy.label"
             mnemonic="H">
       </item>
       <item id="org.eclipse.jdt.ui.actions.ReadAccessInWorkingSet"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.working.set"
             label="%InWorkingSet.label"
             mnemonic="S">
       </item>
       <visibleWhen>
         <with variable="activeContexts">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </menu>
     <menu id="writeAccessSubMenu"
           label="%writeAccessSubMenu.label"
           mnemonic="W">
       <separator name="group1" visible="false" />
       <item id="org.eclipse.jdt.ui.actions.WriteAccessInWorkspace"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.workspace"
             label="%InWorkspace.label"
             mnemonic="W">
       </item>
       <item id="org.eclipse.jdt.ui.actions.WriteAccessInProject"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.project"
             label="%InProject.label"
             mnemonic="P">
       </item>
       <item id="org.eclipse.jdt.ui.actions.WriteAccessInHierarchy"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.hierarchy"
             label="%InHierarchy.label"
             mnemonic="H">
       </item>
       <item id="org.eclipse.jdt.ui.actions.WriteAccessInWorkingSet"
             commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.working.set"
             label="%InWorkingSet.label"
             mnemonic="S">
       </item>
       <visibleWhen>
         <with variable="activeContexts">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </menu>
   </menuRoot>
   <menuRoot location="menu:org.eclipse.search.menu"
             after="occurencesActionsGroup">
     <item id="org.eclipse.jdt.internal.ui.actions.OccurrencesSearchMenuAction"
           commandId="org.eclipse.jdt.ui.edit.text.java.search.occurrences.in.file.quickMenu"
           label="%occurrencesSubMenu.label">
       <dynamic class="org.eclipse.jdt.internal.ui.actions.OccurrencesSearchMenuAction" />
       <visibleWhen>
         <with variable="activeContexts">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </item>
   </menuRoot>
 </extension>

Currently, the java search menus are in the Java Search actionSet, that is dynamically enabled/disabled. This could also be done by specifying a visibleWhen like:

<visibleWhen>
  <with variable="activeEditorId">
    <or>
      <equals value="org.eclipse.jdt.ui.CompilationUnitEditor" />
      <equals value="org.eclipse.jdt.ui.ClassFileEditor" />
    </or>
  </with>
</visibleWhen>

This would make the visible if either the Java or Class File editor was the active editor, and they would disappear otherwise.


Menus API

The API can be used to contribute to the main menu bar:

   public void addSearchMenu() {
       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       IMenuCollection menuRoot = menuServ.getMenuRoot(
               "menu://org.eclipse.ui.main.menu", "navigate"); //$NON-NLS-1$ //$NON-NLS-2$
       Menu searchMenu = new Menu("org.eclipse.search.menu", "Search"); //$NON-NLS-1$ //$NON-NLS-2$
       menuRoot.add(searchMenu, null);

       searchMenu.add(new Separator("internalDialogGroup", false)); //$NON-NLS-1$
       searchMenu.add(new Separator("dialogGroup", false)); //$NON-NLS-1$
       searchMenu
               .add(new Separator("fileSearchContextMenuActionsGroup", true)); //$NON-NLS-1$
       searchMenu.add(new Separator("contextMenuActionsGroup", true)); //$NON-NLS-1$
       searchMenu.add(new Separator("occurencesActionsGroup", true)); //$NON-NLS-1$
       searchMenu.add(new Separator("extraSearchGroup", true)); //$NON-NLS-1$

       menuServ.contribute(menuRoot);
   }

It's just a menu inserted at the menu root location.


Then another plugin can contribute to that menu:

   public void addToSearchMenu() {
       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       IMenuCollection menuRoot = menuServ.getMenuRoot(
               "menu://org.eclipse.search.menu", "dialogGroup"); //$NON-NLS-1$ //$NON-NLS-2$
       Item searchItem = new Item(
               "org.eclipse.jdt.internal.ui.search.openJavaSearchPage"); //$NON-NLS-1$
       searchItem.setMnemonic("J"); //$NON-NLS-1$
       searchItem.setImage(getSharedImage("icons/full/obj16/jsearch_obj.gif")); //$NON-NLS-1$
       searchItem.setHelpContextId("java_search_action_context"); //$NON-NLS-1$
       ActiveContextExpression activeSearchActionSet = new ActiveContextExpression(
               "org.eclipse.jdt.ui.SearchActionSet"); //$NON-NLS-1$
       menuRoot.add("org.eclipse.jdt.ui.actions.OpenJavaSearchPageAction", //$NON-NLS-1$
               searchItem, activeSearchActionSet);

       menuServ.contribute(menuRoot);

       menuRoot = menuServ.getMenuRoot(
               "menu://org.eclipse.search.menu", "contextMenuActionsGroup"); //$NON-NLS-1$ //$NON-NLS-2$

       Menu readMenu = new Menu("readAccessSubMenu", "Read Access"); //$NON-NLS-1$ //$NON-NLS-2$
       readMenu.setMnemonic("R"); //$NON-NLS-1$
       menuRoot.add(readMenu, activeSearchActionSet);

       readMenu.add(new Separator("group1", false)); //$NON-NLS-1$

       Item item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.workspace"); //$NON-NLS-1$
       item.setMnemonic("W"); //$NON-NLS-1$
       readMenu.add("org.eclipse.jdt.ui.actions.ReadAccessInWorkspace", item, //$NON-NLS-1$
               null);
       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.project"); //$NON-NLS-1$
       item.setMnemonic("P"); //$NON-NLS-1$
       readMenu.add("org.eclipse.jdt.ui.actions.ReadAccessInProject", item, //$NON-NLS-1$
               null);
       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.hierarchy"); //$NON-NLS-1$
       item.setMnemonic("H"); //$NON-NLS-1$
       readMenu.add("org.eclipse.jdt.ui.actions.ReadAccessInHierarchy", item, //$NON-NLS-1$
               null);
       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.working.set"); //$NON-NLS-1$
       item.setMnemonic("S"); //$NON-NLS-1$
       readMenu.add("org.eclipse.jdt.ui.actions.ReadAccessInWorkingSet", item, //$NON-NLS-1$
               null);

       Menu writeMenu = new Menu("readAccessSubMenu", "Read Access"); //$NON-NLS-1$ //$NON-NLS-2$
       writeMenu.setMnemonic("R"); //$NON-NLS-1$
       menuRoot.add(writeMenu, activeSearchActionSet);

       writeMenu.add(new Separator("group1", false)); //$NON-NLS-1$

       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.workspace"); //$NON-NLS-1$
       item.setMnemonic("W"); //$NON-NLS-1$
       writeMenu.add(
               "org.eclipse.jdt.ui.actions.WriteAccessInWorkspace", item, //$NON-NLS-1$
               null);
       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.project"); //$NON-NLS-1$
       item.setMnemonic("P"); //$NON-NLS-1$
       writeMenu.add("org.eclipse.jdt.ui.actions.WriteAccessInProject", item, //$NON-NLS-1$
               null);
       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.hierarchy"); //$NON-NLS-1$
       item.setMnemonic("H"); //$NON-NLS-1$
       writeMenu.add(
               "org.eclipse.jdt.ui.actions.WriteAccessInHierarchy", item, //$NON-NLS-1$
               null);
       item = new Item(
               "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.working.set"); //$NON-NLS-1$
       item.setMnemonic("S"); //$NON-NLS-1$
       writeMenu.add(
               "org.eclipse.jdt.ui.actions.WriteAccessInWorkingSet", item, //$NON-NLS-1$
               null);

       menuServ.contribute(menuRoot);
   }

When done, contribute the built menu back to the menu service.

File->Exit

Current RCP using ActionBarAdvisor

The ApplicationActionBarAdvisor should add the exit menu. The QuitAction is already coded and the exit command is already defined.

// create the file menu
MenuManager menu = new MenuManager(IDEWorkbenchMessages.Workbench_file, IWorkbenchActionConstants.M_FILE);
// ...
quitAction = ActionFactory.QUIT.create(window);
register(quitAction);
ActionContributionItem quitItem = new ActionContributionItem(quitAction);
quitItem.setVisible(!"carbon".equals(SWT.getPlatform())); //$NON-NLS-1$
menu.add(quitItem);

Current RCP or Eclipse plugin using an actionSet

Using an actionSet, you can also place the action in the main menu.

<extension
      point="org.eclipse.ui.actionSets">
   <actionSet
         id="org.eclipse.ui.file.exitActions"
         label="Exit Actions"
         visible="true">
      <action
            class="org.eclipse.ui.internal.QuitAction"
            id="quit"
            definitionId="org.eclipse.ui.file.exit"
            label="E&amp;xit"
            menubarPath="file/fileEnd"
            tooltip="Exit Eclipse">
      </action>
   </actionSet>
</extension>

Proposed new menu support

With the new structure, you would have to turn the action into a handler.


<extension
      point="org.eclipse.ui.handlers">
   <handler commandId="org.eclipse.ui.file.exit"
         class="org.eclipse.ui.internal.QuitHandler"/>
</extension>

Then you can place it in the menu:

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="org.eclipse.ui.file.exit"
         mnemonic="x"
         id="org.eclipse.ui.file.exit.menu">
      <location type="menu">
         <menu id="file"/>
         <order after="fileEnd"/>
      </location>
   </item>
</extension>

You can globally place your exit menu in the file menu, or tie it to an actionSet using the <visibleWhen/> clause.

Edit->Copy

Some Copy Stuff.

Global toolbar->Save

save

Global toolbar->Open Type

open type


Search->Registry Search

Provide a basic action set. You have 2 actions, Registry Find and Registry Replace, and you want to add them to the "dialog" section of the Search menu.


Proposed new menu support

Assume you have 2 commands defined, com.example.registry.search.find (Registry Find) and com.example.registry.search.replace (Registry Replace), and you want to add them to the main Search menu at org.eclipse.search.menu/dialogGroup. Your commands already have their images defined in the org.eclipse.ui.commandImages extension point.

Define the action set:

<context
      description="The collection of registry search actions"
      id="com.example.registry.search.actionSet"
      name="Registry Search Action Set"
      parentId="org.eclipse.ui.contexts.actionSet">
</context>

And here is a possible view of the menus extension point:

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="com.example.registry.search.find"
         mnemonic="d"
         id="com.example.registry.search.find.menu">
      <location type="menu">
         <menu id="org.eclipse.search.menu"/>
         <order after="dialogGroup"/>
      </location>
      <visibleWhen>
         <with variable="activeContexts">
            <iterator operator="or">
               <equals value="com.example.registry.search.actionSet"/>
            </iterator>
         </with>
      </visibleWhen>
   </item>
   <item
         commandId="com.example.registry.search.replace"
         mnemonic="p"
         id="com.example.registry.search.replace.menu">
      <location type="menu">
         <menu id="org.eclipse.search.menu"/>
         <order after="com.example.registry.search.find.menu"/>
      </location>
      <visibleWhen>
         <with variable="activeContexts">
            <iterator operator="or">
               <equals value="com.example.registry.search.actionSet"/>
            </iterator>
         </with>
      </visibleWhen>
   </item>
</extension>

Hmmmm, so to programmatically do this, you would have to go through the IMenuService.


ICommandService commandServ = (ICommandService) getSite().getWindow().getService(ICommandService.class);
IMenuService menuServ = (IMenuService) getSite().getWindow().getService(IMenuService.class);
 
Command findCmd = commandServ.getCommand("com.example.registry.search.find");
SLocation findLocation = new SLocation(SBar.MENU, "org.eclipse.search.menu");
findLocation.setOrder(new SOrder(SOrder.AFTER, "dialogGroup"));

SItem findItem = menuServ.getItem(new SLocation(SBar.MENU, "org.eclipse.search.menu/com.example.registry.search.find.menu"));
findItem.define(findCmd, findLocation, "d");
menuServ.contribute(findItem, new ActionSetExpression("com.example.registry.search.actionSet"));

Command replaceCmd = commandServ.getCommand("com.example.registry.search.replace");
SLocation replaceLocation = new SLocation(SBar.MENU, "org.eclipse.search.menu");
replaceLocation.setOrder(new SOrder(SOrder.AFTER, "com.example.registry.search.find.menu"));

SItem replaceItem = menuServ.getItem(new SLocation(SBar.MENU, "org.eclipse.search.menu/com.example.registry.search.replace.menu"));
replaceItem.define(replaceCmd, replaceLocation, "p");
menuServ.contribute(replaceItem, new ActionSetExpression("com.example.registry.search.actionSet"));

Widget in the main toolbar

You can use the extension point to contribute a control to the toolbar. You use the <widget/> element instead of the <item/> element.

<extension
      point="org.eclipse.ui.menus">
   <widget
         class="com.example.registry.search.SearchBar"
         id="com.example.registry.search.find.searchbar">
      <location type="toolbar">
         <menu id="org.eclipse.search.toolbar"/>
      </location>
      <visibleWhen>
         <with variable="activeContexts">
            <iterator operator="or">
               <equals value="com.example.registry.search.actionSet"/>
            </iterator>
         </with>
      </visibleWhen>
   </widget>
</extension>

The widget class must implement org.eclipse.ui.menus.IWorkbenchWidget, and provide fill(Composite parent) for a toolbar.

I'm not sure how far to go with IWorkbenchWidget. We already use this interface for adding controls to the trim, and there are open bug requests about adding arbitrary controls to the toolbars.

Will menus take arbitrary controls?

Edit->Undo relabel action

Like the Undo action, sometimes menu items would want to allow their label to be updated. This is currently handled through the Command objects and the handlers. IMenuStateIds and INamedHandleStateIds define some states that we currently support.

This can be used to update the menu item label:

<extension
      point="org.eclipse.ui.commands">
   <command
         description="Targetted Undo Command"
         id="org.eclipse.ui.commands.targettedUndo"
         name="Undo">
      <state
            class="org.eclipse.jface.menus.TextState"
            id="NAME"/>
   </command>
</extension>

It's placed in the menus just like any other command:

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="org.eclipse.ui.commands.targettedUndo"
         mnemonic="u"
         id="org.eclipse.ui.commands.undo">
      <location type="menu">
         <menu id="edit"/>
         <order after="edit.ext"/>
      </location>
   </item>
</extension>

As a handler becomes active and implement IObjectWithState (for example, derives from AbstractHandlerWithState) it is notified about any states that the handler's command contains. The states can be updated at that time.

class FileDeleteUndoHandler extends AbstractHandlerWithState {
    public final void handleStateChange(final State state, final Object oldValue) {
        if (INamedHandleStateIds.NAME.equals(state.getId()) && oldValue==null) {
            state.setValue("Undo File Delete");
        }
    }
    public final Object execute(final ExecutionEvent event) {
        // undo a file delete using EFS ... very cool
        return null;
    }
}

Search->Registry Search editor action

This should basically be the same as Search->Registry Search with a different visible when clause.

      <visibleWhen>
         <with variable="activeEditorId">
            <equals value="com.example.registry.RegistryEditor"/>
         </with>
      </visibleWhen>

So programmatically, that just translates into a different visible when expression:

menuServ.contribute(editorFindItem, new ActiveEditorIdExpression("com.example.registry.RegistryEditor"));

The menu items must be uniquely identified by their id. Since they only have one location, they can only have one activation in the service at a time.

Search->Registry Search programmatic editor action

Programmatically we have to take the editor action handlers into account.

One of the advantages of EditorActionBars is the lifecycle of the actions that are added. They're created when the first editor of that type is loaded and exist until the last editor of that type is closed.

Our service hierarchy has 2 levels, IWorkbenchWindow and IPartSite. Handlers registered with IWorkbenchWindow are active as long as the window is active, and are disposed when the window is disposed. Handlers registered with IPartSite are active and available as long as that specific part is active and available. When the part is closed, the handlers are disposed.

Two possibilities are:

1. Provide a service locator for the in-between case. Tied to a part type. The service locator expression makes the activation correct and the service locator disposes of them correctly. Basically, we would make IActionBars returned from each IPartSite a service locator as well.

2. Move these actions up to the IWorkbenchWindow level. With the correct expression the handler activation would be correct. But once created they would live as long as the workbench.

Add View submenu and item

View actions have to specify their menus as they are self contained.

<command id="z.ex.editor.commands.SampleViewAction"
    name="Sample View Action"
    description="Sample View Action command"/>
<extension
      point="org.eclipse.ui.commandImages">
   <image commandId="z.ex.editor.commands.SampleViewAction"
         icon="icons/sample.gif"/>
</extension>

Placing the action (which is specifically a menu or button linked to a command) can be accomplished with the org.eclipse.ui.menus extension point.

<extension
      point="org.eclipse.ui.menus">
   <menu
         id="z.ex.view.SampleViewMenu"
         mnemonic="M"
         label="Sample Menu">
      <location type="viewMenu">
         <part partId="z.ex.view.keybindings.views.SampleView"/>
      </location>
   </menu>
   <group
         groupId="z.ex.view.SampleViewGroup"
         separatorsVisible="false">
      <location type="viewMenu">
         <part partId="z.ex.view.keybindings.views.SampleView">
            <menu id="z.ex.view.SampleViewMenu"/>
         </part>
      </location>
   </group>
   <item
         commandId="z.ex.editor.commands.SampleViewAction"
         mnemonic="V"
         id="z.ex.view.SampleViewAction">
      <location type="viewMenu">
         <part partId="z.ex.view.keybindings.views.SampleView">
            <menu id="z.ex.view.SampleViewMenu"/>
         </part>
         <order after="z.ex.view.SampleViewGroup"/>
      </location>
   </item>
</extension>


Hmmmm, so to programmatically do this, you would have to go through the IMenuService.


ICommandService commandServ = (ICommandService) getSite().getWindow().getService(ICommandService.class);
IMenuService menuServ = (IMenuService) getSite().getWindow().getService(IMenuService.class);

SMenu viewMenu = menuServ.getMenu(new SLocation(SBar.VIEW_MENU, 
        "z.ex.view.keybindings.views.SampleView/z.ex.view.SampleViewMenu"));
viewMenu.define("Sample Menu", new SLocation(SBar.VIEW_MENU, 
        "z.ex.view.keybindings.views.SampleView"), "M");
SGroup viewGroup = menuServ.getGroup(new SLocation(SBar.VIEW_MENU, 
        "z.ex.view.keybindings.views.SampleView/z.ex.view.SampleViewMenu/z.ex.view.SampleViewGroup"));
viewGroup.define(new SLocation(SBar.VIEW_MENU, 
        "z.ex.view.keybindings.views.SampleView/z.ex.view.SampleViewMenu", false); 

Command viewCmd = commandServ.getCommand("z.ex.editor.commands.SampleViewAction");
SLocation viewLocation = new SLocation(SBar.VIEW_MENU, 
        "z.ex.view.keybindings.views.SampleView/z.ex.view.SampleViewMenu")
viewLocation.setOrder(new SOrder(SOrder.AFTER, "z.ex.view.SampleViewGroup"));
SItem viewItem = menuServ.getItem(new SLocation(SBar.VIEW_MENU, 
        "z.ex.view.keybindings.views.SampleView/z.ex.view.SampleViewMenu/z.ex.view.SampleViewAction.menu"));
viewItem.define(viewCmd, viewLocation, "V");
menuServ.contribute(viewItem);

Text editor popup action

Popups can be targetted at any registered context menu, or at all of them

<command id="z.ex.view.commands.SampleContributionAction"
    name="Sample Context Action"
    description="Sample Context Action command"/>
<extension
      point="org.eclipse.ui.commandImages">
   <image commandId="z.ex.view.commands.SampleContributionAction"
         icon="icons/sample.gif"/>
</extension>

Placing the action (which is specifically a menu or button linked to a command) can be accomplished with the org.eclipse.ui.menus extension point.

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="z.ex.view.commands.SampleContributionAction"
         mnemonic="C"
         id="z.ex.objectContribution.menu">
      <location type="contextMenu">
         <menu id="#EditorContext"/>
         <order after="z.ex.editor.contextGroup"/>
      </location>
   </item>
</extension>


Hmmmm, so to programmatically do this, you would have to go through the IMenuService.


Command contextCmd = commandServ.getCommand("z.ex.view.commands.SampleContributionAction");
SLocation contextLocation = new SLocation(SBar.CONTEXT_MENU, "#EditorContext");
contextLocation.setOrder(new SOrder(SOrder.AFTER, "z.ex.editor.contextGroup"));
SItem contextItem = menuServ.getItem(new SLocation(SBar.CONTEXT_MENU, "#EditorContext/z.ex.objectContribution.menu"));
contextItem.define(contextCmd, contextLocation, "C");
menuServ.contribute(contextItem);

How does one targetall registered context menus? One of the significant problems with the 3.2 eclipse is creeping IDE inconsistency, esp for popup menus. This inconsistency is a consequence of plugin dependent function. Having file-type dependent popups is a nice optional feature, but more critical for users is to have core function always available. There does not seem to be a mechanism to add menu items to all popups or to remove items on user preference. (I think these abilities are available for the menu bar and tool bars but I never use them, they are too slow). An example of the consequences is the AnyEdit plugin, which has pages of XML attempting to enumerate all editors in all versions of eclipse! One approach would allow regular expressions in the targetID (in the current scheme) for actions and urge editor plugin writers to adopt a uniform naming scheme. A better solution would allow users to configure global settings on popups for editors.

IFile object contribution

There will be a reserved popup ID, "org.eclipse.ui.menus.context.any" that will allow contributions to any popup menu.

<extension
      point="org.eclipse.ui.menus">
   <item
         commandId="z.ex.view.commands.SampleContributionAction"
         mnemonic="C"
         id="z.ex.objectContribution.menu">
      <location type="contextMenu">
         <menu id="org.eclipse.ui.menus.context.any"/>
         <order after="additions"/>
      </location>
     <visibleWhen>
         <with variable="selection">
           <adapt type="org.eclipse.core.resources.IFile"/>
        </with>
     </visibleWhen>
   </item>
</extension>

It's probably that the default variable for core expression evaluations would be selection, so you wouldn't need the <with/> clause. There would probably also be a short-hand to tie the visibility to an active handler. Maybe <visibleWhen handler="true"/>

It can be used programmatically.

Command contextCmd = commandServ.getCommand("z.ex.view.commands.SampleContributionAction");
SLocation contextLocation = new SLocation(SBar.CONTEXT_MENU, "org.eclipse.ui.menus.context.any");
contextLocation.setOrder(new SOrder(SOrder.AFTER, "additions"));
SItem contextItem = menuServ.getItem(new SLocation(SBar.CONTEXT_MENU, "org.eclipse.ui.menus.context.any/z.ex.objectContribution.menu"));
contextItem.define(contextCmd, contextLocation, "C");
menuServ.contribute(contextItem, new SelectionAdaptExpression("org.eclipse.core.resources.IFile"));

The contribution should look exactly the same as a normal contribution.

Product removes Search Registry

It should be possible to override the visibility of menu contributions.

IMenuService menuService = (IMenuService) PlatformUI.getWorkbench().getService(IMenuService.class);
SItem findItem = menuServ.getItem(new SLocation(SBar.MENU, "org.eclipse.search.menu/com.example.registry.search.find.menu"));
menuService.addOverride(findItem, new OverrideAdapter() {
  public Boolean isVisible() {
    return Boolean.FALSE;
  }
});

The idea is to provide this ability at the product level. For example, an RCP app should be able to hide any menu items that it doesn't want but picked up through the inclusion of a plugin.

That implies that it might not be part of the general IMenuService interface. Or (taking a page from the IExtensionRegistry) it might use a token that's available from the WorkbenchWindowAdvisor so that products can use the interface, or even expose the ability to their users.

If it returns null the next level of visibility is evaluated. The null case is to keep it consistent with other overrides.

Dynamic Previous Searches submenu

You want to add a dynamic "Previous Searches" submenu to the main Search menu.

<extension
      point="org.eclipse.ui.menus">
   <menu
         label="Previous Searches"
         mnemonic="c"
         id="com.example.registry.search.recent.menu">
      <dynamic class="com.example.registry.search.RecentMenu"/>
      <location type="menu">
         <menu id="org.eclipse.search.menu"/>
         <order after="additions"/>
      </location>
   </item>
</extension>

You can programmatically accomplish the same thing:

ICommandService commandServ = (ICommandService) getSite().getWindow().getService(ICommandService.class);
IMenuService menuServ = (IMenuService) getSite().getWindow().getService(IMenuService.class);

SMenu recentMenu = menuServ.getMenu(new SLocation(SBar.MENU, 
        "org.eclipse.search.menu/com.example.registry.search.recent.menu"));
SLocation menuLocation = new SLocation(SBar.MENU, 
        "org.eclipse.search.menu");
menuLocation.setOrder(new SOrder(SOrder.AFTER, "additions"));
recentMenu.define("Previous Searches", menuLocation, "c", new RecentMenu());

When your menu is about to show, you should get the callback:

public class RecentMenu implements IDynamicMenu {
    public void aboutToShow(IMenuCollection menu) {
        menu.clear();
        String[] searchIds = getSearchVew().getPreviousSearches();
        Command findCmd = commandServ.getCommand("com.example.registry.search.find");
        SLocation findLocation = new SLocation(SBar.MENU, "org.eclipse.search.menu/com.example.registry.search.recent.menu");
        for (int i=0; i<searchIds.length && i<4; i++) {
            SItem item = menuServ.getItem(new SLocation(SBar.MENU, 
                "org.eclipse.search.menu/com.example.registry.search.recent.menu/recentItem" + i));
            item.undefine();
            ParameterizedCommand parmFindCmd = ....; // findCmd + the searchId parameter
            item.define(parmFindCmd, findLocation);
            menu.add(item);
        }
    }
    public SearchViewPart getSearchView() {
        return ....;
    }
}

Dynamic Previous Searches editors group

You can have a group dynamically insert elements as a menu is shown.

<extension
      point="org.eclipse.ui.menus">
   <group
         groupId="com.example.registry.search.editors.menu"
         separatorsVisible="true">
      <dynamic class="com.example.registry.search.RecentSearchEditors"/>
      <location type="menu">
         <menu id="file"/>
         <order after="mru"/>
      </location>
   </group>
 </extension>

When the File menu is about to show, you will be called with your group IMenuCollection (not the entire File menu). But basically, it looks the same as the dynamic menu case.

public class RecentSearchEditors implements IDynamicMenu {
    public void aboutToShow(IMenuCollection menu) {
        menu.clear();
        String[] editorIds = getSearchVew().getPreviousOpenEditors();
        Command openCmd = commandServ.getCommand("com.example.registry.search.openEditor");
        SLocation fileLocation = new SLocation(SBar.MENU, "file");
        for (int i=0; i<editorIds.length && i<4; i++) {
            SItem item = menuServ.getItem(new SLocation(SBar.MENU, 
                "file/searchOpenEditor" + i));
            item.undefine();
            ParameterizedCommand parmOpenCmd = ....; // openCmd + the editorsId parameter
            item.define(parmOpenCmd, fileLocation);
            menu.add(item);
        }
    }
    public SearchViewPart getSearchView() {
        return ....;
    }
}

Added For Paul

<menu name="Window" id="window">
   <itemRef id="newWindow">
   <itemRef id="newEditor">
   <seperator id="seperator1">
   <menu name="Open Perspective" id="openPerspective"  class="generatePerspectivesMenu"/>
   <menu name="Show View" id="showView"/>
   <seperator id="seperator1">
   <itemRef id="customizePerspective">
   <itemRef id="savePerspective">
   <itemRef id="closePerspective">
   <itemRef id="closeAllPerspectives">
   <seperator id="seperator3">
   <menu name="Navigation" id="navigation">
   <seperator id="seperator4">
   </menu>
</menu>
<menu name="Working Sets" id="workingSets" after="window/navigation">
   <dynamic class="windowworkingsets"/>
   <itemRef id="editWorkingSets"/>
</menu>
<toolbar id="workingSet">
   <itemRef id="editWorkingSets"/>
</toolbar>
<itemRef id="hideToolbars" before="window/seperator1">


Example Matrix

Example Location visible when enabled when defined by placed by handled by comments
Add ProblemView menus view menu always always ViewPart & IActionBars ViewPart & IActionBars SampleViewAction
Add Toggle Mark Occurrences to main toolbar main menu always retarget action supplied org.eclipse.ui.actionSet & IActionBars org.eclipse.ui.actionSet & IActionBars ToggleMarkOccurrencesAction
File->Exit global menu always always ActionFactory WorkbenchActionBuilder QuitAction
Edit->Copy global menu always there is an enabled handler Workbench IDEApplication active part (what about Trim??) enablement determined by handler
Global toolbar->Save global toolbar always the active part needs saving Workbench IDEApplication Workbench
Global toolbar->Open Type global toolbar Java Navigation action set is enabled Java Navigation action set is enabled JDT UI JDT UI JDT UI
Global toolbar->Toggle Mark Occurrences global (editor) toolbar any Java editor is active any Java editor is active all Java Editors all Java Editors active Java Editor push button state is updated based on active editor
Search->Registry Search global menu Registry Search action set is enabled Registry Search action set is enabled plugin actionSet RegistrySearchAction
Widget in the main toolbar global toolbar Registry Search action set is enabled Registry Search action set is enabled plugin actionSet RegistrySearchAction
Edit->Undo relabel action global menu always always ActionFactory WorkbenchActionBuilder LabelRetargetAction
Search->Registry Search editor action global (editor) menu any registry editor is active any registry editor is active org.eclipse.ui.editorActions org.eclipse.ui.editorActions RegistrySearchAction
Search->Registry Search programmatic editor action global (editor) menu any registry editor is active any registry editor is active registry editor action bar contributor registry editor action bar contributor RegistrySearchAction
Add View submenu and item view menu always always org.eclipse.ui.viewActions org.eclipse.ui.viewActions SampleViewAction
Text editor popup action text editor context menu always always org.eclipse.ui.popupMenus org.eclipse.ui.popupMenus SampleContributionAction
IFile object contribution all context menus selection is an IFile always org.eclipse.ui.popupMenus org.eclipse.ui.popupMenus SampleContributionAction
Product removes Search Registry global menu never always ApplicationWorkbenchWindowAdvisor N/A menu service this cannot easily be done
Dynamic Previous Searches submenu global menu always always org.eclipse.ui.actionSets org.eclipse.ui.actionSets IMenuCreator
Dynamic Previous Searches editors group global menu always always N/A N/A IDynamicMenu

Back to the top