Drag Nodes Into Empty NetBeans ListViews

I've blogged quite a lot (especially, in the context below, here, back in 2009) about dragging and dropping Nodes into various places. One place I hadn't looked at yet is inspired by the question of the day, provided by Geoffrey Waardenburg in a comment in this blog today: how to drag a Node into an empty ListView. So, rather than dragging a Node onto another Node, the scenario is that you have an empty ListView, i.e., containing no Nodes at all, like this:

Now you want to drag one or more Nodes into that ListView and then, on the drop, display related data there.

The first thing that you need is to have a reference to the JList which is contained within the ListView. Once you have that, you can set it as a drop target.

  1. Create a Custom ListView. Here's the code of my ListView, which exists for no other reason than to expose the underlying and protected JList:
    import javax.swing.JList;
    import org.openide.explorer.view.ListView;
    
    public class MyListView extends ListView {
    
        public JList jList;
    
        @Override
        protected JList createList() {
            jList = super.createList();
            return jList;
        }
    
        public JList getjList() {
            return jList;
        }
        
    }
  2. Listen for Drops on the JList in the ChildFactory. An unorthodox thing to do, yet I challenge anyone to solve this in a different/better way:
    import java.awt.datatransfer.UnsupportedFlavorException;
    import java.awt.dnd.DnDConstants;
    import java.awt.dnd.DropTarget;
    import java.awt.dnd.DropTargetDragEvent;
    import java.awt.dnd.DropTargetDropEvent;
    import java.awt.dnd.DropTargetEvent;
    import java.awt.dnd.DropTargetListener;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import javax.swing.JList;
    import org.my.domain.Customer;
    import org.openide.awt.StatusDisplayer;
    import org.openide.nodes.AbstractNode;
    import org.openide.nodes.ChildFactory;
    import org.openide.nodes.Children;
    import org.openide.nodes.Node;
    
    class NameChildFactory extends ChildFactory<Customer> {
    
        private ArrayList<Customer> names = new ArrayList<Customer>();
        private final JList list;
    
        public NameChildFactory(JList list) {
            this.list = list;
            MyDropTargetListener dtl = new MyDropTargetListener();
            DropTarget dt = new DropTarget(list, dtl);
            dt.setDefaultActions(DnDConstants.ACTION_COPY_OR_MOVE);
            dt.setActive(true);
        }
    
        @Override
        protected boolean createKeys(List<Customer> list) {
            list.addAll(names);
            return true;
        }
    
        @Override
        protected Node createNodeForKey(final Customer name) {
            Node node = new AbstractNode(Children.LEAF);
            node.setDisplayName(name.getName());
            return node;
        }
    
        public class MyDropTargetListener implements DropTargetListener {
    
            @Override
            public void drop(DropTargetDropEvent dtde) {
                if (dtde.isDataFlavorSupported(Customer.DATA_FLAVOR)) {
                    try {
                        Object transData = dtde.getTransferable().
                                getTransferData(Customer.DATA_FLAVOR);
                        if (transData instanceof Customer) {
                            dtde.acceptDrop(DnDConstants.ACTION_COPY);
                            Customer c = (Customer) dtde.getTransferable().
                                getTransferData(Customer.DATA_FLAVOR);
                            StatusDisplayer.getDefault().setStatusText(c.getName());
                            names.add(c);
                            refresh(true);
                        }
                    } catch (UnsupportedFlavorException ufe) {
                        dtde.rejectDrop();
                        dtde.dropComplete(true);
                    } catch (IOException ioe) {
                        dtde.rejectDrop();
                        dtde.dropComplete(false);
                    }
                } else {
                    dtde.rejectDrop();
                    dtde.dropComplete(false);
                }
            }
    
            @Override
            public void dragEnter(DropTargetDragEvent dtde) {}
            @Override
            public void dragExit(DropTargetEvent dtde) {}
            @Override
            public void dragOver(DropTargetDragEvent dtde) {}
            @Override
            public void dropActionChanged(DropTargetDragEvent dtde) {}
    
        }
    
    }

And that's all you need to do. When a drop takes place on the JList, update the list held by the ChildFactory, and refresh the node hierarchy. (Go here for all the other code in this sample so that you're able to recreate it.)

Comments:

Won't it be practical to just handle this as a DnD action of a node onto an invisible root node of the ListView? Thus, handle this as if it is drops onto RootNode, with the data model hopefully causing the ChildFactory to refresh via listeners. Then, no JList exposure.

I'll try to write up what I mean a bit more clearly via code ...

Posted by Ernest on March 19, 2013 at 04:25 PM PDT #

Hi Ernest! I did try that approach, couldn't get it to work. Would indeed be great if you'd try it and report back here with the results.

Posted by Geertjan on March 20, 2013 at 12:17 AM PDT #

Hi Geertjan! Sure, no problem - this is more of a back-of-the-envelope approach - I'll actually try it and let you know how it goes.

Posted by Ernest on March 21, 2013 at 05:00 AM PDT #

Thanks for the tip, again, you saved me some head scratching. Now to enable drag reordering of the nodes in the listview.

Posted by Justin Smith on September 17, 2013 at 09:15 PM 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
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today