49
votes

I have a JTextField in my Swing application that holds the file path of a file selected to be used. Currently I have a JFileChooser that is used to populate this value. However, I would like to add the ability for a user to drag-and-drop a file onto this JTextField and have it place the file path of that file into the JTextField instead of always having using the JFileChooser.

How can this be done?

5

5 Answers

45
votes

First you should look into Swing DragDrop support. After that there are few little tricks for different operating systems. Once you've got things going you'll be handling the drop() callback. In this callback you'll want to check the DataFlavor of the Transferable.

For Windows you can just check the DataFlavor.isFlavorJavaFileListType() and then get your data like this

List<File> dropppedFiles = (List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor)

For Linux (and probably Solaris) the DataFlavor is a little trickier. You'll need to make your own DataFlavor and the Transferable type will be different

nixFileDataFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
String data = (String)transferable.getTransferData(nixFileDataFlavor);
for(StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();)
{
    String token = st.nextToken().trim();
    if(token.startsWith("#") || token.isEmpty())
    {
         // comment line, by RFC 2483
         continue;
    }
    try
    {
         File file = new File(new URI(token))
         // store this somewhere
    }
    catch(...)
    {
       // do something good
       ....
    }
}
45
votes

In case you don't want to spend too much time researching this relatively complex subject, and you're on Java 7 or later, here's a quick example of how to accept dropped files with a JTextArea as a drop target:

JTextArea myPanel = new JTextArea();
myPanel.setDropTarget(new DropTarget() {
    public synchronized void drop(DropTargetDropEvent evt) {
        try {
            evt.acceptDrop(DnDConstants.ACTION_COPY);
            List<File> droppedFiles = (List<File>)
                evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
            for (File file : droppedFiles) {
                // process files
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
});
34
votes

There is an example program which contains a class which can be used for facilitating drag and drop for files and folders:

http://www.iharder.net/current/java/filedrop/

I tested this with both Windows 7 and Ubuntu 10.10, and it appears to work well in both environments.

To use it, you add something like this to your code:

  JPanel  myPanel = new JPanel();
  new  FileDrop( myPanel, new FileDrop.Listener()
  {   public void  filesDropped( java.io.File[] files )
      {   
          // handle file drop
          ...
      }   // end filesDropped
  }); // end FileDrop.Listener
22
votes

I know this is an old question but the current answers are all a bit outdated:

  • since JDK 1.6 the class 'TransferHandler' should be used with new (overwritten) methods
  • support for Linux became a lot better, no need for custom handling

This works on Linux (KDE5) and Windows 7:

final class FileDropHandler extends TransferHandler {
    @Override
    public boolean canImport(TransferHandler.TransferSupport support) {
        for (DataFlavor flavor : support.getDataFlavors()) {
            if (flavor.isFlavorJavaFileListType()) {
                return true;
            }
        }
        return false;
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean importData(TransferHandler.TransferSupport support) {
        if (!this.canImport(support))
            return false;

        List<File> files;
        try {
            files = (List<File>) support.getTransferable()
                    .getTransferData(DataFlavor.javaFileListFlavor);
        } catch (UnsupportedFlavorException | IOException ex) {
            // should never happen (or JDK is buggy)
            return false;
        }

        for (File file: files) {
            // do something...
        }
        return true;
    }
}

Use it on any component with

myComponent.setTransferHandler(new FileDropHandler());
-4
votes

This works for me. I am using it like this (scala):

def onDrop(files: List[java.io.File]): Unit = { ... }

    val lblDrop = new Label {
      peer.setTransferHandler(new FileDropHandler(onDrop))
      border = EtchedBorder
    }



class FileDropHandler(val onDrop: List[java.io.File] => Unit) extends javax.swing.TransferHandler {
      import javax.swing.JComponent
      import java.awt.datatransfer.{Transferable, DataFlavor}
        import java.net.URI
    import java.io.File

    val stdFileListFlavor = DataFlavor.javaFileListFlavor
    val nixFileListFlavor = new DataFlavor("text/uri-list;class=java.lang.String")

    override def canImport(comp: JComponent, flavors: Array[DataFlavor]): Boolean =
        flavors.exists(flavor =>
            (flavor == stdFileListFlavor) ||
            (flavor == nixFileListFlavor)
        )

    override def importData(comp: JComponent, t: Transferable): Boolean = {

        val flavors = t.getTransferDataFlavors()

        val files = if (flavors.exists(_ == stdFileListFlavor)) {
            val data = t.getTransferData(stdFileListFlavor)
            importStdFileList( data )
        } else if (flavors.exists(_ == nixFileListFlavor)) {
            val data = t.getTransferData(nixFileListFlavor)
            importNixFileList( data )
        } else List()

        onDrop( files )

        !files.isEmpty
    }

    private def importStdFileList(data: Any): List[File] = {
      data.asInstanceOf[List[File]] //XXX NOT TESTED
    }

    private def importNixFileList(data: Any): List[File] = {

        def clean(rawLine: String): Option[String] = {
            val line = rawLine.trim
            if   (line.length == 0 || line == "#") None
            else                                   Some(line)
        }

        def asURI(line: String): Option[URI] = {
            try   { Some(new URI(line)) }
            catch { case e:Exception => println(e); None }
        }

        def asFile(uri: URI): Option[File] = {
            try   { Some(new File(uri)) }
            catch { case e:Exception => println(e); None }
        }

        data.asInstanceOf[java.lang.String].split("\n")
     .toList flatMap clean flatMap asURI flatMap asFile
    }
}