jtree drag and drop to panel as object

260 views Asked by At

Consider the following code:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;

class Element {
  String s;
  public Element(String s) {
    this.s = s;
  }
}

public class DnDTest {
  public static void main(String args[]) {
    JFrame f = new JFrame("Test");
    f.setSize(640, 400);
    Container c = f.getContentPane();
    c.setLayout(new BorderLayout());
    JTree t = new JTree();
    t.setDragEnabled(true);
    DefaultMutableTreeNode root = new DefaultMutableTreeNode(new Element("root"));
    root.add(new DefaultMutableTreeNode(new Element("child")));
    t.setModel(new DefaultTreeModel(root));
    c.add(t, BorderLayout.WEST);
    JPanel p = new JPanel();
    c.add(p, BorderLayout.CENTER);
    p.setTransferHandler(new TransferHandler("entity") {
      @Override
      public boolean canImport(TransferSupport support) {
        return true;
      }
      @Override
      public boolean importData(TransferHandler.TransferSupport support) {
        Transferable t = support.getTransferable();
        DataFlavor[] fv = t.getTransferDataFlavors();
        Object o;
        try {
          for (DataFlavor f: fv) {
            o = t.getTransferData(f);
            System.out.println(f + " => " + o.getClass().getName()+", " + o);
          }
        } catch (UnsupportedFlavorException | IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return false;
      }
    });
    f.setVisible(true);
  }
}

When I drag a node from the tree to the right panel, this prints the following output:

java.awt.datatransfer.DataFlavor[mimetype=text/html;representationclass=java.lang.String] => java.lang.String, <html>
<body>
<ul>
  <li>Element@4d405ef7
</ul>
</body>
</html>
java.awt.datatransfer.DataFlavor[mimetype=text/html;representationclass=java.io.Reader] => java.io.StringReader, java.io.StringReader@217d12b3
java.awt.datatransfer.DataFlavor[mimetype=text/html;representationclass=java.io.InputStream;charset=unicode] => java.io.ByteArrayInputStream, java.io.ByteArrayInputStream@5c9992b3
java.awt.datatransfer.DataFlavor[mimetype=text/plain;representationclass=java.lang.String] => java.lang.String, Element@4d405ef7
java.awt.datatransfer.DataFlavor[mimetype=text/plain;representationclass=java.io.Reader] => java.io.StringReader, java.io.StringReader@34715be
java.awt.datatransfer.DataFlavor[mimetype=text/plain;representationclass=java.io.InputStream;charset=unicode] => java.io.ByteArrayInputStream, java.io.ByteArrayInputStream@325e55cd
java.awt.datatransfer.DataFlavor[mimetype=application/x-java-jvm-local-objectref;representationclass=java.lang.String] => java.lang.String, Element@4d405ef7
java.awt.datatransfer.DataFlavor[mimetype=application/x-java-serialized-object;representationclass=java.lang.String] => java.lang.String, Element@4d405ef7

So, no matter which DataFlavor I use, the result is always a String. What do I need to change to actually get a reference to the Element instance associated as user object to the tree node?

1

There are 1 answers

0
Roberto Attias On

I did some more digging, and found the solution. Posting for reference:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.InputEvent;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

class Element {
  private String id;
  public Element(String id) {
    this.id = id;    
  }

  public String toString() {
    return "A";
  }

  public String getId() {
    return id;
  }
}

class CustomTransferHandler extends TransferHandler {
  @Override
  protected Transferable createTransferable(JComponent c) {
    JTree tree = (JTree)c;
    TreePath[] paths = tree.getSelectionPaths();
    Transferable t = null;
      DataFlavor DF = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, Element.class.getName()); 
      t = new Transferable() {

        @Override
        public DataFlavor[] getTransferDataFlavors() {
          return new DataFlavor[]{DF};
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
          return flavor.equals(DF);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
          if (!flavor.equals(DF)) {
            return null;
          }
          Element[] els = new Element[paths.length];
          int idx = 0;
          for (TreePath path : paths) {
            Object el = path.getLastPathComponent();
            els[idx++] = (Element)((DefaultMutableTreeNode)el).getUserObject();
          }
          return els;
        }          
      };
    return t;        
  }  

  @Override
  public int getSourceActions(JComponent c) {
    return COPY;
  }
}

public class DnDTest {
  public static void main(String args[]) {
    JFrame f = new JFrame("Test");
    f.setSize(640, 400);
    Container c = f.getContentPane();
    c.setLayout(new BorderLayout());
    JTree t = new JTree();
    t.setTransferHandler(new CustomTransferHandler());

    t.setDragEnabled(true);
    DefaultMutableTreeNode root = new DefaultMutableTreeNode(new Element("root"));
    root.add(new DefaultMutableTreeNode(new Element("child")));
    t.setModel(new DefaultTreeModel(root));
    c.add(t, BorderLayout.WEST);
    JPanel p = new JPanel();
    c.add(p, BorderLayout.CENTER);
    p.setTransferHandler(new TransferHandler("entity") {
      @Override
      public boolean canImport(TransferSupport support) {
        return true;
      }
      @Override
      public boolean importData(TransferHandler.TransferSupport support) {
        Transferable t = support.getTransferable();
        DataFlavor[] fv = t.getTransferDataFlavors();
        Element[] els;
        try {
          for (DataFlavor f: fv) {
            try {
              els = (Element[])t.getTransferData(f);
              for (Element el : els) {
                System.out.println(el.getId());
              }
            }catch(RuntimeException ex) {
              ex.printStackTrace();
            }
          }
        } catch (UnsupportedFlavorException | IOException e) {
          return false;
        }
        return true;
      }
    });
    f.setVisible(true);
  }
}