Home > Eclipse development, Software Development > Writing an Eclipse Plug-in (Part 22): Common Navigator: Adding submenus (Presentation)

Writing an Eclipse Plug-in (Part 22): Common Navigator: Adding submenus (Presentation)


[If anyone cares: I have upgraded to Eclipse 3.6 RC3]

Happy Memorial Day weekend, everyone (at least those in the United States)!

For those who have accepted that life is meaningless, short and painful: chow down at the grill! Eat, drink and be merry for tomorrow you die!

For those who believe that life is meaningful, long and joyous: don’t overdo your carbs, remember that hot dogs have artificial colors and mystery meat, and grilling your food causes the formation of cancer causing agents due to carbonization. In other words, don’t eat, drink or be too merry because the odds are you won’t be dying tomorrow (or maybe you will).

But, hey! Enjoy the weekend!

Well, with that sense of merriment out of the way it is time to go back to the real reason we are here: finishing up the popup menu.

In Part 20 we created a default popup menu by defining a popup menu and its insertionPoints, defining an actionProvider and its implementation, and defining an actionExtension.

In Part 21 we extended the magic by having the popup work only with resources of type ICustomProjectElement.

Four parts left and then I am moving on to another topic for awhile:

  1. Add some more menu items to the popup
  2. Test/implement the behavior of the aforementioned menu items
  3. Add a properties page for each of the node types
  4. Fix the main menu New menu item to reflect the same list of New items as our popup

Needless to say, I may stop after any one of the above items. I have commitment issues and probably a touch of ADD.

Let the show begin!

What (are we trying to do?)

So let’s add some more menu items to the popup. Specifically under the New menu:

  • Schema Table
  • Schema View
  • Schema Filter
  • Stored Procedure

To add another menu item to the popup one would think that we should be able to do what we did last time, right?

Actually…no. Not unless we had to. Or your mother made us.

I mean we could, but that’s so…so, Eclipse 3.0. We’re all the way up to Eclipse 3.6; why not use the latest and greatest leading-edge all-new all-natural way of adding menu items to a popup? Espcially since it has been available and officially recommended since Eclipse 3.3?

To clarify the above incoherence: The Common Navigator Framework has two ways of adding behavior to popup menus:

  1. Action Providers (which we’ve used)
  2. Workbench Commands Framework (which we haven’t)

This begs the following questions:
Q: Why are there two ways to do the same thing?
A: From the Eclipse Help documentation:
Commands and handlers have been provided by the workbench in one form or another since 3.0. In 3.2 the commands, handlers, and keybindings portion of the Command Framework became mature. In 3.3 the menu contributions portion of the Command Framework became available.

Though the CNF provides a mechanism to work with actions, it is recommended that you instead use the Workbench Commands instead of actions.

Q: When would you use an action provider?
A: From the Eclipse Help documentation:
Action providers provide a means to configure the retargetable actions and programmatically configure the popup menu in a CNF viewer. These are useful for when you must perform a computation to determine which items are added to the menu, or to adjust the retargetable actions to ensure that the user keystrokes are handled properly (like for Cut/Copy/Paste).

Q: When would you use the Workbench Commands Framework?
A: Use the Workbench Command Framework as a way of adding behavior to the “main menu, view dropdown menus, context menus. They can also be added to the main toolbar, view toolbars, and various trim locations.” In other words, if you are not programmatically changing the popup menu use the command framework, otherwise use an action provider.

As an example: in the last post we created NewCustomActionProvider and CustomRefreshActionProvider. Remember this code?

    ...
    public void fillContextMenu(IMenuManager menu) {
        IMenuManager submenu = new MenuManager(
                Messages.CustomNewActionProvider_popupNewLabel,
                NEW_MENU_NAME);
        if(!contribute) {
            return;
        }

        // fill the menu from the commonWizard contributions
        newWizardActionGroup.setContext(getContext());
        newWizardActionGroup.fillContextMenu(submenu);

        submenu.add(new Separator(ICommonMenuConstants.GROUP_ADDITIONS));

        // Add other ..
        submenu.add(new Separator());
        submenu.add(showDlgAction);

        // append the submenu after the GROUP_NEW group.
        menu.insertAfter(ICommonMenuConstants.GROUP_NEW, submenu);
    }
    ...

