JTreeUtil.java Source code

Java tutorial

Introduction

Here is the source code for JTreeUtil.java

Source

/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Insets;
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.awt.image.BufferedImage;
import java.util.Arrays;

import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.tree.TreePath;

/* This comes from "Denis" at http://forum.java.sun.com/thread.jspa?forumID=57&threadID=296255 */

/*
 * My program use 4 classes. The application displays two trees.
 * You can drag (move by default or copy with ctrl pressed) a node from the left tree to the right one, from the right to the left and inside the same tree.
 * The rules for moving are :
 *   - you can't move the root
 *   - you can't move the selected node to its subtree (in the same tree).
 *   - you can't move the selected node to itself (in the same tree).
 *   - you can't move the selected node to its parent (in the same tree).
 *   - you can move a node to anywhere you want according to the 4 previous rules.
 *  The rules for copying are :
 *   - you can copy a node to anywhere you want.
 *
 * In the implementation I used DnD version of Java 1.3 because in 1.4 the DnD is too restrictive :
 * you can't do what you want (displaying the image of the node while dragging, changing the cursor
 * according to where you are dragging, etc...). In 1.4, the DnD is based on the 1.3 version but
 * it is too encapsulated.
 */

public class JTreeUtil {
    private static final Insets DEFAULT_INSETS = new Insets(20, 20, 20, 20);
    private static final DataFlavor NODE_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, "Node");

    private static Object draggedNode;
    private static BufferedImage image = null; // buff image

    private static class TransferableNode implements Transferable {
        private Object node;
        private DataFlavor[] flavors = { NODE_FLAVOR };

        public TransferableNode(Object nd) {
            node = nd;
        }

        public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
            if (flavor == NODE_FLAVOR) {
                return node;
            } else {
                throw new UnsupportedFlavorException(flavor);
            }
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return Arrays.asList(flavors).contains(flavor);
        }
    }

    /*
     * This class is the most important. It manages all the DnD behavior. It is
     * abstract because it contains two abstract methods:
     *   public abstract boolean canPerformAction(JTree target,
     *     Object draggedNode, int action, Point location);
     *   public abstract boolean executeDrop(DNDTree tree,
     *     Object draggedNode, Object newParentNode, int action);
     * we have to override to give the required behavior of DnD in your tree.
     */
    private static class TreeTransferHandler
            implements DragGestureListener, DragSourceListener, DropTargetListener {
        private JTree tree;
        private JTreeDragController controller;
        private DragSource dragSource; // dragsource
        private Rectangle rect2D = new Rectangle();
        private boolean drawImage;

        protected TreeTransferHandler(JTree tree, JTreeDragController controller, int action, boolean drawIcon) {
            this.tree = tree;
            this.controller = controller;
            drawImage = drawIcon;
            dragSource = new DragSource();
            dragSource.createDefaultDragGestureRecognizer(tree, action, this);
        }

        /* Methods for DragSourceListener */
        public void dragDropEnd(DragSourceDropEvent dsde) {
            /*
            if(dsde.getDropSuccess()
                && 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 = path.getLastPathComponent();
                if (drawImage) {
                    Rectangle pathBounds = tree.getPathBounds(path); // getpathbounds
                                                                     // of
                                                                     // selectionpath
                    JComponent lbl = (JComponent) tree.getCellRenderer().getTreeCellRendererComponent(tree,
                            draggedNode, false, tree.isExpanded(path),
                            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 (controller.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();
            autoscroll(tree, pt);
            if (drawImage) {
                paintImage(pt);
            }
            if (controller.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 (controller.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(NODE_FLAVOR)
                        && controller.canPerformAction(tree, draggedNode, action, pt)) {
                    TreePath pathTarget = tree.getPathForLocation(pt.x, pt.y);
                    Object node = transferable.getTransferData(NODE_FLAVOR);
                    Object newParentNode = pathTarget.getLastPathComponent();
                    if (controller.executeDrop(tree, node, newParentNode, action)) {
                        dtde.acceptDrop(action);
                        dtde.dropComplete(true);
                        return;
                    }
                }
                dtde.rejectDrop();
                dtde.dropComplete(false);
            } catch (Exception 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 static void configureDragAndDrop(JTree tree, JTreeDragController controller) {
        tree.setAutoscrolls(true);
        new TreeTransferHandler(tree, controller, DnDConstants.ACTION_COPY_OR_MOVE, true);
    }

    private static 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);
        }
    }
}
/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

/* This comes from "Denis" at http://forum.java.sun.com/thread.jspa?forumID=57&threadID=296255 */

/*
 * My program use 4 classes. The application displays two trees.
 * You can drag (move by default or copy with ctrl pressed) a node from the left tree to the right one, from the right to the left and inside the same tree.
 * The rules for moving are :
 *   - you can't move the root
 *   - you can't move the selected node to its subtree (in the same tree).
 *   - you can't move the selected node to itself (in the same tree).
 *   - you can't move the selected node to its parent (in the same tree).
 *   - you can move a node to anywhere you want according to the 4 previous rules.
 *  The rules for copying are :
 *   - you can copy a node to anywhere you want.
 *
 * In the implementation I used DnD version of Java 1.3 because in 1.4 the DnD is too restrictive :
 * you can't do what you want (displaying the image of the node while dragging, changing the cursor
 * according to where you are dragging, etc...). In 1.4, the DnD is based on the 1.3 version but
 * it is too encapsulated.
 */

interface JTreeDragController {
    public boolean canPerformAction(JTree target, Object draggedNode, int action, Point location);

    public boolean executeDrop(JTree tree, Object draggedNode, Object newParentNode, int action);
}