/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package de.cismet.cismap.commons.gui.layerwidget;
import org.apache.log4j.Logger;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.TreePath;
/**
* DOCUMENT ME!
*
* @author therter
* @version $Revision$, $Date$
*/
public class TreeTransferHandler extends TransferHandler {
//~ Static fields/initializers ---------------------------------------------
private static final Logger LOG = Logger.getLogger(TreeTransferHandler.class);
//~ Instance fields --------------------------------------------------------
private DataFlavor nodesFlavor;
private DataFlavor[] flavors = new DataFlavor[1];
private List<TreePath> nodesToRemove;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new TreeTransferHandler object.
*/
public TreeTransferHandler() {
try {
final String mimeType = DataFlavor.javaJVMLocalObjectMimeType
+ ";class=\"" + javax.swing.tree.TreePath[].class.getName()
+ "\"";
nodesFlavor = new DataFlavor(mimeType);
flavors[0] = nodesFlavor;
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFound: " + e.getMessage());
}
}
//~ Methods ----------------------------------------------------------------
@Override
public boolean canImport(final TransferHandler.TransferSupport support) {
if (!support.isDrop()) {
return false;
}
support.setShowDropLocation(true);
if (!support.isDataFlavorSupported(nodesFlavor)) {
return true;
}
// Do not allow a drop on the drag source selections
final JTree.DropLocation dl = (JTree.DropLocation)support.getDropLocation();
final JTree tree = (JTree)support.getComponent();
final int dropRow = tree.getRowForPath(dl.getPath());
final int[] selRows = tree.getSelectionRows();
for (int i = 0; i < selRows.length; i++) {
if (selRows[i] == dropRow) {
return false;
}
if (selRows[i] == 0) {
return false;
}
}
// Do not allow a drop on a layer that is not a collection
final Object targetNode = dl.getPath().getLastPathComponent();
if (!(targetNode instanceof LayerCollection) && !targetNode.equals("Layer")) {
return false;
}
if ((targetNode instanceof LayerCollection) && containsDescendantPath(dl.getPath(), tree.getSelectionPaths())) {
return false;
}
return true;
}
/**
* Checks, if one of the source paths is a descendent path of the target path.
*
* @param target the parent path
* @param sourcePath the possible descendent pathes
*
* @return true, iff one of the source paths is a descendent path of the target path.
*/
private boolean containsDescendantPath(final TreePath target, final TreePath[] sourcePath) {
for (final TreePath path : sourcePath) {
if (path.isDescendant(target)) {
return true;
}
}
return false;
}
@Override
protected Transferable createTransferable(final JComponent c) {
final JTree tree = (JTree)c;
final TreePath[] paths = tree.getSelectionPaths();
if (paths != null) {
// Make up a node array for transfer and
// another for the nodes that will be removed in
// exportDone after a successful drop.
final List<TreePath> toTransfer = new ArrayList<TreePath>();
final List<TreePath> toRemove = new ArrayList<TreePath>();
final TreePath path = paths[0];
toTransfer.add(copy(path));
toRemove.add(path);
for (int i = 1; i < paths.length; i++) {
final TreePath next = paths[i];
toTransfer.add(copy(next));
toRemove.add(next);
}
final TreePath[] nodes = toTransfer.toArray(new TreePath[toTransfer.size()]);
nodesToRemove = toRemove;
return new NodesTransferable(nodes);
}
return null;
}
/**
* Copy used in createTransferable.
*
* @param path path the path to copy
*
* @return A copy of the given TreePath
*/
private TreePath copy(final TreePath path) {
return new TreePath(path.getPath());
}
@Override
protected void exportDone(final JComponent source, final Transferable data, final int action) {
if ((action & MOVE) == MOVE) {
// final JTree tree = (JTree)source;
// final ActiveLayerModel model = (ActiveLayerModel)tree.getModel();
// // Remove nodes saved in nodesToRemove in createTransferable.
// for (int i = 0; i < nodesToRemove.size(); i++) {
// final Object parent = nodesToRemove.get(i).getParentPath().getLastPathComponent();
//
// if (parent.equals(model.getRoot())) {
// model.removeLayer(nodesToRemove.get(i));
// } else if (parent instanceof LayerCollection) {
// ((LayerCollection)parent).remove(nodesToRemove.get(i).getLastPathComponent());
// }
// }
//
// model.fireTreeStructureChanged(this, new Object[] { model.getRoot() }, null, null);
}
}
@Override
public int getSourceActions(final JComponent c) {
return COPY_OR_MOVE;
}
@Override
public boolean importData(final TransferHandler.TransferSupport support) {
if (!canImport(support)) {
return false;
}
// Get drop location info.
final JTree.DropLocation dl = (JTree.DropLocation)support.getDropLocation();
final int childIndex = dl.getChildIndex();
final TreePath dest = dl.getPath();
final Object parent = dest.getLastPathComponent();
final JTree tree = (JTree)support.getComponent();
final ActiveLayerModel model = (ActiveLayerModel)((ActiveLayerModelWrapperWithoutProgress)tree.getModel())
.getModel();
// Configure for drop mode.
int index = childIndex; // DropMode.INSERT
if (childIndex == -1) { // DropMode.ON
index = model.getChildCount(parent);
}
if (support.isDataFlavorSupported(nodesFlavor)) {
// Es handelt sich um eine MOVE Aktion --> Die Drag Operation wurde aus dem Themenbaum gestartet
TreePath[] nodes = null;
try {
final Transferable t = support.getTransferable();
nodes = (TreePath[])t.getTransferData(nodesFlavor);
} catch (UnsupportedFlavorException ufe) {
System.out.println("UnsupportedFlavor: " + ufe.getMessage());
} catch (java.io.IOException ioe) {
System.out.println("I/O error: " + ioe.getMessage());
}
for (int i = 0; i < nodes.length; i++) {
final TreePath parentPath = nodes[i].getParentPath();
final Object layer = nodes[i].getLastPathComponent();
// The index must be decreased, if the layer is moved to a higher row number in the same folder
if (parentPath.getLastPathComponent().equals(model.getRoot())) {
if (model.getIndexOfChild(model.getRoot(), layer) > -1) {
if (model.getIndexOfChild(model.getRoot(), layer) < index) {
--index;
}
}
} else if (parentPath.getLastPathComponent() instanceof LayerCollection) {
final LayerCollection parentCollection = (LayerCollection)parentPath.getLastPathComponent();
if (parentCollection.indexOf(layer) > -1) {
if (parentCollection.indexOf(layer) < index) {
--index;
}
}
}
model.moveLayer(parentPath, dest, index, layer);
}
return true;
} else {
return dropPerformed(support, model, index, tree);
}
}
/**
* DOCUMENT ME!
*
* @param support DOCUMENT ME!
* @param activeLayerModel DOCUMENT ME!
* @param index DOCUMENT ME!
* @param parent DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private boolean dropPerformed(final TransferHandler.TransferSupport support,
final ActiveLayerModel activeLayerModel,
final int index,
final JComponent parent) {
return LayerDropUtils.drop(support, activeLayerModel, index, parent);
}
@Override
public String toString() {
return getClass().getName();
}
//~ Inner Classes ----------------------------------------------------------
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
public class NodesTransferable implements Transferable {
//~ Instance fields ----------------------------------------------------
TreePath[] nodes;
//~ Constructors -------------------------------------------------------
/**
* Creates a new NodesTransferable object.
*
* @param nodes DOCUMENT ME!
*/
public NodesTransferable(final TreePath[] nodes) {
this.nodes = nodes;
}
//~ Methods ------------------------------------------------------------
@Override
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException {
if (!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return nodes;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
@Override
public boolean isDataFlavorSupported(final DataFlavor flavor) {
return nodesFlavor.equals(flavor);
}
}
}