Search code examples
javaeclipse-plugin

What are the conditions for Show In > System Explorer to be enabled?


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.

Show In Menu of 'Instance Explorer'

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>

Solution

  • 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:

    1. The current selection is an IResource or can be "adapted" to one (file, folder, project)

    2. 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

    3. 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.