/* Code snippet, copyright 2009 by Michael R. Clements.
	This class is part of a subclass of javax.swing.JPanel,
	which has a JTree with a custom TreeModel.
	This class refers to 2 members of the panel:
	itsTree:	the JTree
	itsTModel:	the JTree's TreeModel

	The tree supports single selection only.
	If an item is dragged, its subtree goes with it (whether copy or move).

	The TreeModel is based on an object of class "Item",
	which is a Composite design pattern, having a (possibly empty)
	list of child Items.

	Classes shown here are the TransferHandler and Transferable,
	which together make up 99% of the work you must do to support drag & drop.
*/

	// This class controls drag & drop on the tree
	class EquipTransferHandler extends TransferHandler
	{
		// This stops the Java compiler from complaining
		private static final long serialVersionUID = 1;

		DataFlavor	itsNodeFlavor;
		DataFlavor[]	itsFlavors;
		Item		itsRemParent, itsRemItem;

		public EquipTransferHandler()
		{
			itsFlavors = new DataFlavor[1];
			try
			{
				StringBuffer mimeType = new StringBuffer();
				mimeType.append(DataFlavor.javaJVMLocalObjectMimeType)
					.append(";class=\"")
					.append(Item.class.getName())
					.append("\"");
				itsNodeFlavor = new DataFlavor(mimeType.toString());
				itsFlavors[0] = itsNodeFlavor;
			}
			catch(ClassNotFoundException e)
			{
				 System.out.println("ClassNotFound: " + e.getMessage());
			}
		}

		/* Called when a drag begins
		   1. remember the item and its parent (for use on the drop, to delete in case it's a move)
		   2. wrap the dragged object with a NodeTransferable and return it
		 */
		protected Transferable createTransferable(JComponent c)
		{
			if(!itsTree.equals(c))
				return null;

			TreePath path = itsTree.getSelectionPath();
			if(path != null)
			{
				Item		item;

				item = (Item)path.getLastPathComponent();
				itsRemItem = item;
				// item parent is the next-to-last path element
				itsRemParent = (Item)path.getParentPath().getLastPathComponent();

				// wrap the object to be dragged and return the wrapper
				return new NodeTransferable(item);
			}
			return null;
		}

		// Tells DnD what actions we support
		public int getSourceActions(JComponent c)
		{
			 return COPY_OR_MOVE;
		}

		// called repeatedly during the drag to see if the item can be dropped
		public boolean canImport(TransferHandler.TransferSupport tranSup)
		{
			if(!tranSup.isDrop())
				 return false;

			tranSup.setShowDropLocation(true);

			// Do not allow unsupported flavors to be dropped
			if(!tranSup.isDataFlavorSupported(itsNodeFlavor))
				 return false;

			// Do not allow a drop from any other tree
			if(!itsTree.equals(tranSup.getComponent()))
				return false;

			// Do not allow a drop on top of itself
			JTree.DropLocation dl = (JTree.DropLocation)tranSup.getDropLocation();
			if(dl.getPath().equals(itsTree.getSelectionPath()))
				return false;

			return true;
		}

		// called when the drop occurs
		public boolean importData(TransferHandler.TransferSupport tranSup)
		{
			if(!canImport(tranSup))
				 return false;

			// Extract transfer data.
			Item	node = null;
			try
			{
				Transferable t = tranSup.getTransferable();
				node = (Item)t.getTransferData(itsNodeFlavor);
			}
			catch(Exception e)
			{
				 MainGui.get().errBox("Drag and Drop Exception", e);
			}
			if(node == null)
				return false;

			// Get drop location info.
			JTree.DropLocation dl = (JTree.DropLocation)tranSup.getDropLocation();
			int childIndex = dl.getChildIndex();
			TreePath dest = dl.getPath();
			Item newParent = (Item)dest.getLastPathComponent();

			// Add the item to its new parent
			newParent.addChild(node, childIndex);
			itsTModel.treeChanged();
			return true;
		}

		// called after the drop
		protected void exportDone(JComponent source, Transferable data, int action)
		{
			if((action & MOVE) == MOVE)
			{
				// Remove the node that was moved
				itsRemParent.delChild(itsRemItem);
				itsTModel.treeChanged();
			}
		}

		// wraps the items being dragged & dropped
		public class NodeTransferable implements Transferable
		{
			Item	node;

			public NodeTransferable(Item node)
			{
				/* Always hold a deep copy of the thing being dragged.
				   This way it can be dropped even at a different location in the same parent
				   without any problems related to it being "equal" to something else.
				*/
				this.node = node.deepCopy();
			}

			public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException
			{
				if(!isDataFlavorSupported(flavor))
					throw new UnsupportedFlavorException(flavor);
				return node;
			}

			public DataFlavor[] getTransferDataFlavors()
			{
				 return itsFlavors;
			}

			public boolean isDataFlavorSupported(DataFlavor flavor)
			{
				return itsNodeFlavor.equals(flavor);
			}
		}
	}
