alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.explorer.view;

import java.awt.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.geom.Line2D.Double;
import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;

import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreePath;
import org.openide.ErrorManager;
import org.openide.nodes.Children;
import org.openide.nodes.Index;
import org.openide.nodes.Node;
import org.openide.util.datatransfer.PasteType;


/** Implementation of drop support for asociated Tree View.
*
* @author Dafe Simonek, Jiri Rechtacek
*/
final class TreeViewDropSupport implements DropTargetListener, Runnable {

    // Attributes

    /** true if support is active, false otherwise */
    boolean active = false;
    
    boolean dropTargetPopupAllowed;

    /** Drop target asociated with the tree */
    DropTarget dropTarget;

    /** Node area which we were during
    * DnD operation. */
    Rectangle lastNodeArea;
    
    private int upperNodeIdx = -1;
    private int lowerNodeIdx = -1;
    
    /** Swing Timer for expand node's parent with delay time. */
    Timer timer;
    
    /** Glass pane for JTree which is associate with this class. */
    DropGlassPane dropPane;
    
    final static protected int FUSSY_POINTING = 3;
    final static private int DELAY_TIME_FOR_EXPAND = 1000;
    final static private int SHIFT_DOWN = -1;
    final static private int SHIFT_RIGHT = 10;
    final static private int SHIFT_LEFT = 15;
    
    private int pointAt = DragDropUtilities.NODE_CENTRAL;

    // Associations

    /** View manager. */
    protected TreeView view;

    /** The component we are supporting with drop support */
    protected JTree tree;

    // Operations
    /** Creates new TreeViewDropSupport */
    public TreeViewDropSupport (TreeView view, JTree tree, boolean dropTargetPopupAllowed) {
        this.view = view;
        this.tree = tree;
        this.dropTargetPopupAllowed = dropTargetPopupAllowed;
    }
	 
    public void setDropTargetPopupAllowed(boolean value) {
        dropTargetPopupAllowed=value;
    }
    
    public boolean isDropTargetPopupAllowed() {
        return dropTargetPopupAllowed;
    }
    
    /** User is starting to drag over us */
    public void dragEnter (DropTargetDragEvent dtde) {
        checkStoredGlassPane ();
        // set a status and cursor of dnd action
        doDragOver (dtde);
    }
    
    /** User drags over us */
    public void dragOver (DropTargetDragEvent dtde) {
        // bugfix #34483; jdk1.4.1 on w2k could calls dragOver() before dragEnter()
        // (jkdbug fixed in 1.4.2)
        // this check make dragOver/Enter more robust
        checkStoredGlassPane ();
        // set a status and cursor of dnd action
        doDragOver (dtde);
    }
    
    private void checkStoredGlassPane () {
        // remember current glass pane to set back at end of dragging over this compoment
        if (!DropGlassPane.isOriginalPaneStored ()) {
            Component comp = tree.getRootPane ().getGlassPane ();
            DropGlassPane.setOriginalPane (tree, comp, comp.isVisible ());
            
            // set glass pane for paint selection line
            dropPane = DropGlassPane.getDefault (tree);
            tree.getRootPane ().setGlassPane (dropPane);
            dropPane.revalidate();
            dropPane.setVisible (true);
        }
    }