Since the NewCustomActionProvider was programmatically configuring the popup menu it made sense to use the action provider framework (remember the new insertionPoint?). The Refresh functionality did not programmatically change (translation: screw with) the popup menu, but the original code was in an action provider so it was easier to use it within the same framework.

Creating an action provider class, while satisfying, is disappointing. We had to hard code the use of the original insertionPoint and hard code the new insertionPoint for the submenus to use in the commonWizard entry. Not very sporting.

This brings us to the rationale of this post: we are not going to use the action provider framework as it has been deprecated by the Workbench Commands Framework and the behavior we are adding will not be reconfiguring the popup menu.

Using the Workbench Commands will lead to code that is cleaner and less icky than the code found using the Action Provider Framework (that is, the technical defintion of icky, not the hold-this-used-diaper-for-me defintion of icky). The downside of the Command Framework is more code and more configuration. How much more? Three times more. I know, it is an embarrassment of riches.

Commands, handlers and command images.

Repeat those three over and over again until you decide to name your children after them. They are the key to adding behavior to Eclipse using the Workbench Commands Framework.

And in the spirit of doing only as much as we have to we will add the menu items to the popup and save the actual behavior for the next post.

How (are we doing it?)

And now for the dry overcooked meat version of what you need to do.

  1. Add The Command: customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.commands
  2. org.eclipse.ui.commands –> New –> command
    • id: customnavigator.newSchemaTable
    • name: Schema Table
    • description: Open the New Custom Schema Table Wizard
  3. Add the Handler: customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.handlers
    • commandId: customnavigator.newSchemaTable
    • class: customnavigator.handler.BogusHandler
  4. org.eclipse.ui.handlers –> New –> enabledWhen
  5. (enabledWhen) –> New –> with
    • variable: selection
  6. selection (with) –> New –> iterate
    • ifEmpty: false
  7. false (iterate) –> New –> instanceof
    • value: customnavigator.navigator.ICustomProjectElement
  8. Add the menu item to the popup: customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.menus
  9. org.eclipse.ui.menus –> New –> menuContribution
    • locationURI: popup:common.new.menu?after=additions
  10. org.eclipse.ui.menus –> menuContribution –> New –> command
    • commandId: customnavigator.newSchemaTable
    • (command) –> New –> visibleWhen
      • checkEnabled: true

    Time for a quick manual test:

    1. Start the runtime workbench
    2. Go to the Custom Perspective
    3. Right-click in the Custom Navigator and create a new Custom Project
    4. Right click on the newly created Custom Project and select the New menu item

    You should see the following:

  11. Add the icon image: customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.commandImages
    • commandId: customnavigator.newSchemaTable (click Browse to find it and save yourself some typing)
    • icon: icons/project-schema-tables.png

    Check that the menu item appears along with its icon. Now let’s add the remaining 3 menu items.

  12. Schema View
    • org.eclipse.ui.commands –> New –> command
      • id: customnavigator.newSchemaView
      • name: Schema View
      • description: Open the New Custom Schema View Wizard
    • org.eclipse.ui.handlers –> New –> handler
      • commandId: customnavigator.newSchemaView
      • class: customnavigator.handler.BogusHandler
      • (handler) –> New –> enabledWhen
      • (enabledWhen) –> New –> with
        • variable: selection
      • selection (with) –> New –> iterate
        • ifEmpty: false
      • false (iterate) –> New –> instanceof
        • value: customnavigator.navigator.ICustomProjectElement
    • org.eclipse.ui.commandImages –> New –> image
      • commandId: customnavigator.newSchemaView (click Browse to find it and save yourself some typing)
      • icon: icons/project-schema-views.png
    • org.eclipse.ui.menus –> (menuContribution) –> New –> command
      • commandId: customnavigator.newSchemaView
      • (command) –> New –> visibleWhen
        • checkEnabled: true
  13. Schema Filter
    • org.eclipse.ui.commands –> New –> command
      • id: customnavigator.newSchemaFilter
      • name: Schema Filter
      • description: Open the New Custom Schema Filter Wizard
    • org.eclipse.ui.handlers –> New –> handler
      • commandId: customnavigator.newSchemaFilter
      • class: customnavigator.handler.BogusHandler
      • (handler) –> New –> enabledWhen
      • (enabledWhen) –> New –> with
        • variable: selection
      • selection (with) –> New –> iterate
        • ifEmpty: false
      • false (iterate) –> New –> instanceof
        • value: customnavigator.navigator.ICustomProjectElement
    • org.eclipse.ui.commandImages –> New –> image
      • commandId: customnavigator.newSchemaFilter (click Browse to find it and blah, blah, blah)
      • icon: icons/project-schema-filters.png
    • org.eclipse.ui.menus –> (menuContribution) –> New –> command
      • commandId: customnavigator.newSchemaFilter
      • (command) –> New –> visibleWhen
        • checkEnabled: true
  14. Stored Procedure
    • org.eclipse.ui.commands –> New –> command
      • id: customnavigator.newStoredProcedure
      • name: Stored Procedure
      • description: Open the New Custom Stored Procedure Wizard
    • org.eclipse.ui.handlers –> New –> handler
      • commandId: customnavigator.newStoredProcedure
      • class: customnavigator.handler.BogusHandler
      • (handler) –> New –> enabledWhen
      • (enabledWhen) –> New –> with
        • variable: selection
      • selection (with) –> New –> iterate
        • ifEmpty: false
      • false (iterate) –> New –> instanceof
        • value: customnavigator.navigator.ICustomProjectElement
    • org.eclipse.ui.commandImages –> New –> image
      • commandId: customnavigator.newStoredProcedure (click Browse to find it and blah, blah, blah)
      • icon: icons/project-stored-procedures.png
    • org.eclipse.ui.menus –> (menuContribution) –> New –> command
      • commandId: customnavigator.newStoredProcedure
      • (command) –> New –> visibleWhen
        • checkEnabled: true

