# JTree Drag and Drop / drop - Probleme



## dndopf (5. Jul 2005)

Hi, ich habe folgendes Problem mit meinen Transferhandler! 
 Um innerhalb meines Baumes einen Knoten abzulegen nutze ich insertNodeInto!
 Löschen will ich dann mit draggedNode.removeFromParent.
 Das Problem ist, das teilweise beim droppen er quasi einen neuen Root Knoten anlegt ( keine Ahnung warum ) und 
 damit funktioniert natürlich auch nicht mehr removeFromParent.

 Hat jemand ne Idee woran das liegen könnte?


```
import java.awt.*;

import javax.swing.*;
import javax.swing.tree.*;

import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.awt.image.*;
import java.awt.geom.*;

public class DndTreeTransferHandler implements DragGestureListener,
        DragSourceListener, DropTargetListener {

    private DndTree tree;

    private DragSource dragSource; // dragsource

    private DropTarget dropTarget; //droptarget

    private static VDM_TreeNode draggedNode;

    private VDM_TreeNode draggedNodeParent;

    private static BufferedImage image = null; //buff image

    private Rectangle rect2D = new Rectangle();

    private boolean drawImage;

    protected DndTreeTransferHandler(DndTree tree, int action, boolean drawIcon) {
        this.tree = tree;
        drawImage = drawIcon;
        dragSource = new DragSource();
        dragSource.createDefaultDragGestureRecognizer(tree, action, this);
        dropTarget = new DropTarget(tree, action, this);
    }

    /* Methods for DragSourceListener */
    public void dragDropEnd(DragSourceDropEvent dsde) {

        draggedNode.getRoot();
        draggedNode.getLevel();

        System.out.println(draggedNode.getRoot());
        System.out.println(draggedNode.getLevel());

        if (dsde.getDropAction() == DnDConstants.ACTION_MOVE
                && draggedNodeParent != null) {

            draggedNode.removeFromParent();

            ((DefaultTreeModel) tree.getModel())
                    .nodeStructureChanged(draggedNodeParent);

        } else if (dsde.getDropAction() == DnDConstants.ACTION_MOVE
                && draggedNodeParent == null) {

                        
            ((DefaultTreeModel) tree.getModel())
            	.nodeStructureChanged(draggedNodeParent);

        }

    }

    public final void dragEnter(DragSourceDragEvent dsde) {
        int action = dsde.getDropAction();
        if (action == DnDConstants.ACTION_COPY) {
            dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
        } else {
            if (action == DnDConstants.ACTION_MOVE) {
                dsde.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveDrop);
            } else {
                dsde.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveNoDrop);
            }
        }
    }

    public final void dragOver(DragSourceDragEvent dsde) {
        int action = dsde.getDropAction();
        if (action == DnDConstants.ACTION_COPY) {
            dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
        } else {
            if (action == DnDConstants.ACTION_MOVE) {
                dsde.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveDrop);
            } else {
                dsde.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveNoDrop);
            }
        }
    }

    public final void dropActionChanged(DragSourceDragEvent dsde) {
        int action = dsde.getDropAction();
        if (action == DnDConstants.ACTION_COPY) {
            dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
        } else {
            if (action == DnDConstants.ACTION_MOVE) {
                dsde.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveDrop);
            } else {
                dsde.getDragSourceContext().setCursor(
                        DragSource.DefaultMoveNoDrop);
            }
        }
    }

    public final void dragExit(DragSourceEvent dse) {
        dse.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
    }

    /* Methods for DragGestureListener */
    public final void dragGestureRecognized(DragGestureEvent dge) {
        TreePath path = tree.getSelectionPath();
        if (path != null) {
            draggedNode = (VDM_TreeNode) path.getLastPathComponent();
            draggedNodeParent = (VDM_TreeNode) draggedNode.getParent();
            if (drawImage) {
                Rectangle pathBounds = tree.getPathBounds(path); //getpathbounds
                // of
                // selectionpath
                JComponent lbl = (JComponent) tree.getCellRenderer()
                        .getTreeCellRendererComponent(
                                tree,
                                draggedNode,
                                false,
                                tree.isExpanded(path),
                                ((DefaultTreeModel) tree.getModel())
                                        .isLeaf(path.getLastPathComponent()),
                                0, false);//returning the label
                lbl.setBounds(pathBounds);//setting bounds to lbl
                image = new BufferedImage(lbl.getWidth(), lbl.getHeight(),
                        java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);//buffered
                // image
                // reference
                // passing
                // the
                // label's
                // ht
                // and
                // width
                Graphics2D graphics = image.createGraphics();//creating the
                // graphics for
                // buffered image
                graphics.setComposite(AlphaComposite.getInstance(
                        AlphaComposite.SRC_OVER, 0.5f)); //Sets the Composite
                // for the Graphics2D
                // context
                lbl.setOpaque(false);
                lbl.paint(graphics); //painting the graphics to label
                graphics.dispose();
            }
            dragSource.startDrag(dge, DragSource.DefaultMoveNoDrop, image,
                    new Point(0, 0), new TransferableNode(draggedNode), this);
        }
    }

    /* Methods for DropTargetListener */

    public final void dragEnter(DropTargetDragEvent dtde) {
        Point pt = dtde.getLocation();
        int action = dtde.getDropAction();
        if (drawImage) {
            paintImage(pt);
        }
        if (canPerformAction(tree, draggedNode, action, pt)) {
            dtde.acceptDrag(action);
        } else {
            dtde.rejectDrag();
        }
    }

    public final void dragExit(DropTargetEvent dte) {
        if (drawImage) {
            clearImage();
        }
    }

    public final void dragOver(DropTargetDragEvent dtde) {
        Point pt = dtde.getLocation();
        int action = dtde.getDropAction();
        if (drawImage) {
            paintImage(pt);
        }
        if (canPerformAction(tree, draggedNode, action, pt)) {
            dtde.acceptDrag(action);
        } else {
            dtde.rejectDrag();
        }
    }

    public final void dropActionChanged(DropTargetDragEvent dtde) {
        Point pt = dtde.getLocation();
        int action = dtde.getDropAction();
        if (drawImage) {
            paintImage(pt);
        }
        if (canPerformAction(tree, draggedNode, action, pt)) {
            dtde.acceptDrag(action);
        } else {
            dtde.rejectDrag();
        }
    }

    public final void drop(DropTargetDropEvent dtde) {
        try {
            if (drawImage) {
                clearImage();
            }
            int action = dtde.getDropAction();
          
            Transferable transferable = dtde.getTransferable();
            Point pt = dtde.getLocation();
            if (transferable
                    .isDataFlavorSupported(TransferableNode.NODE_FLAVOR)
                    && canPerformAction(tree, draggedNode, action, pt)) {
                TreePath pathTarget = tree
                        .getClosestPathForLocation(pt.x, pt.y);
                VDM_TreeNode node = (VDM_TreeNode) transferable
                        .getTransferData(TransferableNode.NODE_FLAVOR);
                VDM_TreeNode newParentNode = (VDM_TreeNode) pathTarget
                        .getLastPathComponent();
                
                               
                if (executeDrop(tree, node, newParentNode, action)) {
                    
                    dtde.acceptDrop(DnDConstants.ACTION_MOVE);
                    dtde.getDropTargetContext().dropComplete(true);

                    return;
                }
            }
            dtde.rejectDrop();
            dtde.dropComplete(false);
        } catch (Exception e) {
            System.out.println(e);
            dtde.rejectDrop();
            dtde.dropComplete(false);
        }
    }

    private final void paintImage(Point pt) {
        tree.paintImmediately(rect2D.getBounds());
        rect2D.setRect((int) pt.getX(), (int) pt.getY(), image.getWidth(),
                image.getHeight());
        tree.getGraphics().drawImage(image, (int) pt.getX(), (int) pt.getY(),
                tree);
    }

    private final void clearImage() {
        tree.paintImmediately(rect2D.getBounds());
    }

    public boolean canPerformAction(DndTree target, VDM_TreeNode draggedNode,
            int action, Point location) {
        TreePath pathTarget = target.getPathForLocation(location.x, location.y);
        if (pathTarget == null) {
            target.setSelectionPath(null);
            return (false);
        } else if (action == DnDConstants.ACTION_MOVE) {
            VDM_TreeNode parentNode = (VDM_TreeNode) pathTarget
                    .getLastPathComponent();

            if (parentNode == draggedNode.getParent()
                    && draggedNodeParent != null) {
                return (false);
            } else {
                return (true);
            }
        } else {
            return (false);
        }
    }

    public boolean executeDrop(DndTree target, VDM_TreeNode draggedNode,
            VDM_TreeNode newParentNode, int action) {
        if (action == DnDConstants.ACTION_MOVE) {
            ((DefaultTreeModel) target.getModel()).insertNodeInto(draggedNode,
                    newParentNode, newParentNode.getChildCount());
            TreePath treePath = new TreePath(draggedNode.getPath());
            target.scrollPathToVisible(treePath);
            target.setSelectionPath(treePath);
            return (true);
        }
        return (false);
    }

}
```


