Archive
Writing an Eclipse Plug-in (Part 20): Return of the Popup Menu (For an Empty Navigator)
[This is a long post. It also feels like a bit of a mess. Whoda thought that creating a popup menu when there are no resources available in a navigator could be so non-trivial?]
So what’s the problem (or as they say in marketing speak: what is the challenge)?
The challenge (or as they say in real life: the pain) is quite easy to describe: when I right click in the custom navigator the popup menu appears. When I create a custom project and right-click on it the popup menu does not appear.
That behavior has to stop or I am turning this blog around right now (I’m not kidding! I’ll turn around right now!).
Alright. I lied. We actually have two problems:
- Remove the undefined menu items from the popup when nothing is available or selected
- Enable a specific set of menus when a Custom Project has been created
What this means is we have to decide when menu items appear/disappear or are enabled/disabled based on items being selected/unselected.
Sounds like a lot of combinations. Sounds like a job for a UML State diagram which I actually like when I am writing a real application. The issue here is that I am still kinda just messing with this and the state diagram makes me become too serious (I find that even the squirrels start to complain).
So let’s list the menu items we know so far:
- New Custom Project
- New Schema File
- New Stored Procedure File
- Open Project
- Close Project
- Copy
- Paste
- Delete
- Import
- Export
- Refresh
- Properties
Looks like a lot. Let’s think about this: copy, paste and delete don’t mean what they usually do, except for projects. I expect them to only copy/paste/delete the nodes they represent not entire files. Let’s leave them for last so let’s just remove them.
Open and Close project sounds too cool to be true. They will come after we do copy, paste and delete (and while we’re at it, how about working sets? Nah.).
Import and Export are also unknown. Since importing/exporting anything but Custom Projects seems rather odd, and we don’t know what it means to import or export Custom Projects, they will have to go too.
That leaves us with:
- New Custom Project
- New Schema File
- New Stored Procedure File
- Refresh
- Properties
Much more managable. Also, we can expand what it means to be a schema and stored procedure in the Custom Navigator: the schema’s child nodes have New behavior as does the Stored Procedure node. So the list really looks like this:
- New Custom Project
- New Schema Table
- New Schema View
- New Schema Filter
- New Stored Procedure
- Refresh
- Properties
When nothing is selected the following are enabled:
- New Custom Project
- Refresh
We can’t go around randomly creating Tables, Views and Filters just because, now can we?
When a Custom Project, or any custom resource, is selected the following are enabled:
- New Custom Project
- New Schema Table
- New Schema View
- New Schema Filter
- New Stored Procedure
- Refresh
- Properties
As Richard Dreyfus once yelled into the open air: What does it mean?
What the above means is that the easiest way to control the popup menu for this custom navigator is to make one (or in this case two) rather than rely on the default popup and reconfigure it as we go. I tried desperately to avoid it, but in order to remove the default presentation of New, Import and Export it is just plain ol’ easier to make a new popup menu. Dems the breaks.
Tasks for this post and the next:
- Create a popup menu when the Custom navigator is empty
- Create a popup menu when a resource is selected in the Custom navigator
How (are we doing it?)
Time to back track. Remove the following:
- Remove all three commonWizard entries found under org.eclipse.ui.navigator.navigatorContent. That removes the menu entries under the popup menu New.
- Remove navigatorplugin –> plugin.xml –> org.eclipse.ui.menus
- Remove org.eclipse.ui.navigator.viewer –> customnavigator.navigator (viewerActionBinding) entry.
That was the easy stuff. The more involved steps for this post are:
- Define the popup menu and its insertionPoints
- Define the actionProvider
- Define the actionExtension
- Implement the action provider code (if not implementing the command framework)
Sounds pretty straightforward doesn’t it?
Let’s see how well we do.
- Define the popup menu and its insertionPoints
- Create a viewer entry in org.eclipse.ui.viewer
- org.eclipse.ui.navigator.viewer –> New –> viewer
- viewerId: customnavigator.navigator
- org.eclipse.ui.navigator.viewer –> New –> viewer
- Create a popupMenu entry under the viewer
- customnavigator.navigator (viewer) –> New –> popupMenu
- id: customnavigator.navigator#PopupMenu
- customnavigator.navigator (viewer) –> New –> popupMenu
- Add two insertion points under the popupMenu
- customnavigator.navigator#PopupMenu (popupMenu) –> New –> insertionPoint
- name: group.new
- customnavigator.navigator#PopupMenu (popupMenu) –> New –> insertionPoint
- name: group.build
- customnavigator.navigator#PopupMenu (popupMenu) –> New –> insertionPoint
- Create a viewer entry in org.eclipse.ui.viewer
- Define the actionProviders
- org.eclipse.ui.navigator.navigatorContent –> New –> actionProvider
- class: customnavigator.popup.actionprovider.CustomNewActionProvider
- id: customnavigator.popup.actionprovider.CustomNewAction
- customnavigator.popup.actionprovider.CustomNewActionProvider (actionProvider) –> enablement –> New –> or
- or –> New –> adapt
- type: org.eclipse.core.resources.IResource
- or –> New –> adapt
- type: java.util.Collection
- java.util.Collection –> New –> count
- value: 0
- org.eclipse.ui.navigator.navigatorContent –> New –> actionProvider
- class: customnavigator.popup.actionprovider.CustomRefreshActionProvider
- id: customnavigator.popup.actionprovider.CustomRefreshAction
- customnavigator.popup.actionprovider.CustomRefreshActionProvider (actionProvider) –> enablement –> New –> or
- or –> New –> adapt
- type: org.eclipse.core.resources.IResource
- or –> New –> adapt
- type: java.util.Collection
- java.util.Collection –> New –> count
- value: 0
- org.eclipse.ui.navigator.navigatorContent –> New –> actionProvider
- Define the actionExtensions
- org.eclipse.ui.navigator.viewer –> New –> viewerActionBinding
- viewerId: customnavigator.navigator
- customnavigator.navigator (viewerActionBinding) –> New –> includes
- includes –> New –> actionExtension
- pattern: customnavigator.popup.actionprovider.CustomNewAction
- org.eclipse.ui.navigator.viewer –> customnavigator.navigator (viewerActionBinding) –> (includes) –> New –> actionExtension and change pattern to:
- pattern: customnavigator.popup.actionprovider.CustomRefreshAction
- org.eclipse.ui.navigator.viewer –> New –> viewerActionBinding
- Implement the action provider code in the customnavigator plug-in
customnavigator.popup.actionprovider.CustomNewActionProvider/** * Coder beware: this code is not warranted to do anything. * Some or all of this code is taken from the Eclipse code base. * * Copyright Mar 28, 2010 Carlos Valcarcel */ package customnavigator.popup.actionprovider; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; import org.eclipse.ui.navigator.ICommonViewerWorkbenchSite; import org.eclipse.ui.navigator.WizardActionGroup; public class CustomNewActionProvider extends CommonActionProvider { private static final String NEW_MENU_NAME = "common.new.menu";//$NON-NLS-1$ private ActionFactory.IWorkbenchAction showDlgAction; private WizardActionGroup newWizardActionGroup; private boolean contribute = false; @Override public void init(ICommonActionExtensionSite anExtensionSite) { if (anExtensionSite.getViewSite() instanceof ICommonViewerWorkbenchSite) { IWorkbenchWindow window = ((ICommonViewerWorkbenchSite) anExtensionSite.getViewSite()).getWorkbenchWindow(); showDlgAction = ActionFactory.NEW.create(window); newWizardActionGroup = new WizardActionGroup(window, PlatformUI.getWorkbench().getNewWizardRegistry(), WizardActionGroup.TYPE_NEW, anExtensionSite.getContentService()); contribute = true; } } @Override public void fillContextMenu(IMenuManager menu) { IMenuManager submenu = new MenuManager( "New", 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); } @Override public void dispose() { if (showDlgAction!=null) { showDlgAction.dispose(); showDlgAction = null; } super.dispose(); } }
customnavigator.popup.actionprovider.CustomRefreshActionProvider
/** * Coder beware: this code is not warranted to do anything. * Some or all of this code is taken from the Eclipse code base. * * Copyright Apr 4, 2010 Carlos Valcarcel */ package customnavigator.popup.actionprovider; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.window.IShellProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.RefreshAction; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; import customnavigator.Activator; /** * The bulk of this code is taken from * org.eclipse.ui.internal.navigator.resources.actions.ResourceMgmtActionProvider * which is provided with Eclipse in case you want to look up the original. * * @author carlos */ public class CustomRefreshActionProvider extends CommonActionProvider { private RefreshAction refreshAction; private Shell shell; /* * (non-Javadoc) * @see * org.eclipse.ui.navigator.CommonActionProvider#init(org.eclipse.ui.navigator.ICommonActionExtensionSite) */ @Override public void init(ICommonActionExtensionSite aSite) { super.init(aSite); shell = aSite.getViewSite().getShell(); makeActions(); } @Override public void fillActionBars(IActionBars actionBars) { actionBars.setGlobalActionHandler(ActionFactory.REFRESH.getId(), refreshAction); updateActionBars(); } /** * Adds the refresh resource actions to the context menu. * * @param menu * context menu to add actions to */ @SuppressWarnings("rawtypes") @Override public void fillContextMenu(IMenuManager menu) { IStructuredSelection selection = (IStructuredSelection) getContext().getSelection(); boolean hasClosedProjects = false; Iterator resources = selection.iterator(); while (resources.hasNext() && (!hasClosedProjects)) { Object next = resources.next(); IProject project = null; if (next instanceof IProject) { project = (IProject) next; } else if (next instanceof IAdaptable) { project = (IProject) ((IAdaptable) next).getAdapter(IProject.class); } if (project == null) { continue; } if (!project.isOpen()) { hasClosedProjects = true; } } if (!hasClosedProjects) { refreshAction.selectionChanged(selection); menu.appendToGroup(ICommonMenuConstants.GROUP_BUILD, refreshAction); } } protected void makeActions() { IShellProvider sp = new IShellProvider() { @SuppressWarnings("synthetic-access") @Override public Shell getShell() { return shell; } }; refreshAction = new RefreshAction(sp) { @Override public void run() { final IStatus[] errorStatus = new IStatus[1]; errorStatus[0] = Status.OK_STATUS; final WorkspaceModifyOperation op = (WorkspaceModifyOperation) createOperation(errorStatus); WorkspaceJob job = new WorkspaceJob("refresh") { //$NON-NLS-1$ @SuppressWarnings("synthetic-access") @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { try { op.run(monitor); if (shell != null && !shell.isDisposed()) { shell.getDisplay().asyncExec(new Runnable() { @Override public void run() { StructuredViewer viewer = getActionSite().getStructuredViewer(); if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { viewer.refresh(); } } }); } } catch (InvocationTargetException e) { String msg = NLS.bind("Exception in {0}. run: {1}", getClass().getName(), e.getTargetException()); throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, msg, e .getTargetException())); } catch (InterruptedException e) { return Status.CANCEL_STATUS; } return errorStatus[0]; } }; ISchedulingRule rule = op.getRule(); if (rule != null) { job.setRule(rule); } job.setUser(true); job.schedule(); } }; refreshAction.setDisabledImageDescriptor(getImageDescriptor("icons/refresh_nav_disabled.gif"));//$NON-NLS-1$ refreshAction.setImageDescriptor(getImageDescriptor("icons/refresh_nav_enabled.gif"));//$NON-NLS-1$ refreshAction.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REFRESH); } /** * Returns the image descriptor with the given relative path. */ protected ImageDescriptor getImageDescriptor(String relativePath) { return Activator.getIDEImageDescriptor(relativePath); } @Override public void updateActionBars() { IStructuredSelection selection = (IStructuredSelection) getContext().getSelection(); refreshAction.selectionChanged(selection); } }
Add the following to customnavigator.Activator:
public class Activator extends AbstractUIPlugin { ... public static ImageDescriptor getIDEImageDescriptor(String imagePath) { return AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, imagePath); } }
Add the following icons to the customnavigator icons folder:
refresh_nav_enabled.gif:
Don’t forget:
- Fix the warnings in MANIFEST.MF and plugin.xml.
- Open the Externalize Strings Wizard and move the two strings to messages.properties in the folder with the action provider code.
Why (did we do it that way?)
To do today’s tasks it is necessary to clean the deck. That means removing all the wonderous things that took advantage of all the default GUI hooks and basically putting them back with new hooks. Think of it like spring cleaning…without spring or the cleaning.
Since we have already decided to have only the menu entries we really need we have to remove the commonWizard and org.eclipse.ui.menus entries for now. Don’t worry, we’ll put them back. It will be easier and cleaner to add them in sequence rather than removing some pieces, moving things around and hoping they work eventually.
- Remove all three commonWizard entries found under org.eclipse.ui.navigator.navigatorContent. That removes the menu entries under the popup menu New.
- Remove navigatorplugin –> plugin.xml –> org.eclipse.ui.menus
As you should already know from the last post, the menuContribution entry found under org.eclipse.ui.menus let’s you directly add new menu items to an existing popup by giving Eclipse the path to the menu being affected. We’ll use this again later. Some things are too good to give up for long.
- Remove org.eclipse.ui.navigator.viewer –> customnavigator.navigator (viewerActionBinding) entry.The value of actionExtension –> pattern refers to the ids of the actionProvider classes that execute the default behavior when a popup menu item is selected. For example, the value we just removed, org.eclipse.ui.navigator.resources.*, refers to the actionProvider ids found in the org.eclipse.ui.navigator.resources plug-in. Remember how the default popup menu displays New, Import, Export and Refresh? Well, if you open org.eclipse.ui.navigator.resources –> plugin.xml you will find an actionProvider entry for the following classes (there are others, but additional actionProvider do not concern me):
- org.eclipse.ui.internal.navigator.resources.actions.NewActionProvider
- org.eclipse.ui.internal.navigator.resources.actions.PortingActionProvider
- org.eclipse.ui.internal.navigator.resources.actions.ResourceMgmtActionProvider
The ids for the above classes are:
- org.eclipse.ui.navigator.resources.NewActions
- org.eclipse.ui.navigator.resources.PortingActions
- org.eclipse.ui.navigator.resources.ResourceMgmtActions
Notice that the above ids fit the pattern org.eclipse.ui.navigator.resources.*. Eclipse doesn’t care about the class name; it cares about the id. Yes, there are other actionProviders, but they have enablement criteria that keeps them from being displayed when nothing is selected.
- Define the new popup menu and the insertionPoints.Time to make the donuts.
Let’s create the popup and add one menu item. In order to do that we have to
- Create a viewer entry in org.eclipse.ui.viewer
- Create a popupMenu entry under the viewer
- Add an insertion point under the popupMenu
- Create a viewerActionBinding and actionExtension entry in org.eclipse.ui.navigator.viewer
Do the following in the customnavigator plugin.xml Extensions tab:
- Create a viewer entry in org.eclipse.ui.viewer
- org.eclipse.ui.navigator.viewer –> New –> viewer
- viewerId: customnavigator.navigator
- org.eclipse.ui.navigator.viewer –> New –> viewer
- Create a popupMenu entry under the viewer
- customnavigator.navigator (viewer) –> New –> popupMenu
- id: customnavigator.navigator#PopupMenu
- customnavigator.navigator (viewer) –> New –> popupMenu
- Add an insertion point under the popupMenu
- customnavigator.navigator#PopupMenu (popupMenu) –> New –> insertionPoint
- name: group.new
- customnavigator.navigator#PopupMenu (popupMenu) –> New –> insertionPoint
- Create a viewerActionBinding and actionExtension entry in org.eclipse.ui.navigator.viewer
- org.eclipse.ui.navigator.viewer –> New –> viewerActionBinding
- viewerId: customnavigator.navigator
- customnavigator.navigator (viewerActionBinding) –> New –> includes
- includes –> New –> actionExtension
- pattern: org.eclipse.ui.navigator.resources.NewActions
- org.eclipse.ui.navigator.viewer –> New –> viewerActionBinding
Quick note: The name group.new comes from ICommonMenuConstants found in org.eclipse.ui.navigator. Whenever possible I recommend adhering to existing naming conventions just to make things easier to find.
Just for yucks we are using an existing action provider: org.eclipse.ui.internal.navigator.resources.actions.NewActionProvider whose id is org.eclipse.ui.navigator.resources.NewActions. What is interesting about the NewActionProvider is that it creates a new menu insertion point in the popup which allows menu items to be added as submenus. What is bad about NewActionProvider is that it does it programmatically.
NewActionProvider.java
public class NewActionProvider extends CommonActionProvider { ... public void fillContextMenu(IMenuManager menu) { IMenuManager submenu = new MenuManager( WorkbenchNavigatorMessages.NewActionProvider_NewMenu_label, NEW_MENU_NAME); if(!contribute) { return; } ... // THIS IS A NEW INSERTION POINT! WHODA THUNK IT? menu.insertAfter(ICommonMenuConstants.GROUP_NEW, submenu); } ... }
That’s right, we cannot declare an insertion point for submenus in plugin.xml; the insertion point for a submenu has to be declared programmatically. Yes, code will have to be written, but we are going to
stealcopy most of it anyway. - Start the runtime workbench and check that the popup menu appears when there is nothing displayed in the navigator. Exit the runtime workbench when you are done. Let’s create our own version of this code.
- Implement a version of CustomNewActionProvider to create an insertion point for the New Wizards.
- org.eclipse.ui.navigator.navigatorContent –> New –> actionProvider
- class: customnavigator.popup.actionprovider.CustomNewActionProvider
- id: customnavigator.popup.actionprovider.CustomNewAction
- customnavigator.popup.actionprovider.CustomNewActionProvider (actionProvider) –> enablement –> New –> or
- or –> New –> adapt
- type: org.eclipse.core.resources.IResource
- or –> New –> adapt
- type: java.util.Collection
- java.util.Collection –> New –> count
- value: 0
- org.eclipse.ui.navigator.navigatorContent –> New –> actionProvider
Return to customnavigator.popup.actionprovider.CustomNewActionProvider (actionProvider). Click the class link. Create the class. Add the following code:
/** * Coder beware: this code is not warranted to do anything. * Some or all of this code is taken from the Eclipse code base. * * Copyright Mar 28, 2010 Carlos Valcarcel */ package customnavigator.popup.actionprovider; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; import org.eclipse.ui.navigator.ICommonViewerWorkbenchSite; import org.eclipse.ui.navigator.WizardActionGroup; public class CustomNewActionProvider extends CommonActionProvider { private static final String NEW_MENU_NAME = "common.new.menu";//$NON-NLS-1$ private ActionFactory.IWorkbenchAction showDlgAction; private WizardActionGroup newWizardActionGroup; private boolean contribute = false; @Override public void init(ICommonActionExtensionSite anExtensionSite) { if (anExtensionSite.getViewSite() instanceof ICommonViewerWorkbenchSite) { IWorkbenchWindow window = ((ICommonViewerWorkbenchSite) anExtensionSite.getViewSite()).getWorkbenchWindow(); showDlgAction = ActionFactory.NEW.create(window); newWizardActionGroup = new WizardActionGroup(window, PlatformUI.getWorkbench().getNewWizardRegistry(), WizardActionGroup.TYPE_NEW, anExtensionSite.getContentService()); contribute = true; } } @Override public void fillContextMenu(IMenuManager menu) { IMenuManager submenu = new MenuManager( "New", 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); } @Override public void dispose() { if (showDlgAction!=null) { showDlgAction.dispose(); showDlgAction = null; } super.dispose(); } }
Now associate (bind) the actionProvider to the popup through the viewerActionBinding:
- Go to org.eclipse.ui.navigator.viewer –> customnavigator.navigator (viewerActionBinding) –> (includes) –> org.eclipse.ui.navigator.resources.NewActions and change pattern to:
- pattern: customnavigator.popup.actionprovider.CustomNewAction
Notice the use of the id, not the class name.
Let’s put back one of the pieces we removed earlier:
- org.eclipse.ui.navigator.navigatorContent –> New –> commonWizard
- type: new
- wizardId: customplugin.wizard.new.custom
Let’s add the Refresh menu without the functionality…just to maintain purity of thought. Besides, I confuse easily. We’ll add the code in a few steps.First, add a new insertionPoint for the Refresh menu:
- customnavigator.navigator#PopupMenu (popupMenu) –> New –> insertionPoint
- name: group.build
- separator: true
The name group.build comes from ICommonMenuConstants found in org.eclipse.ui.navigator.
Let’s define the actionProvider for the Refresh menu:
- org.eclipse.ui.navigator.navigatorContent –> New –> actionProvider
- class: customnavigator.popup.actionprovider.CustomRefreshActionProvider
- id: customnavigator.popup.actionprovider.CustomRefreshAction
- customnavigator.popup.actionprovider.CustomRefreshActionProvider –> enablement –> New –> or
- or –> New –> adapt
- type: org.eclipse.core.resources.IResource
- or –> New –> adapt
- type: java.util.Collection
- java.util.Collection –> New –> count
- value: 0
Next, let’s steal copy, the existing code from ResourceMgmtActionProvider to create CustomRefreshActionProvider.
Return to customnavigator.popup.actionprovider.CustomRefreshActionProvider (actionProvider). Click the class link, create the class and insert this code:
/** * Coder beware: this code is not warranted to do anything. * Some or all of this code is taken from the Eclipse code base. * * Copyright Apr 4, 2010 Carlos Valcarcel */ package customnavigator.popup.actionprovider; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.window.IShellProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.RefreshAction; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; import customnavigator.Activator; /** * The bulk of this code is taken from * org.eclipse.ui.internal.navigator.resources.actions.ResourceMgmtActionProvider * which is provided with Eclipse in case you want to look up the original. * * @author carlos */ public class CustomRefreshActionProvider extends CommonActionProvider { private RefreshAction refreshAction; private Shell shell; /* * (non-Javadoc) * @see * org.eclipse.ui.navigator.CommonActionProvider#init(org.eclipse.ui.navigator.ICommonActionExtensionSite) */ @Override public void init(ICommonActionExtensionSite aSite) { super.init(aSite); shell = aSite.getViewSite().getShell(); makeActions(); } @Override public void fillActionBars(IActionBars actionBars) { actionBars.setGlobalActionHandler(ActionFactory.REFRESH.getId(), refreshAction); updateActionBars(); } /** * Adds the refresh resource actions to the context menu. * * @param menu * context menu to add actions to */ @SuppressWarnings("rawtypes") @Override public void fillContextMenu(IMenuManager menu) { IStructuredSelection selection = (IStructuredSelection) getContext().getSelection(); boolean hasClosedProjects = false; Iterator resources = selection.iterator(); while (resources.hasNext() && (!hasClosedProjects)) { Object next = resources.next(); IProject project = null; if (next instanceof IProject) { project = (IProject) next; } else if (next instanceof IAdaptable) { project = (IProject) ((IAdaptable) next).getAdapter(IProject.class); } if (project == null) { continue; } if (!project.isOpen()) { hasClosedProjects = true; } } if (!hasClosedProjects) { refreshAction.selectionChanged(selection); menu.appendToGroup(ICommonMenuConstants.GROUP_BUILD, refreshAction); } } protected void makeActions() { IShellProvider sp = new IShellProvider() { @SuppressWarnings("synthetic-access") @Override public Shell getShell() { return shell; } }; refreshAction = new RefreshAction(sp) { @Override public void run() { final IStatus[] errorStatus = new IStatus[1]; errorStatus[0] = Status.OK_STATUS; final WorkspaceModifyOperation op = (WorkspaceModifyOperation) createOperation(errorStatus); WorkspaceJob job = new WorkspaceJob("refresh") { //$NON-NLS-1$ @SuppressWarnings("synthetic-access") @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { try { op.run(monitor); if (shell != null && !shell.isDisposed()) { shell.getDisplay().asyncExec(new Runnable() { @Override public void run() { StructuredViewer viewer = getActionSite().getStructuredViewer(); if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { viewer.refresh(); } } }); } } catch (InvocationTargetException e) { String msg = NLS.bind("Exception in {0}. run: {1}", getClass().getName(), e.getTargetException()); throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, msg, e .getTargetException())); } catch (InterruptedException e) { return Status.CANCEL_STATUS; } return errorStatus[0]; } }; ISchedulingRule rule = op.getRule(); if (rule != null) { job.setRule(rule); } job.setUser(true); job.schedule(); } }; refreshAction.setDisabledImageDescriptor(getImageDescriptor("icons/refresh_nav_disabled.gif"));//$NON-NLS-1$ refreshAction.setImageDescriptor(getImageDescriptor("icons/refresh_nav_enabled.gif"));//$NON-NLS-1$ refreshAction.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REFRESH); } /** * Returns the image descriptor with the given relative path. */ protected ImageDescriptor getImageDescriptor(String relativePath) { return Activator.getIDEImageDescriptor(relativePath); } @Override public void updateActionBars() { IStructuredSelection selection = (IStructuredSelection) getContext().getSelection(); refreshAction.selectionChanged(selection); } }
I removed any code that did not contribute to the goal: make RefreshAction work. I changed comments as well.
Yes, there is a compile error in CustomRefreshActionProvider.getImageDescriptor(). To fix that we have to add the following method to customnavigator.Activator:
public class Activator extends AbstractUIPlugin { ... public static ImageDescriptor getIDEImageDescriptor(String imagePath) { return AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, imagePath); } }
Compile error fixed.
In order for the CustomRefreshActionProvider to be called we have to add an entry under org.eclipse.ui.navigator.viewer –> customnavigator.navigator (viewerActionBinding):
- org.eclipse.ui.navigator.viewer –> customnavigator.navigator (viewerActionBinding) –> (includes) –> New –> actionExtension and change pattern to:
- pattern: customnavigator.popup.actionprovider.CustomRefreshAction
Again, notice the use of the id, not the class name. Could I simple have one entry for all of these action providers by putting in a pattern of customnavigator.popup.actionprovider.*? Of course, but where’s the fun in that (in other words, use that kind of pattern once you understand why you are using it. Until then, create individual entries)?
For those of you wondering when we added support for F5: the key binding is added by RefreshAction.
method makeActions() is looking for an enabled and a disabled image for Refresh. Add the following images to your customnavigator icon folder:

What Just Happened?
So we took a few steps back and a few steps forward.
- We removed the plugin.xml entries that reconfigured the default popup.
- We created a new popup menu definition with insertion points.
- We declared and implemented two action providers: New and Refresh.
- We declared two action extensions that referred to the action providers.
- We declared a commonWizard entry to add the New Custom Project Wizard to the New popup menu.
Not bad for a post that I just couldn’t find the time for. For some reason it felt like a lot to do to create a new default menu. Adding the other items will be much simpler. I hope.
In other news: some of you may have noticed that in past posts I occasionally mentioned Deployment files as a feature. I am easily confused. The only things we are going to do are Custom Projects, Schemas and Stored Procedures. Any references to anything else are red herrings, blind alleys, and otherwise dead ends. Avoid them unless you are intent on finding a place to sleep.
References
Building a Common Navigator based viewer, Part III: Configuring Menus
Code
Activator.java
package customnavigator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; /** * The activator class controls the plug-in life cycle */ public class Activator extends AbstractUIPlugin { // The plug-in ID public static final String PLUGIN_ID = "customnavigator"; //$NON-NLS-1$ // The shared instance private static Activator plugin; /** * The constructor */ public Activator() { // empty for now } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ @Override public void start(BundleContext context) throws Exception { super.start(context); plugin = this; } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ @Override public void stop(BundleContext context) throws Exception { plugin = null; super.stop(context); } /** * Returns the shared instance * * @return the shared instance */ public static Activator getDefault() { return plugin; } public static Image getImage(String imagePath) { ImageDescriptor imageDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, imagePath); Image image = imageDescriptor.createImage(); return image; } public static ImageDescriptor getIDEImageDescriptor(String imagePath) { return AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, imagePath); } }
CustomNewActionProvider.java
/** * Coder beware: this code is not warranted to do anything. * Some or all of this code is taken from the Eclipse code base. * * Copyright Mar 28, 2010 Carlos Valcarcel */ package customnavigator.popup.actionprovider; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; import org.eclipse.ui.navigator.ICommonViewerWorkbenchSite; import org.eclipse.ui.navigator.WizardActionGroup; /** * Provides the new (artifact creation) menu options for a context menu. * * <p> * The added submenu has the following structure * </p> * * <ul> * <li>a set of context sensitive wizard shortcuts (as defined by * <b>org.eclipse.ui.navigator.commonWizard</b>), </li> * <li>another separator, </li> * <li>a generic "Other" new wizard shortcut action</li> * </ul> * * @since 3.2 * */ public class CustomNewActionProvider extends CommonActionProvider { private static final String NEW_MENU_NAME = "common.new.menu";//$NON-NLS-1$ private ActionFactory.IWorkbenchAction showDlgAction; private WizardActionGroup newWizardActionGroup; private boolean contribute = false; @Override public void init(ICommonActionExtensionSite anExtensionSite) { if (anExtensionSite.getViewSite() instanceof ICommonViewerWorkbenchSite) { IWorkbenchWindow window = ((ICommonViewerWorkbenchSite) anExtensionSite.getViewSite()).getWorkbenchWindow(); showDlgAction = ActionFactory.NEW.create(window); newWizardActionGroup = new WizardActionGroup(window, PlatformUI.getWorkbench().getNewWizardRegistry(), WizardActionGroup.TYPE_NEW, anExtensionSite.getContentService()); contribute = true; } } /** * Adds a submenu to the given menu with the name "group.new" see * {@link ICommonMenuConstants#GROUP_NEW}). The submenu contains the following structure: * * <ul> * <li>a set of context sensitive wizard shortcuts (as defined by * <b>org.eclipse.ui.navigator.commonWizard</b>), </li> * <li>another separator, </li> * <li>a generic "Other" new wizard shortcut action</li> * </ul> */ @Override 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); } /* (non-Javadoc) * @see org.eclipse.ui.actions.ActionGroup#dispose() */ @Override public void dispose() { if (showDlgAction!=null) { showDlgAction.dispose(); showDlgAction = null; } super.dispose(); } }
CustomRefreshActionProvider.java
/** * Coder beware: this code is not warranted to do anything. * Some or all of this code is taken from the Eclipse code base. * * Copyright Apr 4, 2010 Carlos Valcarcel */ package customnavigator.popup.actionprovider; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.window.IShellProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.actions.RefreshAction; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; import customnavigator.Activator; /** * The bulk of this code is taken from * org.eclipse.ui.internal.navigator.resources.actions.ResourceMgmtActionProvider * which is provided with Eclipse in case you want to look up the original. * * @author carlos */ public class CustomRefreshActionProvider extends CommonActionProvider { private RefreshAction refreshAction; private Shell shell; /* * (non-Javadoc) * @see * org.eclipse.ui.navigator.CommonActionProvider#init(org.eclipse.ui.navigator.ICommonActionExtensionSite) */ @Override public void init(ICommonActionExtensionSite aSite) { super.init(aSite); shell = aSite.getViewSite().getShell(); makeActions(); } @Override public void fillActionBars(IActionBars actionBars) { actionBars.setGlobalActionHandler(ActionFactory.REFRESH.getId(), refreshAction); updateActionBars(); } /** * Adds the refresh resource actions to the context menu. * * @param menu * context menu to add actions to */ @SuppressWarnings("rawtypes") @Override public void fillContextMenu(IMenuManager menu) { IStructuredSelection selection = (IStructuredSelection) getContext().getSelection(); boolean hasClosedProjects = false; Iterator resources = selection.iterator(); while (resources.hasNext() && (!hasClosedProjects)) { Object next = resources.next(); IProject project = null; if (next instanceof IProject) { project = (IProject) next; } else if (next instanceof IAdaptable) { project = (IProject) ((IAdaptable) next).getAdapter(IProject.class); } if (project == null) { continue; } if (!project.isOpen()) { hasClosedProjects = true; } } if (!hasClosedProjects) { refreshAction.selectionChanged(selection); menu.appendToGroup(ICommonMenuConstants.GROUP_BUILD, refreshAction); } } protected void makeActions() { IShellProvider sp = new IShellProvider() { @SuppressWarnings("synthetic-access") @Override public Shell getShell() { return shell; } }; refreshAction = new RefreshAction(sp) { @Override public void run() { final IStatus[] errorStatus = new IStatus[1]; errorStatus[0] = Status.OK_STATUS; final WorkspaceModifyOperation op = (WorkspaceModifyOperation) createOperation(errorStatus); WorkspaceJob job = new WorkspaceJob("refresh") { //$NON-NLS-1$ @SuppressWarnings("synthetic-access") @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { try { op.run(monitor); if (shell != null && !shell.isDisposed()) { shell.getDisplay().asyncExec(new Runnable() { @Override public void run() { StructuredViewer viewer = getActionSite().getStructuredViewer(); if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { viewer.refresh(); } } }); } } catch (InvocationTargetException e) { String msg = NLS.bind(Messages.CustomRefreshActionProvider_invocationTargetExceptionMessage, getClass().getName(), e.getTargetException()); throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, msg, e .getTargetException())); } catch (InterruptedException e) { return Status.CANCEL_STATUS; } return errorStatus[0]; } }; ISchedulingRule rule = op.getRule(); if (rule != null) { job.setRule(rule); } job.setUser(true); job.schedule(); } }; refreshAction.setDisabledImageDescriptor(getImageDescriptor("icons/refresh_nav_disabled.gif"));//$NON-NLS-1$ refreshAction.setImageDescriptor(getImageDescriptor("icons/refresh_nav_enabled.gif"));//$NON-NLS-1$ refreshAction.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REFRESH); } /** * Returns the image descriptor with the given relative path. */ protected ImageDescriptor getImageDescriptor(String relativePath) { return Activator.getIDEImageDescriptor(relativePath); } @Override public void updateActionBars() { IStructuredSelection selection = (IStructuredSelection) getContext().getSelection(); refreshAction.selectionChanged(selection); } }
Messages.java
package customnavigator.popup.actionprovider; import org.eclipse.osgi.util.NLS; public class Messages extends NLS { private static final String BUNDLE_NAME = "customnavigator.popup.actionprovider.messages"; //$NON-NLS-1$ public static String CustomNewActionProvider_popupNewLabel; public static String CustomRefreshActionProvider_invocationTargetExceptionMessage; static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); } private Messages() { } }
messages.properties
CustomNewActionProvider_popupNewLabel=New CustomRefreshActionProvider_invocationTargetExceptionMessage=Exception in {0}. run: {1}
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> <adapt type="org.eclipse.core.resources.IResource"> </adapt> <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> <adapt type="org.eclipse.core.resources.IResource"> </adapt> <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> </plugin>
MANIFEST.MF
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: customnavigator;singleton:=true Bundle-Version: 1.0.2.0 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
Writing an Eclipse Plug-in (Part 16): Custom Project: Customizing the Perspective Menus (Toolbar)
In this post we will add three custom toolbar buttons to the main toolbar of the Custom Perspective of the Eclipse workbench.
Before we do that, I would like to point out that one of the default buttons on the toolbar is the New button. When you click on the downward pointing arrow of the New button you will see something shocking (shocking I say!): the same entries as we added to File –> New appear in the New toolbar button. That’s because they are tied together; change one and you change the other. We could disable it, but why not leave well enough alone? While it is true we didn’t mean to turn on this behavior we can bask in the glory of a job accidentally well done.
Life is full of disappointments.
Today we will add three buttons to the toolbar and have them create either a new Custom Project, Schema file or Deployment file. Seems kinda redundant in light of the behavior of the New toolbar button, but I want single custom command buttons in any case.
We need 3 new images; one for each button. I am reusing the images from a Hidden Clause Custom Project, Schema file and Deployment file:
How (are we doing it?)
Let’s add the buttons to the toolbar together with the configuration needed to open the appropriate New Wizards.
- Open plugin.xml
- Add –> org.eclipse.ui.menus
- org.eclipse.ui.menus –> menuContribution
- locationURI: toolbar:org.eclipse.ui.main.toolbar
- toolbar:org.eclipse.ui.main.toolbar (menuContribution) –> toolbar
- id: customplugin.toolbar
- customplugin.toolbar (toolbar) –> command
- commandId: org.eclipse.ui.newWizard
- label: New Custom Project
- icon: icons/project-folder.png
- tooltip: New Custom Project
- style: push
- New Custom Project (command) –> parameter
- name: newWizardId
- value: customplugin.wizard.new.custom
- customplugin.toolbar (toolbar) –> command
- commandId: org.eclipse.ui.newWizard
- label: New Schema File
- icon: icons/schema-file_16x16.png
- tooltip: New Schema File
- style: push
- New Schema File (command)–> parameter
- name: newWizardId
- value: customplugin.wizard.file.schema
- customplugin.toolbar (toolbar) –> command
- commandId: org.eclipse.ui.newWizard
- label: New Deployment File
- icon: icons/deployment-file_16x16.png
- tooltip: New Deployment File
- style: push
- New Deployment File (command)–> parameter
- name: newWizardId
- value: customplugin.wizard.file.deployment
- Save plugin.xml
- Start the runtime workbench. Pressing any of the new buttons should open the appropriate New Wizard
- Hold the mouse over the buttons; the tooltips should display the string for which we configured each button
Oh oh! A bug! The toolbar buttons appear in all of the perspectives not just in the Custom Perspective. That behavior is just offensive; okay, maybe not offensive, but wrong in this case.
We will fix that with the following configuration.
- New Custom Project (command) –> visibleWhen
- false (visibleWhen) –> with
- variable: activeWorkbenchWindow.activePerspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- value: customplugin.perspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- variable: activeWorkbenchWindow.activePerspective
- false (visibleWhen) –> with
- New Schema File (command)–> visibleWhen
- false (visibleWhen) –> with
- variable: activeWorkbenchWindow.activePerspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- value: customplugin.perspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- variable: activeWorkbenchWindow.activePerspective
- false (visibleWhen) –> with
- New Deployment File (command)–> visibleWhen
- false (visibleWhen) –> with
- variable: activeWorkbenchWindow.activePerspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- value: customplugin.perspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- variable: activeWorkbenchWindow.activePerspective
- false (visibleWhen) –> with
So where did the variable name activeWorkbenchWindow.activePerspective come from? From Eclipse, of course! Oh, okay, take a look at the Command Core Expressions entry in the Eclipse wiki for more interesting variables you can use in your elements. We won’t be using any others…this time.
Why (did we do it that way?)
There is a lot of configuration going on here. There are only two obscure/interesting points.
Step #3:
- org.eclipse.ui.menus –> menuContribution
- locationURI: toolbar:org.eclipse.ui.main.toolbar
The locationURI field tells Eclipse where to place the three buttons: in the toolbar (hence the use of a scheme named toolbar) and which toolbar to use (the main toolbar which has an id of org.eclipse.ui.main.toolbar). Makes sense once you know it, but it would be helpful to have more examples. But that’s just me.
Step #1, 2 and 3 take care of hiding the toolbar buttons in all perspectives except the Custom Perspective.
- false (visibleWhen) –> with
- variable: activeWorkbenchWindow.activePerspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- value: customplugin.perspective
- activeWorkbenchWindow.activePerspective (with) –> equals
- variable: activeWorkbenchWindow.activePerspective
The visibleWhen element works with the usual choices of adapt, and, count, equals, etc. By selecting the with element you have to supply one of the variables listed in the Command Core Expressions listed in the Eclipse wiki. At runtime the variable activeWorkbenchWindow.activePerspective contains the id of the current perspective so including the equals element with the id of the Custom Perspective (customplugin.perspective) means that the only time the selected button will appear is when the Custom Perspective is open.
Don’t forget to open plugin.xml and externalize the new strings otherwise you will be stuck with a bunch of warnings that are not worth tolerating.
What Just Happened?
In today’s episode:
- we added 3 buttons to the main toolbar
- configured the toolbar buttons to open the appropriate wizards
- configured the toolbar buttons to only appear in the custom perspective
Once again, we have managed to add a significant amount of behavior and not had to write any code. It doesn’t get any better than that (well, maybe it does, but I’m not sure if I’m ready to brag about that kind of thing).
Thanks
David Carver and his blog entry Adding Wizards To Toolbars.
Code
Oh, yeah. None.
However, there are the entries in the plugin.xml file.
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.2"?> <plugin> <extension point="org.eclipse.ui.newWizards"> <category id="customplugin.category.wizards" name="%category.name"> </category> <wizard category="customplugin.category.wizards" class="customplugin.wizards.CustomProjectNewWizard" finalPerspective="customplugin.perspective" icon="icons/project-folder.png" id="customplugin.wizard.new.custom" name="%wizard.name"> </wizard> <wizard category="customplugin.category.wizards" class="customplugin.wizards.CustomProjectNewSchemaFile" descriptionImage="icons/schema-file_32x32.png" icon="icons/schema-file_16x16.png" id="customplugin.wizard.file.schema" name="%wizard.name.schema"> </wizard> <wizard category="customplugin.category.wizards" class="customplugin.wizards.CustomProjectNewDeploymentFile" descriptionImage="icons/deployment-file_32x32.png" icon="icons/deployment-file_16x16.png" id="customplugin.wizard.file.deployment" name="%wizard.name.deployment"> </wizard> </extension> <extension point="org.eclipse.ui.perspectives"> <perspective class="customplugin.perspectives.Perspective" icon="icons/perspective.png" id="customplugin.perspective" name="%perspective.name"> </perspective> </extension> <extension point="org.eclipse.ui.perspectiveExtensions"> <perspectiveExtension targetID="customplugin.perspective"> <view id="customnavigator.navigator" minimized="false" ratio=".25" relationship="left" relative="org.eclipse.ui.editorss"> </view> </perspectiveExtension> </extension> <extension id="customplugin.projectNature" point="org.eclipse.core.resources.natures"> <runtime> <run class="customplugin.natures.ProjectNature"> </run> </runtime> </extension> <extension point="org.eclipse.ui.ide.projectNatureImages"> <image icon="icons/project-folder.png" id="customplugin.natureImage" natureId="customplugin.projectNature"> </image> </extension> <extension id="customplugin.contenttype" point="org.eclipse.core.contenttype.contentTypes"> <content-type base-type="org.eclipse.core.runtime.xml" file-extensions="xml" id="customplugin.contenttype.schema" name="%content-type.name.schema" priority="normal"> <describer class="org.eclipse.core.runtime.content.XMLRootElementContentDescriber2"> <parameter name="element" value="hc-schema"> </parameter> </describer> </content-type> <content-type base-type="org.eclipse.core.runtime.xml" file-extensions="xml" id="customplugin.contenttype.deployment" name="%content-type.name.deployment" priority="normal"> <describer class="org.eclipse.core.runtime.content.XMLRootElementContentDescriber2"> <parameter name="element" value="hc-deployment"> </parameter> </describer> </content-type> </extension> <extension point="org.eclipse.ui.perspectiveExtensions"> <perspectiveExtension targetID="customplugin.perspective"> <newWizardShortcut id="customplugin.wizard.new.custom"> </newWizardShortcut> <newWizardShortcut id="customplugin.wizard.file.schema"> </newWizardShortcut> <newWizardShortcut id="customplugin.wizard.file.deployment"> </newWizardShortcut> </perspectiveExtension> </extension> <extension point="org.eclipse.ui.menus"> <menuContribution locationURI="toolbar:org.eclipse.ui.main.toolbar"> <toolbar id="customplugin.toolbar"> <command commandId="org.eclipse.ui.newWizard" icon="icons/project-folder.png" label="%customproject.label" style="push" tooltip="%customproject.tooltip"> <parameter name="newWizardId" value="customplugin.wizard.new.custom"> </parameter> <visibleWhen checkEnabled="false"> <with variable="activeWorkbenchWindow.activePerspective"> <equals value="customplugin.perspective"> </equals> </with> </visibleWhen> </command> <command commandId="org.eclipse.ui.newWizard" icon="icons/schema-file_16x16.png" label="%schema.label" style="push" tooltip="%schema.tooltip"> <parameter name="newWizardId" value="customplugin.wizard.file.schema"> </parameter> <visibleWhen checkEnabled="false"> <with variable="activeWorkbenchWindow.activePerspective"> <equals value="customplugin.perspective"> </equals> </with> </visibleWhen> </command> <command commandId="org.eclipse.ui.newWizard" icon="icons/deployment-file_16x16.png" label="%deployment.label" style="push" tooltip="%deployment.tooltip"> <parameter name="newWizardId" value="customplugin.wizard.file.deployment"> </parameter> <visibleWhen checkEnabled="false"> <with variable="activeWorkbenchWindow.activePerspective"> <equals value="customplugin.perspective"> </equals> </with> </visibleWhen> </command> </toolbar> </menuContribution> </extension> </plugin>
OSGI-INF/I10n/bundle.properties
#Properties file for customplugin Bundle-Name = Customplugin Plug-in category.name = Custom Wizards wizard.name = Custom Project perspective.name = Custom Plug-in Perspective content-type.name.schema = Hidden Clause Schema Definition content-type.name.deployment = Hidden Clause Deployment Definition wizard.name.schema = Schema File wizard.name.deployment = Deployment File customproject.label = New Custom Project customproject.tooltip = New Custom Project schema.label = New Schema File schema.tooltip = New Schema File deployment.label = New Deployment File deployment.tooltip = New Deployment File
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
« Jan | ||||||
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
Top Posts
- JGAP: A First/Simple Tutorial
- Writing an Eclipse Plug-in (Part 2): Creating a custom project in Eclipse - Adding to the New Project Wizard
- Writing an Eclipse Plug-in (Part 7): Creating a Custom Navigator
- Writing an Eclipse Plug-in (Part 1)- What I'm going to do
- Writing an Eclipse Plug-in (Part 11): Common Navigator: Displaying Custom Resources or Refresh Or Die or The Magic of navigatorContent
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
- 459,827 hits