11

I want to implement dragging and dropping of files from a directory such as someones hard drive but can't figure out how to do it. I've read the java api but it talks of color pickers and dragging and dropping between lists but how to drag files from a computers file system and drop into my application. I tried writing the transferhandler class and a mouse event for when the drag starts but nothing seems to work. Now I'm back to just having my JFileChooser set so drag has been enabled but how to drop?

Any info or point in the right direction greatly appreciated.

  import javax.swing.*;

  import java.awt.BorderLayout;
 import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;

 public class FileChooserDemo
 extends JPanel
 implements ActionListener 
  {

JLabel selectedFileLabel;
 JList selectedFilesList;
 JLabel returnCodeLabel;

 public FileChooserDemo() 
    {
 super();
 createContent();
 }


void initFrameContent() 
    {
        JPanel closePanel = new JPanel();
        add(closePanel, BorderLayout.SOUTH);
}


 private void createContent()
    {
 setLayout(new BorderLayout());

        JPanel NorthPanel = new JPanel();

     JMenuBar menuBar = new JMenuBar();
        JMenu menu = new JMenu("File");
        JMenuItem quit = new JMenuItem("Quit");

        menuBar.add(menu);
        menu.add(quit);

        NorthPanel.add(menu,BorderLayout.NORTH);


  JPanel buttonPanel = new JPanel(new GridLayout(7,1 ));
  JButton openButton = new JButton("Open...");
  openButton.setActionCommand("OPEN");
  openButton.addActionListener(this);
 buttonPanel.add(openButton);

 JButton saveButton = new JButton("Save...");
 saveButton.setActionCommand("SAVE");
 saveButton.addActionListener(this);
 buttonPanel.add(saveButton);




  JButton delete = new JButton("Delete");
  delete.addActionListener(this);
  delete.setActionCommand("DELETE");
  buttonPanel.add(delete);

 add(buttonPanel, BorderLayout.WEST);



 // create a panel to display the selected file(s) and the return code
 JPanel displayPanel = new JPanel(new BorderLayout()); 
 selectedFileLabel = new JLabel("-");

 selectedFileLabel.setBorder(BorderFactory.createTitledBorder
 ("Selected File/Directory   "));

 displayPanel.add(selectedFileLabel, BorderLayout.NORTH);

 selectedFilesList = new JList();
 JScrollPane sp = new JScrollPane(selectedFilesList);
 sp.setBorder(BorderFactory.createTitledBorder("Selected Files "));
 MouseListener listener = new MouseAdapter()
 {
   public void mousePressed(MouseEvent me)
   {
       JComponent comp = (JComponent) me.getSource();
       TransferHandler handler = comp.getTransferHandler();
       handler.exportAsDrag(comp, me, TransferHandler.MOVE);   
   }
 };
 selectedFilesList.addMouseListener(listener);

 displayPanel.add(sp);

 returnCodeLabel = new JLabel("");
 returnCodeLabel.setBorder(BorderFactory.createTitledBorder("Return Code"));
 displayPanel.add(returnCodeLabel, BorderLayout.SOUTH);

 add(displayPanel);
}


 public void actionPerformed(ActionEvent e)
    {
          int option = 0;
 File selectedFile = null;
 File[] selectedFiles = new File[0];

 if (e.getActionCommand().equals("CLOSE"))
      {
   System.exit(0);
 }
 else if (e.getActionCommand().equals("OPEN"))
        {
     JFileChooser chooser = new JFileChooser();
        chooser.setDragEnabled(true);
        chooser.setMultiSelectionEnabled(true);
     option = chooser.showOpenDialog(this);
     selectedFiles = chooser.getSelectedFiles();
   }
 else if (e.getActionCommand().equals("SAVE"))
        {
     JFileChooser chooser = new JFileChooser();
     option = chooser.showSaveDialog(this);
     selectedFiles = chooser.getSelectedFiles();
   }

 // display the selection and return code
 if (selectedFile != null)
   selectedFileLabel.setText(selectedFile.toString());
 else
   selectedFileLabel.setText("null");
 DefaultListModel listModel = new DefaultListModel();
 for (int i =0; i < selectedFiles.length; i++)
   listModel.addElement(selectedFiles[i]);

 selectedFilesList.setModel(listModel);
 returnCodeLabel.setText(Integer.toString(option));
 }

 public static void main(String[] args) 
    {
 SwingUtilities.invokeLater
 (new Runnable()
       {
    public void run()
         {
      FileChooserDemo app = new FileChooserDemo();
      app.initFrameContent();
      JFrame frame = new JFrame("LoquetUP");
      frame.getContentPane().add(app);
         frame.setDefaultCloseOperation(3);
         frame.setSize(600,400);
         frame.setResizable(false);
         frame.setLocationRelativeTo(null);

      //frame.pack();
      frame.setVisible(true);
    }
  });
 }

}
rogerthat
  • 1,805
  • 4
  • 20
  • 34
  • 1
    Why does this D'n'D code `import` *nothing* from the [`java.awt.dnd` package](http://docs.oracle.com/javase/7/docs/api/java/awt/dnd/package-frame.html)? – Andrew Thompson Nov 28 '12 at 03:29
  • because I removed my transferHandler and mouselistener code – rogerthat Nov 28 '12 at 03:31
  • Take a look at [this other stack overflow question][1]. I think it has some of what you're looking for. [1]: http://stackoverflow.com/questions/811248/how-can-i-use-drag-and-drop-in-swing-to-get-file-path – Thorn Nov 28 '12 at 03:35
  • @Thorn is there something in java 1.7 that makes this code different? – rogerthat Nov 28 '12 at 03:40

2 Answers2

26

This is my take on the idea. I've used the "traditional" drag and drop API in this example. It has some extra "paint" tweaks just to show off what you might be able to do.

enter image description hereenter image description here

This example doesn't scan folders dropped onto it, so any folder will only register as a single file, but I'm sure you can work it out

public class TestDragNDropFiles {

    public static void main(String[] args) {
        new TestDragNDropFiles();
    }

    public TestDragNDropFiles() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new DropPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class DropPane extends JPanel {

        private DropTarget dropTarget;
        private DropTargetHandler dropTargetHandler;
        private Point dragPoint;

        private boolean dragOver = false;
        private BufferedImage target;

        private JLabel message;

        public DropPane() {
            try {
                target = ImageIO.read(new File("target.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            setLayout(new GridBagLayout());
            message = new JLabel();
            message.setFont(message.getFont().deriveFont(Font.BOLD, 24));
            add(message);

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        protected DropTarget getMyDropTarget() {
            if (dropTarget == null) {
                dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, null);
            }
            return dropTarget;
        }

        protected DropTargetHandler getDropTargetHandler() {
            if (dropTargetHandler == null) {
                dropTargetHandler = new DropTargetHandler();
            }
            return dropTargetHandler;
        }

        @Override
        public void addNotify() {
            super.addNotify();
            try {
                getMyDropTarget().addDropTargetListener(getDropTargetHandler());
            } catch (TooManyListenersException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            getMyDropTarget().removeDropTargetListener(getDropTargetHandler());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (dragOver) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setColor(new Color(0, 255, 0, 64));
                g2d.fill(new Rectangle(getWidth(), getHeight()));
                if (dragPoint != null && target != null) {
                    int x = dragPoint.x - 12;
                    int y = dragPoint.y - 12;
                    g2d.drawImage(target, x, y, this);
                }
                g2d.dispose();
            }
        }

        protected void importFiles(final List files) {
            Runnable run = new Runnable() {
                @Override
                public void run() {
                    message.setText("You dropped " + files.size() + " files");
                }
            };
            SwingUtilities.invokeLater(run);
        }

        protected class DropTargetHandler implements DropTargetListener {

            protected void processDrag(DropTargetDragEvent dtde) {
                if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                    dtde.acceptDrag(DnDConstants.ACTION_COPY);
                } else {
                    dtde.rejectDrag();
                }
            }

            @Override
            public void dragEnter(DropTargetDragEvent dtde) {
                processDrag(dtde);
                SwingUtilities.invokeLater(new DragUpdate(true, dtde.getLocation()));
                repaint();
            }

            @Override
            public void dragOver(DropTargetDragEvent dtde) {
                processDrag(dtde);
                SwingUtilities.invokeLater(new DragUpdate(true, dtde.getLocation()));
                repaint();
            }

            @Override
            public void dropActionChanged(DropTargetDragEvent dtde) {
            }

            @Override
            public void dragExit(DropTargetEvent dte) {
                SwingUtilities.invokeLater(new DragUpdate(false, null));
                repaint();
            }

            @Override
            public void drop(DropTargetDropEvent dtde) {

                SwingUtilities.invokeLater(new DragUpdate(false, null));

                Transferable transferable = dtde.getTransferable();
                if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                    dtde.acceptDrop(dtde.getDropAction());
                    try {

                        List transferData = (List) transferable.getTransferData(DataFlavor.javaFileListFlavor);
                        if (transferData != null && transferData.size() > 0) {
                            importFiles(transferData);
                            dtde.dropComplete(true);
                        }

                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                } else {
                    dtde.rejectDrop();
                }
            }
        }

        public class DragUpdate implements Runnable {

            private boolean dragOver;
            private Point dragPoint;

            public DragUpdate(boolean dragOver, Point dragPoint) {
                this.dragOver = dragOver;
                this.dragPoint = dragPoint;
            }

            @Override
            public void run() {
                DropPane.this.dragOver = dragOver;
                DropPane.this.dragPoint = dragPoint;
                DropPane.this.repaint();
            }
        }

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 3
    Bookmarking this page just for this answer. Very nice, and thanks! 1+ – Hovercraft Full Of Eels Nov 28 '12 at 04:06
  • @healix it's a bit rushed, but the basic idea is sound – MadProgrammer Nov 28 '12 at 04:15
  • @HovercraftFullOfEels been wanting to mix this with `JXLayer` but haven't had the time :( – MadProgrammer Nov 28 '12 at 04:15
  • @MadProgrammer will this example run? I would like to see it work so I can trace what is going on. This is my first drag and drop GUI. Also do you know where I can get more knowledge on DnD besides from the API? – rogerthat Nov 28 '12 at 04:19
  • @healix The example will run, but unless you change the image, you won't get a "drop target" indicator...(I updated the example to allow for `null` images) – MadProgrammer Nov 28 '12 at 04:27
  • 1
    @healix Sorry, I don't normally include them as it takes up room that could be used for the example. What editor are you using? `ctrl+shift+I` will fix the imports in Netbeans – MadProgrammer Nov 28 '12 at 04:50
  • @MadProgrammer I'm using Eclipse. Should be Ok. Also any advice on where to learn more about drag and drop? – rogerthat Nov 28 '12 at 05:08
  • @healix You could take a look at [How to drag and drop with Java 2, Part 1](http://www.javaworld.com/jw-03-1999/jw-03-dragndrop.html), which introduces the basic/core API and [How to drag and drop with Java 2, Part 2](http://www.javaworld.com/jw-08-1999/jw-08-draganddrop.html), which looks like it's focused on the newer "transferable" API – MadProgrammer Nov 28 '12 at 05:19
  • @MadProgrammer So I've been going over your example and I have a question. What does this do? what is target.png? public DropPane() { try { //----> target = ImageIO.read(new File("target.png")); } catch (IOException ex) { ex.printStackTrace(); } Sorry in advance for pasting code. Would be easier than me trying to explain – rogerthat Nov 28 '12 at 06:55
  • @Healix Basically, it loads a little png file I paint at drag location when something is dragged over the panel. It's just an example of what you might be able to do to show the drop location – MadProgrammer Nov 28 '12 at 07:37
  • @MadProgrammer so I can make my own image file to show the drop location? Is there one by default or if you want one you have to load one in? So then whatever I put in there would show up when the drop area in hovered? Because since I don't have target.png I just see an icon of the item being dropped – rogerthat Nov 28 '12 at 08:30
  • @healix No, there isn't one by default. You can create what effect you want (I used an image to illustrate the point). `JTable` for example renders a fall off of the columns as you move the columns (and moves the remaining columns around it) for example – MadProgrammer Nov 28 '12 at 08:32
  • @MadProgrammer So "target.png" is being created in the program not loaded from your computer? I figured you had it in there for yourself but it makes more sense if its being created. Is that right? – rogerthat Nov 28 '12 at 08:37
  • @MadProgrammer That last comment was wrong. target.png is being converted. I've been staring at code too long. – rogerthat Nov 28 '12 at 08:50
  • @healix, Sorry, misunderstood, yes target.png is an image I'm loading from the file system, obviously you will need to supply your own. The code assumes a image size of 24x24, but it wouldn't be difficult to modify it to support variable size images – MadProgrammer Nov 28 '12 at 08:57
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/20227/discussion-between-healix-and-madprogrammer) – rogerthat Nov 28 '12 at 09:03
  • even if it is against the rules: I ll say it Thank you a lot – Mohammed Housseyn Taleb Dec 05 '16 at 19:38
  • Awesome Example! – Krismorte Aug 10 '17 at 20:00
  • @MadProgrammer Is there a reason for actually adding removing the listener in the `addNotify` / `removeNotify` ? – bric3 Aug 22 '23 at 16:25
  • 1
    @bric3 Primarily, this makes it a self contained unit of work - you could gave a "register" and "deregister" workflow you could call manually, be how often are you going to forget to do it? Also, because there are so many strong references been passed around, this is one more additional layer to ensure we don't setup a scenario where the component can't be garbage collected (or at least reduce the risk). – MadProgrammer Aug 22 '23 at 20:49
  • @MadProgrammer Interesting, thank you! Is there a more general documentation article or blog post about this. As I think this pattern may be useful for various kind of listeners. Before I only used `addNotify`/`removeNotify` as a mean to set up specific actions when the component is added to the tree. – bric3 Aug 23 '23 at 09:02
  • 1
    @bric3 No, not specifically, I find it useful for this kind of workflow as it's about the only place where you are notified when the component is added or remove from it's parent – MadProgrammer Aug 23 '23 at 11:55
8

You need to experiment with Drag & Drop and see exactly what flavors are available when you try to drag files. If you do this in your custom TransferHandler you'll be pleasantly surprised one Flavor is the DataFlavor.javaFileListFlavor, which indicates that the item can be used simply as a List. Try it and you'll see that it works!

Note on review of your posted code, I don't see any code for your attempt at using a TransferHandler, so it is hard to say what you could be doing wrong here.

Edit 1
You seem to be trying to use a MouseListener for your drag and drop, and I'm not familiar with this usage. Can you show a reference to a tutorial that tells you to do this?

Edit 2

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class FileDragDemo extends JPanel {
   private JList list = new JList();

   public FileDragDemo() {
      list.setDragEnabled(true);
      list.setTransferHandler(new FileListTransferHandler(list));

      add(new JScrollPane(list));
   }

   private static void createAndShowGui() {
      FileDragDemo mainPanel = new FileDragDemo();

      JFrame frame = new JFrame("FileDragDemo");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

@SuppressWarnings("serial")
class FileListTransferHandler extends TransferHandler {
   private JList list;

   public FileListTransferHandler(JList list) {
      this.list = list;
   }

   public int getSourceActions(JComponent c) {
      return COPY_OR_MOVE;
   }

   public boolean canImport(TransferSupport ts) {
      return ts.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
   }

   public boolean importData(TransferSupport ts) {
      try {
         @SuppressWarnings("rawtypes")
         List data = (List) ts.getTransferable().getTransferData(
               DataFlavor.javaFileListFlavor);
         if (data.size() < 1) {
            return false;
         }

         DefaultListModel listModel = new DefaultListModel();
         for (Object item : data) {
            File file = (File) item;
            listModel.addElement(file);
         }

         list.setModel(listModel);
         return true;

      } catch (UnsupportedFlavorException e) {
         return false;
      } catch (IOException e) {
         return false;
      }
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • @healix: please don't try to post code in a comment since it remains unformatted and thus impossible to read. Instead edit your original question with an addendum, and then comment to let me know that you've done this. – Hovercraft Full Of Eels Nov 28 '12 at 03:34
  • (Actually, unless it's change in Java 7, I always get `java.util.List` which contains `File` objects - I've not begin able to get generics to work for this ;)) – MadProgrammer Nov 28 '12 at 03:34
  • @MadProgrammer: you're probably correct. Let me edit my answer. – Hovercraft Full Of Eels Nov 28 '12 at 03:35
  • I edited the main code and put back in my transferHandler. Its right under the JList implementation – rogerthat Nov 28 '12 at 03:37
  • @Hovercraft I used a mouselistener becuase I figured the drag needs to know where to start – rogerthat Nov 28 '12 at 03:38
  • 1
    @healix: have you gone through any tutorial on this? You don't need or use a MouseListener for this type of behavior, and you certainly can't guess at how to do this. Please see my **Edit 2** for some sample code. – Hovercraft Full Of Eels Nov 28 '12 at 03:50
  • @HovercraftFullOfEels Yeah. I went through some tutorials but most were dragging from list to list. I couldn't really find many on drag and drop files from a directory into a java application. Thanks for the code. Is it really that simple? I'm coming from a c++ b.g and I haven't yet gotten over all the built in functionality of java – rogerthat Nov 28 '12 at 04:00
  • Damn. I feel like in java you can almost write "Make a GUI with buttons and drag and drop functionality" and compile – rogerthat Nov 28 '12 at 04:12