package org.isatools.isacreator.common; import org.apache.log4j.Logger; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.*; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by the ISA team * * @author Eamonn Maguire (eamonnmag@gmail.com) * <p/> * Date: 23/06/2011 * Time: 18:31 */ public class ReOrderableJList extends JList implements DragSourceListener, DropTargetListener, DragGestureListener { private static Logger log = Logger.getLogger(ReOrderableJList.class.getName()); static DataFlavor localObjectFlavor; static { try { localObjectFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } } static DataFlavor[] supportedFlavors = {localObjectFlavor}; DragSource dragSource; DropTarget dropTarget; public Object dropTargetCell; int draggedIndex = -1; public ReOrderableJList(DefaultListModel model) { setCellRenderer(new ReOrderableListCellRenderer(this)); setModel(model); dragSource = new DragSource(); dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this); dropTarget = new DropTarget(this, this); } // public void setCustomCellRenderer(ListCellRenderer renderer) { // setCellRenderer(renderer); // } // DragGestureListener public void dragGestureRecognized(DragGestureEvent dge) { try { // find object at this x,y Point clickPoint = dge.getDragOrigin(); int index = locationToIndex(clickPoint); if (index == -1) return; Transferable trans; if (getSelectedValues().length > 1) { List<Object> target = new ArrayList<Object>(); target.addAll(Arrays.asList(getSelectedValues())); trans = new RJLTransferable(target); } else { Object target = getModel().getElementAt(index); trans = new RJLTransferable(target); } draggedIndex = index; dragSource.startDrag(dge, Cursor.getDefaultCursor(), trans, this); } catch (InvalidDnDOperationException e) { log.info(e.getMessage()); } } // DragSourceListener events public void dragDropEnd(DragSourceDropEvent dsde) { dropTargetCell = null; 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) { if (dtde.getSource() != dropTarget) dtde.rejectDrag(); else { dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); } } public void dragExit(DropTargetEvent dte) { } 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); if (index == -1) dropTargetCell = null; else dropTargetCell = getModel().getElementAt(index); repaint(); } public void drop(DropTargetDropEvent dtde) { 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); boolean dropped = false; try { if ((index == -1) || (index == draggedIndex)) { //dropped onto self dtde.rejectDrop(); return; } dtde.acceptDrop(DnDConstants.ACTION_MOVE); Object dragged = dtde.getTransferable().getTransferData(localObjectFlavor); // move items - note that indicies for insert will // change if [removed] source was before target boolean sourceBeforeTarget = (draggedIndex < index); DefaultListModel mod = (DefaultListModel) getModel(); if (dragged instanceof List) { // todo check if dragged is of Instance ArrayList/List. If so, we need to iterate through them, adding the items sequentially to their new position List<Object> toInsert = (ArrayList<Object>) dragged; int startIndex = draggedIndex; int dropIndex = index; if (sourceBeforeTarget) { // we need to perform some specialised behaviour // need to remove the elements from the list from the last element selected in the drag to the first. // First to last would cause a conflict in indexes. for (int toRemove = startIndex + toInsert.size() - 1; toRemove >= startIndex; toRemove--) { mod.remove(toRemove); } // we have just removed items from the list, so we need to compensate for this removal by // modifying the dropIndex through subtraction of the items to be entered in their new positions. dropIndex = dropIndex - toInsert.size(); // now add all the elements to be added in their rightful place. for (Object draggedObject : toInsert) { mod.add(dropIndex, draggedObject); dropIndex++; } } else { for (Object draggedObject : toInsert) { mod.remove(startIndex); mod.add((sourceBeforeTarget ? dropIndex - 1 : dropIndex), draggedObject); startIndex++; dropIndex++; } } } else { mod.remove(draggedIndex); mod.add((sourceBeforeTarget ? index - 1 : index), dragged); } dropped = true; firePropertyChange("orderChanged", "", "changedOrder"); } catch (Exception e) { log.info(e.getMessage()); } dtde.dropComplete(dropped); } public void dropActionChanged(DropTargetDragEvent dtde) { } 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; } } }