TreeTableView in J2SE Application

One thing (in fact, the first thing) the developers we visited in Budapest asked us for was "a tutorial on TreeTableView". Not surprising, is it. Using the approach described yesterday, (i.e., create a J2SE application instead of a NetBeans module), I adapted Jarda's file browser, so that you can see the full path as well as whether the folder is hidden or not, in a sortable TreeTableView:

I've found thus far that it is much easier to learn explorer views via J2SE projects than to do so via NetBeans modules. (More than likely this is also true for most other NetBeans APIs.) Firstly, because compilation and deployment is much faster. (I imagine that debugging will be a piece of cake too.) Secondly, because you're able to focus on a very specific API, instead of needing to first create a scenario in which the API makes sense. Also, this approach forces you to think very explicitly about which APIs you need, because you can't rely on the search functionality in the NetBeans module project's Project Properties dialog box. (Although, you could use another module project for this purpose, but you still need to explicitly add the required JAR to your J2SE project, which entails a more explicit thought process than simply working though a dialog.) As a result, you're more aware of the location of the classes you need. That can't be a bad thing, can it? So, here's my entire source structure:

Note: I used the Library Manager to create a library called 'ExplorerViews'. Then I added the five JARs you see above, from the NetBeans IDE distribution. Then I attached that library to my project's Libraries node.

And here is all my code:

package demo;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;

public final class FileNode extends AbstractNode {

    static String PROP_FULL_PATH = "space";
    static String IS_HIDDEN = "is hidden";
    private File file;

    private FileNode(File f) {
        super(new FileKids(f));
        file = f;
        setName(f.getName());
    }

    public static Node files() {
        AbstractNode n = new AbstractNode(new FileKids(null));
        n.setName("Root");
        return n;
    }

    public static class FileKids extends Children.Keys<File> {

        File file;

        public FileKids(File file) {
            this.file = file;
        }

        @Override
        protected void addNotify() {
            if (file == null) {
                File[] arr = File.listRoots();
                if (arr.length == 1) {
                    arr = arr[0].listFiles();
                }
                setKeys(arr);
            } else {
                File[] arr = file.listFiles();
                if (arr != null) {
                    setKeys(arr);
                }
            }
        }

        @Override
        public Node[] createNodes(File f) {
            FileNode n = new FileNode(f);
            return new Node[]{n};
        }

        @Override
        public Node[] getNodes(boolean arg0) {
            return super.getNodes(arg0);
        }
    }

    @Override
    protected Sheet createSheet() {
        Sheet s = super.createSheet();
        Sheet.Set ss = s.get(Sheet.PROPERTIES);
        if (ss == null) {
            ss = Sheet.createPropertiesSet();
            s.put(ss);
        }
        ss.put(new FullPathProperty(file));
        ss.put(new IsHiddenProperty(file));
        return s;
    }

    private class FullPathProperty extends PropertySupport.ReadOnly<String> {

        File file;

        public FullPathProperty(File file) {
            super(FileNode.PROP_FULL_PATH, String.class, "Full path", "Complete path is shown");
            this.file = file;
        }

        public String getValue() throws IllegalAccessException, InvocationTargetException {
            return file.getAbsolutePath();
        }
    }

    private class IsHiddenProperty extends PropertySupport.ReadOnly<String> {

        File file;

        public IsHiddenProperty(File file) {
            super(FileNode.IS_HIDDEN, String.class, "Is hidden", "Is hidden status is shown");
            this.file = file;
        }

        public String getValue() throws IllegalAccessException, InvocationTargetException {
            return file.isHidden();
        }
    }

}

By the way, before showing the TreeTableView code, it is worth pointing out that, as described yesterday, I can drag and drop the TreeTableView from the Palette, after adding the org.openide.explorer.ExplorerManager JAR to it. By doing so, I was also able to set some properties on the TreeTableView via the IDE's Properties sheet, instead of doing so via code. That was a pretty handy approach to working with TreeTableView.

package demo;

import org.openide.explorer.ExplorerManager;
import org.openide.explorer.view.NodeTableModel;
import org.openide.nodes.Node;

public class TreeTableView extends javax.swing.JFrame implements ExplorerManager.Provider {

    private ExplorerManager manager;
    private NodeTableModel nodeTableModel;
    private FileNode f;
    private Node.Property[] props;

