Java 在JTable中使用鼠标移动行

Java 在JTable中使用鼠标移动行,java,swing,jtable,Java,Swing,Jtable,我有几行的表格。我希望当用鼠标移动一行(拖放)时,该行将与鼠标一起移动,当鼠标移动时,表行将被重新排序(如移动JTable的列)。 这正是我想要的,但我必须在JavaSwing(或下面的gif图像)中完成。示例来自,但我无法理解它 我的转帐处理人: public class TableRowTransferHandler extends TransferHandler { private final DataFlavor localObjectFlavor = new Activat

我有几行的表格。我希望当用鼠标移动一行(拖放)时,该行将与鼠标一起移动,当鼠标移动时,表行将被重新排序(如移动JTable的列)。 这正是我想要的,但我必须在JavaSwing(或下面的gif图像)中完成。示例来自,但我无法理解它

我的转帐处理人:

public class TableRowTransferHandler extends TransferHandler {

    private final DataFlavor localObjectFlavor = new ActivationDataFlavor(Integer.class, "application/x-java-Integer;class=java.lang.Integer", "Integer Row Index");
    private JTable table = null;

    private static final Logger logger = Logger.getLogger(TableRowTransferHandler.class.getName());

    public TableRowTransferHandler(JTable table) {
        this.table = table;
    }

    @Override
    protected Transferable createTransferable(JComponent c) {
        assert (c == table);
        return new DataHandler(table.getSelectedRow(), localObjectFlavor.getMimeType());
    }

    @Override
    public boolean canImport(TransferHandler.TransferSupport info) {
        boolean b = info.getComponent() == table && info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
        table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
        return b;
    }

    @Override
    public int getSourceActions(JComponent c) {
        return TransferHandler.COPY_OR_MOVE;
    }

    @Override
    public boolean importData(TransferHandler.TransferSupport info) {
        JTable target = (JTable) info.getComponent();
        JTable.DropLocation dl = (JTable.DropLocation) info.getDropLocation();
        int index = dl.getRow();
        int max = table.getModel().getRowCount();
        if (index < 0 || index > max) {
            index = max;
        }
        target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        try {
            Integer rowFrom = (Integer) info.getTransferable().getTransferData(localObjectFlavor);
            if (rowFrom != -1 && rowFrom != index) {
                ((Reorderable) table.getModel()).reorder(rowFrom, index);
                if (index > rowFrom) {
                    index--;
                }
                target.getSelectionModel().addSelectionInterval(index, index);
                return true;
            }
        } catch (UnsupportedFlavorException | IOException e) {
            e.printStackTrace();
            logger.log(Level.SEVERE, null, e);
        }
        return false;
    }

    @Override
    protected void exportDone(JComponent c, Transferable t, int act) {
        if ((act == TransferHandler.MOVE) || (act == TransferHandler.NONE)) {
            table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }
}
当您用鼠标在行之间移动行时,只有一条粗体线(如下图中红色椭圆所示)。如果无法使行像这样移动,我希望这条粗体线显示在整行中(而不仅仅是在一个单元格中)


阅读Swing教程中有关拖放的部分,了解基本知识

下面是我在web上找到的一些旧代码,其中有一个自定义的
TransferHandler
来支持JTable:

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.IOException;
public class DnD_Demo extends JFrame {
    public DnD_Demo() {
        setTitle("DnD Demo (Version 3)");
        JTextArea tips = new JTextArea("1. Select a row in Table A. " +
                "Press the row again and drag. \n     " +
                "As you drag the cursor icon over Table B, the row that is currently under the cursor highlights " +
                "- the new data will be inserted after the selected row. \n     " +
                "Drop the row onto Table B. Note that the row has been removed from Table A, " +
                "and now appears in Table B. \n" +
                "2. Select two rows from Table A and drop onto Table B. " +
                "Now there are two new rows in Table B. ");
        tips.setEditable(false);
        tips.setBackground(new Color(255,255,204));
        tips.setBorder(new LineBorder(Color.orange,5));
        getContentPane().add(tips,BorderLayout.NORTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridLayout(2,1));
        panel.add(createTable("Table A"));
        panel.add(createTable("Table B"));
        getContentPane().add(panel,BorderLayout.CENTER);
        pack();
    }
    private JPanel createTable(String tableId) {
        DefaultTableModel model = new DefaultTableModel();
        model.addColumn("Column 0");
        model.addColumn("Column 1");
        model.addColumn("Column 2");
        model.addColumn("Column 3");
        model.addRow(new String[]{tableId+" 00", tableId+" 01", tableId+" 02", tableId+" 03"});
        model.addRow(new String[]{tableId+" 10", tableId+" 11", tableId+" 12", tableId+" 13"});
        model.addRow(new String[]{tableId+" 20", tableId+" 21", tableId+" 22", tableId+" 23"});
        model.addRow(new String[]{tableId+" 30", tableId+" 31", tableId+" 32", tableId+" 33"});
        JTable table = new JTable(model);
        table.getTableHeader().setReorderingAllowed(false);
        table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        JScrollPane scrollPane = new JScrollPane(table);
        scrollPane.setPreferredSize(new Dimension(400,100));
        table.setDragEnabled(true);
        table.setTransferHandler(new TableTransferHandler());
        JPanel panel = new JPanel();
        panel.add(scrollPane);
        panel.setBorder(BorderFactory.createTitledBorder(tableId));
        return panel;
    }
    public static void main(String[] args) {
        new DnD_Demo().setVisible(true);
    }

    abstract class StringTransferHandler extends TransferHandler {
        protected abstract String exportString(JComponent c);
        protected abstract void importString(JComponent c, String str);
        protected abstract void cleanup(JComponent c, boolean remove);
        protected Transferable createTransferable(JComponent c) {
            return new StringSelection(exportString(c));
        }
        public int getSourceActions(JComponent c) {
            return COPY_OR_MOVE;
        }
        public boolean importData(JComponent c, Transferable t) {
            if (canImport(c, t.getTransferDataFlavors())) {
                try {
                    String str = (String)t.getTransferData(DataFlavor.stringFlavor);
                    importString(c, str);
                    return true;
                } catch (UnsupportedFlavorException ufe) {
                } catch (IOException ioe) {
                }
            }
            return false;
        }
        protected void exportDone(JComponent c, Transferable data, int action) {
            cleanup(c, action == MOVE);
        }
        public boolean canImport(JComponent c, DataFlavor[] flavors) {
            for (int i = 0; i < flavors.length; i++) {
                if (DataFlavor.stringFlavor.equals(flavors[i])) {
                    return true;
                }
            }
            return false;
        }
    }
    class TableTransferHandler extends StringTransferHandler {
        public JTable target;
        public int[] rows = null;
        public int addIndex = -1; //Location where items were added
        public int addCount = 0;  //Number of items added.
        protected String exportString(JComponent c) {
            JTable table = (JTable)c;
            rows = table.getSelectedRows();
            int colCount = table.getColumnCount();
            StringBuffer buff = new StringBuffer();
            for (int i = 0; i < rows.length; i++) {
                for (int j = 0; j < colCount; j++) {
                    Object val = table.getValueAt(rows[i], j);
                    buff.append(val == null ? "" : val.toString());
                    if (j != colCount - 1) {
                        buff.append(",");
                    }
                }
                if (i != rows.length - 1) {
                    buff.append("\n");
                }
            }
            return buff.toString();
        }
        protected void importString(JComponent c, String str) {
            target = (JTable)c;
            DefaultTableModel model = (DefaultTableModel)target.getModel();
            int index = target.getSelectedRow();
            //Prevent the user from dropping data back on itself.
            //For example, if the user is moving rows #4,#5,#6 and #7 and
            //attempts to insert the rows after row #5, this would
            //be problematic when removing the original rows.
            //So this is not allowed.
            if (rows != null && index >= rows[0] - 1 &&
                    index <= rows[rows.length - 1]) {
                rows = null;
                return;
            }
            int max = model.getRowCount();
            if (index < 0) {
                index = max;
            } else {
                index++;
                if (index > max) {
                    index = max;
                }
            }
            addIndex = index;
            String[] values = str.split("\n");
            addCount = values.length;
            int colCount = target.getColumnCount();
            for (int i = 0; i < values.length ; i++) {
                model.insertRow(index++, values[i].split(","));
            }
            //If we are moving items around in the same table, we
            //need to adjust the rows accordingly, since those
            //after the insertion point have moved.
            if (rows!= null && addCount > 0) {
                for (int i = 0; i < rows.length; i++) {
                    if (rows[i] > addIndex) {
                        rows[i] += addCount;
                    }
                }
            }
        }
        protected void cleanup(JComponent c, boolean remove) {
            JTable source = (JTable)c;
            if (remove && rows != null) {
                DefaultTableModel model =
                        (DefaultTableModel)source.getModel();
                for (int i = rows.length - 1; i >= 0; i--) {
                    model.removeRow(rows[i]);
                }
            }
            rows = null;
            addCount = 0;
            addIndex = -1;
        }
    }
}
import javax.swing.*;
导入javax.swing.border.*;
导入javax.swing.table.*;
导入java.awt.*;
导入java.awt.datatransfer.*;
导入java.awt.event.*;
导入java.io.IOException;
公共类DnD_演示扩展JFrame{
公共DnD_演示(){
setTitle(“DnD演示(第3版)”;
JTextArea tips=newjtextarea(“1.在表a中选择一行。”+
“再次按该行并拖动。\n”+
将光标图标拖到表B上时,光标下的行将高亮显示+
“-新数据将插入选定行之后。\n”+
将该行放到表B上。请注意,该行已从表A中删除+
“现在显示在表B中。\n”+
“2.从表A中选择两行并放到表B上。”+
“现在表B中有两个新行。”;
tips.setEditable(false);
提示:挫折背景(新颜色(255255204));
提示.设置顺序(新的线边框(颜色为橙色,5));
getContentPane().add(提示,BorderLayout.NORTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel面板=新JPanel(新网格布局(2,1));
添加(createTable(“表A”));
添加(createTable(“表B”));
getContentPane().add(面板,BorderLayout.CENTER);
包装();
}
私有JPanel createTable(字符串tableId){
DefaultTableModel=新的DefaultTableModel();
model.addColumn(“第0列”);
模型。添加栏(“第1栏”);
模型。添加列(“第2列”);
模型。添加列(“第3列”);
model.addRow(新字符串[]{tableId+“00”、tableId+“01”、tableId+“02”、tableId+“03”});
addRow(新字符串[]{tableId+“10”、tableId+“11”、tableId+“12”、tableId+“13”});
addRow(新字符串[]{tableId+“20”、tableId+“21”、tableId+“22”、tableId+“23”});
addRow(新字符串[]{tableId+“30”、tableId+“31”、tableId+“32”、tableId+“33”});
JTable table=新的JTable(模型);
table.getTableHeader().setReorderingAllowed(false);
表.setSelectionMode(ListSelectionModel.单间隔选择);
JScrollPane scrollPane=新的JScrollPane(表);
scrollPane.setPreferredSize(新维度(400100));
表2.setDragEnabled(真);
setTransferHandler(新的TableTransferHandler());
JPanel面板=新的JPanel();
panel.add(滚动窗格);
panel.setboorder(BorderFactory.createTitledBorder(tableId));
返回面板;
}
公共静态void main(字符串[]args){
新的DnD_Demo().setVisible(true);
}
抽象类StringTransferHandler扩展了TransferHandler{
受保护的抽象字符串exportString(JComponent c);
受保护的抽象void importString(JComponent c,String str);
受保护的抽象空洞清理(JComponentC,布尔删除);
受保护的可转让组件(JComponent c){
返回新的StringSelection(exportString(c));
}
public int getSourceActions(JComponent c){
返回拷贝或移动;
}
公共布尔输入数据(JComponent c,t){
if(canImport(c,t.getTransferDataFlavors()){
试一试{
String str=(String)t.getTransferData(DataFlavor.stringFlavor);
进口管柱(c,str);
返回true;
}捕获(无支持的LavorUFE异常){
}捕获(ioe异常ioe){
}
}
返回false;
}
受保护的void exportDone(JComponent c、可转移数据、int操作){
清理(c,action==移动);
}
公共布尔值canImport(JComponent c,DataFlavor[]flavors){
for(int i=0;iimport javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.IOException;
public class DnD_Demo extends JFrame {
    public DnD_Demo() {
        setTitle("DnD Demo (Version 3)");
        JTextArea tips = new JTextArea("1. Select a row in Table A. " +
                "Press the row again and drag. \n     " +
                "As you drag the cursor icon over Table B, the row that is currently under the cursor highlights " +
                "- the new data will be inserted after the selected row. \n     " +
                "Drop the row onto Table B. Note that the row has been removed from Table A, " +
                "and now appears in Table B. \n" +
                "2. Select two rows from Table A and drop onto Table B. " +
                "Now there are two new rows in Table B. ");
        tips.setEditable(false);
        tips.setBackground(new Color(255,255,204));
        tips.setBorder(new LineBorder(Color.orange,5));
        getContentPane().add(tips,BorderLayout.NORTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(new GridLayout(2,1));
        panel.add(createTable("Table A"));
        panel.add(createTable("Table B"));
        getContentPane().add(panel,BorderLayout.CENTER);
        pack();
    }
    private JPanel createTable(String tableId) {
        DefaultTableModel model = new DefaultTableModel();
        model.addColumn("Column 0");
        model.addColumn("Column 1");
        model.addColumn("Column 2");
        model.addColumn("Column 3");
        model.addRow(new String[]{tableId+" 00", tableId+" 01", tableId+" 02", tableId+" 03"});
        model.addRow(new String[]{tableId+" 10", tableId+" 11", tableId+" 12", tableId+" 13"});
        model.addRow(new String[]{tableId+" 20", tableId+" 21", tableId+" 22", tableId+" 23"});
        model.addRow(new String[]{tableId+" 30", tableId+" 31", tableId+" 32", tableId+" 33"});
        JTable table = new JTable(model);
        table.getTableHeader().setReorderingAllowed(false);
        table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        JScrollPane scrollPane = new JScrollPane(table);
        scrollPane.setPreferredSize(new Dimension(400,100));
        table.setDragEnabled(true);
        table.setTransferHandler(new TableTransferHandler());
        JPanel panel = new JPanel();
        panel.add(scrollPane);
        panel.setBorder(BorderFactory.createTitledBorder(tableId));
        return panel;
    }
    public static void main(String[] args) {
        new DnD_Demo().setVisible(true);
    }

    abstract class StringTransferHandler extends TransferHandler {
        protected abstract String exportString(JComponent c);
        protected abstract void importString(JComponent c, String str);
        protected abstract void cleanup(JComponent c, boolean remove);
        protected Transferable createTransferable(JComponent c) {
            return new StringSelection(exportString(c));
        }
        public int getSourceActions(JComponent c) {
            return COPY_OR_MOVE;
        }
        public boolean importData(JComponent c, Transferable t) {
            if (canImport(c, t.getTransferDataFlavors())) {
                try {
                    String str = (String)t.getTransferData(DataFlavor.stringFlavor);
                    importString(c, str);
                    return true;
                } catch (UnsupportedFlavorException ufe) {
                } catch (IOException ioe) {
                }
            }
            return false;
        }
        protected void exportDone(JComponent c, Transferable data, int action) {
            cleanup(c, action == MOVE);
        }
        public boolean canImport(JComponent c, DataFlavor[] flavors) {
            for (int i = 0; i < flavors.length; i++) {
                if (DataFlavor.stringFlavor.equals(flavors[i])) {
                    return true;
                }
            }
            return false;
        }
    }
    class TableTransferHandler extends StringTransferHandler {
        public JTable target;
        public int[] rows = null;
        public int addIndex = -1; //Location where items were added
        public int addCount = 0;  //Number of items added.
        protected String exportString(JComponent c) {
            JTable table = (JTable)c;
            rows = table.getSelectedRows();
            int colCount = table.getColumnCount();
            StringBuffer buff = new StringBuffer();
            for (int i = 0; i < rows.length; i++) {
                for (int j = 0; j < colCount; j++) {
                    Object val = table.getValueAt(rows[i], j);
                    buff.append(val == null ? "" : val.toString());
                    if (j != colCount - 1) {
                        buff.append(",");
                    }
                }
                if (i != rows.length - 1) {
                    buff.append("\n");
                }
            }
            return buff.toString();
        }
        protected void importString(JComponent c, String str) {
            target = (JTable)c;
            DefaultTableModel model = (DefaultTableModel)target.getModel();
            int index = target.getSelectedRow();
            //Prevent the user from dropping data back on itself.
            //For example, if the user is moving rows #4,#5,#6 and #7 and
            //attempts to insert the rows after row #5, this would
            //be problematic when removing the original rows.
            //So this is not allowed.
            if (rows != null && index >= rows[0] - 1 &&
                    index <= rows[rows.length - 1]) {
                rows = null;
                return;
            }
            int max = model.getRowCount();
            if (index < 0) {
                index = max;
            } else {
                index++;
                if (index > max) {
                    index = max;
                }
            }
            addIndex = index;
            String[] values = str.split("\n");
            addCount = values.length;
            int colCount = target.getColumnCount();
            for (int i = 0; i < values.length ; i++) {
                model.insertRow(index++, values[i].split(","));
            }
            //If we are moving items around in the same table, we
            //need to adjust the rows accordingly, since those
            //after the insertion point have moved.
            if (rows!= null && addCount > 0) {
                for (int i = 0; i < rows.length; i++) {
                    if (rows[i] > addIndex) {
                        rows[i] += addCount;
                    }
                }
            }
        }
        protected void cleanup(JComponent c, boolean remove) {
            JTable source = (JTable)c;
            if (remove && rows != null) {
                DefaultTableModel model =
                        (DefaultTableModel)source.getModel();
                for (int i = rows.length - 1; i >= 0; i--) {
                    model.removeRow(rows[i]);
                }
            }
            rows = null;
            addCount = 0;
            addIndex = -1;
        }
    }
}
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.*;
import java.util.List;
import javax.activation.*;
import javax.swing.*;
import javax.swing.table.*;

public final class TableRowsDnDTest {
  private final TransferHandler handler = new TableRowTransferHandler();
  private final String[] columnNames = {"String", "Integer", "Boolean"};
  private final Object[][] data = {
    {"AAA", 12, true}, {"aaa", 1, false},
    {"BBB", 13, true}, {"bbb", 2, false},
    {"CCC", 15, true}, {"ccc", 3, false},
    {"DDD", 17, true}, {"ddd", 4, false},
    {"EEE", 18, true}, {"eee", 5, false},
    {"FFF", 19, true}, {"fff", 6, false},
    {"GGG", 92, true}, {"ggg", 0, false}
  };
  private final TableModel model = new DefaultTableModel(data, columnNames) {
    @Override public Class<?> getColumnClass(int column) {
      switch (column) {
      case 0:
        return String.class;
      case 1:
        return Number.class;
      case 2:
        return Boolean.class;
      default:
        return super.getColumnClass(column);
      }
    }
  };
  private final JTable table = new JTable(model);

  public JComponent makeUI() {
    table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    table.setTransferHandler(handler);
    table.setDropMode(DropMode.INSERT_ROWS);
    table.setDragEnabled(true);
    table.setFillsViewportHeight(true);
    //table.setAutoCreateRowSorter(true); //XXX

    //Disable row Cut, Copy, Paste
    ActionMap map = table.getActionMap();
    Action dummy = new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) { /* Dummy action */ }
    };
    map.put(TransferHandler.getCutAction().getValue(Action.NAME),   dummy);
    map.put(TransferHandler.getCopyAction().getValue(Action.NAME),  dummy);
    map.put(TransferHandler.getPasteAction().getValue(Action.NAME), dummy);

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    p.setBorder(BorderFactory.createTitledBorder("Drag & Drop JTable"));
    return p;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new TableRowsDnDTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

//Demo - BasicDnD (Drag and Drop and Data Transfer) https://docs.oracle.com/javase/tutorial/uiswing/dnd/basicdemo.html
//Demo - DropDemo (Drag and Drop and Data Transfer) https://docs.oracle.com/javase/tutorial/uiswing/dnd/dropmodedemo.html
//@see https://docs.oracle.com/javase/tutorial/uiswing/examples/dnd/DropDemoProject/src/dnd/ListTransferHandler.java
//@see https://github.com/aterai/java-swing-tips/blob/master/DnDReorderTable/src/java/example/TableRowsDnDTest.java
class TableRowTransferHandler extends TransferHandler {
  private final DataFlavor localObjectFlavor;
  private int[] indices;
  private int addIndex = -1; //Location where items were added
  private int addCount; //Number of items added.

  protected TableRowTransferHandler() {
    super();
    localObjectFlavor = new ActivationDataFlavor(Object[].class, DataFlavor.javaJVMLocalObjectMimeType, "Array of items");
  }
  @Override protected Transferable createTransferable(JComponent c) {
    JTable table = (JTable) c;
    DefaultTableModel model = (DefaultTableModel) table.getModel();
    List<Object> list = new ArrayList<>();
    indices = table.getSelectedRows();
    for (int i : indices) {
      list.add(model.getDataVector().get(i));
    }
    Object[] transferedObjects = list.toArray();
    return new DataHandler(transferedObjects, localObjectFlavor.getMimeType());
  }
  @Override public boolean canImport(TransferHandler.TransferSupport info) {
    JTable table = (JTable) info.getComponent();
    boolean isDropable = info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
    table.setCursor(isDropable ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
    return isDropable;
  }
  @Override public int getSourceActions(JComponent c) {
    return TransferHandler.MOVE;
  }
  @Override public boolean importData(TransferHandler.TransferSupport info) {
    if (!canImport(info)) {
      return false;
    }
    TransferHandler.DropLocation tdl = info.getDropLocation();
    if (!(tdl instanceof JTable.DropLocation)) {
      return false;
    }
    JTable.DropLocation dl = (JTable.DropLocation) tdl;
    JTable target = (JTable) info.getComponent();
    DefaultTableModel model = (DefaultTableModel) target.getModel();
    int index = dl.getRow();
    int max = model.getRowCount();
    if (index < 0 || index > max) {
      index = max;
    }
    addIndex = index;
    target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    try {
      Object[] values = (Object[]) info.getTransferable().getTransferData(localObjectFlavor);
      addCount = values.length;
      for (int i = 0; i < values.length; i++) {
        int idx = index++;
        model.insertRow(idx, (Vector) values[i]);
        target.getSelectionModel().addSelectionInterval(idx, idx);
      }
      return true;
    } catch (UnsupportedFlavorException | IOException ex) {
      ex.printStackTrace();
    }
    return false;
  }
  @Override protected void exportDone(JComponent c, Transferable data, int action) {
    cleanup(c, action == TransferHandler.MOVE);
  }

  //If the remove argument is true, the drop has been
  //successful and it's time to remove the selected items
  //from the list. If the remove argument is false, it
  //was a Copy operation and the original list is left
  //intact.
  protected void cleanup(JComponent c, boolean remove) {
    if (remove && indices != null) {
      c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      DefaultTableModel model = (DefaultTableModel)((JTable) c).getModel();
      //If we are moving items around in the same list, we
      //need to adjust the indices accordingly, since those
      //after the insertion point have moved.
      if (addCount > 0) {
        for (int i = 0; i < indices.length; i++) {
          if (indices[i] >= addIndex) {
            indices[i] += addCount;
          }
        }
      }
      for (int i = indices.length - 1; i >= 0; i--) {
        model.removeRow(indices[i]);
      }
    }
    indices  = null;
    addCount = 0;
    addIndex = -1;
  }
}