package fr.orsay.lri.varna.components;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
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.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JList;
public class ReorderableJList extends JList
implements DragSourceListener, DropTargetListener, DragGestureListener {
static DataFlavor localObjectFlavor;
static {
try {
localObjectFlavor =
new DataFlavor (DataFlavor.javaJVMLocalObjectMimeType);
} catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); }
}
static DataFlavor[] supportedFlavors = { localObjectFlavor };
DragSource dragSource;
DropTarget dropTarget;
int dropTargetIndex;
int draggedIndex = -1;
public ReorderableJList () {
super();
setCellRenderer (new ReorderableListCellRenderer());
setModel (new DefaultListModel());
dragSource = new DragSource();
DragGestureRecognizer dgr =
dragSource.createDefaultDragGestureRecognizer (this,
DnDConstants.ACTION_MOVE,
this);
dropTarget = new DropTarget (this, this);
}
// DragGestureListener
public void dragGestureRecognized (DragGestureEvent dge) {
//System.out.println ("dragGestureRecognized");
// find object at this x,y
Point clickPoint = dge.getDragOrigin();
int index = locationToIndex(clickPoint);
if (index == -1)
return;
Object target = getModel().getElementAt(index);
Transferable trans = new RJLTransferable (target);
draggedIndex = index;
dragSource.startDrag (dge,Cursor.getDefaultCursor(),
trans, this);
}
// DragSourceListener events
public void dragDropEnd (DragSourceDropEvent dsde) {
//System.out.println ("dragDropEnd()");
dropTargetIndex = -1;
draggedIndex = -1;
repaint();
}
public void dragEnter (DragSourceDragEvent dsde) {}
public void dragExit (DragSourceEvent dse) {}
public void dragOver (DragSourceDragEvent dsde) {}
public void dropActionChanged (DragSourceDragEvent dsde) {}
// DropTargetListener events
public void dragEnter (DropTargetDragEvent dtde) {
//System.out.println ("dragEnter");
if (dtde.getSource() != dropTarget)
dtde.rejectDrag();
else {
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
//System.out.println ("accepted dragEnter");
}
}
public void dragExit (DropTargetEvent dte) {}
public void dropActionChanged (DropTargetDragEvent dtde) {}
public void dragOver (DropTargetDragEvent dtde) {
// figure out which cell it's over, no drag to self
if (dtde.getSource() != dropTarget)
dtde.rejectDrag();
Point dragPoint = dtde.getLocation();
int index = locationToIndex (dragPoint);
dropTargetIndex = index;
if (dropTargetIndex != -1)
{
Rectangle r = getCellBounds(dropTargetIndex,dropTargetIndex);
if (dragPoint.y > r.y+r.height/2)
{
dropTargetIndex += 1;
}
}
//System.out.println(dropTargetIndex);
repaint();
}
public void drop (DropTargetDropEvent dtde) {
//System.out.println ("drop()!");
if (dtde.getSource() != dropTarget) {
//System.out.println ("rejecting for bad source (" +
// dtde.getSource().getClass().getName() + ")");
dtde.rejectDrop();
return;
}
Point dropPoint = dtde.getLocation();
int index = locationToIndex (dropPoint);
if (index != -1)
{
Rectangle r = getCellBounds(index,index);
if (dropPoint.y > r.y+r.height/2)
{
index += 1;
}
}
//System.out.println ("drop index is " + index);
boolean dropped = false;
try {
if ((index == -1) || (index == draggedIndex)|| (index == draggedIndex+1)) {
//System.out.println ("dropped onto self");
dtde.rejectDrop();
return;
}
dtde.acceptDrop (DnDConstants.ACTION_MOVE);
//System.out.println ("accepted");
Object dragged =
dtde.getTransferable().getTransferData(localObjectFlavor);
// move items - note that indicies for insert will
// change if [removed] source was before target
//System.out.println ("drop " + draggedIndex + " to " + index);
boolean sourceBeforeTarget = (draggedIndex < index);
//System.out.println ("source is" +
// (sourceBeforeTarget ? "" : " not") +
// " before target");
//System.out.println ("insert at " +
// (sourceBeforeTarget ? index-1 : index));
DefaultListModel mod = (DefaultListModel) getModel();
mod.remove (draggedIndex);
mod.add ((sourceBeforeTarget ? index-1 : index), dragged);
dropped = true;
} catch (Exception e) {
e.printStackTrace();
}
dtde.dropComplete (dropped);
}
private class ReorderableListCellRenderer
extends DefaultListCellRenderer {
boolean isTargetCell;
boolean isLastItem;
public ReorderableListCellRenderer() {
super();
}
public Component getListCellRendererComponent (JList list,
Object value,
int index,
boolean isSelected, boolean hasFocus) {
isTargetCell = (index == dropTargetIndex);
isLastItem = (index == list.getModel().getSize()-1) && (dropTargetIndex == list.getModel().getSize());
boolean showSelected = isSelected;
return super.getListCellRendererComponent (list, value,
index, showSelected,
hasFocus);
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
if (isTargetCell) {
g.setColor(Color.black);
g.drawLine (0, 0, getSize().width, 0);
}
if (isLastItem) {
g.setColor(Color.black);
g.drawLine (0, getSize().height-1, getSize().width, getSize().height-1);
}
}
}
private class RJLTransferable implements Transferable {
Object object;
public RJLTransferable (Object o) {
object = o;
}
public Object getTransferData(DataFlavor df)
throws UnsupportedFlavorException, IOException {
if (isDataFlavorSupported (df))
return object;
else
throw new UnsupportedFlavorException(df);
}
public boolean isDataFlavorSupported (DataFlavor df) {
return (df.equals (localObjectFlavor));
}
public DataFlavor[] getTransferDataFlavors () {
return supportedFlavors; }
}
}