Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Menu Contributions"

(Available in 3.3M4)
(Available in 3.3M4)
Line 48: Line 48:
 
| org.eclipse.ui.viewActions || <action/> || <item/> ||  Controlled by toolbarPath, a named group within the view toolbar. || Rooted in the view toolbar. || Always visible. || The <visibleWhen/> element can be tied to any provided source.
 
| org.eclipse.ui.viewActions || <action/> || <item/> ||  Controlled by toolbarPath, a named group within the view toolbar. || Rooted in the view toolbar. || Always visible. || The <visibleWhen/> element can be tied to any provided source.
 
|-
 
|-
| '''org.eclipse.ui.editorActions''' || <menu/>, <action/>, etc || <menu/>, <item/> || These are the same as org.eclipse.ui.actionSets menus and actions. || They can just be placed in the '''org.eclipse.ui.main.menu''' or '''org.eclipse.ui.main.toolbar''' || Visible when an editor type is active. || The <visibleWhen/> element can be tied to any provided source, including the active editor type.
+
| '''org.eclipse.ui.editorActions''' || <menu/>, <action/>, etc || <menu/>, <item/> || These are the same as org.eclipse.ui.actionSets menus and actions || They can just be placed in the '''org.eclipse.ui.main.menu''' or '''org.eclipse.ui.main.toolbar''' || Visible when an editor type is active. || The <visibleWhen/> element can be tied to any provided source, including the active editor type.
 
|-
 
|-
 
| org.eclipse.ui.popupMenus || <viewerContribution/> || <menuContribution/> with a location of '''popup:<context_menu_id>''' ||  The context menu with the id registered using PartSite#registerContextMenu(*). || The context menu with the id registered using PartSite#registerContextMenu(*). || Always visible. || The <visibleWhen/> element can be tied to any provided source.
 
| org.eclipse.ui.popupMenus || <viewerContribution/> || <menuContribution/> with a location of '''popup:<context_menu_id>''' ||  The context menu with the id registered using PartSite#registerContextMenu(*). || The context menu with the id registered using PartSite#registerContextMenu(*). || Always visible. || The <visibleWhen/> element can be tied to any provided source.

Revision as of 15:17, 18 December 2006

Placement examples that describe the proposed new way of placing menu items for 3.3. Please contribute comments and suggestions in the discussion area or on Bug 154130 -KeyBindings- Finish re-work of commands and key bindings.


Placement and visibility

The 4 extension points that deal with menus now org.eclipse.ui.actionSets, org.eclipse.ui.viewActions, org.eclipse.ui.editorActions, and org.eclipse.ui.popupMenus specify both menu placement and their visibility criteria. In the new menu mechanism they are separate concepts, placement and visibility.

Available in 3.3M4

The basic menu API will be available in 3.3M4. It includes both declarative org.eclipse.ui.menus extension point with core expression support for visibility, and a programmatic interface accessed through the IMenuService.

There is an example of the RCP Mail application template converted to use the org.eclipse.ui.menus extension point as much as possible at Contribution Example.

We support contributing to the main menu, and the view menu, view toolbar, and any IDed context menu. We support contributing to existing toolbars in the main coolbar, and contributing trim widgets.

Programmatically we support the following types of contributions:

  • MenuManager
  • CommandContributionItem
  • CompoundContributionItem
  • WidgetContributionItem
  • Separator
  • GroupMarker

An old vs new comparison chart.

