/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.examples.aut.dvdtool.control;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.eclipse.jubula.examples.aut.dvdtool.model.DvdCategory;
import org.eclipse.jubula.examples.aut.dvdtool.model.DvdDataObject;
import org.eclipse.jubula.examples.aut.dvdtool.model.DvdTransferableCategory;
/**
* A drop target wrapper for a JTree. This class can be used to control
* a rearrangeable tree.
* The operation MOVE is the only supported 'drag and drop' action type.
*
* @author BREDEX GmbH
* @created 05.02.2008
*/
public class DvdTreeDropTarget implements DropTargetListener {
/** the supported action type */
private static final int ACTION_TYPE = DnDConstants.ACTION_MOVE;
/**
* public constructor
* @param tree The tree to be controlled
*/
public DvdTreeDropTarget(JTree tree) {
new DropTarget(tree, this);
}
/**
* {@inheritDoc}
*/
public void drop(DropTargetDropEvent dtde) {
Point pt = dtde.getLocation();
DropTargetContext dtc = dtde.getDropTargetContext();
JTree tree = (JTree) dtc.getComponent();
TreePath parentpath = tree.getClosestPathForLocation(pt.x, pt.y);
DefaultMutableTreeNode newParent = (DefaultMutableTreeNode) parentpath
.getLastPathComponent();
try {
Transferable tr = dtde.getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (int i = 0; i < flavors.length; i++) {
if (tr.isDataFlavorSupported(flavors[i])) {
DvdTransferableCategory transferableTreePath =
(DvdTransferableCategory)tr.getTransferData(flavors[i]);
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DvdCategory draggedNodeCategory =
transferableTreePath.getCategory();
int draggedNodeHashCode =
transferableTreePath.getNodeHashCode();
// create dragged node and all subnodes of it using
// the transferred data in 'draggedNodeCategory'
DefaultMutableTreeNode draggedNode =
createNodeWithSubTreeForCategory(model,
draggedNodeCategory);
// check, whether the drop operation is allowed
// (dropping a node to itself or any subnode is forbidden)
DefaultMutableTreeNode nodeToCheck = newParent;
do {
if (isNodeEqualToDraggedNode(nodeToCheck, draggedNode,
draggedNodeHashCode)) {
dtde.rejectDrop();
return;
}
// check parent node next
nodeToCheck =
(DefaultMutableTreeNode) nodeToCheck.getParent();
} while (nodeToCheck != null);
dtde.acceptDrop(ACTION_TYPE);
// add to data model of new parent
DvdDataObject newParentData =
(DvdDataObject) newParent.getUserObject();
DvdCategory newParentCategory =
newParentData.getCategory();
newParentCategory.insert(draggedNodeCategory);
// add to new parent node as last child
model.insertNodeInto(draggedNode, newParent,
newParent.getChildCount());
// select dragged node
tree.setSelectionPath(new TreePath(draggedNode.getPath()));
dtde.dropComplete(true);
return;
}
}
dtde.rejectDrop();
} catch (Exception e) {
dtde.rejectDrop();
}
}
/**
* Creates a node and all needed subnodes for the given DvdCategory.
* The given tree model is updated.
*
* @param category The data to be used for node creation
* @param model The tree model to be updated
* @return the created node containing the given data
*/
private DefaultMutableTreeNode createNodeWithSubTreeForCategory(
DefaultTreeModel model,
DvdCategory category) {
DefaultMutableTreeNode node =
new DefaultMutableTreeNode(new DvdDataObject(category));
// create all sub nodes
for (int i = 0; i < category.getCategories().size(); i++) {
DvdCategory childCategory =
(DvdCategory) category.getCategories().get(i);
// do recursive call
DefaultMutableTreeNode childNode =
createNodeWithSubTreeForCategory(model, childCategory);
// add to new child node to new node
model.insertNodeInto(childNode, node, node.getChildCount());
}
return node;
}
/**
* Compares two nodes using the string representation and the hash codes.
* Nodes are equal only if both kinds of values are equal.
* (The draggedNode might be newly created after deserialization, so its
* original hash code must be given and cannot be retrieved from the node.)
*
* @param node The node to be compared to 'draggedNode'
* @param draggedNode The node to be compared to 'node'
* @param draggedNodeHashCode The original hash code of 'draggedNode'
* before it has been serialized
* @return boolean indicating whether both DefaultMutableTreeNodes are equal
*/
private boolean isNodeEqualToDraggedNode(DefaultMutableTreeNode node,
DefaultMutableTreeNode draggedNode,
int draggedNodeHashCode) {
// compare string representation of the nodes
if (node.toString().equals(draggedNode.toString())) {
// compare hash code of the nodes
return (node.hashCode() == draggedNodeHashCode);
}
return false;
}
/**
* {@inheritDoc}
*/
public void dragEnter(DropTargetDragEvent dtde) {
dtde.acceptDrag(ACTION_TYPE);
}
/**
* {@inheritDoc}
*/
public void dragOver(DropTargetDragEvent dtde) {
dtde.acceptDrag(ACTION_TYPE);
}
/**
* {@inheritDoc}
*/
public void dragExit(DropTargetEvent dte) {
// no action
}
/**
* {@inheritDoc}
*/
public void dropActionChanged(DropTargetDragEvent dtde) {
// no action
}
}