badhazelnoot
badhazelnoot

Reputation: 59

Drag and Drop Java with Swing Worker

I want to create a DND action from a JList to the OS. My solution for now is to use a TransferHandler. In the method createTransferable I create the Transferable with the files I want to copy.
But now there is my Problem: in some cases I have to download the files from a FTP-Server before I can copy the files. The very heavy download operation runs in a JavaSwingWorker (hidden behind the statement d.download(tmpDir);). Now the system trys to copy files which are not downloaded already.

Now i need a mechanism that allows me to create the transferable after I have downloaded the files. Is there a solution for my problem? Please help me!

Thanks!

Here is my method:

public Transferable createTransferable(JComponent c) {
    JList list = (JList) c; // we know it's a JList
    List<PictureDecorator> selectedPictures = getSelectedValues(list.getModel());
    Vector cpFiles = new Vector();

    List<Picture> donePictures = new ArrayList<Picture>();
    List<Picture> notDonePictures = new ArrayList<Picture>();
    String tmpDir = System.getProperty("java.io.tmpdir");

    for(PictureDecorator pd : selectedPictures){
        if(pd.getPic().getStatus() == PictureStatus.DONE)
            donePictures.add(pd.getPic());
        else
            notDonePictures.add(pd.getPic());
    }

    Downloader d = new Downloader(parent, loginInformation, sced, donePictures, order);

    d.download(tmpDir);

    for(Picture p : donePictures){
        cpFiles.add(new File(tmpDir + File.separator + p.getPicture().getName()));
    }

    for(Picture p : notDonePictures) {
        cpFiles.add(p.getPicture());
    }

    TransferableFile tf = new TransferableFile(cpFiles);
    return tf;
}

I need something that initiates the drag procedure then I get the path where the drag goes and then I can download the pictures and copy it to the destination path.

EDIT: Or another formulation: How I can find out the drop destination when I drop into the operating system?

Upvotes: 3

Views: 370

Answers (1)

c.s.
c.s.

Reputation: 4816

To start the drag you need either a TransferHandler on the JList or alternatively a DragSource in combination with a DragGestureListener. Below you can see an example for doing that with a JTextField:

final JTextField textField = new JTextField(50);
DragGestureListener dragListener = new DragGestureListener() {
    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {
        // how the drag cursor should look like
        Cursor cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);

        // the component being dragged
        JTextField tf = (JTextField) dge.getComponent();

        // Here a Transferable is created directly for a single file name
        dge.startDrag(cursor, new TransferableFile(tf.getText()));
    }
};

final DragSource ds = new DragSource();
ds.createDefaultDragGestureRecognizer(textField, DnDConstants.ACTION_COPY, dragListener);

You can put the above code inside your window creation procedure.

Your resulting transferable (TransferableFile in your case) should support the DataFlavor.javaFileListFlavor and you should return a List of Files from the getTransferData(DataFlavor flavor) method.

I believe this is also the method where the downloading should take place because that's the last point under your control before JVM-OS take over.

Now regarding the SwingWorker problem you can wait inside the method until the download completes. Perhaps modify your Downloader class to expose a boolean flag so you would be able to do something like while (!downloader.isDone()) { Thread.sleep(millisToSleep) };

[Edit: I must admit I don't like the idea of keeping the EventDispath thread busy but if this solves your current problem perhaps you can investigate later a more elegant solution]

A little warning: Since you don't have access to the drop location you cannot know how many times the getTransferData will be called. It is better to take this into account and create a simple cache (a Map sounds reasonable) with the temp files you have downloaded so far. In case you find the file in the cache you return its corresponding temp file directly and don't download it again.

Hope that helps

Upvotes: 1

Related Questions