Tuesday Mar 19, 2013

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

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
« March 2013 »
SunMonTueWedThuFriSat
     
10
11
12
13
14
15
18
23
24
27
28
      
Today