----------



## André Uhres (23. Okt 2005)

Hier ist ein funktionierendes Beispiel:

```
/*****************************************************************************************************
* Simple_Tree.java
 *author: Ulrich Hilger
 *[url]http://articles.lightdev.com/tree/tree_article.pdf[/url]
 *
 *Für "Drag and Drop" muss man erstmal "setDragEnabled" aufrufen
 *und einen "Transferhandler" setzen (NodeMoveTransferHandler).
 *Ausserdem braucht man einen "Transferable" (GenericTransferable), der
 *die Nodes transportieren kann.
 *Erkennt der Transferhandler eine "drag" Operation, dann erzeugt er einen "Transferable" 
 *und transportiert diesen beim drop von einem Node zum anderen.
 *
 *Ausserdem setzen wir hier auch noch ein "DropTarget".
 *DropTarget implementiert die Schnittstelle "DropTargetListener", die einige Methoden hat,
 *welche während der Drag-Operation von Swing automatisch aufgerufen werden 
 *(dragOver, dragExit, drop). Dadurch kann man "autoscroll" und 
 *"automatic node expansion" implementieren (Einzelheiten siehe Klasse "TreeDropTarget").
 *
 *Oft sind die Nodes von einem Tree nicht nur einfache Strings, sondern kompliziertere Objekte.
 *Damit beim Editieren das original Objekt nicht verloren geht und wir trotzdem 
 *den Namen eines Nodes ändern können, brauchen wir einen eigenen "TreeCellEditor" (UserTreeCellEditor).
 *Dieser wird mit "setCellEditor" an den Tree gebunden (Einzelheiten siehe Klasse "UserTreeCellEditor").
****************************************************************************************************/
package treeDnD1;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.tree.DefaultTreeCellRenderer;
public class Simple_Tree extends JFrame {
    public Simple_Tree() {
        initComponents();
        tree.setDragEnabled(true);
        tree.setShowsRootHandles(true);
        NodeMoveTransferHandler th = new NodeMoveTransferHandler();
        tree.setTransferHandler(th);
        tree.setDropTarget(new TreeDropTarget(th));
        tree.setCellEditor(new UserTreeCellEditor(tree,(DefaultTreeCellRenderer)tree.getCellRenderer()));
        tree.setEditable(true);
    }
    private void initComponents(){
        scroller = new JScrollPane();
        tree = new JTree();
        toolbar = new JToolBar();
        neuB = new JButton();
        runterB = new JButton();
        hochB = new JButton();
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        scroller.setViewportView(tree);
        getContentPane().add(scroller, BorderLayout.CENTER);
        neuB.setText("Add node");
        neuB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                addNode();
            }
        });
        toolbar.add(neuB);
        runterB.setText("Move up");
        runterB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                move(UP);
            }
        });
        toolbar.add(runterB);
        hochB.setText("Move down");
        hochB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                move(DOWN);
            }
        });
        toolbar.add(hochB);
        getContentPane().add(toolbar, BorderLayout.NORTH);
        pack();
    }
    private void addNode(){
        TreePath selectedPath = tree.getSelectionPath();
        if(selectedPath != null){
            Object o = selectedPath.getLastPathComponent();
            if(o != null){
                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) o;
                DefaultMutableTreeNode newChild = new DefaultMutableTreeNode("new node "+count++);
                ((DefaultTreeModel)tree.getModel())
                .insertNodeInto(newChild, selectedNode, selectedNode.getChildCount());
                TreePath newPath = selectedPath.pathByAddingChild(newChild);
                tree.setSelectionPath(newPath);
                tree.startEditingAtPath(newPath);
            }
        }
    }
    private void move(final int direction){
        DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
        MutableTreeNode moveNode = (MutableTreeNode) tree.getLastSelectedPathComponent();
        if(moveNode == null) return ;
        MutableTreeNode parent = (MutableTreeNode) moveNode.getParent();
        if(parent == null) return ;
        int targetIndex = model.getIndexOfChild(parent, moveNode) + direction;
        if(targetIndex < 0 || targetIndex >= parent.getChildCount()) return;
        model.removeNodeFromParent(moveNode);
        model.insertNodeInto(moveNode, parent, targetIndex);
        //make the node visible by scroll to it
        TreeNode[] nodes = model.getPathToRoot(moveNode);
        TreePath path = new TreePath(nodes);
        tree.scrollPathToVisible(path);
        //select the newly added node
        tree.setSelectionPath(path);
    }
    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Simple_Tree().setVisible(true);
            }
        });
    }
    public JButton neuB;
    public JButton runterB;
    public JButton hochB;
    public JScrollPane scroller;
    public JToolBar toolbar;
    public JTree tree;
    private int count=1;
    private final int UP = -1;
    private final int DOWN = 1;
}




/*****************************************************************************************************
 * NodeMoveTransferHandler.java
 *author: Ulrich Hilger
 *[url]http://articles.lightdev.com/tree/tree_article.pdf[/url]
 *
 * Der NodeMoveTransferHandler erweitert die Klasse "TransferHandler" und
 * überschreibt die Methode "createTransferable"
 * in der festgelegt wird, was zu Beginn der Drag-Operation geschieht.
 * Dort wird einfach nur festgestellt, ob die Komponente ein JTree ist
 * und alle selektierten Tree Paths werden in einem Array an den "GenericTransferable"
 * weitergegeben. Ausserdem wird hier die Methode "createDragImage" aufgerufen, um 
 * ein halbdurchsichtiges Bild der zu verschiebenden Nodes zu erzeugen.
 *
 * Sobald der Benutzer die Maustaste loslässt, ruft Swing automatisch die Methode "exportDone" auf.
 * Dort wird festgestellt, ob ein Node selektiert ist, und dieser dient dann als Target Node (Ziel).
 * Dann werden alle Tree Paths vom Transferable geholt und jeder wird zum neuen
 * Parent Node verschoben (addNodes), bzw. zwischen zwei Nodes eingefügt (insertNodes).
 *
 * Die Methode "createDragImage" benutzt den "cell renderer" vom Tree um die Komponenten
 * zu holen, durch die die zu verschiebenden Nodes dargestellt werden (gewöhnlich JLabels).
 * Sie erzeugt dann ein halbdurchsichtiges BufferedImage und zeichnet die Labels darauf.
 * Das fertige Bild wird über die Methode "getDragImage" an die aufrufende Methode zurückgegeben
 * (das ist die Methode "paintImage" der Klasse "TreeDropTarget").
 ****************************************************************************************************/
package treeDnD1;

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.swing.tree.*;
public class NodeMoveTransferHandler extends TransferHandler {
    public NodeMoveTransferHandler() {
        super();
    }
    protected Transferable createTransferable(JComponent c) {
        Transferable t = null;
        if(c instanceof JTree) {
            JTree tree = (JTree) c;
            t = new GenericTransferable(tree.getSelectionPaths());
            dragPath = tree.getSelectionPaths();
            createDragImage(tree);
        }
        return t;
    }
    protected void exportDone(JComponent source, Transferable data, int action) {
        if(source instanceof JTree) {
            JTree tree = (JTree) source;
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
            TreePath currentPath = tree.getSelectionPath();
            if(currentPath != null) {
                addNodes(currentPath, model, data);
            } else {
                insertNodes(tree, model, data);
            }
        }
        dragPath = null;
        super.exportDone(source, data, action);
    }
    private void addNodes(TreePath currentPath, DefaultTreeModel model, Transferable data) {
        MutableTreeNode targetNode = (MutableTreeNode) currentPath.getLastPathComponent();
        try {
            TreePath[] movedPaths = (TreePath[]) data.getTransferData(DataFlavor.stringFlavor);
            for(int i = 0; i < movedPaths.length; i++) {
                MutableTreeNode moveNode = (MutableTreeNode) movedPaths[i].getLastPathComponent();
                if(!moveNode.equals(targetNode)) {
                    MutableTreeNode oldParent = (MutableTreeNode) moveNode.getParent();
                    int oldIndex = model.getIndexOfChild(oldParent, moveNode);
                    if(oldParent == null) return ;
                    model.removeNodeFromParent(moveNode);
                    try{
                        model.insertNodeInto(moveNode, targetNode, targetNode.getChildCount());
                    }catch(IllegalArgumentException ex){
                        model.insertNodeInto(moveNode, oldParent, oldIndex);
                    }
                }
            }
        } catch (UnsupportedFlavorException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void insertNodes(JTree tree, DefaultTreeModel model, Transferable data) {
        Point location = ((TreeDropTarget) tree.getDropTarget()).getMostRecentDragLocation();
        TreePath path = tree.getClosestPathForLocation(location.x, location.y);
        MutableTreeNode targetNode = (MutableTreeNode) path.getLastPathComponent();
        MutableTreeNode parent = (MutableTreeNode) targetNode.getParent();
        try {
            TreePath[] movedPaths = (TreePath[]) data.getTransferData(DataFlavor.stringFlavor);
            for(int i = 0; i < movedPaths.length; i++) {
                MutableTreeNode moveNode = (MutableTreeNode) movedPaths[i].getLastPathComponent();
                if(!moveNode.equals(targetNode)) {
                    MutableTreeNode oldParent = (MutableTreeNode) moveNode.getParent();
                    int oldIndex = model.getIndexOfChild(oldParent, moveNode);
                    if(oldParent == null) return ;
                    model.removeNodeFromParent(moveNode);
                    try{
                        model.insertNodeInto(moveNode, parent, model.getIndexOfChild(parent, targetNode));
                    }catch(IllegalArgumentException ex){
                        model.insertNodeInto(moveNode, oldParent, oldIndex);
                    }
                }
            }
        } catch (UnsupportedFlavorException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public int getSourceActions(JComponent c) {
        return TransferHandler.MOVE;
    }
    public BufferedImage[] getDragImage() {
        return image;
    }
    private void createDragImage(JTree tree) {
        if (dragPath != null) {
            try {
                image = new BufferedImage[dragPath.length];
                for (int i = 0; i < dragPath.length; i++) {
                    Rectangle pathBounds = tree.getPathBounds(dragPath[i]);
                    TreeCellRenderer r = tree.getCellRenderer();
                    DefaultTreeModel m = (DefaultTreeModel)tree.getModel();
                    boolean nIsLeaf = m.isLeaf(dragPath[i].getLastPathComponent());
                    MutableTreeNode draggedNode = (MutableTreeNode) dragPath[i].getLastPathComponent();
                    JComponent lbl = (JComponent)r.getTreeCellRendererComponent(tree, draggedNode, false ,
                            tree.isExpanded(dragPath[i]),nIsLeaf, 0,false);
                    lbl.setBounds(pathBounds);
                    BufferedImage img = new BufferedImage(lbl.getWidth(), lbl.getHeight(),
                            BufferedImage.TYPE_INT_ARGB_PRE);
                    Graphics2D graphics = img.createGraphics();
                    graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
                    lbl.setOpaque(false);
                    lbl.paint(graphics);
                    graphics.dispose();
                    image[i] = img;
                }
            } catch (RuntimeException re) {}
        }
    }
    private TreePath[] dragPath;
    private BufferedImage[] image;
}








/*****************************************************************************************************
 * TreeDropTarget.java
 *author: Ulrich Hilger
 *[url]http://articles.lightdev.com/tree/tree_article.pdf[/url]
 *
 *TreeDropTarget erweitert die Klasse "DropTarget", welche die Schnittstelle "DropTargetListener" implementiert.
 *DropTargetListener hat einige Methoden, die während der Drag-Operation automatisch von Swing
 *aufgerufen werden (dragOver, dragExit, drop). Dadurch können wir "autoscroll" und
 *"automatic node expansion" implementieren.
 *"autoscroll" bedeutet, den sichbaren Teil vom Tree so im Fenster zu verschieben,
 *daß man immer den Node sehen kann, über dem man während der Drag-Operation angekommen ist.
 *Swing ruft während der Drag-Operation automatisch die "dragOver" Methode auf.
 *Dort können wir also auch unsere Methode "autoscroll" aufrufen.
 *In "autoscroll" prüfen wir, ob der Node, der gerade verschoben wird,
 *ausserhalb eines bestimmten Bereichs des sichtbaren Rechtecks vom Tree ist.
 *Im zutreffenden Fall wird mit "scrollRectToVisible" ein neues Rechteck sichtbar gemacht.
 *
 *Ein "collapsed node" muss "expanded" werden, damit seine Kinder sichtbar werden und
 *man auf sie einen drop ausführen kann. Die Funktion "automatic node expansion" wird ebenfalls
 *in der Methode "dragOver" implementiert. Dort rufen wir die Methode "updateDragMark" auf,
 *die mehrere Funktionen erfüllt:
 *  - sie markiert einen Node über dem man gerade einen Drag ausführt (markNode),
 *  - sie expandiert diesen Node, wenn er "collapsed" ist (auch in markNode),
 *  - sie zeichnet einen "insert marker", wenn der zu verschiebende Node gerade
 *    zwischen zwei Nodes ist (paintInsertMarker).
 *
 *Damit man immer sieht, welche Nodes gerade verschoben werden, brauchen wir eine eigene
 *"paintImage"-Methode, die ebenfalls in dragOver aufgerufen wird.
 *Die Methode "paintImage" nimmt die aktuelle Mausposition vom DropTargetDragEvent
 *und zeichnet ein eigenes Bild. Das eigentliche Bild wird aber in der Klasse "NodeMoveTransferHandler"
 *erstellt, weil nur dort bekannt ist, welche Nodes gerade verschoben werden.
 *Deshalb wurde dort die Variable "dragPath" eingeführt, die in der Methode "createTransferable"
 *initialisiert wird und in der Methde "createDragImage" benutzt wird, um das Bild vom "dragPath"
 *zu zeichnen.
 *
 ****************************************************************************************************/
package treeDnD1;
import java.awt.*;
import java.awt.dnd.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.tree.*;

public class TreeDropTarget extends DropTarget {
    public TreeDropTarget(NodeMoveTransferHandler h) {
        super();
        this.handler = h;
    }
    public void dragOver(DropTargetDragEvent dtde) {
        JTree tree = (JTree) dtde.getDropTargetContext().getComponent();
        Point loc = dtde.getLocation();
        updateDragMark(tree, loc);
        paintImage(tree, loc);
        autoscroll(tree, loc);
        super.dragOver(dtde);
    }
    public void dragExit(DropTargetDragEvent dtde) {
        clearImage((JTree) dtde.getDropTargetContext().getComponent());
        super.dragExit(dtde);
    }
    public void drop(DropTargetDropEvent dtde) {
        clearImage((JTree) dtde.getDropTargetContext().getComponent());
        super.drop(dtde);
    }
    private final void paintImage(JTree tree, Point location) {
        Point pt = new Point(location);
        BufferedImage[] image = handler.getDragImage();
        if(image != null) {
            tree.paintImmediately(rect2D.getBounds());
            rect2D.setLocation(pt.x-15, pt.y-15);
            int wRect2D = 0;
            int hRect2D= 0;
            for (int i = 0; i < image.length; i++) {
                tree.getGraphics().drawImage(image[i], pt.x-15, pt.y-15, tree);
                pt.y += image[i].getHeight();
                if(wRect2D < image[i].getWidth())
                    wRect2D = image[i].getWidth();
                hRect2D += image[i].getHeight();
            }
            rect2D.setSize(wRect2D, hRect2D);
        }
    }
    private final void clearImage(JTree tree) {
        tree.paintImmediately(rect2D.getBounds());
    }
    private Insets getAutoscrollInsets() {
        return autoscrollInsets;
    }
    private void autoscroll(JTree tree, Point cursorLocation) {
        Insets insets = getAutoscrollInsets();
        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 void updateDragMark(JTree tree, Point location) {
        mostRecentLocation = location;
        int row = tree.getRowForPath(tree.getClosestPathForLocation(location.x, location.y));
        TreePath path = tree.getPathForRow(row);
        if(path != null) {
            Rectangle rowBounds = tree.getPathBounds(path);
      /*
       * find out if we have to mark a tree node or if we
       * have to draw an insertion marker
       */
            int rby = rowBounds.y;
            int topBottomDist = insertAreaHeight / 2;
            // x = top, y = bottom of insert area
            Point topBottom = new Point(rby - topBottomDist, rby + topBottomDist);
            if(topBottom.x <= location.y && topBottom.y >= location.y) {
                // we are inside an insertArea
                paintInsertMarker(tree, location);
            } else {
                // we are inside a node
                markNode(tree, location);
            }
        }
    }
    public Point getMostRecentDragLocation() {
        return mostRecentLocation;
    }
    private void markNode(JTree tree, Point location) {
        TreePath path = tree.getClosestPathForLocation(location.x, location.y);
        if(path != null) {
            if(lastRowBounds != null) {
                Graphics g = tree.getGraphics();
                g.setColor(Color.white);
                g.drawLine(lastRowBounds.x, lastRowBounds.y,
                        lastRowBounds.x + lastRowBounds.width, lastRowBounds.y);
            }
            tree.setSelectionPath(path);
            tree.expandPath(path);
        }
    }
    private void paintInsertMarker(JTree tree, Point location) {
        Graphics g = tree.getGraphics();
        tree.clearSelection();
        int row = tree.getRowForPath(tree.getClosestPathForLocation(location.x, location.y));
        TreePath path = tree.getPathForRow(row);
        if(path != null) {
            Rectangle rowBounds = tree.getPathBounds(path);
            if(lastRowBounds != null) {
                g.setColor(Color.white);
                g.drawLine(lastRowBounds.x, lastRowBounds.y,
                        lastRowBounds.x + lastRowBounds.width, lastRowBounds.y);
            }
            if(rowBounds != null) {
                g.setColor(Color.black);
                g.drawLine(rowBounds.x, rowBounds.y, rowBounds.x + rowBounds.width, rowBounds.y);
            }
            lastRowBounds = rowBounds;
        }
    }
    private Rectangle lastRowBounds;
    private int insertAreaHeight = 8;
    private Insets autoscrollInsets = new Insets(20, 20, 20, 20);
    private Rectangle rect2D = new Rectangle();
    private NodeMoveTransferHandler handler;
    private Point mostRecentLocation;
}



/*****************************************************************************************************
* GenericTransferable.java
 *author: Ulrich Hilger
 *[url]http://articles.lightdev.com/tree/tree_article.pdf[/url]
*
 * Diese Klasse ist sehr allgemein gehalten und kann dadurch auch einen Array 
 * von selektierten Tree Paths im Objekt "data" beinhalten.
 * Das Objekt wird dem Konstruktor übergeben und über die Methode "getTransferData" wieder zurückgegeben.
 * Obschon hier "DataFlavor.stringFlavor" benutzt wird, wird dies aber in der Implementation ignoriert.
****************************************************************************************************/
package treeDnD1;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
public class GenericTransferable implements Transferable {
  public GenericTransferable(Object data) {
    super();
    this.data = data;
  }
  public DataFlavor[] getTransferDataFlavors() {
    return flavors;
  }
  public boolean isDataFlavorSupported(DataFlavor flavor) {
    return true;
  }
  public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
    return data;
  }
  private Object data;
  private static final DataFlavor[] flavors = new DataFlavor[1];
  static {
    flavors[0] = DataFlavor.stringFlavor;
  }
}




/*****************************************************************************************************
* UserTreeCellEditor.java
 *author: Ulrich Hilger
 *[url]http://articles.lightdev.com/tree/tree_article.pdf[/url]
*
 *Oft sind die Nodes von einem Tree nicht nur einfache Strings, sondern kompliziertere Objekte.
 *Damit beim Editieren das original Userobjekt nicht verloren geht und wir trotzdem 
 *den Namen eines Nodes ändern können, brauchen wir einen eigenen "TreeCellEditor" (UserTreeCellEditor).
 *In "UserTreeCellEditor" wird als Beispiel angenommen, daß die Objekte vom Tree 
 *die Schnittstelle "HierarchicalItem" implementieren. 
 *Die Methode "getTreeCellEditorComponent"  speichert die Referenz zum Userobjekt
 *in der privaten Variablen "item". 
 *Die Methode "getCellEditorValue" speichert erstmal den editierten Wert im Userobjekt 
 *(mit "item.setData(value)") und gibt dann das ganze Userobjekt zurück anstatt nur den editierten String. 
 *Dabei wird angenommen, daß die "toString"-Methode vom Userobjekt den editierten Wert zurückgibt.
****************************************************************************************************/
package treeDnD1;
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
public class UserTreeCellEditor extends DefaultTreeCellEditor {
    public UserTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
        super(tree, renderer);
    }
    public Object getCellEditorValue() {
        Object returnValue = null;
        Object value = super.getCellEditorValue();
        if(item == null) {
            returnValue = value;
        } else {
            item.setData(value);
            returnValue = item;
        }
        return returnValue;
    }
    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
        if(value instanceof DefaultMutableTreeNode) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            Object userObject = node.getUserObject();
            if(userObject instanceof HierarchicalItem) {
                item = (HierarchicalItem) node.getUserObject();
            }
        }
        return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
    }
    private HierarchicalItem item;
}
interface HierarchicalItem{
    public abstract void setData(Object data);
    public abstract Object getData();
    public abstract void setId(Object id);
    public abstract Object getId();
    public abstract Object getParentId();
    public abstract void setParentId(Object parentId);
    public abstract boolean isRoot();
}
```


----------



## André Uhres (23. Okt 2005)

> Hier ist ein funktionierendes Beispiel: 

Ich habe noch Verbesserungen in obigem Beispiel vorgenommen.
Jetzt läuft's rund.


----------



## André Uhres (3. Jun 2007)

Auf Anfrage von nerothnis habe ich jetzt am Anfang jeder Klasse jeweils eine kurze Beschreibung eingefügt  :wink: 
www.java-forum.org/de/viewtopic.php?p=139820#139820


----------



## André Uhres (5. Jun 2007)

Ich habe die Klassen *NodeMoveTransferHandler* und *TreeDropTarget* jetzt noch ein wenig angepasst:
für den Fall, wo man mehrere Nodes gleichzeitig verschieben will, sieht man während der 
Drag-Operation jetzt  *alle *zu verschiebenden Nodes, anstatt nur den ersten Node.


----------