Extension Old Element New Element Old Location New Location Old Visible When New Visible When
org.eclipse.ui.actionSets <menu/> <menu/> Controlled by path, rooted in the main menu. Each actionSet must be completely self-contained. Any menu rooted off of the main menu, org.eclipse.ui.main.menu. The location can reference other contributions. Visible when the actionSet is visible. The <visibleWhen/> element can be tied to an actionSet or context (or any provided source).
org.eclipse.ui.actionSets <groupMarker/> <separator/> with visible="false" Placed in the <menu/> element Placed in the <menu/> element not visible not visible
org.eclipse.ui.actionSets <separator/> <separator/> with visible="true" Placed in the <menu/> element Placed in the <menu/> element Visible when the actionSet is visible. The <visibleWhen/> element can be tied to an actionSet or context.
org.eclipse.ui.actionSets <action/> <item/> Controlled by menubarPath, rooted in the main menu. Any menu rooted off of the main menu, org.eclipse.ui.main.menu. Visible when the actionSet is visible. The <visibleWhen/> element can be tied to an actionSet or context (or any provided source).
org.eclipse.ui.actionSets <action/> <item/> Controlled by toolbarPath, and appear in the main coolbar. Any toolbar in the main coolbar, org.eclipse.ui.main.toolbar. Visible when the actionSet is visible. The <visibleWhen/> element can be tied to an actionSet or context (or any provided source).
org.eclipse.ui.viewActions <menu/> <menu/> Controlled by path and rooted in the view dropdown menu. Rooted in the view dropdown menu. The location can reference other contributions to this view. Always visible. The <visibleWhen/> element can be tied to any provided source.
org.eclipse.ui.viewActions <groupMarker/> <separator/> with visible="false" Placed in the <menu/> element Placed in the <menu/> element not visible not visible
org.eclipse.ui.viewActions <separator/> <separator/> with visible="true" Placed in the <menu/> element Placed in the <menu/> element Visible when the actionSet is visible. The <visibleWhen/> element can be tied to any provided source.
org.eclipse.ui.viewActions <action/> <item/> Controlled by menubarPath, rooted in view dropdown menu. Rooted in the view dropdown menu. The location can reference other contributions to this view. Always visible. The <visibleWhen/> element can be tied to any provided source.
org.eclipse.ui.viewActions <action/> <item/> Controlled by toolbarPath, a named group within the view toolbar. Rooted in the view toolbar. Always visible. The <visibleWhen/> element can be tied to any provided source.
org.eclipse.ui.editorActions <menu/>, <action/>, etc <menu/>, <item/> These are the same as org.eclipse.ui.actionSets menus and actions They can just be placed in the org.eclipse.ui.main.menu or org.eclipse.ui.main.toolbar Visible when an editor type is active. The <visibleWhen/> element can be tied to any provided source, including the active editor type.
org.eclipse.ui.popupMenus <viewerContribution/> <menuContribution/> with a location of popup:<context_menu_id> The context menu with the id registered using PartSite#registerContextMenu(*). The context menu with the id registered using PartSite#registerContextMenu(*). Always visible. The <visibleWhen/> element can be tied to any provided source.
org.eclipse.ui.popupMenus <objectContribution/> <menuContribution/> with a location of popup:org.eclipse.ui.menus.popup.any All context menus All context menus When the objectClass or filtering criteria are met The <visibleWhen/> element can be tied to any provided source.


Deferred until 3.3M5

A list of behaviours not supported or shipped with the 3.3M4 API.

  • Shortcuts to define reusable core expressions for <activeWhen/>, <enabledWhen/>, and <visibleWhen/>
  • the mnemonic field for <item/> elements
  • the <separator/> element should have a name not an id
  • full visibleWhen support in the MenuManagers
  • creating new toolbars in the main coolbar
  • action sets as contexts
  • status manager contributions
  • org.eclipse.ui.menus.popup.any as a context menu contribution
  • changing the menu item or tool item state from a handler, like updating the label or tooltip. Commands can contain <state/> elements, but that might not be appropriate to propagate UI information
  • A set of default programmatic core expressions. For example, ActionContextExpression or ActivePartExpression
  • Drop down toolbar items
  • toolbar <visibleWhen/> expressions
  • the menu override capability
  • the editor action bar contributor solution

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="%ProblemView.category.name">
   </category>
   <command categoryId="org.eclipse.ui.views.problems"
            defaultHandler="org.eclipse.ui.views.markers.internal.TableSortHandler"
            description="%ProblemView.Sorting.description"
            id="org.eclipse.ui.views.problems.sorting"
            name="%ProblemView.Sorting.name">
   </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="%ViewPreferences.description"
           id="org.eclipse.ui.preferences.viewPreferences"
           name="%ViewPreferences.name">
     <commandParameter id="markerEnablementName"
                       name="%ViewPreferences.markerEnablementName.name"
                       optional="false" />
     <commandParameter id="markerLimitName"
                       name="%ViewPreferences.markerLimitName.name"
                       optional="false" />
   </command>
   <command categoryId="org.eclipse.ui.views.problems"
            defaultHandler="org.eclipse.ui.views.markers.internal.FiltersHandler"
            description="%ProblemView.ConfigureFilters.description"
            id="org.eclipse.ui.views.problems.configureFilters"
            name="%ProblemView.ConfigureFilters.name">
   </command>
   <command categoryId="org.eclipse.ui.views.problems"
            defaultHandler="org.eclipse.ui.views.markers.internal.OpenMarkerHandler"
            description="%ProblemView.GoTo.description"
            id="org.eclipse.ui.views.problems.goTo"
            name="%ProblemView.GoTo.name" />
 </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.


A similar option to reuse expressions as much as possible without turning them into their own procedural language would be to allow global definitions and then reuse them. No parameters and no expression composition:

 <extension point="org.eclipse.core.expression.templates">
   <expression id="isProblemViewActive">
     <with variable="activePartId">
       <equals value="org.eclipse.ui.views.ProblemView" />
     </with>
   </expression>
   <expression id="isSelectionAvailable">
     <not>
       <count value="0" />
     </not>
   </expression>
 </extension>
 <extension point="org.eclipse.ui.handlers">
   <handler commandId="org.eclipse.ui.edit.copy"
            class="org.eclipse.ui.views.markers.internal.CopyMarkerHandler">
     <enabledWhen ref="isSelectionAvailable" />
     <activeWhen ref="isProblemViewActive" />
   </handler>
 </extension>

