Sharing ExplorerManagers Between TopComponents

The NetBeans ExplorerManager class is great—it lets you display the same set of nodes in multiple explorer views while synchronizing the selection. In other words, if you select a node in one explorer view, it will automatically also be selected in another explorer view.

An explorer view is placed on a TopComponent that implements ExplorerManager.Provider, with the ExplorerManager's root context being set to the root node. Then everything happens magically from there thanks to the ExplorerManager: each explorer view in the TopComponent will have synchronized selection management for the node hierarchy. But... what if the explorer views are in DIFFERENT TopComponents? Below is a perfect example. The explorer view on the left (BeanTreeView) displays nodes, while the explorer view on the right (PropertySheetView) presents a different view on the same nodes, which should therefore be synchronized with each other:

In other words, the explorer views are in different TopComponents. If, as normal, you instantiate the ExplorerManager in each of the TopComponents, you have TWO ExplorerManagers... which means you will have no selection management between the nodes in the different explorer views.

Here's the solution to this problem.

  1. Create a new Java class that defines an explorer manager:
    package org.my.demo;
    
    import org.openide.explorer.ExplorerManager;
    
    public class MyExplorerManager implements ExplorerManager.Provider {
    
        private ExplorerManager em;
    
        public ExplorerManager getExplorerManager() {
            if (em == null) {
                em = new ExplorerManager();
            }
            return em;
        }
        
    }

  2. Create a new folder (with any name you like) in the layer.xml file and then register the above class there:
    <folder name="ExplorerManagers">
        <file name="org-my-demo-MyExplorerManager.instance"/>
    </folder>

    At runtime, the ExplorerManager is available in the filesystem and any module in the application can make use of it.

  3. In each of your TopComponents, implement ExplorerManager.Provider, exactly as you would always do. However, instead of creating a new ExplorerManager, get the one above from the filesystem via the Lookup. For example, this is the PropertiesTopComponent you see above:
    private ExplorerManager em;
    
    private PropertiesTopComponent() {
    
        initComponents();
    
        setName(NbBundle.getMessage(PropertiesTopComponent.class, "CTL_PropertiesTopComponent"));
        setToolTipText(NbBundle.getMessage(PropertiesTopComponent.class, "HINT_PropertiesTopComponent"));
    
        em = Lookups.forPath("/ExplorerManagers").lookup(Provider.class).getExplorerManager();
        em.setRootContext(new AbstractNode(new WordChildren()));
    
    }

    Note the line in bold, of course, which looks in the specified filesystem folder for a class of the required type.

  4. Do the same for all the other TopComponents that need to have the same ExplorerManager handling the selection for their explorer views.

What's nice about this solution is that it leverages some of the central tenets of the NetBeans Platform: selection management, explorer views, nodes, lookup, filesystem... It's a perfectly NetBeans-centric approach to this problem. The most interesting part is the registration of the ExplorerManager in the layer.xml, because it is yet another example of the cool things you can do if your application has a filesystem. And all NetBeans Platform applications automatically have one of those for the whole system.

Comments:

Seems excessively elaborate. All you need is some public class in some public package:

public class CommonExplorers {
public static final ExplorerManager SHARED_MANAGER = new ExplorerManager();
}

which you can then reference from your EM.P impls. The layer registration is just overhead for this example.

Posted by Jesse Glick on November 24, 2008 at 11:50 PM PST #

But I'm trying to avoid static stuff.

Posted by Geertjan on November 25, 2008 at 10:48 PM PST #

A layer registration IS static stuff. There are just two reasons to put something in a layer, neither of which apply in this case:

1. You expect that the data might need to be customized and the customizations relative to the default value persisted in the userdir; or, similarly, that it might need to be branded.

2. You want inversion of control: the module reading the entry has no dependency on the module providing the entry, so they need a way to communicate (and active procedural registration is undesirable for performance reasons).

Posted by Jesse Glick on November 26, 2008 at 12:26 AM PST #

Shouldn't MyExplorerManager be named MyExplorerManagerProvider ?

Posted by Vincent Cantin on December 01, 2008 at 01:42 PM PST #

Same remark for "lookup(Provider.class)" -> "lookup(ExplorerManager.Provider.class)" which may be more clear for the reader.

Posted by guest on December 01, 2008 at 01:51 PM PST #

Good point, Vincent.

Posted by Geertjan on December 01, 2008 at 10:57 PM PST #

Thanks for the nice code's and nice blog on u.

Posted by Ankara Bilgisayar Satış Teknoloji Mağazası Elektronik Alışveriş on March 14, 2009 at 08:42 PM PDT #

thanks..

Posted by kelebek on April 25, 2009 at 07:07 PM PDT #

This is insightful, and helpful. Thanks!

Posted by emxsys on June 02, 2011 at 03:21 AM PDT #

Interesting...

Posted by Jorge on December 21, 2012 at 08:18 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
24
25
26
27
28
29
30
   
       
Today