    /** Process events dragEnter or dragOver. */
    private void doDragOver (DropTargetDragEvent dtde) {
        
        // 1. test if I'm over any node
        TreePath tp = getTreePath(dtde);
        if (tp == null) {
            dtde.rejectDrag();
            removeDropLine ();
            return ;
        }
        
        // 2. find node for drop
        Point p = dtde.getLocation ();
        Node dropNode = getNodeForDrop (p);
        // if I haven't any node for drop then reject drop
        if (dropNode==null) {
            dtde.rejectDrag ();
            removeDropLine ();
            return ;
        }
        
        Rectangle nodeArea = tree.getPathBounds (tp);
        int endPointX = nodeArea.x + nodeArea.width;
        int row = tree.getRowForPath (tp);
        if (nodeArea!=null) {
            pointAt = DragDropUtilities.NODE_CENTRAL;
            if (p.y <= nodeArea.y+FUSSY_POINTING) {
                // don't get line above root
                if (row != 0) {
                    // point above node
                    pointAt = DragDropUtilities.NODE_UP;
                    TreePath upPath = tree.getPathForRow (row-1);
                    if (upPath!=null && !upPath.equals (tp)) {
                        endPointX = Math.max (nodeArea.x + nodeArea.width, 
                                        tree.getPathBounds (upPath).x + tree.getPathBounds (upPath).width);
                    }
                    // drop candidate is parent
                    if (dropNode.getParentNode ()!=null) {
                        dropNode = dropNode.getParentNode ();
                        tp = null;
                    }
                }
            } else if (p.y >= (nodeArea.y+nodeArea.height-FUSSY_POINTING)) {
                // exclude expanded folder
                if (!view.isExpanded (dropNode)) {
                    // point bellow node
                    pointAt = DragDropUtilities.NODE_DOWN;
                    TreePath downPath = tree.getPathForRow (row+1);
                    if (downPath!=null && !downPath.equals (tp)) {
                        endPointX = Math.max (nodeArea.x + nodeArea.width, 
                                        tree.getPathBounds (downPath).x + tree.getPathBounds (downPath).width);
                    }
                    // drop candidate is parent
                    if (dropNode.getParentNode ()!=null) {
                        dropNode = dropNode.getParentNode ();
                        tp = null;
                    }
                }
            }
        }
        endPointX = endPointX + SHIFT_RIGHT;
        
        // 2.b. check index cookie
        Index indexCookie = (Index)dropNode.getCookie (Index.class);
        if (indexCookie!=null) {
            if (pointAt==DragDropUtilities.NODE_UP) {
                lowerNodeIdx = indexCookie.indexOf (getNodeForDrop (p));
                upperNodeIdx = lowerNodeIdx - 1;
            } else if (pointAt==DragDropUtilities.NODE_DOWN) {
                upperNodeIdx = indexCookie.indexOf (getNodeForDrop (p));
                lowerNodeIdx = upperNodeIdx + 1;
            }
        }
        
        // 3. expand with a delay
        if ((timer==null || !timer.isRunning ()) &&
                    dropNode!=null &&
                    !dropNode.isLeaf() &&
                    !view.isExpanded (dropNode)) {
                        
            // ok, let's expand in a while
            // node is candidate for expand
            final Node cn = dropNode;
            
            // remove old timer
            removeTimer ();
            
            // create new timer
            timer = new Timer (DELAY_TIME_FOR_EXPAND, new ActionListener () {
                final public void actionPerformed (ActionEvent e) {
                    view.expandNode (cn);
                }
            });
            timer.setRepeats (false);
            timer.start ();
        }

        // 4. present node for drop

        // prepare selection or line
        if (pointAt==DragDropUtilities.NODE_CENTRAL) {
            // no line
            dropPane.setDropLine (null);
        } else {
            // line and selection of parent if any
            if (pointAt==DragDropUtilities.NODE_UP) {
                Line2D line = new Double (nodeArea.x-SHIFT_LEFT, nodeArea.y+SHIFT_DOWN,
                                    endPointX, nodeArea.y+SHIFT_DOWN);
                convertBoundsAndSetDropLine (line);
                // enlagre node area with area for line
                Rectangle lineArea = new Rectangle (nodeArea.x-SHIFT_LEFT, nodeArea.y+SHIFT_DOWN-3,
                                            endPointX-nodeArea.x+SHIFT_LEFT, 5);
                nodeArea = (Rectangle)nodeArea.createUnion (lineArea);
            } else {
                Line2D line = new Double (nodeArea.x-SHIFT_LEFT,
                                                 nodeArea.y+nodeArea.height+SHIFT_DOWN,
                                    endPointX, nodeArea.y+nodeArea.height+SHIFT_DOWN);
                convertBoundsAndSetDropLine (line);
                // enlagre node area with area for line
                Rectangle lineArea = new Rectangle (nodeArea.x-SHIFT_LEFT, nodeArea.y+nodeArea.height,
                                            endPointX-nodeArea.x+SHIFT_LEFT, SHIFT_DOWN+3);
                nodeArea = (Rectangle)nodeArea.createUnion (lineArea);
                //System.out.println("OLD + LINE: "+lineArea+" = AREA: "+nodeArea);
            }
            // the parent node won't be selected
            /*// select parent and enlarge paint area
            if (tp.getParentPath ()!=null) {
                tp = tp.getParentPath ();
            }
            nodeArea = (Rectangle)nodeArea.createUnion (tree.getPathBounds (tp));*/
        }
        
        // back normal view w/o any selecetion nor line
        if ((lastNodeArea != null) && (!lastNodeArea.equals (nodeArea))) {
            NodeRenderer.dragExit ();
            repaint (lastNodeArea);
        }
        
        // paint new state
        if (!nodeArea.equals (lastNodeArea)) {
            if (tp!=null)
                NodeRenderer.dragEnter (tp.getLastPathComponent ());
            repaint (nodeArea);
            lastNodeArea = nodeArea;
            removeTimer ();
        }
        
        // 5 show to cursor belong to state
        if (canDrop (dropNode, dtde.getDropAction ())) {
            // ok, can accept
            dtde.acceptDrag (dtde.getDropAction ());
        } else {
            // can only reorder?
            if (canReorder (dropNode, ExplorerDnDManager.getDefault ().getDraggedNodes ())) {
                // ok, can accept only reoder
                dtde.acceptDrag (dtde.getDropAction ());
            } else {
                dtde.rejectDrag ();
            }
        }
    }
    