When all is said and done you should have the following popup menu in your runtime workbench.

Don’t forget to fix the warnings in plugin.xml. We’ve fixed them before; you know the drill.

Why (did we do it that way?)

Some ground rules for the Custom Navigator popup:

  • Nothing selected:
    • New
      • Custom Project
    • Refresh
  • Anything selected (project or child nodes):
    • New
      • Custom Project
      • Schema Table
      • Schema View
      • Schema Filter
      • Stored Procedure
    • Refresh

The before and after pictures:

  • Before
    Nothing selected:


    Something selected:
  • After
    Nothing selected: [same as above]

    Something selected:

Let’s pretend we haven’t done anything yet (in this case that’s not very hard since we haven’t).

Commands, and handlers and command images.

At a high-level there are three things we need to do:

  • declare the command, but not the code for the command. It defines a commandId which allows us to associate a handler and an icon with it (extension point: org.eclipse.ui.commands)
  • declare the class that contains the code (extension point: org.eclipse.ui.handlers)
  • declare the icon associated with the command (extension point: org.eclipse.ui.commandImages)

Yes: commands, and handlers and command images. Each uses it own extension point, which is more configuration, but only one of them needs code which can be reused through the indirection of the commandId.

Let’s start in my favorite place: plugin.xml.

  • customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.commands
  • org.eclipse.ui.commands –> New –> command
    • id: customnavigator.newSchemaTable
    • name: Schema Table
    • description: Open the New Custom Schema Table Wizard

    The use of the defaultHandler element is interesting as it guarantees some behavior if there is no actual handler to go along with it. We will pretend our stuff is not that complicated so we will ignore it this time around.

  • customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.handlers
      Conveniently, adding the org.eclipse.ui.handlers extension automatically added an empty handler element (if it didn’t just right click on org.eclipse.ui.handlers, and select New –> handlers).
      Yes, use the commandId defined in the command above and define a class with a fully resolved name…which we have not implemented yet, but we will…in the next post.

    • commandId: customnavigator.newSchemaTable
    • class: customnavigator.handler.BogusHandler

    The next child elements are to configure the handler to only be enabled when the selected element in the navigator is of type ICustomProjectElement. The Eclipse Help documentation makes it quite clear that without all this boilerplate configuration the handler will not be enabled properly or when desired.

  • org.eclipse.ui.handlers –> New –> enabledWhen
  • (enabledWhen) –> New –> with
    • variable: selection

    The default list of valid values for the variable attribute can be found at the Command Core Expressions page of the Eclipse wiki.

  • selection (with) –> New –> iterate
    • ifEmpty: false
  • false (iterate) –> New –> instanceof
    • value: customnavigator.navigator.ICustomProjectElement
  • Now let’s assign the commandId for the Schema Table command (defined in org.eclipse.ui.commands) as a popup menu entry in a org.eclipse.ui.menus entry.

  • customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.menus
  • org.eclipse.ui.menus –> New –> menuContribution
    • locationURI: popup:common.new.menu?after=additions

    The URI defined above, common.new.menu, was programmatically defined in NewCustomActionProvider. It used the same naming convention used by the generic common navigator popup menu.

    Tell the menu which command to use…and by extension which handler to use.

  • org.eclipse.ui.menus –> menuContribution –> New –> command
    • commandId: customnavigator.newSchemaTable

    And finally, by setting checkEnabled to true the menu will check if the handler has been enabled; if it has then it will appear in the menu, otherwise it will not.

    • (command) –> New –> visibleWhen
      • checkEnabled: true

    Time for a quick manual test:

    1. Start the runtime workbench
    2. Go to the Custom Perspective
    3. Right-click in the Custom Navigator and create a new Custom Project
    4. Right click on the newly created Custom Project and select the New menu item

    Woo hoo! You should see the Schema Table menu sans an icon image (for the monolingual Americans reading this page: sans is French for without. Try to keep up).

    We’ll add the icon next. Exit the runtime workbench.

  • customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.commandImages
    • commandId: customnavigator.newSchemaTable (click Browse to find it and save yourself some typing)
    • icon: icons/project-schema-tables.png
  • Perform the manual test above again. You should see a wonderful icon next to the Schema Table menu item.

