/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ExplorerDragAndDropHandler.java
* Creation date: Jan 3rd 2002
* By: Ken Wong
*/
package org.openquark.gems.client.explorer;
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.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.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.List;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import org.openquark.cal.services.GemEntity;
import org.openquark.gems.client.AutoburnLogic;
import org.openquark.gems.client.CollectorGem;
import org.openquark.gems.client.Gem;
import org.openquark.gems.client.GemEntitySelection;
import org.openquark.gems.client.InputTransferable;
import org.openquark.gems.client.ReflectorGem;
import org.openquark.gems.client.SingleGemEntityDataFlavor;
import org.openquark.util.UnsafeCast;
/**
* This is the drag and drop handler for the explorer
* todoKW: Add support for the Ctrl-Drag clone gesture.
* @author Ken Wong
* Creation Date: Jan 6th 2003
*/
class ExplorerDragAndDropHandler implements DragGestureListener, DragSourceListener, DropTargetListener {
/** the explorer we're dealing with */
private TableTopExplorer tableTopExplorer;
/** Whether or not we are currently handling a drag operation */
private boolean inDragMode = false;
/**
* Constructor for ExplorerDragAndDropHandler.
* @param tableTopExplorer
*/
public ExplorerDragAndDropHandler(TableTopExplorer tableTopExplorer) {
super();
this.tableTopExplorer = tableTopExplorer;
// create the drag gesture recognizer for this tree.
DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(tableTopExplorer.getExplorerTree(),
DnDConstants.ACTION_MOVE, this);
}
/**
* @see java.awt.dnd.DragGestureListener#dragGestureRecognized(DragGestureEvent)
*/
public void dragGestureRecognized(DragGestureEvent dragGestureEvent) {
if (!tableTopExplorer.getExplorerOwner().isDNDEnabled()) {
return;
}
//todoKW: Add drag image for this drag gesture
inDragMode = true;
Point dragOrigin = dragGestureEvent.getDragOrigin();
JTree explorerTree = tableTopExplorer.getExplorerTree();
// if the explorer is not enabled for dragging (ie run mode) we just quit
if (!tableTopExplorer.isMouseEnabled()) {
return;
}
// Find the proper node
TreePath pathToNode = explorerTree.getPathForLocation(dragOrigin.x, dragOrigin.y);
if (pathToNode == null) {
return;
}
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)pathToNode.getLastPathComponent();
Rectangle pathBounds = explorerTree.getPathBounds(pathToNode);
Point ptDragOrigin = dragGestureEvent.getDragOrigin();
Point mousePointOffset = new Point(ptDragOrigin.x - pathBounds.x, ptDragOrigin.y - pathBounds.y);
// If we are dragging the gems...
if (treeNode instanceof ExplorerGemNode) {
ExplorerGemNode explorerGemNode = (ExplorerGemNode) treeNode;
Gem gem = explorerGemNode.getGem();
if (gem instanceof CollectorGem) {
CollectorGem collectorGem = (CollectorGem) gem;
gem = new ReflectorGem(collectorGem);
}
tableTopExplorer.getExplorerOwner().beginUndoableEdit();
dragGestureEvent.startDrag(null, null, mousePointOffset, new TableTopExplorerSelection(gem, tableTopExplorer), this);
}
else if (treeNode instanceof ExplorerInputNode) {
ExplorerInputNode inputNode = (ExplorerInputNode) treeNode;
Gem.PartInput input = inputNode.getPartInput();
tableTopExplorer.getExplorerOwner().beginUndoableEdit();
dragGestureEvent.startDrag(null, null, mousePointOffset, new InputTransferable(input), this);
}
}
/**
* @see java.awt.dnd.DragSourceListener#dragEnter(DragSourceDragEvent)
*/
public void dragEnter(DragSourceDragEvent dsde) {
}
/**
* @see java.awt.dnd.DragSourceListener#dragOver(DragSourceDragEvent)
*/
public void dragOver(DragSourceDragEvent dsde) {
}
/**
* @see java.awt.dnd.DragSourceListener#dropActionChanged(DragSourceDragEvent)
*/
public void dropActionChanged(DragSourceDragEvent dsde) {
}
/**
* @see java.awt.dnd.DragSourceListener#dragExit(DragSourceEvent)
*/
public void dragExit(DragSourceEvent dse) {
}
/**
* @see java.awt.dnd.DragSourceListener#dragDropEnd(DragSourceDropEvent)
*/
public void dragDropEnd(DragSourceDropEvent dsde) {
if (inDragMode) {
tableTopExplorer.getExplorerOwner().endUndoableEdit();
inDragMode = false;
}
}
/**
* Finishes off the drag and drop action by restoring the states of the undo and of the explorer
* @param e
* @param accepted
*/
private void completeDragAndDropAction(DropTargetDropEvent e, boolean accepted) {
if (inDragMode) {
tableTopExplorer.getExplorerOwner().endUndoableEdit();
TreePath selectionPath = tableTopExplorer.getExplorerTree().getSelectionPath();
if (selectionPath != null){
Rectangle bounds = tableTopExplorer.getExplorerTree().getPathBounds(selectionPath);
if (bounds != null) {
tableTopExplorer.scrollExplorerTreeToVisible(bounds);
}
}
if (accepted) {
e.acceptDrop(DnDConstants.ACTION_MOVE);
} else {
e.rejectDrop();
}
inDragMode = false;
}
}
/**
* @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
*/
public void dragEnter(DropTargetDragEvent dtde) {
}
/**
* @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent)
*/
public void dragOver(DropTargetDragEvent dropTargetDragEvent) {
// if the explorer is not ready, then we reject all drag sources!
if (!tableTopExplorer.isMouseEnabled()) {
dropTargetDragEvent.rejectDrag();
return;
}
// We need to make sure that the dataflavor of the transferable is supported.
if (!((dropTargetDragEvent.isDataFlavorSupported(TableTopExplorerSingleGemDataFlavor.getSingleGemDataFlavor())) ||
(dropTargetDragEvent.isDataFlavorSupported(GemEntitySelection.getEntityListDF())) ||
(dropTargetDragEvent.isDataFlavorSupported(SingleGemEntityDataFlavor.getSingleGemEntityDataFlavor())))){
dropTargetDragEvent.rejectDrag();
return;
}
// We want to highlight the node that the drop would affect
Point dragLocation = dropTargetDragEvent.getLocation();
JTree explorerTree = tableTopExplorer.getExplorerTree();
TreePath selectedNodePath = explorerTree.getPathForLocation(dragLocation.x, dragLocation.y);
if (selectedNodePath == null) {
selectedNodePath = new TreePath(((ExplorerRootNode)explorerTree.getModel().getRoot()).getPath());
}
explorerTree.setSelectionPath(selectedNodePath);
DataFlavor[] flavors = dropTargetDragEvent.getCurrentDataFlavors();
if (selectedNodePath.getLastPathComponent() == explorerTree.getModel().getRoot()) {
// accept all drops on the root (just disconnection)
dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE);
return;
}
Object userObject = ((DefaultMutableTreeNode)selectedNodePath.getLastPathComponent()).getUserObject();
if (!(userObject instanceof Gem.PartInput)) {
// if it wasn't the root that we are dragging over, and it's not an input,
// the we don't care what it is. We're rejecting it.
dropTargetDragEvent.rejectDrag();
return;
}
// Now we do the type checking and stuff to make sure the drag is acceptable
Gem.PartInput input = (Gem.PartInput)userObject;
for (final DataFlavor flavor : flavors) {
if (flavor.equals(SingleGemEntityDataFlavor.getSingleGemEntityDataFlavor())) {
SingleGemEntityDataFlavor entityDataFlavor = (SingleGemEntityDataFlavor)flavor;
AutoburnLogic.AutoburnUnifyStatus unifyStatus = tableTopExplorer.getExplorerOwner().canConnect(entityDataFlavor.getGemEntity(), input);
if (unifyStatus.isAutoConnectable()) {
dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE);
} else {
dropTargetDragEvent.rejectDrag();
}
return;
} else if (flavor.equals(TableTopExplorerSingleGemDataFlavor.getSingleGemDataFlavor())) {
// The dataflavor actually stores the gem itself! so we can check if the parts are connectable
TableTopExplorerSingleGemDataFlavor gemDataFlavor = (TableTopExplorerSingleGemDataFlavor)flavor;
AutoburnLogic.AutoburnUnifyStatus unifyStatus= gemDataFlavor.canConnectTo(input);
if (unifyStatus.isAutoConnectable()) {
dropTargetDragEvent.acceptDrag(DnDConstants.ACTION_MOVE);
} else {
dropTargetDragEvent.rejectDrag();
}
return;
} else {
// if for some reason, some other flavor exists, we will reject!
dropTargetDragEvent.rejectDrag();
}
}
}
/**
* @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent)
*/
public void dropActionChanged(DropTargetDragEvent dtde) {
}
/**
* @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
*/
public void dragExit(DropTargetEvent dte) {
}
/**
* @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
*/
public void drop(DropTargetDropEvent dropTargetDropEvent) {
// maybe a foreign source
if (inDragMode == false) {
tableTopExplorer.getExplorerOwner().beginUndoableEdit();
inDragMode = true;
}
String editName = "";
// if undo support is available, we'll use it.
// get the transferable object
Transferable transferable = dropTargetDropEvent.getTransferable();
Gem[] gems = null;
JTree tree = tableTopExplorer.getExplorerTree();
TreePath path = tree.getSelectionPath();
DefaultMutableTreeNode node = (path == null) ? (DefaultMutableTreeNode)tree.getModel().getRoot() : (DefaultMutableTreeNode)path.getLastPathComponent();
Gem.PartInput input = null;
// if we are transporting gem entities
if (transferable.isDataFlavorSupported(GemEntitySelection.getEntityListDF())) {
// we assume all the type checking and stuff is done in drag over, such that inappropriate drags would have been weeded
// out by now.
try{
// so we grab the list of entities
List<GemEntity> entities = UnsafeCast.unsafeCast(transferable.getTransferData(GemEntitySelection.getEntityListDF()));
// and we add the gems to the tabletop (worry about connection later)
gems = tableTopExplorer.getExplorerOwner().addGems(entities.toArray(new GemEntity[entities.size()]));
if (node.getUserObject() instanceof Gem.PartInput) {
input = (Gem.PartInput) node.getUserObject();
}
if (node instanceof ExplorerRootNode) {
editName = ExplorerMessages.getString("TTX_Undo_AddNew");
} else {
editName = ExplorerMessages.getString("TTX_Undo_ConnectNew");
}
} catch (IOException ioException) {
ioException.printStackTrace();
completeDragAndDropAction(dropTargetDropEvent, false);
return;
} catch (UnsupportedFlavorException unsupportedFlavorException) {
unsupportedFlavorException.printStackTrace();
completeDragAndDropAction(dropTargetDropEvent, false);
return;
}
} else if (transferable.isDataFlavorSupported(TableTopExplorerSingleGemDataFlavor.getSingleGemDataFlavor())) {
// if it is a single gem that we are transferring
try{
// then we'll identify it too.
gems = new Gem[1];
gems[0] = (Gem)transferable.getTransferData(TableTopExplorerSingleGemDataFlavor.getSingleGemDataFlavor());
if (node instanceof ExplorerInputNode) {
input = ((ExplorerInputNode)node).getPartInput();
editName = ExplorerMessages.getString("TTX_Undo_Connect");
} else if (node instanceof ExplorerRootNode) {
editName = ExplorerMessages.getString("TTX_Undo_Disconnect");
}
} catch (IOException ioException) {
ioException.printStackTrace();
completeDragAndDropAction(dropTargetDropEvent, false);
return;
} catch (UnsupportedFlavorException unsupportedFlavorException) {
unsupportedFlavorException.printStackTrace();
completeDragAndDropAction(dropTargetDropEvent, false);
return;
}
} else {
completeDragAndDropAction(dropTargetDropEvent, false);
return;
}
tableTopExplorer.getExplorerOwner().setUndoableName(editName);
for (final Gem gem : gems) {
// now we go through all the gems that we have identified,
// disconnect them and connect them to the target
Gem.PartOutput output = gem.getOutputPart();
if (output != null && output.isConnected()) {
tableTopExplorer.getExplorerOwner().disconnect(output.getConnection());
}
// dropping a gem onto an input
if (input != null && output != null) {
tableTopExplorer.getExplorerOwner().connect(output, input);
}
}
// We're done our action, just to show that we accepted the drop
// select the gem we just dragged.
tableTopExplorer.selectGem(gems[0]);
completeDragAndDropAction(dropTargetDropEvent, true);
}
}