/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.snap.rcp.layermanager;
import com.bc.ceres.glayer.Layer;
import org.esa.snap.ui.product.ProductSceneView;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.TreePath;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.List;
/**
* Class that enables the support for Drag & Drop.
* <p>
* Limitations:
* This handler only supports {@link JTree} with an {@link javax.swing.tree.TreeModel model}
* which is backed by {@link Layer layer}.
*
* @author Marco Peters
* @version $Revision: $ $Date: $
* @since BEAM 4.6
*/
class LayerTreeTransferHandler extends TransferHandler {
private static final DataFlavor layerFlavor = new DataFlavor(LayerContainer.class, "LayerContainer");
private final ProductSceneView view;
private final JTree tree;
LayerTreeTransferHandler(ProductSceneView view, JTree tree) {
this.view = view;
this.tree = tree;
}
@Override
public boolean canImport(TransferSupport support) {
JTree.DropLocation dropLocation = (JTree.DropLocation) support.getDropLocation();
TreePath treePath = dropLocation.getPath();
Layer targetLayer = (Layer) treePath.getLastPathComponent();
int targetIndex = dropLocation.getChildIndex();
boolean moveAllowed = true;
if (targetIndex == -1) { // -1 indicates move into other layer
moveAllowed = targetLayer.isCollectionLayer();
}
return support.isDataFlavorSupported(layerFlavor) &&
support.isDrop() &&
support.getComponent() == tree &&
moveAllowed;
}
@Override
public boolean importData(TransferSupport support) {
if (support.getComponent() != tree) {
return false;
}
final Transferable transferable = support.getTransferable();
final LayerContainer transferLayer;
try {
transferLayer = (LayerContainer) transferable.getTransferData(layerFlavor);
} catch (UnsupportedFlavorException ignore) {
return false;
} catch (IOException ignore) {
return false;
}
if (!isValidDrag(support, transferLayer)) {
return false;
}
// remove and add for Drag&Drop
// cannot use exportDone(Transferable); layer has to be removed first and then added,
// because the parent of the layer is set to null when removing
removeLayer(transferLayer);
addLayer(transferLayer, support);
return true;
}
@Override
public int getSourceActions(JComponent component) {
return component == tree ? MOVE : NONE;
}
@Override
protected Transferable createTransferable(JComponent component) {
if (component == tree) {
final Layer draggedLayer = view.getSelectedLayer();
final Layer oldParentLayer = draggedLayer.getParent();
final int oldChildIndex = oldParentLayer.getChildIndex(draggedLayer.getId());
return new LayerTransferable(draggedLayer, oldParentLayer, oldChildIndex);
}
return null;
}
private void addLayer(LayerContainer layerContainer, TransferSupport support) {
Layer transferLayer = layerContainer.getDraggedLayer();
JTree.DropLocation dropLocation = (JTree.DropLocation) support.getDropLocation();
TreePath treePath = dropLocation.getPath();
Layer targetLayer = (Layer) treePath.getLastPathComponent();
List<Layer> targetList = targetLayer.getChildren();
int targetIndex = dropLocation.getChildIndex();
if (targetIndex == -1) { // moving into target layer
targetIndex = 0; // insert at the beginning
}
if (targetList.size() <= targetIndex) {
targetList.add(transferLayer);
} else {
targetList.add(targetIndex, transferLayer);
}
final TreePath newTreePath = treePath.pathByAddingChild(transferLayer);
tree.makeVisible(newTreePath);
tree.scrollPathToVisible(newTreePath);
}
private static void removeLayer(LayerContainer layerContainer) {
final Layer oldParentayer = layerContainer.getOldParentLayer();
final int oldIndex = layerContainer.getOldChildIndex();
oldParentayer.getChildren().remove(oldIndex);
}
private static boolean isValidDrag(TransferSupport support, LayerContainer layerContainer) {
JTree.DropLocation dropLocation = (JTree.DropLocation) support.getDropLocation();
TreePath treePath = dropLocation.getPath();
final Object[] path = treePath.getPath();
for (Object o : path) {
final Layer layer = (Layer) o;
if (layer == layerContainer.getDraggedLayer()) {
return false;
}
}
Layer targetLayer = (Layer) treePath.getLastPathComponent();
int targetIndex = dropLocation.getChildIndex();
if (targetIndex == -1) { // -1 indicates move into other layer
return targetLayer.isCollectionLayer();
}
return true;
}
private static class LayerTransferable implements Transferable {
private LayerContainer layerContainer;
private LayerTransferable(Layer draggedLayer, Layer oldParentLayer, int oldChildIndex) {
layerContainer = new LayerContainer(draggedLayer, oldParentLayer, oldChildIndex);
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{layerFlavor};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(layerFlavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (!flavor.equals(layerFlavor)) {
throw new UnsupportedFlavorException(flavor);
}
return layerContainer;
}
}
private static class LayerContainer {
private final Layer draggedLayer;
private final Layer oldParentLayer;
private final int oldChildIndex;
private LayerContainer(Layer draggedLayer, Layer oldParentLayer, int oldChildIndex) {
this.draggedLayer = draggedLayer;
this.oldParentLayer = oldParentLayer;
this.oldChildIndex = oldChildIndex;
}
public Layer getDraggedLayer() {
return draggedLayer;
}
public Layer getOldParentLayer() {
return oldParentLayer;
}
public int getOldChildIndex() {
return oldChildIndex;
}
}
}