    /\*\* Creates new form NewJFrame \*/
    public TreeTableView() {

        manager = new ExplorerManager();
        manager.setRootContext(FileNode.files());

        nodeTableModel = new NodeTableModel();
        nodeTableModel.setNodes(new Node[]{FileNode.files()});

        Node[] nodes = FileNode.files().getChildren().getNodes();

        props = nodes[0].getPropertySets()[0].getProperties();

        props[0].setValue("ComparableColumnTTV", Boolean.TRUE); //NOI18N
        props[0].setValue("SortingColumnTTV", Boolean.TRUE);

        //Second property column is sortable, but not initially sorted,
        //so initially will have no arrow icon:
        props[1].setValue("ComparableColumnTTV", Boolean.TRUE); //NOI18N

        initComponents();
        
    }

    /\*\* This method is called from within the constructor to
     \* initialize the form.
     \* WARNING: Do NOT modify this code. The content of this method is
     \* always regenerated by the Form Editor.
     \*/
    //                           
    private void initComponents() {

        treeTableView1 = new org.openide.explorer.view.TreeTableView(nodeTableModel);

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        treeTableView1.setBorder(javax.swing.BorderFactory.createTitledBorder("File System Browser"));
        treeTableView1.setRootVisible(false);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(treeTableView1, javax.swing.GroupLayout.DEFAULT_SIZE, 
               javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(treeTableView1, javax.swing.GroupLayout.DEFAULT_SIZE, 358, Short.MAX_VALUE)
        );

        treeTableView1.setProperties(props);

        pack();
    }//                         

    /\*\*
     \* @param args the command line arguments
     \*/
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new TreeTableView().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify                     
    private org.openide.explorer.view.TreeTableView treeTableView1;
    // End of variables declaration                   

    public ExplorerManager getExplorerManager() {
        return manager;
    }

}

Finally, if you want to learn about the properties that relate to TreeTableView, see the following:

Also, I found, after googling, that I have quite a lot of relatively useful information in this blog on this subject already. I learned quite a lot from searching in my blog and found myself being surprised at my own insights.

Comments:

Too bad not all Platform APIs are so JAR-friendly as to be usable in a standard j2se project.

Trust me, once you need to use some API that loves for no particular reason the layer filesystem for example, you're in a world of unneeded workarounds.

Heh, I liked the stuff about searching your own blog. I also use it as a part-time note-taking app because I know I'll need it later. So I just put it there and when I run into an issue again I'll just know it's on the blog (this probably decreases the quality of the blog posts, but oh well...)

Posted by Emilian Bold on October 06, 2007 at 07:26 AM PDT #

I had to change :

@Override
public Node[] createNodes(File f)

into:

@Override
public Node[] createNodes(Object f)

Note: I don't build with netbeans but with my own build.xml. (I tried the jars from netbeans 5.5 and netbeans 6.0 with the same result)

Kees

Posted by guest on October 06, 2007 at 06:22 PM PDT #

You need to change

public static class FileKids extends Children.Keys {

to

public static class FileKids extends Children.Keys<File> {

for NetBeans6 to get @Override working.

Posted by Sven Reimers on October 06, 2007 at 07:16 PM PDT #

Did you ever tried to change this into a table view and hide the TreeTableColumn?

Posted by Sven Reimers on October 06, 2007 at 07:21 PM PDT #

Sorry, Kees, I didn't add the HTML tags as indicated by Sven above, so you couldn't see the &lt;File&gt; part in the signature. I've fixed it in the code listing above. That will fix your problem. Sven interesting idea, haven't tried it, but will look at it.

Posted by Geertjan on October 06, 2007 at 07:29 PM PDT #

I want to try to replace the swingx treetable for the one in netbeans for my open source project 'jmeld'.

Is it possible to change the background color of a row? Swingx has RowHighLighters. I want to even and uneven rows to have a different color.

Is it possible to change the disabled text color? The color now is lightgray (on my system) and is hardly readable.

Posted by Kees Kuip on October 07, 2007 at 02:03 AM PDT #

Kees, see the next blog entry for some pointers to help you.

Posted by Geertjan on October 07, 2007 at 05:11 AM PDT #

Geertjan - Really love and appreciate your tutorials. However I think it would be great if you added something to the top of this pointing to OutlineView as a better, newer alternative to TTV.

Posted by John on August 31, 2010 at 06:09 AM PDT #

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
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today