Now do the same four steps for the remaining menu items listed above by adding a new command, handler, command image and menus entry into the appropriate extensions:

  • org.eclipse.ui.commands –> New –> command
  • org.eclipse.ui.handlers –> New –> handler
  • org.eclipse.ui.commandImages –> New –> image
  • org.eclipse.ui.menus –> (menuContribution) –> New –> command

All the steps are explicitly listed in the How section in steps 12, 13 and 14. Once you’re done your popup menu should look like this handsome devil:

Don’t forget to fix the warnings in plugin.xml. We’ve fixed them before; you know the drill.

What Just Happened?

So we started out adding 4 menu items to the New popup menu item and we did it with no new code. We won’t be so lucky in the next post, but that’s the price of behavior.

The cat looks pretty good for only coming out of/staying in the box twice this month.

Run! I think the burger is on fire!

References

Re: Handler enabledWhen troubles… help!
Eclipse 3.6 Help: Workbench Core Expressions

Code

plugin.xml

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.views">
      <category
            id="customnavigator.category"
            name="%category.name">
      </category>
      <view
            allowMultiple="false"
            category="customnavigator.category"
            class="org.eclipse.ui.navigator.CommonNavigator"
            icon="icons/navigator.png"
            id="customnavigator.navigator"
            name="%view.name">
      </view>
   </extension>
   <extension
         point="org.eclipse.ui.navigator.viewer">
      <viewer
            viewerId="customnavigator.navigator">
         <popupMenu
               id="customnavigator.navigator#PopupMenu">
            <insertionPoint
                  name="group.new">
            </insertionPoint>
            <insertionPoint
                  name="group.build"
                  separator="true">
            </insertionPoint>
         </popupMenu>
      </viewer>
      <viewerContentBinding
            viewerId="customnavigator.navigator">
         <includes>
            <contentExtension
                  pattern="customnavigator.navigatorContent">
            </contentExtension>
         </includes>
      </viewerContentBinding>
      <viewerActionBinding
            viewerId="customnavigator.navigator">
         <includes>
            <actionExtension
                  pattern="customnavigator.popup.actionprovider.CustomNewAction">
            </actionExtension>
            <actionExtension
                  pattern="customnavigator.popup.actionprovider.CustomRefreshAction">
            </actionExtension>
         </includes>
      </viewerActionBinding>
   </extension>
   <extension
         point="org.eclipse.ui.navigator.navigatorContent">
      <navigatorContent
            activeByDefault="true"
            contentProvider="customnavigator.navigator.ContentProvider"
            id="customnavigator.navigatorContent"
            labelProvider="customnavigator.navigator.LabelProvider"
            name="%navigatorContent.name">
         <triggerPoints>
            <instanceof
                  value="org.eclipse.core.resources.IWorkspaceRoot">
            </instanceof>
         </triggerPoints>
         <commonSorter
               class="customnavigator.sorter.SchemaCategorySorter"
               id="customnavigator.sorter.schemacategorysorter">
            <parentExpression>
               <or>
                  <instanceof
                        value="customnavigator.navigator.CustomProjectSchema">
                  </instanceof>
               </or>
            </parentExpression>
         </commonSorter>
      </navigatorContent>
      <actionProvider
            class="customnavigator.popup.actionprovider.CustomNewActionProvider"
            id="customnavigator.popup.actionprovider.CustomNewAction">
         <enablement>
            <or>
               <instanceof
                     value="customnavigator.navigator.ICustomProjectElement">
               </instanceof>
               <adapt
                     type="java.util.Collection">
                  <count
                        value="0">
                  </count>
               </adapt>
            </or>
         </enablement>
      </actionProvider>
      <actionProvider
            class="customnavigator.popup.actionprovider.CustomRefreshActionProvider"
            id="customnavigator.popup.actionprovider.CustomRefreshAction">
         <enablement>
            <or>
               <instanceof
                     value="customnavigator.navigator.ICustomProjectElement">
               </instanceof>
               <adapt
                     type="java.util.Collection">
                  <count
                        value="0">
                  </count>
               </adapt>
            </or>
         </enablement>
      </actionProvider>
      <commonWizard
            type="new"
            wizardId="customplugin.wizard.new.custom">
         <enablement></enablement>
      </commonWizard>
   </extension>
   <extension
         point="org.eclipse.ui.commands">
      <command
            description="%command.schemaTable.description"
            id="customnavigator.newSchemaTable"
            name="%command.schemaTable.name">
      </command>
      <command
            description="%command.schemaView.description"
            id="customnavigator.newSchemaView"
            name="%command.schemaView.name">
      </command>
      <command
            description="%command.schemaFilter.description"
            id="customnavigator.newSchemaFilter"
            name="%command.schemaFilter.name">
      </command>
      <command
            description="%command.storedProcedure.description"
            id="customnavigator.newStoredProcedure"
            name="%command.storedProcedure.name">
      </command>
   </extension>
   <extension
         point="org.eclipse.ui.handlers">
      <handler
            class="customnavigator.handler.BogusHandler"
            commandId="customnavigator.newSchemaTable">
         <enabledWhen>
            <with
                  variable="selection">
               <iterate
                     ifEmpty="false">
                  <instanceof
                        value="customnavigator.navigator.ICustomProjectElement">
                  </instanceof>
               </iterate>
            </with>
         </enabledWhen>
      </handler>
      <handler
            class="customnavigator.handler.BogusHandler"
            commandId="customnavigator.newSchemaView">
         <enabledWhen>
            <with
                  variable="selection">
               <iterate
                     ifEmpty="false">
                  <instanceof
                        value="customnavigator.navigator.ICustomProjectElement">
                  </instanceof>
               </iterate>
            </with>
         </enabledWhen>
      </handler>
      <handler
            class="customnavigator.handler.BogusHandler"
            commandId="customnavigator.newSchemaFilter">
         <enabledWhen>
            <with
                  variable="selection">
               <iterate
                     ifEmpty="false">
                  <instanceof
                        value="customnavigator.navigator.ICustomProjectElement">
                  </instanceof>
               </iterate>
            </with>
         </enabledWhen>
      </handler>
      <handler
            class="customnavigator.handler.BogusHandler"
            commandId="customnavigator.newStoredProcedure">
         <enabledWhen>
            <with
                  variable="selection">
               <iterate
                     ifEmpty="false">
                  <instanceof
                        value="customnavigator.navigator.ICustomProjectElement">
                  </instanceof>
               </iterate>
            </with>
         </enabledWhen>
      </handler>
   </extension>
   <extension
         point="org.eclipse.ui.menus">
      <menuContribution
            allPopups="false"
            locationURI="popup:common.new.menu?after=additions">
         <command
               commandId="customnavigator.newSchemaTable"
               style="push">
            <visibleWhen
                  checkEnabled="true">
            </visibleWhen>
         </command>
         <command
               commandId="customnavigator.newSchemaView"
               style="push">
            <visibleWhen
                  checkEnabled="true">
            </visibleWhen>
         </command>
         <command
               commandId="customnavigator.newSchemaFilter"
               style="push">
            <visibleWhen
                  checkEnabled="true">
            </visibleWhen>
         </command>
         <command
               commandId="customnavigator.newStoredProcedure"
               style="push">
            <visibleWhen
                  checkEnabled="true">
            </visibleWhen>
         </command>
      </menuContribution>
   </extension>
   <extension
         point="org.eclipse.ui.commandImages">
      <image
            commandId="customnavigator.newSchemaTable"
            icon="icons/project-schema-tables.png">
      </image>
      <image
            commandId="customnavigator.newSchemaView"
            icon="icons/project-schema-views.png">
      </image>
      <image
            commandId="customnavigator.newSchemaFilter"
            icon="icons/project-schema-filters.png">
      </image>
      <image
            commandId="customnavigator.newStoredProcedure"
            icon="icons/project-stored-procedures.png">
      </image>
   </extension>

