We are developing an eclipse plugin to assist managing product suites at customer sites.
This plugin contains multiple views between which we established a linking mechanism through the 'Show In' menu. Since a customer site may have multiple licenses in use, we have a LicenseDetail
view that implements IShowInTarget
, so we can utilize the 'Show In' menu to quickly identify the license for a software instance and obtain its information.
All of this works great except the resulting 'Show In' menu also contains an entry for 'System Explorer', which is disabled. For most occasions, this is fine, but the InstanceExplorer
view also contains items for the instance's home directories, for which this entry is still not enabled, although one might assume it should be available.
First I assumed that it is disabled, because the ShowInContext
provided by our InstanceExplorer
has proprietary interfaces in its selection that are not adaptable to IResource
. However, some debugging revealed that trying to execute 'Show In (License Detail)' on such a folder source does end up in LicenseDetail#show
returning false
, but the entry in the 'Show In' menu is still enabled. Looking at the underlying implementation confirms this.
// excerpt of 'org.eclipse.ui.internal.ShowInHandler#execute'
try {
IViewPart view = page.showView(targetId);
IShowInTarget target = getShowInTarget(view);
if (!(target != null && target.show(context)) {
// this is the only location where the return value of 'ShowInTarget#show' is used
page.getWorkbenchWindow().getShell().getDisplay().beep();
}
((WorkbenchPage) page).performedShowIn(targetId);
} catch (PartInitException e) {
throw new ExecutionException("Failed to show in", e);
}
Therefore I see no way to determine the enabled state of the menu entry through the contents of a ShowInContext
. So how does the menu entry for 'System Explorer' determine this and how can I take advantage of it?
Excerpts of the working view classes
Among others, the plugin contains a view that keeps track of the instances available on the system, similar to egit's 'Repostitory View'. This view implements IShowInSource
and adds the 'Show In' menu to its popup menu.
public class InstanceExplorer extends ViewPart implements IShowInSource {
@Override
public void createPartControl(Composite parent) {
mMainViewer = InstanceExplorerViewerFactory.createViewer(parent);
// omitted further viewer customization
MenuManager contextMenu = new MenuManager();
contextMenu.setRemoveAllWhenShown(true);
contextMenu.addMenuListener(this::fillContextMenu);
mMainViewer.getControl().setMenu(contextMenu.createContextMenu(mMainViewer.getControl());
getSite().registerContextMenu(contextMenu, mMainViewer);
}
private void fillContextMenu(IMenuManager menu) {
// omitted other context menu entries
MenuManager showInMenu = new MenuManager("Show In");
showInMenu.setActionDefinitionId("org.eclipse.ui.navigate.showInQuickMenu");
menu.add(showInMenu);
showInMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create(getSite().getWorkbenchWindow()));
}
@Override
public void setFocus() {
mMainViewer.getControl().setFocus();
}
@Override
public ShowInContext getShowInContext() {
return new ShowInContext(mMainViewer.getInput(), mMainViewer.getSelection());
}
private TreeViewer mMainViewer;
}
Furthermore it contains a view that shows information about a product license. Since a customer site may have multiple licenses in use, it implements IShowInTarget
, so we can utilize the 'Show In' menu to quickly identify the license for an instance and obtain its information.
public class LicenseDetail extends ViewPart implements IShowInTarget {
@Override
public void createPartControl(Composite parent) {
mMainViewer = LicenseDetailViewerFactory.createViewer(parent);
// omitted further viewer customization
}
@Override
public void setFocus() {
mMainViewer.getControl().setFocus();
}
@Override
public boolean show(ShowInContext context) {
// LicenseInfo is a proprietary interface, modelling a license of our product
LicenseInfo input = null;
if (context.getSelection() instanceof IStructuredSelection) {
input = findInput(((IStructuredSelection)context.getSelection()).getFirstElement());
}
if (null == input) {
input = findInput(context.getInput());
}
if (null != input) {
setInput(LicenseDetailViewerFactory.getInputs(input));
return true;
}
return false;
}
private void setInput(Object input) {
mMainViewer.setInput(input);
// omitted: determines enabled states for actions and controls based on the new input
validate();
}
private TreeViewer mMainViewer;
}
Finally, the LicenseDetail
view is declared as 'Show In' target in our perspective factory.
public class PerspectiveFactory implements IPerspectiveFactory {
@Override
public void createInitialLayout(IPageLayout layout) {
// omitted page layout and shortcuts and more
layout.addShowInPart("com.spell.moreen.suite.licenseDetail");
}
}
And for completion, the plugin.xml
file.
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<!-- omitted other extensions provided by the plugin -->
<extension point="org.eclipse.ui.perspectives">
<perspective
class="com.spell.moreen.suite.gui.PerspectiveFactory"
id="com.spell.moreen.suite.main"
name="moreen Suite" />
</extension>
<extension point="org.eclipse.ui.views">
<view
class="com.spell.moreen.suite.gui.part.InstanceExplorer"
id="com.spell.moreen.suite.instanceExplorer"
name="Instance Explorer"
restorable="true" />
<view
class="com.spell.moreen.suite.gui.part.LicenseDetail"
id="com.spell.moreen.suite.licenseDetail"
name="License Detail"
restorable="true" />
</extension>
</plugin>
Show In System Explorer is defined in the org.eclipse.ui.ide
plugin.xml and is controlled by this expression:
<extension
point="org.eclipse.core.expressions.definitions">
<definition
id="org.eclipse.ui.ide.showInDefinition">
<or>
<with variable="selection">
<count value="1" />
<iterate ifEmpty="false">
<adapt type="org.eclipse.core.resources.IResource" />
</iterate>
</with>
<with
variable="activePart">
<test
property="org.eclipse.ui.ide.editor.input">
</test>
</with>
<with
variable="activeShell">
<test
property="org.eclipse.ui.ide.page.activePreferencePage"
value="org.eclipse.ui.internal.ide.dialogs.ResourceInfoPage">
</test>
</with>
</or>
</definition>
</extension>
This allows three possibiliities:
The current selection is an IResource
or can be "adapted" to one (file, folder, project)
The org.eclipse.ui.ide.editor.input
test on the active part returns true. This tester returns true for editors with an IFileEditorInput
or other inputs which can be "adapted" to IResource
The Resource Info property page is active (used by the button on that page)
The actual show in code is org.eclipse.ui.internal.ide.handlers.ShowInSystemExplorerHandler
. Looking at the source it will only work if it can find an IResource
.