package com.ebenius;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Stack;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.SoftBevelBorder;
public class JListDnDFun {
static class ListMoveDataFlavor extends DataFlavor {
private final DefaultListModel model;
public ListMoveDataFlavor(DefaultListModel model) {
super(ListMoveTransferData.class, "List Data");
this.model = model;
}
public DefaultListModel getModel() {
return model;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((model == null) ? 0 : model.hashCode());
return result;
}
@Override
public boolean equals(DataFlavor that) {
if (this == that) {
return true;
}
if (!super.equals(that) || getClass() != that.getClass()) {
return false;
}
return match(model, that);
}
public static boolean match(DefaultListModel model, DataFlavor flavor) {
return flavor instanceof ListMoveDataFlavor
&& ((ListMoveDataFlavor) flavor).getModel() == model;
}
}
private static class ListMoveTransferData {
private final DefaultListModel model;
private final int[] indices;
ListMoveTransferData(DefaultListModel model, int[] indices) {
this.model = model;
this.indices = indices;
}
int[] getIndices() {
return indices;
}
public DefaultListModel getModel() {
return model;
}
}
static class ListMoveTransferable implements Transferable {
private final ListMoveTransferData data;
public ListMoveTransferable(ListMoveTransferData data) {
this.data = data;
}
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { new ListMoveDataFlavor(data.getModel()) };
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return ListMoveDataFlavor.match(data.getModel(), flavor);
}
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return data;
}
}
static class ListMoveTransferHandler extends TransferHandler {
private static final long serialVersionUID = 6703461043403098490L;
@Override
public int getSourceActions(JComponent c) {
final JList list = (JList) c;
return list.getModel() instanceof DefaultListModel ? MOVE : NONE;
}
@Override
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
if (!(comp instanceof JList)
|| !(((JList) comp).getModel() instanceof DefaultListModel)) {
return false;
}
final DefaultListModel model =
(DefaultListModel) ((JList) comp).getModel();
for (DataFlavor f : transferFlavors) {
if (ListMoveDataFlavor.match(model, f)) {
return true;
}
}
return false;
}
@Override
protected Transferable createTransferable(JComponent c) {
final JList list = (JList) c;
final int[] selectedIndices = list.getSelectedIndices();
return new ListMoveTransferable(new ListMoveTransferData(
(DefaultListModel) list.getModel(), selectedIndices));
}
@Override
public boolean importData(TransferHandler.TransferSupport info) {
final Component comp = info.getComponent();
if (!info.isDrop() || !(comp instanceof JList)) {
return false;
}
final JList list = (JList) comp;
final ListModel lm = list.getModel();
if (!(lm instanceof DefaultListModel)) {
return false;
}
final DefaultListModel listModel = (DefaultListModel) lm;
final DataFlavor flavor = new ListMoveDataFlavor(listModel);
if (!info.isDataFlavorSupported(flavor)) {
return false;
}
final Transferable transferable = info.getTransferable();
try {
final ListMoveTransferData data =
(ListMoveTransferData) transferable.getTransferData(flavor);
final JList.DropLocation dropLocation = list.getDropLocation();
int insertAt = dropLocation.getIndex();
final int[] indices = data.getIndices();
Arrays.sort(indices);
final Stack<Object> elements = new Stack<Object>();
int shift = 0;
for (int i = indices.length - 1; i >= 0; i--) {
final int index = indices[i];
if (index < insertAt) {
shift--;
}
elements.push(listModel.remove(index));
}
insertAt += shift;
final ListSelectionModel sm = list.getSelectionModel();
try {
sm.setValueIsAdjusting(true);
sm.clearSelection();
final int anchor = insertAt;
while (!elements.isEmpty()) {
listModel.insertElementAt(elements.pop(), insertAt);
sm.addSelectionInterval(insertAt, insertAt++);
}
final int lead = insertAt - 1;
if (!sm.isSelectionEmpty()) {
sm.setAnchorSelectionIndex(anchor);
sm.setLeadSelectionIndex(lead);
}
} finally {
sm.setValueIsAdjusting(false);
}
return true;
} catch (UnsupportedFlavorException ex) {
return false;
} catch (IOException ex) {
return false;
}
}
}
public static void main(String[] args) {
final DefaultListModel lm1 = new DefaultListModel();
final DefaultListModel lm2 = new DefaultListModel();
for (Object o : new Object[] { "A", "B", "C", "D", "E", "F", "G", "H" }) {
lm1.addElement(o);
lm2.addElement(o);
}
final JComponent sp1 = createListAndScrollPane(lm1);
final JComponent sp2 = createListAndScrollPane(lm2);
final JComponent sp3 = createListAndScrollPane(lm2);
final JPanel indiPanel = new JPanel(new BorderLayout(6, 6));
indiPanel.add(sp1, BorderLayout.CENTER);
indiPanel.setBorder(BorderFactory.createTitledBorder(new SoftBevelBorder(
BevelBorder.LOWERED), "Independent Model"));
final JPanel sharedPanel = new JPanel(new GridLayout(1, 0, 6, 6));
sharedPanel.add(sp2);
sharedPanel.add(sp3);
sharedPanel.setBorder(BorderFactory.createTitledBorder(
new SoftBevelBorder(BevelBorder.LOWERED), "Shared Model"));
final JPanel contentPane = new JPanel(new BorderLayout(6, 6));
contentPane.add(indiPanel, BorderLayout.LINE_START);
contentPane.add(sharedPanel, BorderLayout.CENTER);
final JFrame f = new JFrame("Test Frame: List DnD Fun");
f.setContentPane(contentPane);
f.pack();
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setVisible(true);
}
private static JScrollPane createListAndScrollPane(DefaultListModel model) {
final JList list = new JList(model);
list.setDragEnabled(true);
list.setDropMode(DropMode.INSERT);
list.setTransferHandler(new ListMoveTransferHandler());
list.setPrototypeCellValue("WWWWWWWWWWWWWWWWWW");
final JScrollPane sp = new JScrollPane(list);
return sp;
}
}