Another Alternative: Specify Context at Extension Level

Since enabledWhen and activeWhen specify context and the simple way to specify context in XML is enclosure, how about scoping context to the extension point rather than the handler:

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

This gives compact markup without inventing a new language. Elements nested in the handler element could override the extension-wide settings.

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 <menuCollection/> location attribute is a URI that defines the starting point for inserting the menu elements. The XML hierarchy mirrors the menu hierarchy, in that you can define items and menus within the body of other menus.

 <extension point="org.eclipse.ui.menus">
   <menuCollection location="menu:org.eclipse.ui.views.ProblemView">
     <item commandId="org.eclipse.ui.views.problems.sorting"
           mnemonic="%ProblemView.Sorting.mnemonic"
           tooltip="%ProblemView.Sorting.tooltip" />
     <menu id="org.eclipse.ui.views.problems.groupBy.menu"
           label="%ProblemView.GroupBy.label"
           mnemonic="%ProblemView.GroupBy.mnemonic">
       <dynamicContribution class="org.eclipse.ui.views.markers.internal.GroupByItems"
                            id="org.eclipse.ui.views.problems.groupBy.items">
     </menu>
     <separator id="group.filter" visible="true" />
     <menu id="org.eclipse.ui.views.problems.filters.menu"
           label="%ProblemView.Filters.label"
           mnemonic="%ProblemView.Filters.mnemonic">
       <dynamicContribution class="org.eclipse.ui.views.markers.internal.FilterItems"
                            id="org.eclipse.ui.views.problems.filters.items">
     </menu>
     <item commandId="org.eclipse.ui.views.problems.configureFilters"
           mnemonic="%ProblemView.ConfigureFilters.mnemonic"
           icon="$nl$/elcl16/filter_ps.gif"
           tooltip="%ProblemView.ConfigureFilters.tooltip" />
     <item commandId="org.eclipse.ui.preferences.viewPreferences"
           mnemonic="%ViewPreferences.mnemonic">
       <parameter name="markerEnablementName" value="LIMIT_PROBLEMS" />
       <parameter name="markerLimitName" value="PROBLEMS_LIMIT" />
     </item>
   </menuCollection>
   <menuCollection location="toolbar:org.eclipse.ui.views.ProblemView">
     <item commandId="org.eclipse.ui.views.problems.configureFilters"
           icon="$nl$/elcl16/filter_ps.gif"
           tooltip="%ProblemView.ConfigureFilters.tooltip" />
   </menuCollection>
   <menuCollection location="popup:org.eclipse.ui.views.ProblemView">
     <item commandId="org.eclipse.ui.views.problems.goTo"
           mnemonic="%ProblemView.GoTo.mnemonic"
           icon="$nl$/elcl16/gotoobj_tsk.gif"
           disabledIcon="$nl$/dlcl16/gotoobj_tsk.gif"
           tooltip="%ProblemView.GoTo.tooltip" />
     <separator id="group.showIn" visible="true" />
     <menu id="org.eclipse.ui.views.problems.showIn.menu"
           label="%ProblemView.ShowIn.label"
           mnemonic="%ProblemView.ShowIn.mnemonic">
       <dynamicContribution class="org.eclipse.ui.actions.ShowInContributions"
                            id="org.eclipse.ui.views.problems.showIn.items">
     </menu>
     <separator id="group.edit" visible="true" />
     <item commandId="org.eclipse.ui.edit.copy"
           mnemonic="%ProblemView.copy.mnemonic"
           icon="$nl$/icons/full/etool16/copy_edit.gif"
           disabledIcon="$nl$/icons/full/dtool16/copy_edit.gif" />
     <item commandId="org.eclipse.ui.edit.paste"
           mnemonic="%ProblemView.paste.mnemonic"
           icon="$nl$/icons/full/etool16/paste_edit.gif"
           disabledIcon="$nl$/icons/full/dtool16/paste_edit.gif" />
     <item commandId="org.eclipse.ui.edit.delete"
           mnemonic="%ProblemView.delete.mnemonic"
           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 commandId="org.eclipse.ui.edit.selectAll"
           mnemonic="%ProblemView.selectAll.mnemonic" />
     <separator id="group.resolve" visible="true" />
     <item commandId="org.eclipse.jdt.ui.edit.text.java.correction.assist.proposals"
           mnemonic="%ProblemView.Resolve.mnemonic"
           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 commandId="org.eclipse.ui.file.properties"
           mnemonic="%ProblemView.Properties.mnemonic" />
   </menuCollection>
 </extension>


