package gui.bsvComponents;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.DropMode;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
/**
* List, in which elements can be resorted by drag and drop.
*/
public class DragDropList extends JList {
private static final long serialVersionUID = 647149035420106625L;
/**
* Model used by the list.
*/
private final DefaultListModel model;
/**
* The drag listener. It is not used locally, but it avoids the garbage collector to clean up.
*/
private final DragListener dl;
/**
* Stores last selected item.
*/
private int lastSelected = -1;
/**
* Constructs a new list using the given elements.
*
* @param elements
* elements shown in the list
*/
public DragDropList(Object[] elements) {
super(new DefaultListModel());
this.model = (DefaultListModel) this.getModel();
this.setDragEnabled(true);
this.setDropMode(DropMode.INSERT);
this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Change the standard selection model and do nothing in setSelectionInterval
this.setSelectionModel(new DefaultListSelectionModel() {
private static final long serialVersionUID = -4669197776774227317L;
@Override
public void setSelectionInterval(int a, int b) {
// ignore
}
});
this.setTransferHandler(new DropHandler(this));
this.dl = new DragListener(this);
this.setCellRenderer(new CellRenderer());
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int index = locationToIndex(e.getPoint());
lastSelected = index;
if (index != -1) {
if (isSelectedIndex(index)) {
removeSelectionInterval(index, index);
} else {
addSelectionInterval(index, index);
}
repaint();
}
}
});
for (Object o : elements) {
this.model.addElement(o);
}
}
/**
* Gets sorted Elements stored in the list.
*
* @return sorted elements
*/
public Object[] getElements() {
return this.model.toArray();
}
/**
* Implements a cell renderer with check boxes.
*/
private class CellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = -1346558652020551278L;
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
JCheckBox checkbox = new JCheckBox(value.toString());
checkbox.setSelected(isSelected);
return checkbox;
}
}
/**
* Listener for dragging.
*/
private class DragListener extends DragSourceAdapter implements DragGestureListener {
/**
* The list.
*/
private final DragDropList list;
/**
* Our drag source.
*/
private final DragSource ds = new DragSource();
/**
* Constructs a new listener.
*
* @param list
* list that the listener should manage
*/
public DragListener(DragDropList list) {
this.list = list;
DragGestureRecognizer dgr = ds
.createDefaultDragGestureRecognizer(this.list, DnDConstants.ACTION_MOVE, this);
}
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
StringSelection transferable = new StringSelection(Integer.toString(lastSelected));
this.ds.startDrag(dge, DragSource.DefaultCopyDrop, transferable, this);
}
}
/**
* Transfer handler for list index transfer (resorting).
*/
private class DropHandler extends TransferHandler {
private static final long serialVersionUID = 647149035023101337L;
/**
* The list.
*/
private final DragDropList list;
/**
* Constructs a new DropHandler.
*
* @param list
* list that the handler should manage
*/
public DropHandler(DragDropList list) {
this.list = list;
}
@Override
public boolean canImport(TransferHandler.TransferSupport support) {
if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
return false;
}
JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
return dl.getIndex() != -1;
}
@Override
public boolean importData(TransferHandler.TransferSupport support) {
if (!this.canImport(support)) {
return false;
}
Transferable transferable = support.getTransferable();
String indexString;
try {
indexString = (String) transferable.getTransferData(DataFlavor.stringFlavor);
} catch (UnsupportedFlavorException e) {
return false;
} catch (IOException e) {
return false;
}
int index = Integer.parseInt(indexString);
JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
int dropTargetIndex = dl.getIndex();
// resort list
DefaultListModel model = (DefaultListModel) this.list.getModel();
Object o = model.remove(index);
if (dropTargetIndex > index) {
dropTargetIndex--;
}
model.add(dropTargetIndex, o);
return true;
}
}
}