    /** Repaints TreeView, the given rectangle is enlarged for 5 pixels
     * because some parts was not repainted correctly.
     * @param Rectangle r rectangle which will be repainted.*/
    private void repaint (Rectangle r) {
        tree.repaint (r.x-5, r.y-5, r.width+10, r.height+10);
    }
    
    /** Converts line's bounds by the bounds of the root pane. Drop glass pane
     * is over this root pane. After covert a given line is set to drop glass pane.
     * @param line line for show in drop glass pane */
    private void convertBoundsAndSetDropLine (final Line2D line) {
        int x1 = (int)line.getX1 (), x2 = (int)line.getX2 ();
        int y1 = (int)line.getY1 (), y2 = (int)line.getY2 ();
        Point p1 = SwingUtilities.convertPoint (tree, x1, y1, tree.getRootPane ());
        Point p2 = SwingUtilities.convertPoint (tree, x2, y2, tree.getRootPane ());
        line.setLine (p1, p2);
        dropPane.setDropLine (line);
    }
    
    /** Removes timer and all listeners. */
    private void removeTimer () {
        if (timer!=null) {
            ActionListener[] l = (ActionListener[])timer.getListeners (ActionListener.class);
            for (int i=0; i= 0 && idx < perm.length) {
                        indexes[indexesLength++] = idx;
                    }
                }

                // XXX: normally indexes of dragged nodes should be in ascending order, but
                // it seems that Tree.getSelectionPaths doesn't keep this order
                Arrays.sort(indexes);

                if (lNode < 0 || uNode >= perm.length || indexesLength == 0) {
                    return;
                }

                int k = 0;
                for (int i = 0; i < perm.length; i++) {
                    if (i <= uNode) {
                        if (!containsNumber(indexes, indexesLength, i)) {
                            perm[i] = k++;
                        }

                        if (i == uNode) {
                            for (int j = 0; j < indexesLength; j++) {
                                if (indexes[j] <= uNode) {
                                    perm[indexes[j]] = k++;
                                }
                            }
                        }
                    } else {
                        if (i == lNode) {
                            for (int j = 0; j < indexesLength; j++) {
                                if (indexes[j] >= lNode) {
                                    perm[indexes[j]] = k++;
                                }
                            }
                        }

                        if (!containsNumber(indexes, indexesLength, i)) {
                            perm[i] = k++;
                        }
                    }
                }
                
                // check for identity permutation
                for (int i = 0; i < perm.length; i++) {
                    if (perm[i] != i) {
                        indexCookie.reorder(perm);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            // Pending: add annotation or remove try/catch block
            ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, e);
        }
    }
    
    private boolean containsNumber(int [] arr, int arrLength, int n) {
        for (int i = 0; i < arrLength; i++) {
            if (arr[i] == n) {
                return true;
            }
        }
        return false;
    }
    
    private Node[] findDropedNodes (Node folder, Node[] dragNodes) {
        if (folder==null||dragNodes.length==0) {
            return null;
        }
        Node[] dropNodes = new Node[dragNodes.length];
        Children children = folder.getChildren ();
        for (int i=0; i=DnDConstants.ACTION_COPY) {
                ptCopy = DragDropUtilities.getPasteTypes (dropNode,
                        ExplorerDnDManager.getDefault ().getDraggedTransferable (false));
            }
            TreeSet setPasteTypes = new TreeSet (new Comparator () {
                public int compare (Object obj1, Object obj2) {
                    return ((PasteType)obj1).getName ().compareTo (((PasteType)obj2).getName ());
                    
                   // have to fix: the different actions can have same name!!!
                    /*int res = ((PasteType)obj1).getName ().compareTo (((PasteType)obj2).getName ());
                    System.out.println("res1: "+res);
                    if (res == 0) {
                        res = System.identityHashCode(obj1)-System.identityHashCode(obj2);
                    }
                    System.out.println("res2: "+res);
                    return res;*/
                }}
            );
            for (int i=0; i
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.