Some constraints on the system:

  1. Identifiers (id) for <menu/> elements must be globally unique.
  2. Identifiers (id) for <item/> elements must be globally unique if they are specified.
  3. You can reference a <menu/> by id.
  4. If you are just creating menu items for your commands, you can leave them with only a command id. You don't have to specify an item id.
  5. You can reference an <item/> for placement options (after, before, etc) by id.
  6. <separator/> ids only have to be unique within that menu level. This might be changed to name instead of id in 3.3M5.
  7. You can provide an <item/> label attribute. If none is provided, it will take the command name.
  8. In this design the item contains most of the same rendering information that <action/> did.
  9. <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.
  10. All of the display-able attributes are translatable.
  11. The mnemonic is specified as you place your <item/> elements in their respective menus, since it is possible that the same command might need a different mnemonic depending on which menu it is placed. Also, when defaulting to command names they don't contain any mnemonic information.

Menu URIs

For location placement we need a path and placement modifier, and to specify how the paths are built. First pass we are going to look at URIs.

  • <scheme>:<menu-id>[?<placement-modifier>]

scheme is about how to interpret the URI path. For example, menu, toolbar, popup, status (although status may be deprecated).


For menu: valid root ids will be any viewId for that view's menu, and org.eclipse.ui.main.menu for the main menu. Then specify the id of the menu this contribution applies to. The placement modifier helps position the menu contribution. ex: after=<id>, where <id> can be a separator name, menu id, or item id. An example of a path: menu:org.eclipse.search.menu?after=contextMenuActionsGroup

Since menu ids must be unique, you can specify your menu location relative to an existing id: menu:org.eclipse.search.menu?after=contextMenuActionsGroup

For toolbar: valid root ids will be any viewId for that view's toolbar, org.eclipse.ui.main.toolbar for the main toolbar, and any toolbar id that is contained in the main toolbar. Toolbars can support invisible separators. Toolbars in the main toolbar (technically a coolbar) can have ids as well as separators, but only one level. For example: toolbar:org.eclipse.ui.edit.text.actionSet.presentation?after=Presentation

In this example, Presentation is an invisible separator in the org.eclipse.ui.edit.text.actionSet.presentation toolbar.

The use of org.eclipse.ui.main.toolbar might change if all "main" toolbars have ids anyway, so the only options for interpretting the toolbar root is 1) the view toolbar or 2) an IDed main toolbar.


For popup: valid root ids are any registered context id (which defaults to the part id if no context menu id was given at registration time) and org.eclipse.ui.menus.popup.any for all registered context menus. For example, to add to the default Text Editor context menu: popup:#TextEditorContext?after=additions

Popup submenus are treated like menu submenus, except the form continues to be popup:submenuId.

There will be constants defined for the ids that the eclipse workbench provides.

Menu - JSR198

Note: for novelty purposes only.

For comparison, 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

Note: for novelty purposes only.


For comparison, 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 menu definitions through the IMenuService API.

The above example can be done for the view menus:

public void addProblemsViewMenuContribution() {
    IMenuService menuService = (IMenuService) PlatformUI.getWorkbench()
            .getService(IMenuService.class);
    
    AbstractContributionFactory viewMenuAddition = new AbstractContributionFactory(
            "menu:org.eclipse.ui.views.ProblemView?after=additions") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            CommandContributionItem item = new CommandContributionItem(
                    null, "org.eclipse.ui.views.problems.sorting", null,
                    null, null, null, "Sorting...", "S",
                    "Change the Sort order");
            additions.add(item);

            MenuManager submenu = new MenuManager("Group &By",
                    "org.eclipse.ui.views.problems.groupBy.menu");
            IContributionItem dynamicItem = new CompoundContributionItem(
                    "org.eclipse.ui.views.problems.groupBy.items") {
                protected IContributionItem[] getContributionItems() {
                    // Here's where you would dynamically generate your list
                    IContributionItem[] list = new IContributionItem[2];
                    Map parms = new HashMap();
                    parms.put("groupBy", "Severity");
                    list[0] = new CommandContributionItem(null,
                            "org.eclipse.ui.views.problems.grouping",
                            parms, null, null, null, "Severity", null, null);

                    parms = new HashMap();
                    parms.put("groupBy", "None");
                    list[1] = new CommandContributionItem(null,
                            "org.eclipse.ui.views.problems.grouping",
                            parms, null, null, null, "None", null, null);
                    return list;
                }
            };
            submenu.add(dynamicItem);

            additions.add(submenu);
            additions.add(new Separator("group.filter"));

            submenu = new MenuManager("&Filters",
                    "org.eclipse.ui.views.problems.filters.menu");
            dynamicItem = new CompoundContributionItem(
                    "org.eclipse.ui.views.problems.filters.items") {
                protected IContributionItem[] getContributionItems() {
                    // Here's where you would dynamically generate your list
                    IContributionItem[] list = new IContributionItem[1];
                    Map parms = new HashMap();
                    parms.put("filter", "Default");
                    list[0] = new CommandContributionItem(null,
                            "org.eclipse.ui.views.problems.filters", parms,
                            null, null, null, "Default", null, null);
                    return list;
                }
            };
            submenu.add(dynamicItem);

            additions.add(submenu);

            ImageDescriptor filterIcon = PlatformUI.getWorkbench()
                    .getSharedImages().getImageDescriptor(
                            "elcl16/filter_ps.gif");
            item = new CommandContributionItem(null,
                    "org.eclipse.ui.views.problems.configureFilters", null,
                    filterIcon, null, null, "Configure Filters...", "C",
                    "Configure the filters to be applied to this view");
            additions.add(item);

            Map parms = new HashMap();
            parms.put("markerEnablementName", "LIMIT_PROBLEMS");
            parms.put("markerLimitName", "PROBLEMS_LIMIT");
            item = new CommandContributionItem(null,
                    "org.eclipse.ui.preferences.viewPreferences", parms,
                    null, null, null, "Preference", "P",
                    "Open the preference dialog");
            additions.add(item);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
            // for us this is a no-op
        }
    };
    menuService.addContributionFactory(viewMenuAddition);
}