</plugin>

MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: customnavigator;singleton:=true
Bundle-Version: 1.0.2.2
Bundle-Activator: customnavigator.Activator
Require-Bundle: org.eclipse.ui,
 org.eclipse.core.runtime,
 org.eclipse.core.resources,
 org.eclipse.ui.ide;bundle-version="3.6.0",
 org.eclipse.ui.navigator,
 customplugin;bundle-version="1.0.1"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: customnavigator,
 customnavigator.navigator,
 customnavigator.popup.actionprovider,
 customnavigator.sorter

bundle.properties

#Properties file for customnavigator_1.0.0.7
Bundle-Name = Custom Navigator Plug-in
view.name = Custom Plug-in Navigator
category.name = Custom Projects
navigatorContent.name = Custom Navigator Content
copy.command.label = Copy
paste.command.label = Paste
delete.command.label = Delete
command.schemaTable.description = Open the New Schema Table Wizard
command.schemaTable.name = Schema Table
command.schemaView.description = Open the New Schema View Wizard
command.schemaView.name = Schema View
command.schemaFilter.description = Open the New Schema Filter Wizard
command.schemaFilter.name = Schema Filter
command.storedProcedure.description = Open the New Stored Procedure Wizard
command.storedProcedure.name = Stored Procedure
About these ads
  1. Doug Adams
    June 3, 2010 at 4:48 pm

    These are fantastic tutorials. Is this code in a publicly viewable repository someplace, or might you be willing to publish the whole plugin on this site? The inline examples are good, but I’d find it helpful to see all the code as well.

    • cvalcarcel
      June 10, 2010 at 10:02 am

      I have considered putting them up for easy download I just haven’t been motivated enough. I will post the code so far in a few days.

      Thanks for the suggestion!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 3,182 other followers

%d bloggers like this: