Wednesday, December 29, 2010

Building a Workspace Resources-powered Common Navigator Framework (CNF) View

Common Navigator Framework (CNF) View is a very useful and flexible component to display structure of any object in an Eclipse RCP Application.

There are two ways to use CNF :

  1. Displaying Eclipse Resources inside Workspace
  2. Displaying custom content (non-resources)
The good news is the two ways can be mixed.
If you use Resources you can extend it with "custom content" underneath (using Navigator Content Extensions aka NCEs).

The bad news is even on the current milestone of Eclipse Indigo 3.7 it's still not straightforward to use CNF with Resources.
Again the good news is that there is a solution. :-)

I'll focus on displaying workspace resources (folders and files inside the workspace physical directory) in CNF because it's not obvious how to do it inside Eclipse RCP.
Note that if you use the IDE Application (at least the IDE WorkbenchAdvisor) it just works by default. But in Eclipse RCP you have to do some preparatory work.

The steps are outlined in Eclipse Help > Common Navigator Framework > Content and Action Binding, as follows.

Please note, that using CNF inside of your own RCP requires three additional steps.

  1. Add the
org.eclipse.ui.ide as dependent plug-in
  • To get the resource workspace root (
  • IWorkspaceRoot) as default page input, override the WorkbenchAdvisor.getDefaultPageInput() method in your WorkbenchAdvisor:
    public IAdaptable getDefaultPageInput()         {                 return ResourcesPlugin.getWorkspace().getRoot();         }
  • Hook the workbench adapters correctly before they are used, so add this code to the
  • WorkbenchAdvisor.initialize(IWorkbenchConfigurer) method:
    public void initialize(IWorkbenchConfigurer configurer)         {                 IDE.registerAdapters();         }

    This will display workspace resources with labels but there are no icons yet.

    To display icons you still need to "steal" the internal declareWorkbenchImages() from org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java in org.eclipse.ide.ui.application plugin.

    So the complete relevant parts become like this:

    import org.eclipse.core.resources.IWorkspaceRoot;
    import org.eclipse.core.resources.ResourcesPlugin;
    import org.eclipse.core.runtime.FileLocator;
    import org.eclipse.core.runtime.IAdaptable;
    import org.eclipse.core.runtime.Path;
    import org.eclipse.core.runtime.Platform;
    import org.eclipse.jface.resource.ImageDescriptor;
    import org.eclipse.ui.application.IWorkbenchConfigurer;
    import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
    import org.eclipse.ui.application.WorkbenchAdvisor;
    import org.eclipse.ui.application.WorkbenchWindowAdvisor;
    import org.eclipse.ui.ide.IDE;
    import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;
    import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
    import org.osgi.framework.Bundle;
    ...
    @Override
    public void initialize(IWorkbenchConfigurer configurer) {
    super.initialize(configurer);
    //configurer.setSaveAndRestore(true); // if you want
    IDE.registerAdapters();
    declareWorkbenchImages();
    }
    ...
    /**
    * Declares all IDE-specific workbench images. This includes both "shared"
    * images (named in {@link IDE.SharedImages}) and internal images (named in
    * {@link org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages}).
    *
    * @see IWorkbenchConfigurer#declareImage
    */
    private void declareWorkbenchImages() {

    final String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$
    final String PATH_ELOCALTOOL = ICONS_PATH + "elcl16/"; // Enabled //$NON-NLS-1$

    // toolbar
    // icons.
    final String PATH_DLOCALTOOL = ICONS_PATH + "dlcl16/"; // Disabled //$NON-NLS-1$
    // //$NON-NLS-1$
    // toolbar
    // icons.
    final String PATH_ETOOL = ICONS_PATH + "etool16/"; // Enabled toolbar //$NON-NLS-1$
    // //$NON-NLS-1$
    // icons.
    final String PATH_DTOOL = ICONS_PATH + "dtool16/"; // Disabled toolbar //$NON-NLS-1$
    // //$NON-NLS-1$
    // icons.
    final String PATH_OBJECT = ICONS_PATH + "obj16/"; // Model object //$NON-NLS-1$
    // //$NON-NLS-1$
    // icons
    final String PATH_WIZBAN = ICONS_PATH + "wizban/"; // Wizard //$NON-NLS-1$
    // //$NON-NLS-1$
    // icons

    // View icons
    final String PATH_EVIEW= ICONS_PATH + "eview16/"; //$NON-NLS-1$


    Bundle ideBundle = Platform.getBundle(IDEWorkbenchPlugin.IDE_WORKBENCH);

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC, PATH_ETOOL
    + "build_exec.gif", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_HOVER,
    PATH_ETOOL + "build_exec.gif", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_DISABLED,
    PATH_DTOOL + "build_exec.gif", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC, PATH_ETOOL
    + "search_src.gif", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_HOVER,
    PATH_ETOOL + "search_src.gif", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_DISABLED,
    PATH_DTOOL + "search_src.gif", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_NEXT_NAV, PATH_ETOOL
    + "next_nav.gif", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_PREVIOUS_NAV, PATH_ETOOL
    + "prev_nav.gif", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_NEWPRJ_WIZ, PATH_WIZBAN
    + "newprj_wiz.png", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFOLDER_WIZ,
    PATH_WIZBAN + "newfolder_wiz.png", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFILE_WIZ, PATH_WIZBAN
    + "newfile_wiz.png", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTDIR_WIZ,
    PATH_WIZBAN + "importdir_wiz.png", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTZIP_WIZ,
    PATH_WIZBAN + "importzip_wiz.png", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTDIR_WIZ,
    PATH_WIZBAN + "exportdir_wiz.png", false); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTZIP_WIZ,
    PATH_WIZBAN + "exportzip_wiz.png", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ,
    PATH_WIZBAN + "workset_wiz.png", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_DLGBAN_SAVEAS_DLG, PATH_WIZBAN
    + "saveas_wiz.png", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_DLGBAN_QUICKFIX_DLG, PATH_WIZBAN
    + "quick_fix.png", false); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJ_PROJECT,
    PATH_OBJECT + "prj_obj.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDE.SharedImages.IMG_OBJ_PROJECT_CLOSED, PATH_OBJECT
    + "cprj_obj.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OPEN_MARKER,
    PATH_ELOCALTOOL + "gotoobj_tsk.gif", true); //$NON-NLS-1$


    // Quick fix icons
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ELCL_QUICK_FIX_ENABLED,
    PATH_ELOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_DLCL_QUICK_FIX_DISABLED,
    PATH_DLOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_WARNING,
    PATH_OBJECT + "quickfix_warning_obj.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_ERROR,
    PATH_OBJECT + "quickfix_error_obj.gif", true); //$NON-NLS-1$


    // task objects
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_HPRIO_TSK,
    // PATH_OBJECT+"hprio_tsk.gif");
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_MPRIO_TSK,
    // PATH_OBJECT+"mprio_tsk.gif");
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LPRIO_TSK,
    // PATH_OBJECT+"lprio_tsk.gif");

    declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_TASK_TSK,
    PATH_OBJECT + "taskmrk_tsk.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_BKMRK_TSK,
    PATH_OBJECT + "bkmrk_tsk.gif", true); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_COMPLETE_TSK, PATH_OBJECT
    + "complete_tsk.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_INCOMPLETE_TSK, PATH_OBJECT
    + "incomplete_tsk.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM, PATH_OBJECT
    + "welcome_item.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER, PATH_OBJECT
    + "welcome_banner.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_ERROR_PATH, PATH_OBJECT
    + "error_tsk.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_WARNING_PATH, PATH_OBJECT
    + "warn_tsk.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_OBJS_INFO_PATH, PATH_OBJECT
    + "info_tsk.gif", true); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_LCL_FLAT_LAYOUT, PATH_ELOCALTOOL
    + "flatLayout.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_LCL_HIERARCHICAL_LAYOUT,
    PATH_ELOCALTOOL + "hierarchicalLayout.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY,
    PATH_ETOOL + "problem_category.gif", true); //$NON-NLS-1$

    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW,
    PATH_EVIEW + "problems_view.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_ERROR,
    PATH_EVIEW + "problems_view_error.gif", true); //$NON-NLS-1$
    declareWorkbenchImage(ideBundle,
    IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_WARNING,
    PATH_EVIEW + "problems_view_warning.gif", true); //$NON-NLS-1$

    // synchronization indicator objects
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_WBET_STAT,
    // PATH_OVERLAY+"wbet_stat.gif");
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_SBET_STAT,
    // PATH_OVERLAY+"sbet_stat.gif");
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_CONFLICT_STAT,
    // PATH_OVERLAY+"conflict_stat.gif");

    // content locality indicator objects
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_NOTLOCAL_STAT,
    // PATH_STAT+"notlocal_stat.gif");
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LOCAL_STAT,
    // PATH_STAT+"local_stat.gif");
    // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_FILLLOCAL_STAT,
    // PATH_STAT+"filllocal_stat.gif");
    }

    /**
    * Declares an IDE-specific workbench image.
    *
    * @param symbolicName
    *            the symbolic name of the image
    * @param path
    *            the path of the image file; this path is relative to the base
    *            of the IDE plug-in
    * @param shared
    *            <code>true</code> if this is a shared image, and
    *            <code>false</code> if this is not a shared image
    * @see IWorkbenchConfigurer#declareImage
    */
    private void declareWorkbenchImage(Bundle ideBundle, String symbolicName,
    String path, boolean shared) {
    URL url = FileLocator.find(ideBundle, new Path(path), null);
    ImageDescriptor desc = ImageDescriptor.createFromURL(url);
    getWorkbenchConfigurer().declareImage(symbolicName, desc, shared);
    }

    Additional resources:

    6 comments:

    1. Nice article, thanks. We're considering using CNF to create a "navigator" view to which other teams can contribute root nodes. Our contribution will be a database-backed virtual file system. What I'm not sure of is whether to go with a completely custom model, or IResources with EFS, or some combination. Any suggestions?

      ReplyDelete
    2. @CraigFoote. You're welcome.

      With regard to CNF, it doesn't matter.

      I would strongly suggest using EMF though, and simply use the EMF-provided AdapterFactory-Content/Label-Providers for your CNF. You'll save lots of boilerplate code that way.

      See http://eclipsedriven.blogspot.com/2010/12/reusing-emfedit-generated-itemproviders.html for proof.

      With the UI side solved, all you have to do is connect your EMF models to database. You can do this the usual way, as you'd still be doing this anyway without EMF (i.e. using JPA or even your custom JDBC logic), but with EMF it's actually much easier than "plain POJO" and you can use EMF's metamodel reflection API if you want.

      If you want an easy way to link EMF to RDBMS, take a look at these projects:
      - CDO http://wiki.eclipse.org/CDO
      - Teneo http://wiki.eclipse.org/Teneo
      - Texo http://wiki.eclipse.org/Texo

      See also my CDO post: http://eclipsedriven.blogspot.com/2010/12/cdo-30-with-hibernate-experiences.html

      ReplyDelete
    3. While using User interfaceBuy RS Gold part fixed, all you have to perform will be hook up the EMF versions to repository. This can be achieved the typical way, since you'll be accomplishing this in any case without EMF (we.at the. using JPA as well as your current customized JDBC common sense), though EMF is in reality easier when compared with "plain POJO" and you can MapleStory Mesosutilize EMF's metamodel representation API if you need.

      ReplyDelete
    4. The Gnomes now cheap gw2 gold are tabernacle in Ironforge with the Dwarves. This member of WOW Alliance consists of four Gnome racial traits such as Escape Artist, Expansive mind, Arcane Resistance gw2 gold and Engineering Specialization.

      ReplyDelete
    5. The Personnel dread Cheap Diablo 3 Gold is incredibly underwhelming for any Forty-five second cooldown ability. Maybe own it spawn 2-3 jagged disasters as well with the dread as well as slightly improve the injury done on the mark GW2 Gems.

      ReplyDelete
    6. This can be achieved the typical way, since you'll be accomplishing this in any case without EMF (we.at the. using JPA as well as your current customized JDBC common sense),
      xiaomi mi 4i review
      buy Meizu Metal
      meizu mx5

      ReplyDelete