views:

2448

answers:

3

OK, this problem is out of my league. I'm trying to implement a GUI widget in swing that allows files to be dropped onto a JTable, and allows the rows of the JTable to be dragged for re-sorting. Think VLC's playlists or the one in iTunes.

I got dropping files from the OS (Explorer, Finder, etc.) working just fine, but I'm having an impossible time with re-arranging the rows of the table, once the files are in. The problem is that when I add a custom TransferHandler to the table, dragging from the table is instantly killed. Here is some example code:

import javax.swing.*;

public class TableTest
{
    public static void main (String [] argv)
    {
     // setup table data
     String [] columns = new String [] {"Foo", "Bar", "Baz", "Quux"};
     String [][] data = new String [][] {{"A", "B", "C", "D"},
         {"1", "2", "3", "4"},
         {"i", "ii", "iii", "iv"}};
     // create table
     JTable table = new JTable(data, columns);

     // set up drag and drop
     table.setDragEnabled(true);
     table.setDropMode(DropMode.INSERT_ROWS);
     table.setFillsViewportHeight(true);
     TransferHandler dnd = new TransferHandler() {
      // here be code to handle drops, and one would
      // presume drag exporting, too
     };
     table.setTransferHandler(dnd);
     JScrollPane scroll = new JScrollPane(table);

     // create and show window
     JFrame window = new JFrame();
     window.getContentPane().add(scroll);
     window.pack();
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     window.setVisible(true);
    }
}

Run this code as-is and you'll see that you cannot initiate a drag on the table.If you comment out the call to setTransferHandler() on the table, dragging works (i.e., when I start dragging a table row, I get the X'd out circle cursor saying I can't drop there). But as soon as a TransferHandler is set for the table, I can't drag any rows. The problem has to be in the TransferHandler, but I've thoroughly troubleshot and debugged it, and have determined that dragging is never started once there is a TransferHandler on the table. What am I doing wrong?

+2  A: 

It doesn't look like you are using the TransferHandler properly. Try to read through the tutorial here.

See the TransferHandler doc here. The empty constructor doesn't look like it's meant for usage outside a subclass of TransferHandler.

And you don't implement any of the functionality provided in the standard TransferHandler provided on Swing components. See exerpt from the DnD tutorial here (my bold):

Note: If you install a custom TransferHandler onto a Swing component, the default support is replaced. For example, if you replace JTextField's TransferHandler with one that handles colors only, you will disable its ability to support import and export of text. If you must replace a default TransferHandler — for example, one that handles text — you will need to re-implement the text import and export ability. This does not need to be as extensive as what Swing provides — it could be as simple as supporting the StringFlavor data flavor, depending on your application's needs.

Erik A. Brandstadmoen
+1  A: 

I think the problem is that the empty TransferHandler actually prevents DnD events from occurring. There is a sample here which may be relevant.

http://www.java2s.com/Code/Java/Swing-JFC/ExtendedDnDDragandDropDemo.htm

Dave
A: 

I had the same problem, it's got nothing to do with your custom implementation of the TransferHandler. When you replace the TransferHandler you also need to get a hold of the default DragSource and tell it to recognize drag gesture. You may also need to implement your own Transferable because you'll need to pass it to the DragGestureEvent.startDrag() method.

    table.setTransferHandler(new MyTransferHandler());
    table.setDragEnabled(true);
    DragSource source = DragSource.getDefaultDragSource();
    source.createDefaultDragGestureRecognizer(table, DnDConstants.ACTION_COPY, new DragGestureListener() {

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {
            //grab the selected files from the table model
            ArrayList<File> files = new ArrayList<File>();
            for (int row : table.getSelectedRows()) {
                files.add((File) dm.getValueAt(row, 1));
            }

            //FileTransferable is a custom Transferable implementation
            Transferable transferable = new FileTransferable(files); 

            //and this is the magic right here
            dge.startDrag(null,transferable);
        }
    });
Gubatron