The AbstractContributionFactory creates new contribution items every time createContributionItems(List) is called. The factory location tells the framework where to insert the contributions when populating ContributionManagers.

You add your IContributionItems in the list. Supported contributions include:

  • MenuManagers
  • Separators
  • GroupMarkers
  • CommandContributionItems
  • CompoundContributionItems
  • WidgetContributionItems

If you need to reference an Item at some time you will need to create it with an id, which must be unique.

Menus cannot be re-used, and so they have an intrinsic id value. Separators are unique within one menu level, so they also contain their name.

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

It was suggested in 3.2 that state on the command could be used to implement the old contribution story behaviours:

  1. changing label text and tooltips
  2. changing icons
  3. changing enablement
  4. getting and setting the item state (like checked state)

In 3.3 the enablement is tied to the command, but for the other behaviours there is:

Option 1:

Provide an optional callback element in the ExecutionEvent. It would be provided just like the optional trigger element. The object would have a useful interface to communicate with the UI element that initiated the execution request.

Option 2:

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="%jdt.ui.ToggleMarkOccurrences.description"
            id="org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences"
            name="%jdt.ui.ToggleMarkOccurrences.name">
     <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);

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. But as written, the toggleOccurrencesHandler will exist as long as the workbench window exists.

Menus

In 3.3M4 the action sets are still managed by activeActionSets. We'll determine if that needs to change in 3.3M5.


 <extension point="org.eclipse.ui.menus">
   <menuCollection location="toolbar:org.eclipse.ui.edit.text.actionSet.presentation?after=Presentation">
     <item commandId="org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences"
           tooltip="%jdt.ui.ToggleMarkOccurrences.tooltip"
           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>
   </menuCollection>
 </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 static void createToggleMarkOccurrences() {
    final IMenuService menuService = (IMenuService) PlatformUI
            .getWorkbench().getService(IMenuService.class);
    final ImageDescriptor markOccurDesc = AbstractUIPlugin
            .imageDescriptorFromPlugin("org.eclise.ui.tests",
                    "icons/full/etool16/mark_occurrences.gif");
    final ImageDescriptor disabledMarkOccurDesc = AbstractUIPlugin
            .imageDescriptorFromPlugin("org.eclise.ui.tests",
                    "icons/full/dtool16/mark_occurrences.gif");

    AbstractContributionFactory contribution = new AbstractContributionFactory(
            "toolbar:org.eclipse.ui.edit.text.actionSet.presentation?after=Presentation") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            IContributionItem item = new CommandContributionItem(
                    null,
                    "org.eclipse.jdt.ui.edit.text.java.toggleMarkOccurrences",
                    null, markOccurDesc, disabledMarkOccurDesc, null, null,
                    null, "Toggle Mark Occurrences");
            menuService
                    .registerVisibleWhen(
                            item,
                            new ActiveActionSetExpression(
                                    "org.eclipse.jdt.ui.text.java.actionSet.presentation"));
            additions.add(item);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
        }
    };
    menuService.addContributionFactory(contribution);
}


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

It's contributed with a visibleWhen clause ActiveActionSetExpression("org.eclipse.jdt.ui.text.java.actionSet.presentation"), so it will be visible when the actionSet is active.

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

For something 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">
   <menuCollection location="menu:org.eclipse.ui.main.menu?after=navigate">
     <menu label="%searchMenu.label"
           mnemonic="%searchMenu.mnemonic"
           id="org.eclipse.search.menu">
       <!-- not to be used by clients  -->
       <separator name="internalDialogGroup" visible="false" />
       <!-- to be used by clients -->
       <separator name="dialogGroup" visible="false" />
       <!-- to be used by clients      -->
       <separator name="fileSearchContextMenuActionsGroup" visible="true" />
       <!-- to be used by clients      -->
       <separator name="contextMenuActionsGroup" visible="true" />
       <!-- to be used by clients -->
       <separator name="occurencesActionsGroup" visible="true" />
       <!-- to be used by clients -->
       <separator name="extraSearchGroup" visible="true" />
     </menu>
   </menuCollection>
 </extension>

Then the JDT plugin would contribute the menu items to search, where the menuCollection 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.


 <extension point="org.eclipse.ui.menus">
   <menuCollection location="menu:org.eclipse.search.menu?after=dialogGroup">
     <item commandId="org.eclipse.jdt.internal.ui.search.openJavaSearchPage"
           label="%openJavaSearchPageAction.label"
           mnemonic="%openJavaSearchPageAction.mnemonic"
           icon="$nl$/icons/full/obj16/jsearch_obj.gif"
           helpContextId="java_search_action_context">
       <visibleWhen>
         <with variable="activeActionSets">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </item>
   </menuCollection>
   <menuCollection location="menu:org.eclipse.search.menu?after=contextMenuActionsGroup">
     <menu id="readAccessSubMenu"
           label="%readAccessSubMenu.label"
           mnemonic="%readAccessSubMenu.mnemonic">
       <separator name="group1" visible="false" />
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.workspace"
             label="%InWorkspace.label"
             mnemonic="%InWorkspace.mnemonic">
       </item>
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.project"
             label="%InProject.label"
             mnemonic="%InProject.mnemonic">
       </item>
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.hierarchy"
             label="%InHierarchy.label"
             mnemonic="%InHierarchy.mnemonic">
       </item>
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.read.access.in.working.set"
             label="%InWorkingSet.label"
             mnemonic="%InWorkingSet.mnemonic">
       </item>
       <visibleWhen>
         <with variable="activeActionSets">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </menu>
     <menu id="writeAccessSubMenu"
           label="%writeAccessSubMenu.label"
           mnemonic="%writeAccessSubMenu.mnemonic">
       <separator name="group1" visible="false" />
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.workspace"
             label="%InWorkspace.label"
             mnemonic="%InWorkspace.mnemonic">
       </item>
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.project"
             label="%InProject.label"
             mnemonic="%InProject.mnemonic">
       </item>
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.hierarchy"
             label="%InHierarchy.label"
             mnemonic="%InHierarchy.mnemonic">
       </item>
       <item commandId="org.eclipse.jdt.ui.edit.text.java.search.write.access.in.working.set"
             label="%InWorkingSet.label"
             mnemonic="%InWorkingSet.mnemonic">
       </item>
       <visibleWhen>
         <with variable="activeActionSets">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </menu>
   </menuCollection>
   <menuCollection location="menu:org.eclipse.search.menu?after=occurencesActionsGroup">
     <item commandId="org.eclipse.jdt.ui.edit.text.java.search.occurrences.in.file.quickMenu"
           label="%occurrencesSubMenu.label">
       <visibleWhen>
         <with variable="activeActionSets">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </item>
   </menuCollection>
 </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 static void addSearchMenu() {
    IMenuService menuService = (IMenuService) PlatformUI.getWorkbench()
            .getService(IMenuService.class);

    AbstractContributionFactory searchContribution = new AbstractContributionFactory(
            "menu:org.eclipse.ui.main.menu?after=navigate") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            MenuManager search = new MenuManager("Se&arch",
                    "org.eclipse.search.menu");

            search.add(new GroupMarker("internalDialogGroup"));
            search.add(new GroupMarker("dialogGroup"));
            search.add(new Separator("fileSearchContextMenuActionsGroup"));
            search.add(new Separator("contextMenuActionsGroup"));
            search.add(new Separator("occurencesActionsGroup"));
            search.add(new Separator("extraSearchGroup"));

            additions.add(search);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
            // nothing to do here
        }
    };

    menuService.addContributionFactory(searchContribution);
}

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


Then another plugin can contribute to the search menu:

public static void addToSearchMenu() {
    final IMenuService menuService = (IMenuService) PlatformUI
            .getWorkbench().getService(IMenuService.class);
    final ActiveActionSetExpression activeSearchActionSet = new ActiveActionSetExpression(
            "org.eclipse.jdt.ui.SearchActionSet");

    final ImageDescriptor searchIcon = AbstractUIPlugin
            .imageDescriptorFromPlugin("org.eclise.ui.tests",
                    "icons/full/obj16/jsearch_obj.gif");
    AbstractContributionFactory factory = new AbstractContributionFactory(
            "menu:org.eclipse.search.menu?after=dialogGroup") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            CommandContributionItem item = new CommandContributionItem(
                    "org.eclipse.jdt.internal.ui.search.openJavaSearchPage",
                    "org.eclipse.jdt.internal.ui.search.openJavaSearchPage",
                    null, searchIcon, null, null, null, null, null);
            menuService.registerVisibleWhen(item, activeSearchActionSet);
            additions.add(item);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
        }
    };
    menuService.addContributionFactory(factory);

    factory = new AbstractContributionFactory(
            "menu:org.eclipse.search.menu?after=contextMenuActionsGroup") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            MenuManager readMenu = new MenuManager("&Read Access",
                    "readAccessSubMenu");
            menuService
                    .registerVisibleWhen(readMenu, activeSearchActionSet);
            additions.add(readMenu);

            readMenu.add(new GroupMarker("group1"));

            CommandContributionItem item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.workspace",
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.workspace",
                    null, null, null, null, null, "W", null);
            readMenu.add(item);
            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.project",
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.project",
                    null, null, null, null, null, "P", null);
            readMenu.add(item);
            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.hierarchy",
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.hierarchy",
                    null, null, null, null, null, "H", null);
            readMenu.add(item);
            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.working.set",
                    "org.eclipse.jdt.ui.edit.text.java.search.read.access.in.working.set",
                    null, null, null, null, null, "S", null);
            readMenu.add(item);

            MenuManager writeMenu = new MenuManager("&Write Access",
                    "writeAccessSubMenu");
            menuService.registerVisibleWhen(writeMenu,
                    activeSearchActionSet);
            additions.add(writeMenu);

            writeMenu.add(new GroupMarker("group1"));

            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.workspace",
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.workspace",
                    null, null, null, null, null, "W", null);
            writeMenu.add(item);
            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.project",
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.project",
                    null, null, null, null, null, "P", null);
            writeMenu.add(item);
            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.hierarchy",
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.hierarchy",
                    null, null, null, null, null, "H", null);
            writeMenu.add(item);
            item = new CommandContributionItem(
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.working.set",
                    "org.eclipse.jdt.ui.edit.text.java.search.write.access.in.working.set",
                    null, null, null, null, null, "S", null);
            writeMenu.add(item);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
        }
    };
    menuService.addContributionFactory(factory);
}


When the main menu is populated, these contribution factories will be called.

IFile object contribution

We also have to provide object contributions (which in the past were scoped by objectClass).

Menus

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

 <extension point="org.eclipse.ui.menus">
   <menuCollection location="popup:org.eclipse.ui.menus.popup.any?after=additions">
     <item commandId="org.eclipse.ui.examples.wiki.post"
           mnemonic="%WikiExample.post.mnemonic"
           icon="$nl$/icons/full/elcl16/post_wiki.gif">
       <visibleWhen>
         <with variable="selection">
           <adapt type="org.eclipse.core.resources.IFile" />
         </with>
       </visibleWhen>
     </item>
     <item commandId="org.eclipse.ui.examples.wiki.load"
           mnemonic="%WikiExample.load.mnemonic"
           icon="$nl$/icons/full/elcl16/load_wiki.gif">
       <visibleWhen>
         <adapt type="org.eclipse.core.resources.IFile" />
       </visibleWhen>
     </item>
   </menuCollection>
 </extension>


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

Menus API

So programmatically it is similar to all other menu contributions.

public static void addFileContribution() {
    final IMenuService menuService = (IMenuService) PlatformUI
            .getWorkbench().getService(IMenuService.class);
    final ObjectClassExpression ifileExpression = new ObjectClassExpression(
            "org.eclipse.core.resources.IFile");

    final ImageDescriptor postIcon = AbstractUIPlugin
            .imageDescriptorFromPlugin("org.eclise.ui.tests",
                    "icons/full/elcl16/post_wiki.gif");
    final ImageDescriptor loadIcon = AbstractUIPlugin
            .imageDescriptorFromPlugin("org.eclise.ui.tests",
                    "icons/full/elcl16/load_wiki.gif");
    AbstractContributionFactory factory = new AbstractContributionFactory(
            "popup:org.eclipse.ui.menus.popup.any?after=additions") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            CommandContributionItem item = new CommandContributionItem(
                    "org.eclipse.ui.examples.wiki.post",
                    "org.eclipse.ui.examples.wiki.post", null, postIcon,
                    null, null, null, "P", null);
            menuService.registerVisibleWhen(item, ifileExpression);
            additions.add(item);

            item = new CommandContributionItem(
                    "org.eclipse.ui.examples.wiki.load",
                    "org.eclipse.ui.examples.wiki.load", null, loadIcon,
                    null, null, null, "L", null);
            menuService.registerVisibleWhen(item, ifileExpression);
            additions.add(item);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
        }
    };
    menuService.addContributionFactory(factory);
}


The location of org.eclipse.ui.menus.popup.any specifies any context menu, and the expression ties it to a specific objectClass. Using the new expression syntax you can make your conditions more complex.

You can set your visibleWhen expression on each item as you create it.

Text editor popup action

Popups can be targetted at any registered context menu, or at all of them. This is the Scramble Text command to be added the the standard text editor.

Commands

First define the command and its handler.

 <extension point="org.eclipse.ui.commands">
   <command id="org.eclipse.ui.examples.menus.scramble.text"
            defaultHandler="org.eclipse.ui.examples.menus.internal.ScrambleTextHandler"
            name="%ScrambleText.name"
            description="%ScrambleText.description" />
 </extension>

Menus

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">
   <menuCollection location="popup:#TextEditorContext?after=additions">
     <item commandId="org.eclipse.ui.examples.menus.scramble.text"
           mnemonic="%ScrambleText.mnemonic"
           icon="$nl$/icons/full/eobj16/scramble.gif" />
   </menuCollection>
 </extension>

Menus API

Programmatically do this, you would have to go through the IMenuService.


public static void addTextMenuContribition() {
    final IMenuService menuService = (IMenuService) PlatformUI
            .getWorkbench().getService(IMenuService.class);

    final ImageDescriptor scrambleIcon = AbstractUIPlugin
            .imageDescriptorFromPlugin("org.eclise.ui.tests",
                    "icons/full/eobj16/scramble.gif");
    AbstractContributionFactory factory = new AbstractContributionFactory(
            "popup:#TextEditorContext?after=additions") {
        public void createContributionItems(IMenuService menuService,
                List additions) {
            CommandContributionItem item = new CommandContributionItem(
                    "org.eclipse.ui.examples.menus.scramble.text",
                    "org.eclipse.ui.examples.menus.scramble.text",
                    null, scrambleIcon, null, null, null, "c", null);
            additions.add(item);
        }

        public void releaseContributionItems(IMenuService menuService,
                List items) {
        }
    };
    menuService.addContributionFactory(factory);
}

Product removes the Project menu

An RCP product wishes to remove the Project menu. It should be possible to override the visibility of menu contributions.

   public void addOverride() {
       // the RCP app would already have its product key
       Object productKey = null;

       IMenuService menuServ = (IMenuService) PlatformUI.getWorkbench()
               .getActiveWorkbenchWindow().getService(IMenuService.class);
       menuServ.addOverride(productKey, "menu:project", new OverrideAdapter() {
           public Boolean getVisible() {
               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.

The override service is ID based. For items which haven't specified their ID, the override will be applied to the commandId (which is required on every item).


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">
   <menuCollection location="toolbar:org.eclipse.search.toolbar">
     <widget id="org.eclipse.ui.examples.menus.searchBar"
             class="org.eclipse.ui.examples.menus.internal.SearchBar">
       <visibleWhen>
         <with variable="activeContexts">
           <iterator operator="or">
             <equals value="org.eclipse.jdt.ui.SearchActionSet" />
           </iterator>
         </with>
       </visibleWhen>
     </widget>
   </menuCollection>
 </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. This problem might shake out to just adding controls to the trim.

Will menus take arbitrary controls?

Also, there are risks associated with this like eager plugin activation. Maybe we allow widget activation but restrict it to programmatic API only (after the plugin has been instantiated) or still allow declarative contributions but only with certain types of <visibleWhen/> clauses.

Edit->Undo relabel action

Work on propogating changes from handlers to the UI are still being worked on in 3.3M5.

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.

After the item has been placed in a menu, the NAME state can be used by the handler to update the label.

<extension
      point="org.eclipse.ui.commands">
 <extension point="org.eclipse.ui.menus">
   <menuCollection location="menu:edit?after=undo.ext">
     <item commandId="org.eclipse.ui.examples.menus.targettedUndo"
           mnemonic="%TargettedUndo.mnemonic" />
   </menuCollection>
 </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. When a handler becomes active and receives state, the oldValue is always null.

   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;
       }
   }

Adding programmatic menus and handlers tied to an editor

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. The menus contributed through the IPartSite would also potentially be disposed when the part is closed, although not necessarily.

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 window.

Add a dynamic submenu to the ProblemView menu

In Add ProblemView menus we added 2 dynamic menus. You then have to implement IDynamicMenu in your provided class.

     <menu id="org.eclipse.ui.views.problems.groupBy.menu"
           label="%ProblemView.GroupBy.label"
           mnemonic="%ProblemView.GroupBy.mnemonic">
       <dynamicContribution class="org.eclipse.ui.views.markers.internal.GroupByItems"
                            id="org.eclipse.ui.views.problems.groupBy.items">
     </menu>


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

   class GroupByItems implements AbstractDynamicContribution {
       public void createContributionItems(List items) {
           String[] filterIds = getCurrentFilters();
           for (int i = 0; i < filterIds.length; i++) {
               String id = filterIds[i];
               Map parms = new HashMap();
               parms.put("filterId", id);
               CommandContributionItem item = new CommandContributionItem(
                       null,
                       "org.eclipse.ui.examples.menus.groupByCommand",
                       parms, null, null, null, getFilterName(id), null, 
                       null);
               items.add(item);
           }
       }
   }

Dynamic item replacement

See Add_a_dynamic_submenu_to_the_ProblemView_menu

A dynamic contribution item is expanded in place. It disposes the provided IContributionItems and requests new ones on every show.

Example Matrix

This will be updated later.

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

Back to the top