Popup Menus

The Eclipse plugin mechanism is very powerful because it can be easily used to assemble a lot of software components into a sophisticated working application but that same mechanism can also make it harder to discover for yourself how things work together. Popup menu items involve the interaction of several systems so they are harder than normal to understand.

Popup menus are those menus that appear when the popup menu 'trigger' is invoked (usually a right mouse click). Popup menus display menu items that are context sensitive. That is, popup menus usually display menu items that are particularly relevent to the UI component associated with the trigger and with the current state of the workbench.
The JLense workbench manages popup menus in the workbench and provides extension points for contributing popup menus to views. Plugins may not only contribute items to the popup menus associated with thier own views but may also contribute items to the popup views contributed by other plugins.

The items to display on a popup menu can come from two places, the current selection and the current view. Both types of popup menu items are defined using the org.jlense.uiworks.popupMenus extension point using <objectContribution> and <viewerContribution> elements. objectContributions are items that are associated with the current selection and viewerContributions are items that are associated with the current view. ;viewerContributions will always be displayed on popup menus when thier associated view is the currently active view. objectContributions are displayed on a popup menu when the current selection satisfies the objectContribution's selection criteria as specified in the <objectContribution> element.

The org.jlense.uiworks.popupMenus extension point allows a plug-in to contribute to a view's popup menus.

You can contribute an action to a specific view by its id (viewerContribution), or by associating it with a particular object type (objectContribution).

So, if you want an item to always be displayed on a popup menu for a particular view then you would use a <viewerContribution> element.
If you want an item to be displayed for a particular type of object, no matter in what view the object is displayed then use an <objectContribution> element.

  • An objectContribution will cause the menu item to appear in popup menus for views where objects of the specified type are selected.
  • A viewerContribution will cause the menu item to appear in the popup menu of a view specified by id in the markup.

For example, the org.jlense.uiworks plugin defines two object contributions that can contribute items to the popup menus (a.k.a. "context" menus) of all other contributed views. Let's take a look at those object contributions.

<extension point="org.jlense.uiworks.popupMenus">
  <objectContribution objectClass="org.jlense.uiworks.action.IOpenable" >
      <action
            id="org.jlense.uiworks.open.selection"
            menubarPath="new.ext"
            definitionID="org.jlense.uiworks.open.selection"
            label="&Open@Ctrl+O"
            tooltip="Open the selected item(s)"
            helpContextId="org.jlense.uiworks.open.selection"
            icon="icons/full/ctool16/open.gif"
            class="org.jlense.uiworks.action.OpenSelectedActionDelegate"
            enablesFor="+">
      </action>
   </objectContribution>
  <objectContribution objectClass="org.jlense.uiworks.action.IDeleteable" >
      <action
            id="org.jlense.uiworks.delete"
            menubarPath="cut.ext"
            definitionID="org.jlense.uiworks.delete.selection"
            label="&Delete@Ctrl+D"
            tooltip="Delete the selected item(s)"
            helpContextId="org.jlense.uiworks.delete"
            icon="icons/full/ctool16/deleteable.gif"
            class="org.jlense.uiworks.action.DeleteActionDelegate"
            enablesFor="+">
      </action>
   </objectContribution>
</extension>

What these definitions say is that if the currently selected item implements the IOpenable interface (or has an IOpenable adapter) then display an 'Open' menu item for opening the item. Or if the currently selected item implements the IDeleteable interface (or has an IDeleteable adapter) then display a 'Delete' menu item for deleting the item.

Let's take a look at how the workbench uses these extension definitions to actually crate a popup menu.

Obviously, if the workbench is going to create an appropraite popupo menu for an object then the workbench has know what the 'current selection' is. The details of selection providers and selection listeners are provided elsewhere. For right now it is sufficient to know that any view that wishes to take advantage of the workbench's ability to generate popup menu items for selected objects must supply the workbench with a 'Selection Provider'. In this way the workbench can get the current selection from the current view.

That takes care of actually telling the workbench what the current selection is. When a user selects a item in a view then the view's selection provider tells the workbench exactly what has been selected. From this information the workbench can create items for the popup menu based on registered popupMenu extenstion points.

After determining what object's are selected the workbench must then determine if the selected items satisfy the criteria of any <objectContribution> extension. If so then the workbench will create a popup menu item for the selected items that represents the <objectContribution>.
So, for the <objectContribution> extension definitions shown above, how exactly does the workbench determine if the selected items are 'openable' or 'deletable'?
One obvious way that an object can be openable or deleteable is to actually implement the org.jlense.uiworks.action.IDeleteable interface or the org.jlense.uiworks.action.IOpenable interface. However, it can often be difficult to create objects that implement a particular interface or interfaces. For instance, suppose you got a list of objects from a remote object store and you display those objects in your view. To get the objects to implement the IOpenable interface would require you to create an entirely new class of object that implemented the interface. Not very convenient. That approach isn't very flexible either. Suppose a third party plugin wanted to add support for the org.jlense.uiworks.open.selection action to the objects created by another action. There would be no way to do it, the third party plugin can magically add an interface to the objects created by another plugin. Or can it?

Clearly a better method than inheritence is needed to determine if an object is of a certain type or has certain capabilities. The solutuion is adapters. The workbench popup menu implementation works with the Eclipse Adapter Manager. If the workbench can find and adapter for the currently selected object that implements IOpenable then it will display the IOpenable meni item.

The adapter mechanism may seem like an unneccessary level of indirection but it is important because it makes it possible for plugins to extend objects created by other plugins in a loosely coupled, independent manner.

So, to sum up, to display popup menu items...

  • you must define <objectContribution>s and/or <viewContribution>s. There are two <objectContribution>s already available for your use.
  • your view must supply the workbench with a selection provider.
  • your plugin must supply appropriate adapters for selection objects.