/** * author: Marcel Genzmehr * 27.07.2011 */ package org.freeplane.plugin.workspace.dnd; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.Transferable; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.util.LinkedHashMap; import java.util.Map; import javax.swing.JComponent; import javax.swing.JTree; import javax.swing.TransferHandler; import javax.swing.tree.TreePath; import org.freeplane.core.util.LogUtils; import org.freeplane.plugin.workspace.WorkspaceController; import org.freeplane.plugin.workspace.components.IWorkspaceView; import org.freeplane.plugin.workspace.components.WorkspaceNodeRenderer; import org.freeplane.plugin.workspace.features.AWorkspaceModeExtension; import org.freeplane.plugin.workspace.model.AWorkspaceTreeNode; import org.freeplane.plugin.workspace.nodes.WorkspaceRootNode; /** * */ public class WorkspaceTransferHandler extends TransferHandler implements IWorkspaceTransferHandler, DropTargetListener { /** * */ private static final long serialVersionUID = 1L; private static final Insets DEFAULT_INSETS = new Insets(20, 20, 20, 20); private JTree tree; private Map<Class<? extends AWorkspaceTreeNode>, INodeDropHandler> dropHandlers = new LinkedHashMap<Class<? extends AWorkspaceTreeNode>, INodeDropHandler>(); /*********************************************************************************** * CONSTRUCTORS **********************************************************************************/ public WorkspaceTransferHandler(JTree tree) { this.tree = tree; this.tree.setTransferHandler(this); this.tree.setDragEnabled(true); this.tree.setAutoscrolls(true); new DropTarget(tree, COPY_OR_MOVE, this); } /*********************************************************************************** * METHODS **********************************************************************************/ public static WorkspaceTransferHandler configureDragAndDrop(JTree tree) { return new WorkspaceTransferHandler(tree); } private void autoscroll(JTree tree, Point cursorLocation) { Insets insets = DEFAULT_INSETS; Rectangle outer = tree.getVisibleRect(); Rectangle inner = new Rectangle(outer.x + insets.left, outer.y + insets.top, outer.width - (insets.left + insets.right), outer.height - (insets.top + insets.bottom)); if (!inner.contains(cursorLocation)) { Rectangle scrollRect = new Rectangle(cursorLocation.x - insets.left, cursorLocation.y - insets.top, insets.left + insets.right, insets.top + insets.bottom); tree.scrollRectToVisible(scrollRect); } } public Transferable createTransferable(JComponent comp) { WorkspaceTransferable transferable = null; if (comp instanceof JTree) { JTree t = (JTree) comp; for (TreePath p : t.getSelectionPaths()) { AWorkspaceTreeNode node = (AWorkspaceTreeNode) p.getLastPathComponent(); if (node instanceof IWorkspaceTransferableCreator) { if(transferable == null) { transferable = ((IWorkspaceTransferableCreator)node).getTransferable(); } else { transferable.merge(((IWorkspaceTransferableCreator)node).getTransferable()); } } } } return transferable; } public boolean importData(JComponent comp, Transferable transf) { if (comp instanceof JTree) { JTree t = (JTree) comp; for (TreePath p : t.getSelectionPaths()) { AWorkspaceTreeNode targetNode = (AWorkspaceTreeNode) p.getLastPathComponent(); if (DnDController.isDropAllowed(targetNode)) { if(transf == null) { return false; } try { return handleDrop(targetNode, transf, DnDConstants.ACTION_COPY); } catch (NoDropHandlerFoundExeption e) { LogUtils.info("org.freeplane.plugin.workspace.dnd.WorkspaceTransferHandler.importData(comp, transf): " + e.getMessage()); } } } } return false; } public int getSourceActions(JComponent comp) { AWorkspaceModeExtension ctrl = WorkspaceController.getCurrentModeExtension(); if(ctrl.getView().containsComponent(comp)) { TreePath selectionPath = ctrl.getView().getSelectionPath(); if(selectionPath != null) { if(selectionPath.getLastPathComponent() instanceof WorkspaceRootNode) { return NONE; } //SYSTEM NODES are vulnerable for DnD move events (e.g. Copy a system file link and delete the copy hard afterwards, maybe the original will also be deleted.) if(selectionPath.getLastPathComponent() instanceof AWorkspaceTreeNode && ( ((AWorkspaceTreeNode) selectionPath.getLastPathComponent()).isSystem() || !((AWorkspaceTreeNode) selectionPath.getLastPathComponent()).isTransferable() ) ) { //DOCEAR: REJECT DnD on system nodes for now return NONE; } } } return comp.getDropTarget().getDefaultActions(); } public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException { super.exportToClipboard(comp, clip, action); } // Causes the Swing drag support to be initiated. public void exportAsDrag(JComponent comp, java.awt.event.InputEvent e, int action) { super.exportAsDrag(comp, e, action); } // Invoked after data has been exported. public void exportDone(JComponent source, Transferable data, int action) { super.exportDone(source, data, action); } public boolean handleDrop(AWorkspaceTreeNode targetNode, Transferable transf, int dndAction) throws NoDropHandlerFoundExeption { if(targetNode == null) { throw new IllegalArgumentException("targetNode is NULL"); } INodeDropHandler h = findHandler(targetNode.getClass()); if(h == null) { throw new NoDropHandlerFoundExeption(targetNode); } if(h.acceptDrop(transf)) { return h.processDrop(targetNode, transf, dndAction); } return false; } private INodeDropHandler findHandler(Class<?> clzz) { if(clzz == null) { return null; } INodeDropHandler h = dropHandlers.get(clzz); // if(h == null) { // // for (Class<?> interf : clzz.getInterfaces()) { // h = findHandler(interf); // if(h != null) { // return h; // } // } // // h = findHandler((Class<?>) clzz.getSuperclass()); // } // return h; } public void registerNodeDropHandler(Class<? extends AWorkspaceTreeNode> clzz, INodeDropHandler handler) { if(clzz == null || handler == null) { return; } synchronized (dropHandlers) { dropHandlers.put(clzz, handler); } } private boolean processDrop(AWorkspaceTreeNode targetNode, DropTargetDropEvent event) { event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); final Transferable transferable = event.getTransferable(); final int dropAction = event.getDropAction(); try { if(!targetNode.getAllowsChildren()) { targetNode = targetNode.getParent(); } if(!DnDController.isDropAllowed(targetNode)) { event.dropComplete(false); return false; } if(handleDrop(targetNode, transferable, dropAction)) { event.dropComplete(true); return true; } } catch (NoDropHandlerFoundExeption e) { LogUtils.info("org.freeplane.plugin.workspace.dnd.WorkspaceTransferHandler.processDrop(targetNode, event): "+ e.getMessage()); } event.dropComplete(false); return false; } /*********************************************************************************** * REQUIRED METHODS FOR INTERFACES **********************************************************************************/ /* DropTarget Methods */ public final void drop(DropTargetDropEvent event) { IWorkspaceView view = WorkspaceController.getCurrentModeExtension().getView(); if(view == null) { return; } TreePath targetPath = view.getPathForLocation(event.getLocation().x, event.getLocation().y); if(targetPath != null) { AWorkspaceTreeNode targetNode = (AWorkspaceTreeNode) targetPath.getLastPathComponent(); if(processDrop(targetNode, event)) { return; } } event.rejectDrop(); } public final void dragEnter(DropTargetDragEvent dtde) { dtde.getDropTargetContext().getDropTarget().setDefaultActions(COPY_OR_MOVE); //LogUtils.info("org.freeplane.plugin.workspace.dnd.WorkspaceTransferHandler.dragEnter(dtde)"); } public final void dragExit(DropTargetEvent dte) { dte.getDropTargetContext().getDropTarget().setDefaultActions(COPY); //LogUtils.info("org.freeplane.plugin.workspace.dnd.WorkspaceTransferHandler.dragExit(dte)"); } private TreePath lastPathLocation = null; public final void dragOver(DropTargetDragEvent dtde) { autoscroll(this.tree, dtde.getLocation()); TreePath path = tree.getPathForLocation(dtde.getLocation().x, dtde.getLocation().y); if(path == lastPathLocation) { return; } WorkspaceNodeRenderer renderer = (WorkspaceNodeRenderer) tree.getCellRenderer(); if(path != null && path != lastPathLocation) { lastPathLocation = path; renderer.highlightRow(tree.getRowForLocation(dtde.getLocation().x, dtde.getLocation().y)); tree.repaint(); } else if(lastPathLocation != null) { lastPathLocation = null; renderer.highlightRow(-1); tree.repaint(); } } public final void dropActionChanged(DropTargetDragEvent dtde) { //LogUtils.info("org.freeplane.plugin.workspace.dnd.WorkspaceTransferHandler.dropActionChanged(dtde)"); } /*********************************************************************************** * INTERNAL CLASSES **********************************************************************************/ }