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:
- Add some more menu items to the popup
- Test/implement the behavior of the aforementioned menu items
- Add a properties page for each of the node types
- 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:
- Action Providers (which we’ve used)
- 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.
- Add The Command: 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
- Add the Handler: customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.handlers
- commandId: customnavigator.newSchemaTable
- class: customnavigator.handler.BogusHandler
- org.eclipse.ui.handlers –> New –> enabledWhen
- (enabledWhen) –> New –> with
- variable: selection
- selection (with) –> New –> iterate
- ifEmpty: false
- false (iterate) –> New –> instanceof
- value: customnavigator.navigator.ICustomProjectElement
- Add the menu item to the popup: customnavigator –> plugin.xml –> Extensions –> Add –> org.eclipse.ui.menus
- org.eclipse.ui.menus –> New –> menuContribution
- locationURI: popup:common.new.menu?after=additions
- org.eclipse.ui.menus –> menuContribution –> New –> command
- commandId: customnavigator.newSchemaTable
- (command) –> New –> visibleWhen
- checkEnabled: true
Time for a quick manual test:
- Start the runtime workbench
- Go to the Custom Perspective
- Right-click in the Custom Navigator and create a new Custom Project
- Right click on the newly created Custom Project and select the New menu item
- 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.
- 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.commands –> New –> command
- 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
- 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.commands –> New –> command
- 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
- 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.commands –> New –> command
- 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
- New
- Anything selected (project or child nodes):
- New
- Custom Project
- Schema Table
- Schema View
- Schema Filter
- Stored Procedure
- Refresh
- New
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).
- commandId: customnavigator.newSchemaTable
- class: customnavigator.handler.BogusHandler
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.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
- 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:
- Start the runtime workbench
- Go to the Custom Perspective
- Right-click in the Custom Navigator and create a new Custom Project
- 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
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.
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
Leave a Reply Cancel reply
Top Posts
Archives
- January 2012 (1)
- February 2011 (1)
- January 2011 (1)
- August 2010 (2)
- June 2010 (1)
- May 2010 (2)
- April 2010 (1)
- March 2010 (1)
- February 2010 (4)
- January 2010 (2)
- December 2009 (5)
- November 2009 (2)
- October 2009 (6)
- September 2009 (6)
- August 2009 (4)
- July 2009 (6)
- April 2009 (2)
- February 2009 (2)
- December 2008 (1)
- October 2008 (2)
- September 2008 (2)
- August 2008 (1)
Top Rated
Blog Stats
- 484,940 hits
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.
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!