/* ****************************************************************************** * * Copyright 2008-2010 Hans Dijkema * * JRichTextEditor is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * JRichTextEditor 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JRichTextEditor. If not, see <http://www.gnu.org/licenses/>. * * ******************************************************************************/ package nl.dykema.jxmlnote.widgets; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.io.IOException; import java.util.Iterator; import java.util.Vector; import javax.swing.DropMode; import javax.swing.JComponent; import javax.swing.JTree; import javax.swing.TransferHandler; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; /** * Implements a subclass of JTree that supports drag and drop on its own elements. * Using this drag/drop, users can move nodes around in the tree. * * @author Hans Dijkema */ public class JTreeMovable extends JTree implements MouseMotionListener { private static final long serialVersionUID = 1L; private static DataFlavor treePathFlavor=new DataFlavor(Integer.class,"Source HashCode of JTreeMovable"); private TreePath _sourceOfDrag=null; private Vector<Integer> _sourceOfDragVector=null; //private JTreeMovable _sourceTree; class MoveTransferHandler extends TransferHandler { private static final long serialVersionUID = 1L; private TransferHandler _default; public void exportAsDrag(JComponent c,InputEvent e,int a) { if (e instanceof MouseEvent) { MouseEvent me=(MouseEvent) e; // These are copied TreeNodes _sourceOfDrag=JTreeMovable.this.getPathForLocation(me.getX(), me.getY()); if (_sourceOfDrag!=null) { // Map the treepath straight to the model Vector<Integer> indices=new Vector<Integer>(); TreeNode p=(TreeNode) _sourceOfDrag.getLastPathComponent(); while (p.getParent()!=null) { Integer index=p.getParent().getIndex(p); indices.insertElementAt(index, 0); p=p.getParent(); } _sourceOfDragVector=indices; } } _default.exportAsDrag(c, e, a); } public boolean canImport(TransferHandler.TransferSupport info) { //System.out.println(info); // only support drop here if (!info.isDrop()) { return false; } //info.setShowDropLocation(true); //TODO: check if necessary // we only import treepaths if (!info.isDataFlavorSupported(treePathFlavor)) { return false; } return true; } public boolean importData(TransferHandler.TransferSupport info) { // if we can't handle the import, say so if (!canImport(info)) { return false; } // fetch the drop location JTree.DropLocation dl = (JTree.DropLocation)info.getDropLocation(); // fetch the path and child index from the drop location TreePath path = dl.getPath(); int childIndex = dl.getChildIndex(); // fetch the data and bail if this fails Vector<Integer> sourcePath; Object sourceT; try { sourceT = info.getTransferable().getTransferData(treePathFlavor); } catch (UnsupportedFlavorException e) { return false; } catch (IOException e) { return false; } if (sourceT instanceof Integer) { int sourceHash=(Integer) sourceT; if (sourceHash==JTreeMovable.this.hashCode()) { JTreeMovable tree=JTreeMovable.this; sourcePath=tree._sourceOfDragVector; if (sourcePath==null) { return false; } // if child index is -1, the drop was on top of the path, so we'll // treat it as inserting at the end of that path's list of children if (childIndex == -1) { childIndex = JTreeMovable.this.getModel().getChildCount(path.getLastPathComponent()); } // Move the node. Remove it at the source location, insert it at the drop location TreeModel dmodel=tree.getModel(); if (dmodel instanceof DefaultTreeModel) { DefaultTreeModel model=(DefaultTreeModel) dmodel; // Object dnode=sourcePath.getLastPathComponent(); TreeNode source=(TreeNode) model.getRoot(); Iterator<Integer> it=sourcePath.iterator(); int sourceIndex=-1; while (it.hasNext()) { sourceIndex=it.next(); source=source.getChildAt(sourceIndex); } Object tnode=path.getLastPathComponent(); if (tnode instanceof MutableTreeNode && source instanceof MutableTreeNode) { MutableTreeNode sourceNode=(MutableTreeNode) source; MutableTreeNode targetNode=(MutableTreeNode) tnode; TreeNode sourceParent=sourceNode.getParent(); model.removeNodeFromParent(sourceNode); if (sourceParent==targetNode && sourceIndex<childIndex) { childIndex-=1; } model.insertNodeInto(sourceNode, targetNode, childIndex); TreePath p=path.pathByAddingChild(sourceNode); JTreeMovable.this.makeVisible(p); JTreeMovable.this.scrollPathToVisible(p); //tree.scrollRectToVisible(tree.getPathBounds(path.pathByAddingChild(newNode))); tree._sourceOfDrag=null; tree._sourceOfDragVector=null; } else { return false; } } else { return false; } } else { return false; } } else { return false; } return true; } protected Transferable createTransferable(JComponent c) { return new Transferable() { public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (!isDataFlavorSupported(flavor)) { throw new UnsupportedFlavorException(flavor); } else if (_sourceOfDrag==null) { throw new IOException("No source"); } return (Integer) JTreeMovable.this.hashCode(); } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { treePathFlavor }; } public boolean isDataFlavorSupported(DataFlavor flavor) { return (flavor==treePathFlavor); } }; } public MoveTransferHandler(TransferHandler old) { _default=old; } } public void mouseDragged(MouseEvent e) { this.getTransferHandler().exportAsDrag(this, e, TransferHandler.COPY); } public void mouseMoved(MouseEvent e) { } public JTreeMovable(DefaultTreeModel model) { super(model); super.setTransferHandler(new MoveTransferHandler(super.getTransferHandler())); super.setDragEnabled(true); super.setDropMode(DropMode.ON_OR_INSERT); super.addMouseMotionListener(this); } }