Vlad Skurtolov
Vlad Skurtolov

Reputation: 1054

Strange error with Drag and Drop in AWT in Java

Allright here is the error: java.awt.dnd.InvalidDnDOperationException: The operation requested cannot be performed by the DnD system since it is not in the appropriate state. The error appears when I drop some File inside the program (grabbed from the desktop). I am using Ubuntu 16.04 with Nautilus.

import javax.swing.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.*;
import java.io.File;
import java.util.List;

class UI extends JFrame {
List<File> droppedFiles;

UI(){
    super("My Program");
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    this.setLayout(null);
    this.setVisible(true);
    this.setResizable(true);
    this.setSize(800, 500);
    this.setExtendedState(MAXIMIZED_BOTH);
    JTextField dropArea = getDropArea();
    this.add(dropArea);
}

private JTextField getDropArea(){
    JTextField dropArea = new JTextField("Drop file here");
    dropArea.setBounds(50, 50, 200, 200);
    dropArea.setDropTarget(createNewDropTarget(dropArea));

    return dropArea;
}

private DropTarget createNewDropTarget(JTextField dropArea) {
    DropTarget dt = new DropTarget(){
        @Override
        public synchronized void drop(DropTargetDropEvent dtde) {
            super.drop(dtde);
            try {
                dtde.acceptDrop(DnDConstants.ACTION_COPY);
                droppedFiles = (List<File>) dtde.getTransferable().
                        getTransferData(DataFlavor.javaFileListFlavor);
                dropArea.setText(droppedFiles.get(0).getName());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    };
    return dt;
}
}

The Error appears on the line where droppedFiles is initialized. (in the try catch block).

Upvotes: 0

Views: 670

Answers (1)

tenorsax
tenorsax

Reputation: 21223

In a way you set up DropTarget there is no need to call super.drop(dtde);. This is actually the reason for the exception. Here is the implementation of DropTarget.drop():

public synchronized void drop(DropTargetDropEvent dtde) {
    clearAutoscroll();

    if (dtListener != null && active)
        dtListener.drop(dtde);
    else { // we should'nt get here ...
        dtde.rejectDrop();
    }
}  

Since you are not initializing DropTarget with a listener the drop is rejected, and the subsequent call getTransferable() fails with InvalidDnDOperationException. If you comment super.drop(dtde); the problem should go away. A cleaner alternative would be to create a listener and pass it to DropTarget. Here is a small example:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class DDTest extends JPanel {
    public DDTest() {
        setLayout(new BorderLayout());
        final JTextField dropArea = new JTextField("Drop file here");
        add(dropArea);
        new DropTarget(dropArea, new DropTargetAdapter() {
            @Override
            public void drop(DropTargetDropEvent dtde) {
                try {
                    dtde.acceptDrop(DnDConstants.ACTION_COPY);
                    List<File> droppedFiles = (List<File>) dtde
                            .getTransferable().getTransferData(
                                    DataFlavor.javaFileListFlavor);
                    dropArea.setText(droppedFiles.get(0).getName());
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }           
        });
    }

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

    private static void createAndShowGUI() {
        final JFrame frame = new JFrame("DDTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new DDTest());
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

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

PS:

Note that using absolute layout can be complex and usually can be avoided. See A Visual Guide to Layout Managers for some ideas.

Upvotes: 2

Related Questions