Source Code Cross Referenced for VariableHeightLayoutCache.java in  » 6.0-JDK-Core » swing » javax » swing » tree » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.tree 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1998-2004 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        package javax.swing.tree;
0027
0028        import javax.swing.event.TreeModelEvent;
0029        import java.awt.Dimension;
0030        import java.awt.Rectangle;
0031        import java.util.Enumeration;
0032        import java.util.Hashtable;
0033        import java.util.NoSuchElementException;
0034        import java.util.Stack;
0035        import java.util.Vector;
0036
0037        /**
0038         * NOTE: This will become more open in a future release.
0039         * <p>
0040         * <strong>Warning:</strong>
0041         * Serialized objects of this class will not be compatible with
0042         * future Swing releases. The current serialization support is
0043         * appropriate for short term storage or RMI between applications running
0044         * the same version of Swing.  As of 1.4, support for long term storage
0045         * of all JavaBeans<sup><font size="-2">TM</font></sup>
0046         * has been added to the <code>java.beans</code> package.
0047         * Please see {@link java.beans.XMLEncoder}.
0048         *
0049         * @version 1.28 05/05/07
0050         * @author Rob Davis
0051         * @author Ray Ryan
0052         * @author Scott Violet
0053         */
0054
0055        public class VariableHeightLayoutCache extends AbstractLayoutCache {
0056            /**
0057             * The array of nodes that are currently visible, in the order they
0058             * are displayed.
0059             */
0060            private Vector visibleNodes;
0061
0062            /**
0063             * This is set to true if one of the entries has an invalid size.
0064             */
0065            private boolean updateNodeSizes;
0066
0067            /**
0068             * The root node of the internal cache of nodes that have been shown.
0069             * If the treeModel is vending a network rather than a true tree,
0070             * there may be one cached node for each path to a modeled node.
0071             */
0072            private TreeStateNode root;
0073
0074            /**
0075             * Used in getting sizes for nodes to avoid creating a new Rectangle
0076             * every time a size is needed.
0077             */
0078            private Rectangle boundsBuffer;
0079
0080            /**
0081             * Maps from <code>TreePath</code> to a <code>TreeStateNode</code>.
0082             */
0083            private Hashtable treePathMapping;
0084
0085            /**
0086             * A stack of stacks.
0087             */
0088            private Stack tempStacks;
0089
0090            public VariableHeightLayoutCache() {
0091                super ();
0092                tempStacks = new Stack();
0093                visibleNodes = new Vector();
0094                boundsBuffer = new Rectangle();
0095                treePathMapping = new Hashtable();
0096            }
0097
0098            /**
0099             * Sets the <code>TreeModel</code> that will provide the data.
0100             *
0101             * @param newModel the <code>TreeModel</code> that is to provide the data
0102             * @beaninfo
0103             *        bound: true
0104             *  description: The TreeModel that will provide the data.
0105             */
0106            public void setModel(TreeModel newModel) {
0107                super .setModel(newModel);
0108                rebuild(false);
0109            }
0110
0111            /**
0112             * Determines whether or not the root node from
0113             * the <code>TreeModel</code> is visible.
0114             *
0115             * @param rootVisible true if the root node of the tree is to be displayed
0116             * @see #rootVisible
0117             * @beaninfo
0118             *        bound: true
0119             *  description: Whether or not the root node
0120             *               from the TreeModel is visible.
0121             */
0122            public void setRootVisible(boolean rootVisible) {
0123                if (isRootVisible() != rootVisible && root != null) {
0124                    if (rootVisible) {
0125                        root.updatePreferredSize(0);
0126                        visibleNodes.insertElementAt(root, 0);
0127                    } else if (visibleNodes.size() > 0) {
0128                        visibleNodes.removeElementAt(0);
0129                        if (treeSelectionModel != null)
0130                            treeSelectionModel.removeSelectionPath(root
0131                                    .getTreePath());
0132                    }
0133                    if (treeSelectionModel != null)
0134                        treeSelectionModel.resetRowSelection();
0135                    if (getRowCount() > 0)
0136                        getNode(0).setYOrigin(0);
0137                    updateYLocationsFrom(0);
0138                    visibleNodesChanged();
0139                }
0140                super .setRootVisible(rootVisible);
0141            }
0142
0143            /**
0144             * Sets the height of each cell.  If the specified value
0145             * is less than or equal to zero the current cell renderer is
0146             * queried for each row's height.
0147             *
0148             * @param rowHeight the height of each cell, in pixels
0149             * @beaninfo
0150             *        bound: true
0151             *  description: The height of each cell.
0152             */
0153            public void setRowHeight(int rowHeight) {
0154                if (rowHeight != getRowHeight()) {
0155                    super .setRowHeight(rowHeight);
0156                    invalidateSizes();
0157                    this .visibleNodesChanged();
0158                }
0159            }
0160
0161            /**
0162             * Sets the renderer that is responsible for drawing nodes in the tree.
0163             * @param nd the renderer
0164             */
0165            public void setNodeDimensions(NodeDimensions nd) {
0166                super .setNodeDimensions(nd);
0167                invalidateSizes();
0168                visibleNodesChanged();
0169            }
0170
0171            /**
0172             * Marks the path <code>path</code> expanded state to
0173             * <code>isExpanded</code>.
0174             * @param path the <code>TreePath</code> of interest
0175             * @param isExpanded true if the path should be expanded, otherwise false
0176             */
0177            public void setExpandedState(TreePath path, boolean isExpanded) {
0178                if (path != null) {
0179                    if (isExpanded)
0180                        ensurePathIsExpanded(path, true);
0181                    else {
0182                        TreeStateNode node = getNodeForPath(path, false, true);
0183
0184                        if (node != null) {
0185                            node.makeVisible();
0186                            node.collapse();
0187                        }
0188                    }
0189                }
0190            }
0191
0192            /**
0193             * Returns true if the path is expanded, and visible.
0194             * @return true if the path is expanded and visible, otherwise false
0195             */
0196            public boolean getExpandedState(TreePath path) {
0197                TreeStateNode node = getNodeForPath(path, true, false);
0198
0199                return (node != null) ? (node.isVisible() && node.isExpanded())
0200                        : false;
0201            }
0202
0203            /**
0204             * Returns the <code>Rectangle</code> enclosing the label portion
0205             * into which the item identified by <code>path</code> will be drawn.
0206             *
0207             * @param path  the path to be drawn
0208             * @param placeIn the bounds of the enclosing rectangle
0209             * @return the bounds of the enclosing rectangle or <code>null</code>
0210             *    if the node could not be ascertained
0211             */
0212            public Rectangle getBounds(TreePath path, Rectangle placeIn) {
0213                TreeStateNode node = getNodeForPath(path, true, false);
0214
0215                if (node != null) {
0216                    if (updateNodeSizes)
0217                        updateNodeSizes(false);
0218                    return node.getNodeBounds(placeIn);
0219                }
0220                return null;
0221            }
0222
0223            /**
0224             * Returns the path for <code>row</code>.  If <code>row</code>
0225             * is not visible, <code>null</code> is returned.
0226             *
0227             * @param row the location of interest
0228             * @return the path for <code>row</code>, or <code>null</code>
0229             * if <code>row</code> is not visible
0230             */
0231            public TreePath getPathForRow(int row) {
0232                if (row >= 0 && row < getRowCount()) {
0233                    return getNode(row).getTreePath();
0234                }
0235                return null;
0236            }
0237
0238            /**
0239             * Returns the row where the last item identified in path is visible.
0240             * Will return -1 if any of the elements in path are not
0241             * currently visible.
0242             * 
0243             * @param path the <code>TreePath</code> of interest
0244             * @return the row where the last item in path is visible
0245             */
0246            public int getRowForPath(TreePath path) {
0247                if (path == null)
0248                    return -1;
0249
0250                TreeStateNode visNode = getNodeForPath(path, true, false);
0251
0252                if (visNode != null)
0253                    return visNode.getRow();
0254                return -1;
0255            }
0256
0257            /**
0258             * Returns the number of visible rows.
0259             * @return the number of visible rows
0260             */
0261            public int getRowCount() {
0262                return visibleNodes.size();
0263            }
0264
0265            /**
0266             * Instructs the <code>LayoutCache</code> that the bounds for
0267             * <code>path</code> are invalid, and need to be updated.
0268             *
0269             * @param path the <code>TreePath</code> which is now invalid
0270             */
0271            public void invalidatePathBounds(TreePath path) {
0272                TreeStateNode node = getNodeForPath(path, true, false);
0273
0274                if (node != null) {
0275                    node.markSizeInvalid();
0276                    if (node.isVisible())
0277                        updateYLocationsFrom(node.getRow());
0278                }
0279            }
0280
0281            /**
0282             * Returns the preferred height.
0283             * @return the preferred height
0284             */
0285            public int getPreferredHeight() {
0286                // Get the height
0287                int rowCount = getRowCount();
0288
0289                if (rowCount > 0) {
0290                    TreeStateNode node = getNode(rowCount - 1);
0291
0292                    return node.getYOrigin() + node.getPreferredHeight();
0293                }
0294                return 0;
0295            }
0296
0297            /**
0298             * Returns the preferred width and height for the region in
0299             * <code>visibleRegion</code>.
0300             *
0301             * @param bounds  the region being queried
0302             */
0303            public int getPreferredWidth(Rectangle bounds) {
0304                if (updateNodeSizes)
0305                    updateNodeSizes(false);
0306
0307                return getMaxNodeWidth();
0308            }
0309
0310            /**
0311             * Returns the path to the node that is closest to x,y.  If
0312             * there is nothing currently visible this will return <code>null</code>,
0313             * otherwise it will always return a valid path. 
0314             * If you need to test if the
0315             * returned object is exactly at x, y you should get the bounds for
0316             * the returned path and test x, y against that.
0317             *
0318             * @param x  the x-coordinate 
0319             * @param y  the y-coordinate 
0320             * @return the path to the node that is closest to x, y
0321             */
0322            public TreePath getPathClosestTo(int x, int y) {
0323                if (getRowCount() == 0)
0324                    return null;
0325
0326                if (updateNodeSizes)
0327                    updateNodeSizes(false);
0328
0329                int row = getRowContainingYLocation(y);
0330
0331                return getNode(row).getTreePath();
0332            }
0333
0334            /**
0335             * Returns an <code>Enumerator</code> that increments over the visible paths
0336             * starting at the passed in location. The ordering of the enumeration
0337             * is based on how the paths are displayed.
0338             *
0339             * @param path the location in the <code>TreePath</code> to start
0340             * @return an <code>Enumerator</code> that increments over the visible
0341             *     paths
0342             */
0343            public Enumeration<TreePath> getVisiblePathsFrom(TreePath path) {
0344                TreeStateNode node = getNodeForPath(path, true, false);
0345
0346                if (node != null) {
0347                    return new VisibleTreeStateNodeEnumeration(node);
0348                }
0349                return null;
0350            }
0351
0352            /**
0353             * Returns the number of visible children for <code>path</code>.
0354             * @return the number of visible children for <code>path</code>
0355             */
0356            public int getVisibleChildCount(TreePath path) {
0357                TreeStateNode node = getNodeForPath(path, true, false);
0358
0359                return (node != null) ? node.getVisibleChildCount() : 0;
0360            }
0361
0362            /**
0363             * Informs the <code>TreeState</code> that it needs to recalculate
0364             * all the sizes it is referencing.
0365             */
0366            public void invalidateSizes() {
0367                if (root != null)
0368                    root.deepMarkSizeInvalid();
0369                if (!isFixedRowHeight() && visibleNodes.size() > 0) {
0370                    updateNodeSizes(true);
0371                }
0372            }
0373
0374            /**
0375             * Returns true if the value identified by <code>path</code> is
0376             * currently expanded.
0377             * @return true if the value identified by <code>path</code> is
0378             *    currently expanded
0379             */
0380            public boolean isExpanded(TreePath path) {
0381                if (path != null) {
0382                    TreeStateNode lastNode = getNodeForPath(path, true, false);
0383
0384                    return (lastNode != null && lastNode.isExpanded());
0385                }
0386                return false;
0387            }
0388
0389            //
0390            // TreeModelListener methods
0391            //
0392
0393            /**
0394             * Invoked after a node (or a set of siblings) has changed in some
0395             * way. The node(s) have not changed locations in the tree or
0396             * altered their children arrays, but other attributes have
0397             * changed and may affect presentation. Example: the name of a
0398             * file has changed, but it is in the same location in the file
0399             * system.
0400             *
0401             * <p><code>e.path</code> returns the path the parent of the
0402             * changed node(s).
0403             *
0404             * <p><code>e.childIndices</code> returns the index(es) of the
0405             * changed node(s).
0406             *
0407             * @param e the <code>TreeModelEvent</code> of interest
0408             */
0409            public void treeNodesChanged(TreeModelEvent e) {
0410                if (e != null) {
0411                    int changedIndexs[];
0412                    TreeStateNode changedNode;
0413
0414                    changedIndexs = e.getChildIndices();
0415                    changedNode = getNodeForPath(e.getTreePath(), false, false);
0416                    if (changedNode != null) {
0417                        Object changedValue = changedNode.getValue();
0418
0419                        /* Update the size of the changed node, as well as all the
0420                           child indexs that are passed in. */
0421                        changedNode.updatePreferredSize();
0422                        if (changedNode.hasBeenExpanded()
0423                                && changedIndexs != null) {
0424                            int counter;
0425                            TreeStateNode changedChildNode;
0426
0427                            for (counter = 0; counter < changedIndexs.length; counter++) {
0428                                changedChildNode = (TreeStateNode) changedNode
0429                                        .getChildAt(changedIndexs[counter]);
0430                                /* Reset the user object. */
0431                                changedChildNode.setUserObject(treeModel
0432                                        .getChild(changedValue,
0433                                                changedIndexs[counter]));
0434                                changedChildNode.updatePreferredSize();
0435                            }
0436                        } else if (changedNode == root) {
0437                            // Null indicies for root indicates it changed.
0438                            changedNode.updatePreferredSize();
0439                        }
0440                        if (!isFixedRowHeight()) {
0441                            int aRow = changedNode.getRow();
0442
0443                            if (aRow != -1)
0444                                this .updateYLocationsFrom(aRow);
0445                        }
0446                        this .visibleNodesChanged();
0447                    }
0448                }
0449            }
0450
0451            /**
0452             * Invoked after nodes have been inserted into the tree.
0453             *
0454             * <p><code>e.path</code> returns the parent of the new nodes.
0455             * <p><code>e.childIndices</code> returns the indices of the new nodes in
0456             * ascending order.
0457             *
0458             * @param e the <code>TreeModelEvent</code> of interest
0459             */
0460            public void treeNodesInserted(TreeModelEvent e) {
0461                if (e != null) {
0462                    int changedIndexs[];
0463                    TreeStateNode changedParentNode;
0464
0465                    changedIndexs = e.getChildIndices();
0466                    changedParentNode = getNodeForPath(e.getTreePath(), false,
0467                            false);
0468                    /* Only need to update the children if the node has been
0469                       expanded once. */
0470                    // PENDING(scott): make sure childIndexs is sorted!
0471                    if (changedParentNode != null && changedIndexs != null
0472                            && changedIndexs.length > 0) {
0473                        if (changedParentNode.hasBeenExpanded()) {
0474                            boolean makeVisible;
0475                            int counter;
0476                            Object changedParent;
0477                            TreeStateNode newNode;
0478                            int oldChildCount = changedParentNode
0479                                    .getChildCount();
0480
0481                            changedParent = changedParentNode.getValue();
0482                            makeVisible = ((changedParentNode == root && !rootVisible) || (changedParentNode
0483                                    .getRow() != -1 && changedParentNode
0484                                    .isExpanded()));
0485                            for (counter = 0; counter < changedIndexs.length; counter++) {
0486                                newNode = this .createNodeAt(changedParentNode,
0487                                        changedIndexs[counter]);
0488                            }
0489                            if (oldChildCount == 0) {
0490                                // Update the size of the parent.
0491                                changedParentNode.updatePreferredSize();
0492                            }
0493                            if (treeSelectionModel != null)
0494                                treeSelectionModel.resetRowSelection();
0495                            /* Update the y origins from the index of the parent
0496                               to the end of the visible rows. */
0497                            if (!isFixedRowHeight()
0498                                    && (makeVisible || (oldChildCount == 0 && changedParentNode
0499                                            .isVisible()))) {
0500                                if (changedParentNode == root)
0501                                    this .updateYLocationsFrom(0);
0502                                else
0503                                    this .updateYLocationsFrom(changedParentNode
0504                                            .getRow());
0505                                this .visibleNodesChanged();
0506                            } else if (makeVisible)
0507                                this .visibleNodesChanged();
0508                        } else if (treeModel.getChildCount(changedParentNode
0509                                .getValue())
0510                                - changedIndexs.length == 0) {
0511                            changedParentNode.updatePreferredSize();
0512                            if (!isFixedRowHeight()
0513                                    && changedParentNode.isVisible())
0514                                updateYLocationsFrom(changedParentNode.getRow());
0515                        }
0516                    }
0517                }
0518            }
0519
0520            /**
0521             * Invoked after nodes have been removed from the tree.  Note that
0522             * if a subtree is removed from the tree, this method may only be
0523             * invoked once for the root of the removed subtree, not once for
0524             * each individual set of siblings removed.
0525             *
0526             * <p><code>e.path</code> returns the former parent of the deleted nodes.
0527             *
0528             * <p><code>e.childIndices</code> returns the indices the nodes had
0529             * before they were deleted in ascending order.
0530             *
0531             * @param e the <code>TreeModelEvent</code> of interest
0532             */
0533            public void treeNodesRemoved(TreeModelEvent e) {
0534                if (e != null) {
0535                    int changedIndexs[];
0536                    TreeStateNode changedParentNode;
0537
0538                    changedIndexs = e.getChildIndices();
0539                    changedParentNode = getNodeForPath(e.getTreePath(), false,
0540                            false);
0541                    // PENDING(scott): make sure that changedIndexs are sorted in
0542                    // ascending order.
0543                    if (changedParentNode != null && changedIndexs != null
0544                            && changedIndexs.length > 0) {
0545                        if (changedParentNode.hasBeenExpanded()) {
0546                            boolean makeInvisible;
0547                            int counter;
0548                            int removedRow;
0549                            TreeStateNode removedNode;
0550
0551                            makeInvisible = ((changedParentNode == root && !rootVisible) || (changedParentNode
0552                                    .getRow() != -1 && changedParentNode
0553                                    .isExpanded()));
0554                            for (counter = changedIndexs.length - 1; counter >= 0; counter--) {
0555                                removedNode = (TreeStateNode) changedParentNode
0556                                        .getChildAt(changedIndexs[counter]);
0557                                if (removedNode.isExpanded()) {
0558                                    removedNode.collapse(false);
0559                                }
0560
0561                                /* Let the selection model now. */
0562                                if (makeInvisible) {
0563                                    removedRow = removedNode.getRow();
0564                                    if (removedRow != -1) {
0565                                        visibleNodes
0566                                                .removeElementAt(removedRow);
0567                                    }
0568                                }
0569                                changedParentNode
0570                                        .remove(changedIndexs[counter]);
0571                            }
0572                            if (changedParentNode.getChildCount() == 0) {
0573                                // Update the size of the parent.
0574                                changedParentNode.updatePreferredSize();
0575                                if (changedParentNode.isExpanded()
0576                                        && changedParentNode.isLeaf()) {
0577                                    // Node has become a leaf, collapse it.
0578                                    changedParentNode.collapse(false);
0579                                }
0580                            }
0581                            if (treeSelectionModel != null)
0582                                treeSelectionModel.resetRowSelection();
0583                            /* Update the y origins from the index of the parent
0584                               to the end of the visible rows. */
0585                            if (!isFixedRowHeight()
0586                                    && (makeInvisible || (changedParentNode
0587                                            .getChildCount() == 0 && changedParentNode
0588                                            .isVisible()))) {
0589                                if (changedParentNode == root) {
0590                                    /* It is possible for first row to have been
0591                                       removed if the root isn't visible, in which
0592                                       case ylocations will be off! */
0593                                    if (getRowCount() > 0)
0594                                        getNode(0).setYOrigin(0);
0595                                    updateYLocationsFrom(0);
0596                                } else
0597                                    updateYLocationsFrom(changedParentNode
0598                                            .getRow());
0599                                this .visibleNodesChanged();
0600                            } else if (makeInvisible)
0601                                this .visibleNodesChanged();
0602                        } else if (treeModel.getChildCount(changedParentNode
0603                                .getValue()) == 0) {
0604                            changedParentNode.updatePreferredSize();
0605                            if (!isFixedRowHeight()
0606                                    && changedParentNode.isVisible())
0607                                this .updateYLocationsFrom(changedParentNode
0608                                        .getRow());
0609                        }
0610                    }
0611                }
0612            }
0613
0614            /**
0615             * Invoked after the tree has drastically changed structure from a
0616             * given node down.  If the path returned by <code>e.getPath</code>
0617             * is of length one and the first element does not identify the
0618             * current root node the first element should become the new root
0619             * of the tree.
0620             *
0621             * <p><code>e.path</code> holds the path to the node.
0622             * <p><code>e.childIndices</code> returns <code>null</code>.
0623             *
0624             * @param e the <code>TreeModelEvent</code> of interest
0625             */
0626            public void treeStructureChanged(TreeModelEvent e) {
0627                if (e != null) {
0628                    TreePath changedPath = e.getTreePath();
0629                    TreeStateNode changedNode;
0630
0631                    changedNode = getNodeForPath(changedPath, false, false);
0632
0633                    // Check if root has changed, either to a null root, or
0634                    // to an entirely new root.
0635                    if (changedNode == root
0636                            || (changedNode == null && ((changedPath == null
0637                                    && treeModel != null && treeModel.getRoot() == null) || (changedPath != null && changedPath
0638                                    .getPathCount() == 1)))) {
0639                        rebuild(true);
0640                    } else if (changedNode != null) {
0641                        int nodeIndex, oldRow;
0642                        TreeStateNode newNode, parent;
0643                        boolean wasExpanded, wasVisible;
0644                        int newIndex;
0645
0646                        wasExpanded = changedNode.isExpanded();
0647                        wasVisible = (changedNode.getRow() != -1);
0648                        /* Remove the current node and recreate a new one. */
0649                        parent = (TreeStateNode) changedNode.getParent();
0650                        nodeIndex = parent.getIndex(changedNode);
0651                        if (wasVisible && wasExpanded) {
0652                            changedNode.collapse(false);
0653                        }
0654                        if (wasVisible)
0655                            visibleNodes.removeElement(changedNode);
0656                        changedNode.removeFromParent();
0657                        createNodeAt(parent, nodeIndex);
0658                        newNode = (TreeStateNode) parent.getChildAt(nodeIndex);
0659                        if (wasVisible && wasExpanded)
0660                            newNode.expand(false);
0661                        newIndex = newNode.getRow();
0662                        if (!isFixedRowHeight() && wasVisible) {
0663                            if (newIndex == 0)
0664                                updateYLocationsFrom(newIndex);
0665                            else
0666                                updateYLocationsFrom(newIndex - 1);
0667                            this .visibleNodesChanged();
0668                        } else if (wasVisible)
0669                            this .visibleNodesChanged();
0670                    }
0671                }
0672            }
0673
0674            //
0675            // Local methods
0676            //
0677
0678            private void visibleNodesChanged() {
0679            }
0680
0681            /**
0682             * Adds a mapping for node.
0683             */
0684            private void addMapping(TreeStateNode node) {
0685                treePathMapping.put(node.getTreePath(), node);
0686            }
0687
0688            /**
0689             * Removes the mapping for a previously added node.
0690             */
0691            private void removeMapping(TreeStateNode node) {
0692                treePathMapping.remove(node.getTreePath());
0693            }
0694
0695            /**
0696             * Returns the node previously added for <code>path</code>. This may
0697             * return null, if you to create a node use getNodeForPath.
0698             */
0699            private TreeStateNode getMapping(TreePath path) {
0700                return (TreeStateNode) treePathMapping.get(path);
0701            }
0702
0703            /**
0704             * Retursn the bounds for row, <code>row</code> by reference in
0705             * <code>placeIn</code>. If <code>placeIn</code> is null a new
0706             * Rectangle will be created and returned.
0707             */
0708            private Rectangle getBounds(int row, Rectangle placeIn) {
0709                if (updateNodeSizes)
0710                    updateNodeSizes(false);
0711
0712                if (row >= 0 && row < getRowCount()) {
0713                    return getNode(row).getNodeBounds(placeIn);
0714                }
0715                return null;
0716            }
0717
0718            /**
0719             * Completely rebuild the tree, all expanded state, and node caches are
0720             * removed. All nodes are collapsed, except the root.
0721             */
0722            private void rebuild(boolean clearSelection) {
0723                Object rootObject;
0724
0725                treePathMapping.clear();
0726                if (treeModel != null
0727                        && (rootObject = treeModel.getRoot()) != null) {
0728                    root = createNodeForValue(rootObject);
0729                    root.path = new TreePath(rootObject);
0730                    addMapping(root);
0731                    root.updatePreferredSize(0);
0732                    visibleNodes.removeAllElements();
0733                    if (isRootVisible())
0734                        visibleNodes.addElement(root);
0735                    if (!root.isExpanded())
0736                        root.expand();
0737                    else {
0738                        Enumeration cursor = root.children();
0739                        while (cursor.hasMoreElements()) {
0740                            visibleNodes.addElement(cursor.nextElement());
0741                        }
0742                        if (!isFixedRowHeight())
0743                            updateYLocationsFrom(0);
0744                    }
0745                } else {
0746                    visibleNodes.removeAllElements();
0747                    root = null;
0748                }
0749                if (clearSelection && treeSelectionModel != null) {
0750                    treeSelectionModel.clearSelection();
0751                }
0752                this .visibleNodesChanged();
0753            }
0754
0755            /**
0756             * Creates a new node to represent the node at <I>childIndex</I> in
0757             * <I>parent</I>s children.  This should be called if the node doesn't
0758             * already exist and <I>parent</I> has been expanded at least once.
0759             * The newly created node will be made visible if <I>parent</I> is
0760             * currently expanded.  This does not update the position of any
0761             * cells, nor update the selection if it needs to be.  If succesful
0762             * in creating the new TreeStateNode, it is returned, otherwise
0763             * null is returned.
0764             */
0765            private TreeStateNode createNodeAt(TreeStateNode parent,
0766                    int childIndex) {
0767                boolean isParentRoot;
0768                Object newValue;
0769                TreeStateNode newChildNode;
0770
0771                newValue = treeModel.getChild(parent.getValue(), childIndex);
0772                newChildNode = createNodeForValue(newValue);
0773                parent.insert(newChildNode, childIndex);
0774                newChildNode.updatePreferredSize(-1);
0775                isParentRoot = (parent == root);
0776                if (newChildNode != null && parent.isExpanded()
0777                        && (parent.getRow() != -1 || isParentRoot)) {
0778                    int newRow;
0779
0780                    /* Find the new row to insert this newly visible node at. */
0781                    if (childIndex == 0) {
0782                        if (isParentRoot && !isRootVisible())
0783                            newRow = 0;
0784                        else
0785                            newRow = parent.getRow() + 1;
0786                    } else if (childIndex == parent.getChildCount())
0787                        newRow = parent.getLastVisibleNode().getRow() + 1;
0788                    else {
0789                        TreeStateNode previousNode;
0790
0791                        previousNode = (TreeStateNode) parent
0792                                .getChildAt(childIndex - 1);
0793                        newRow = previousNode.getLastVisibleNode().getRow() + 1;
0794                    }
0795                    visibleNodes.insertElementAt(newChildNode, newRow);
0796                }
0797                return newChildNode;
0798            }
0799
0800            /**
0801             * Returns the TreeStateNode identified by path.  This mirrors
0802             * the behavior of getNodeForPath, but tries to take advantage of
0803             * path if it is an instance of AbstractTreePath.
0804             */
0805            private TreeStateNode getNodeForPath(TreePath path,
0806                    boolean onlyIfVisible, boolean shouldCreate) {
0807                if (path != null) {
0808                    TreeStateNode node;
0809
0810                    node = getMapping(path);
0811                    if (node != null) {
0812                        if (onlyIfVisible && !node.isVisible())
0813                            return null;
0814                        return node;
0815                    }
0816
0817                    // Check all the parent paths, until a match is found.
0818                    Stack paths;
0819
0820                    if (tempStacks.size() == 0) {
0821                        paths = new Stack();
0822                    } else {
0823                        paths = (Stack) tempStacks.pop();
0824                    }
0825
0826                    try {
0827                        paths.push(path);
0828                        path = path.getParentPath();
0829                        node = null;
0830                        while (path != null) {
0831                            node = getMapping(path);
0832                            if (node != null) {
0833                                // Found a match, create entries for all paths in
0834                                // paths.
0835                                while (node != null && paths.size() > 0) {
0836                                    path = (TreePath) paths.pop();
0837                                    node.getLoadedChildren(shouldCreate);
0838
0839                                    int childIndex = treeModel.getIndexOfChild(
0840                                            node.getUserObject(), path
0841                                                    .getLastPathComponent());
0842
0843                                    if (childIndex == -1
0844                                            || childIndex >= node
0845                                                    .getChildCount()
0846                                            || (onlyIfVisible && !node
0847                                                    .isVisible())) {
0848                                        node = null;
0849                                    } else
0850                                        node = (TreeStateNode) node
0851                                                .getChildAt(childIndex);
0852                                }
0853                                return node;
0854                            }
0855                            paths.push(path);
0856                            path = path.getParentPath();
0857                        }
0858                    } finally {
0859                        paths.removeAllElements();
0860                        tempStacks.push(paths);
0861                    }
0862                    // If we get here it means they share a different root!
0863                    // We could throw an exception...
0864                }
0865                return null;
0866            }
0867
0868            /**
0869             * Updates the y locations of all of the visible nodes after
0870             * location.
0871             */
0872            private void updateYLocationsFrom(int location) {
0873                if (location >= 0 && location < getRowCount()) {
0874                    int counter, maxCounter, newYOrigin;
0875                    TreeStateNode aNode;
0876
0877                    aNode = getNode(location);
0878                    newYOrigin = aNode.getYOrigin()
0879                            + aNode.getPreferredHeight();
0880                    for (counter = location + 1, maxCounter = visibleNodes
0881                            .size(); counter < maxCounter; counter++) {
0882                        aNode = (TreeStateNode) visibleNodes.elementAt(counter);
0883                        aNode.setYOrigin(newYOrigin);
0884                        newYOrigin += aNode.getPreferredHeight();
0885                    }
0886                }
0887            }
0888
0889            /**
0890             * Resets the y origin of all the visible nodes as well as messaging
0891             * all the visible nodes to updatePreferredSize().  You should not
0892             * normally have to call this.  Expanding and contracting the nodes
0893             * automaticly adjusts the locations.
0894             * updateAll determines if updatePreferredSize() is call on all nodes
0895             * or just those that don't have a valid size.
0896             */
0897            private void updateNodeSizes(boolean updateAll) {
0898                int aY, counter, maxCounter;
0899                TreeStateNode node;
0900
0901                updateNodeSizes = false;
0902                for (aY = counter = 0, maxCounter = visibleNodes.size(); counter < maxCounter; counter++) {
0903                    node = (TreeStateNode) visibleNodes.elementAt(counter);
0904                    node.setYOrigin(aY);
0905                    if (updateAll || !node.hasValidSize())
0906                        node.updatePreferredSize(counter);
0907                    aY += node.getPreferredHeight();
0908                }
0909            }
0910
0911            /**
0912             * Returns the index of the row containing location.  If there
0913             * are no rows, -1 is returned.  If location is beyond the last
0914             * row index, the last row index is returned.
0915             */
0916            private int getRowContainingYLocation(int location) {
0917                if (isFixedRowHeight()) {
0918                    if (getRowCount() == 0)
0919                        return -1;
0920                    return Math.max(0, Math.min(getRowCount() - 1, location
0921                            / getRowHeight()));
0922                }
0923
0924                int max, maxY, mid, min, minY;
0925                TreeStateNode node;
0926
0927                if ((max = getRowCount()) <= 0)
0928                    return -1;
0929                mid = min = 0;
0930                while (min < max) {
0931                    mid = (max - min) / 2 + min;
0932                    node = (TreeStateNode) visibleNodes.elementAt(mid);
0933                    minY = node.getYOrigin();
0934                    maxY = minY + node.getPreferredHeight();
0935                    if (location < minY) {
0936                        max = mid - 1;
0937                    } else if (location >= maxY) {
0938                        min = mid + 1;
0939                    } else
0940                        break;
0941                }
0942                if (min == max) {
0943                    mid = min;
0944                    if (mid >= getRowCount())
0945                        mid = getRowCount() - 1;
0946                }
0947                return mid;
0948            }
0949
0950            /**
0951             * Ensures that all the path components in path are expanded, accept
0952             * for the last component which will only be expanded if expandLast
0953             * is true.
0954             * Returns true if succesful in finding the path.
0955             */
0956            private void ensurePathIsExpanded(TreePath aPath, boolean expandLast) {
0957                if (aPath != null) {
0958                    // Make sure the last entry isn't a leaf.
0959                    if (treeModel.isLeaf(aPath.getLastPathComponent())) {
0960                        aPath = aPath.getParentPath();
0961                        expandLast = true;
0962                    }
0963                    if (aPath != null) {
0964                        TreeStateNode lastNode = getNodeForPath(aPath, false,
0965                                true);
0966
0967                        if (lastNode != null) {
0968                            lastNode.makeVisible();
0969                            if (expandLast)
0970                                lastNode.expand();
0971                        }
0972                    }
0973                }
0974            }
0975
0976            /**
0977             * Returns the AbstractTreeUI.VisibleNode displayed at the given row
0978             */
0979            private TreeStateNode getNode(int row) {
0980                return (TreeStateNode) visibleNodes.elementAt(row);
0981            }
0982
0983            /**
0984             * Returns the maximum node width.
0985             */
0986            private int getMaxNodeWidth() {
0987                int maxWidth = 0;
0988                int nodeWidth;
0989                int counter;
0990                TreeStateNode node;
0991
0992                for (counter = getRowCount() - 1; counter >= 0; counter--) {
0993                    node = this .getNode(counter);
0994                    nodeWidth = node.getPreferredWidth() + node.getXOrigin();
0995                    if (nodeWidth > maxWidth)
0996                        maxWidth = nodeWidth;
0997                }
0998                return maxWidth;
0999            }
1000
1001            /**
1002             * Responsible for creating a TreeStateNode that will be used
1003             * to track display information about value.
1004             */
1005            private TreeStateNode createNodeForValue(Object value) {
1006                return new TreeStateNode(value);
1007            }
1008
1009            /**
1010             * TreeStateNode is used to keep track of each of
1011             * the nodes that have been expanded. This will also cache the preferred
1012             * size of the value it represents.
1013             */
1014            private class TreeStateNode extends DefaultMutableTreeNode {
1015                /** Preferred size needed to draw the user object. */
1016                protected int preferredWidth;
1017                protected int preferredHeight;
1018
1019                /** X location that the user object will be drawn at. */
1020                protected int xOrigin;
1021
1022                /** Y location that the user object will be drawn at. */
1023                protected int yOrigin;
1024
1025                /** Is this node currently expanded? */
1026                protected boolean expanded;
1027
1028                /** Has this node been expanded at least once? */
1029                protected boolean hasBeenExpanded;
1030
1031                /** Path of this node. */
1032                protected TreePath path;
1033
1034                public TreeStateNode(Object value) {
1035                    super (value);
1036                }
1037
1038                //
1039                // Overriden DefaultMutableTreeNode methods
1040                //
1041
1042                /**
1043                 * Messaged when this node is added somewhere, resets the path
1044                 * and adds a mapping from path to this node.
1045                 */
1046                public void setParent(MutableTreeNode parent) {
1047                    super .setParent(parent);
1048                    if (parent != null) {
1049                        path = ((TreeStateNode) parent).getTreePath()
1050                                .pathByAddingChild(getUserObject());
1051                        addMapping(this );
1052                    }
1053                }
1054
1055                /**
1056                 * Messaged when this node is removed from its parent, this messages
1057                 * <code>removedFromMapping</code> to remove all the children.
1058                 */
1059                public void remove(int childIndex) {
1060                    TreeStateNode node = (TreeStateNode) getChildAt(childIndex);
1061
1062                    node.removeFromMapping();
1063                    super .remove(childIndex);
1064                }
1065
1066                /**
1067                 * Messaged to set the user object. This resets the path.
1068                 */
1069                public void setUserObject(Object o) {
1070                    super .setUserObject(o);
1071                    if (path != null) {
1072                        TreeStateNode parent = (TreeStateNode) getParent();
1073
1074                        if (parent != null)
1075                            resetChildrenPaths(parent.getTreePath());
1076                        else
1077                            resetChildrenPaths(null);
1078                    }
1079                }
1080
1081                /**
1082                 * Returns the children of the receiver.
1083                 * If the receiver is not currently expanded, this will return an
1084                 * empty enumeration.
1085                 */
1086                public Enumeration children() {
1087                    if (!this .isExpanded()) {
1088                        return DefaultMutableTreeNode.EMPTY_ENUMERATION;
1089                    } else {
1090                        return super .children();
1091                    }
1092                }
1093
1094                /**
1095                 * Returns true if the receiver is a leaf.
1096                 */
1097                public boolean isLeaf() {
1098                    return getModel().isLeaf(this .getValue());
1099                }
1100
1101                //
1102                // VariableHeightLayoutCache
1103                //
1104
1105                /**
1106                 * Returns the location and size of this node.
1107                 */
1108                public Rectangle getNodeBounds(Rectangle placeIn) {
1109                    if (placeIn == null)
1110                        placeIn = new Rectangle(getXOrigin(), getYOrigin(),
1111                                getPreferredWidth(), getPreferredHeight());
1112                    else {
1113                        placeIn.x = getXOrigin();
1114                        placeIn.y = getYOrigin();
1115                        placeIn.width = getPreferredWidth();
1116                        placeIn.height = getPreferredHeight();
1117                    }
1118                    return placeIn;
1119                }
1120
1121                /**
1122                 * @return x location to draw node at.
1123                 */
1124                public int getXOrigin() {
1125                    if (!hasValidSize())
1126                        updatePreferredSize(getRow());
1127                    return xOrigin;
1128                }
1129
1130                /**
1131                 * Returns the y origin the user object will be drawn at.
1132                 */
1133                public int getYOrigin() {
1134                    if (isFixedRowHeight()) {
1135                        int aRow = getRow();
1136
1137                        if (aRow == -1)
1138                            return -1;
1139                        return getRowHeight() * aRow;
1140                    }
1141                    return yOrigin;
1142                }
1143
1144                /**
1145                 * Returns the preferred height of the receiver.
1146                 */
1147                public int getPreferredHeight() {
1148                    if (isFixedRowHeight())
1149                        return getRowHeight();
1150                    else if (!hasValidSize())
1151                        updatePreferredSize(getRow());
1152                    return preferredHeight;
1153                }
1154
1155                /**
1156                 * Returns the preferred width of the receiver.
1157                 */
1158                public int getPreferredWidth() {
1159                    if (!hasValidSize())
1160                        updatePreferredSize(getRow());
1161                    return preferredWidth;
1162                }
1163
1164                /**
1165                 * Returns true if this node has a valid size.
1166                 */
1167                public boolean hasValidSize() {
1168                    return (preferredHeight != 0);
1169                }
1170
1171                /**
1172                 * Returns the row of the receiver.
1173                 */
1174                public int getRow() {
1175                    return visibleNodes.indexOf(this );
1176                }
1177
1178                /**
1179                 * Returns true if this node has been expanded at least once.
1180                 */
1181                public boolean hasBeenExpanded() {
1182                    return hasBeenExpanded;
1183                }
1184
1185                /**
1186                 * Returns true if the receiver has been expanded.
1187                 */
1188                public boolean isExpanded() {
1189                    return expanded;
1190                }
1191
1192                /**
1193                 * Returns the last visible node that is a child of this
1194                 * instance.
1195                 */
1196                public TreeStateNode getLastVisibleNode() {
1197                    TreeStateNode node = this ;
1198
1199                    while (node.isExpanded() && node.getChildCount() > 0)
1200                        node = (TreeStateNode) node.getLastChild();
1201                    return node;
1202                }
1203
1204                /**
1205                 * Returns true if the receiver is currently visible.
1206                 */
1207                public boolean isVisible() {
1208                    if (this  == root)
1209                        return true;
1210
1211                    TreeStateNode parent = (TreeStateNode) getParent();
1212
1213                    return (parent != null && parent.isExpanded() && parent
1214                            .isVisible());
1215                }
1216
1217                /**
1218                 * Returns the number of children this will have. If the children
1219                 * have not yet been loaded, this messages the model.
1220                 */
1221                public int getModelChildCount() {
1222                    if (hasBeenExpanded)
1223                        return super .getChildCount();
1224                    return getModel().getChildCount(getValue());
1225                }
1226
1227                /**
1228                 * Returns the number of visible children, that is the number of
1229                 * children that are expanded, or leafs.
1230                 */
1231                public int getVisibleChildCount() {
1232                    int childCount = 0;
1233
1234                    if (isExpanded()) {
1235                        int maxCounter = getChildCount();
1236
1237                        childCount += maxCounter;
1238                        for (int counter = 0; counter < maxCounter; counter++)
1239                            childCount += ((TreeStateNode) getChildAt(counter))
1240                                    .getVisibleChildCount();
1241                    }
1242                    return childCount;
1243                }
1244
1245                /**
1246                 * Toggles the receiver between expanded and collapsed.
1247                 */
1248                public void toggleExpanded() {
1249                    if (isExpanded()) {
1250                        collapse();
1251                    } else {
1252                        expand();
1253                    }
1254                }
1255
1256                /**
1257                 * Makes the receiver visible, but invoking
1258                 * <code>expandParentAndReceiver</code> on the superclass.
1259                 */
1260                public void makeVisible() {
1261                    TreeStateNode parent = (TreeStateNode) getParent();
1262
1263                    if (parent != null)
1264                        parent.expandParentAndReceiver();
1265                }
1266
1267                /**
1268                 * Expands the receiver.
1269                 */
1270                public void expand() {
1271                    expand(true);
1272                }
1273
1274                /**
1275                 * Collapses the receiver.
1276                 */
1277                public void collapse() {
1278                    collapse(true);
1279                }
1280
1281                /**
1282                 * Returns the value the receiver is representing. This is a cover
1283                 * for getUserObject.
1284                 */
1285                public Object getValue() {
1286                    return getUserObject();
1287                }
1288
1289                /**
1290                 * Returns a TreePath instance for this node.
1291                 */
1292                public TreePath getTreePath() {
1293                    return path;
1294                }
1295
1296                //
1297                // Local methods
1298                //
1299
1300                /**
1301                 * Recreates the receivers path, and all its childrens paths.
1302                 */
1303                protected void resetChildrenPaths(TreePath parentPath) {
1304                    removeMapping(this );
1305                    if (parentPath == null)
1306                        path = new TreePath(getUserObject());
1307                    else
1308                        path = parentPath.pathByAddingChild(getUserObject());
1309                    addMapping(this );
1310                    for (int counter = getChildCount() - 1; counter >= 0; counter--)
1311                        ((TreeStateNode) getChildAt(counter))
1312                                .resetChildrenPaths(path);
1313                }
1314
1315                /**
1316                 * Sets y origin the user object will be drawn at to
1317                 * <I>newYOrigin</I>.
1318                 */
1319                protected void setYOrigin(int newYOrigin) {
1320                    yOrigin = newYOrigin;
1321                }
1322
1323                /**
1324                 * Shifts the y origin by <code>offset</code>.
1325                 */
1326                protected void shiftYOriginBy(int offset) {
1327                    yOrigin += offset;
1328                }
1329
1330                /**
1331                 * Updates the receivers preferredSize by invoking
1332                 * <code>updatePreferredSize</code> with an argument of -1.
1333                 */
1334                protected void updatePreferredSize() {
1335                    updatePreferredSize(getRow());
1336                }
1337
1338                /**
1339                 * Updates the preferred size by asking the current renderer
1340                 * for the Dimension needed to draw the user object this
1341                 * instance represents.
1342                 */
1343                protected void updatePreferredSize(int index) {
1344                    Rectangle bounds = getNodeDimensions(this .getUserObject(),
1345                            index, getLevel(), isExpanded(), boundsBuffer);
1346
1347                    if (bounds == null) {
1348                        xOrigin = 0;
1349                        preferredWidth = preferredHeight = 0;
1350                        updateNodeSizes = true;
1351                    } else if (bounds.height == 0) {
1352                        xOrigin = 0;
1353                        preferredWidth = preferredHeight = 0;
1354                        updateNodeSizes = true;
1355                    } else {
1356                        xOrigin = bounds.x;
1357                        preferredWidth = bounds.width;
1358                        if (isFixedRowHeight())
1359                            preferredHeight = getRowHeight();
1360                        else
1361                            preferredHeight = bounds.height;
1362                    }
1363                }
1364
1365                /**
1366                 * Marks the receivers size as invalid. Next time the size, location
1367                 * is asked for it will be obtained.
1368                 */
1369                protected void markSizeInvalid() {
1370                    preferredHeight = 0;
1371                }
1372
1373                /**
1374                 * Marks the receivers size, and all its descendants sizes, as invalid.
1375                 */
1376                protected void deepMarkSizeInvalid() {
1377                    markSizeInvalid();
1378                    for (int counter = getChildCount() - 1; counter >= 0; counter--)
1379                        ((TreeStateNode) getChildAt(counter))
1380                                .deepMarkSizeInvalid();
1381                }
1382
1383                /**
1384                 * Returns the children of the receiver. If the children haven't
1385                 * been loaded from the model and
1386                 * <code>createIfNeeded</code> is true, the children are first
1387                 * loaded.
1388                 */
1389                protected Enumeration getLoadedChildren(boolean createIfNeeded) {
1390                    if (!createIfNeeded || hasBeenExpanded)
1391                        return super .children();
1392
1393                    TreeStateNode newNode;
1394                    Object realNode = getValue();
1395                    TreeModel treeModel = getModel();
1396                    int count = treeModel.getChildCount(realNode);
1397
1398                    hasBeenExpanded = true;
1399
1400                    int childRow = getRow();
1401
1402                    if (childRow == -1) {
1403                        for (int i = 0; i < count; i++) {
1404                            newNode = createNodeForValue(treeModel.getChild(
1405                                    realNode, i));
1406                            this .add(newNode);
1407                            newNode.updatePreferredSize(-1);
1408                        }
1409                    } else {
1410                        childRow++;
1411                        for (int i = 0; i < count; i++) {
1412                            newNode = createNodeForValue(treeModel.getChild(
1413                                    realNode, i));
1414                            this .add(newNode);
1415                            newNode.updatePreferredSize(childRow++);
1416                        }
1417                    }
1418                    return super .children();
1419                }
1420
1421                /**
1422                 * Messaged from expand and collapse. This is meant for subclassers
1423                 * that may wish to do something interesting with this.
1424                 */
1425                protected void didAdjustTree() {
1426                }
1427
1428                /**
1429                 * Invokes <code>expandParentAndReceiver</code> on the parent,
1430                 * and expands the receiver.
1431                 */
1432                protected void expandParentAndReceiver() {
1433                    TreeStateNode parent = (TreeStateNode) getParent();
1434
1435                    if (parent != null)
1436                        parent.expandParentAndReceiver();
1437                    expand();
1438                }
1439
1440                /**
1441                 * Expands this node in the tree.  This will load the children
1442                 * from the treeModel if this node has not previously been
1443                 * expanded.  If <I>adjustTree</I> is true the tree and selection
1444                 * are updated accordingly.
1445                 */
1446                protected void expand(boolean adjustTree) {
1447                    if (!isExpanded() && !isLeaf()) {
1448                        boolean isFixed = isFixedRowHeight();
1449                        int startHeight = getPreferredHeight();
1450                        int originalRow = getRow();
1451
1452                        expanded = true;
1453                        updatePreferredSize(originalRow);
1454
1455                        if (!hasBeenExpanded) {
1456                            TreeStateNode newNode;
1457                            Object realNode = getValue();
1458                            TreeModel treeModel = getModel();
1459                            int count = treeModel.getChildCount(realNode);
1460
1461                            hasBeenExpanded = true;
1462                            if (originalRow == -1) {
1463                                for (int i = 0; i < count; i++) {
1464                                    newNode = createNodeForValue(treeModel
1465                                            .getChild(realNode, i));
1466                                    this .add(newNode);
1467                                    newNode.updatePreferredSize(-1);
1468                                }
1469                            } else {
1470                                int offset = originalRow + 1;
1471                                for (int i = 0; i < count; i++) {
1472                                    newNode = createNodeForValue(treeModel
1473                                            .getChild(realNode, i));
1474                                    this .add(newNode);
1475                                    newNode.updatePreferredSize(offset);
1476                                }
1477                            }
1478                        }
1479
1480                        int i = originalRow;
1481                        Enumeration cursor = preorderEnumeration();
1482                        cursor.nextElement(); // don't add me, I'm already in
1483
1484                        int newYOrigin;
1485
1486                        if (isFixed)
1487                            newYOrigin = 0;
1488                        else if (this  == root && !isRootVisible())
1489                            newYOrigin = 0;
1490                        else
1491                            newYOrigin = getYOrigin()
1492                                    + this .getPreferredHeight();
1493                        TreeStateNode aNode;
1494                        if (!isFixed) {
1495                            while (cursor.hasMoreElements()) {
1496                                aNode = (TreeStateNode) cursor.nextElement();
1497                                if (!updateNodeSizes && !aNode.hasValidSize())
1498                                    aNode.updatePreferredSize(i + 1);
1499                                aNode.setYOrigin(newYOrigin);
1500                                newYOrigin += aNode.getPreferredHeight();
1501                                visibleNodes.insertElementAt(aNode, ++i);
1502                            }
1503                        } else {
1504                            while (cursor.hasMoreElements()) {
1505                                aNode = (TreeStateNode) cursor.nextElement();
1506                                visibleNodes.insertElementAt(aNode, ++i);
1507                            }
1508                        }
1509
1510                        if (adjustTree
1511                                && (originalRow != i || getPreferredHeight() != startHeight)) {
1512                            // Adjust the Y origin of any nodes following this row.
1513                            if (!isFixed && ++i < getRowCount()) {
1514                                int counter;
1515                                int heightDiff = newYOrigin
1516                                        - (getYOrigin() + getPreferredHeight())
1517                                        + (getPreferredHeight() - startHeight);
1518
1519                                for (counter = visibleNodes.size() - 1; counter >= i; counter--)
1520                                    ((TreeStateNode) visibleNodes
1521                                            .elementAt(counter))
1522                                            .shiftYOriginBy(heightDiff);
1523                            }
1524                            didAdjustTree();
1525                            visibleNodesChanged();
1526                        }
1527
1528                        // Update the rows in the selection
1529                        if (treeSelectionModel != null) {
1530                            treeSelectionModel.resetRowSelection();
1531                        }
1532                    }
1533                }
1534
1535                /**
1536                 * Collapses this node in the tree.  If <I>adjustTree</I> is
1537                 * true the tree and selection are updated accordingly.
1538                 */
1539                protected void collapse(boolean adjustTree) {
1540                    if (isExpanded()) {
1541                        Enumeration cursor = preorderEnumeration();
1542                        cursor.nextElement(); // don't remove me, I'm still visible
1543                        int rowsDeleted = 0;
1544                        boolean isFixed = isFixedRowHeight();
1545                        int lastYEnd;
1546                        if (isFixed)
1547                            lastYEnd = 0;
1548                        else
1549                            lastYEnd = getPreferredHeight() + getYOrigin();
1550                        int startHeight = getPreferredHeight();
1551                        int startYEnd = lastYEnd;
1552                        int myRow = getRow();
1553
1554                        if (!isFixed) {
1555                            while (cursor.hasMoreElements()) {
1556                                TreeStateNode node = (TreeStateNode) cursor
1557                                        .nextElement();
1558                                if (node.isVisible()) {
1559                                    rowsDeleted++;
1560                                    //visibleNodes.removeElement(node);
1561                                    lastYEnd = node.getYOrigin()
1562                                            + node.getPreferredHeight();
1563                                }
1564                            }
1565                        } else {
1566                            while (cursor.hasMoreElements()) {
1567                                TreeStateNode node = (TreeStateNode) cursor
1568                                        .nextElement();
1569                                if (node.isVisible()) {
1570                                    rowsDeleted++;
1571                                    //visibleNodes.removeElement(node);
1572                                }
1573                            }
1574                        }
1575
1576                        // Clean up the visible nodes.
1577                        for (int counter = rowsDeleted + myRow; counter > myRow; counter--) {
1578                            visibleNodes.removeElementAt(counter);
1579                        }
1580
1581                        expanded = false;
1582
1583                        if (myRow == -1)
1584                            markSizeInvalid();
1585                        else if (adjustTree)
1586                            updatePreferredSize(myRow);
1587
1588                        if (myRow != -1
1589                                && adjustTree
1590                                && (rowsDeleted > 0 || startHeight != getPreferredHeight())) {
1591                            // Adjust the Y origin of any rows following this one.
1592                            startYEnd += (getPreferredHeight() - startHeight);
1593                            if (!isFixed && (myRow + 1) < getRowCount()
1594                                    && startYEnd != lastYEnd) {
1595                                int counter, maxCounter, shiftAmount;
1596
1597                                shiftAmount = startYEnd - lastYEnd;
1598                                for (counter = myRow + 1, maxCounter = visibleNodes
1599                                        .size(); counter < maxCounter; counter++)
1600                                    ((TreeStateNode) visibleNodes
1601                                            .elementAt(counter))
1602                                            .shiftYOriginBy(shiftAmount);
1603                            }
1604                            didAdjustTree();
1605                            visibleNodesChanged();
1606                        }
1607                        if (treeSelectionModel != null && rowsDeleted > 0
1608                                && myRow != -1) {
1609                            treeSelectionModel.resetRowSelection();
1610                        }
1611                    }
1612                }
1613
1614                /**
1615                 * Removes the receiver, and all its children, from the mapping
1616                 * table.
1617                 */
1618                protected void removeFromMapping() {
1619                    if (path != null) {
1620                        removeMapping(this );
1621                        for (int counter = getChildCount() - 1; counter >= 0; counter--)
1622                            ((TreeStateNode) getChildAt(counter))
1623                                    .removeFromMapping();
1624                    }
1625                }
1626            } // End of VariableHeightLayoutCache.TreeStateNode
1627
1628            /**
1629             * An enumerator to iterate through visible nodes.
1630             */
1631            private class VisibleTreeStateNodeEnumeration implements 
1632                    Enumeration<TreePath> {
1633                /** Parent thats children are being enumerated. */
1634                protected TreeStateNode parent;
1635                /** Index of next child. An index of -1 signifies parent should be
1636                 * visibled next. */
1637                protected int nextIndex;
1638                /** Number of children in parent. */
1639                protected int childCount;
1640
1641                protected VisibleTreeStateNodeEnumeration(TreeStateNode node) {
1642                    this (node, -1);
1643                }
1644
1645                protected VisibleTreeStateNodeEnumeration(TreeStateNode parent,
1646                        int startIndex) {
1647                    this .parent = parent;
1648                    this .nextIndex = startIndex;
1649                    this .childCount = this .parent.getChildCount();
1650                }
1651
1652                /**
1653                 * @return true if more visible nodes.
1654                 */
1655                public boolean hasMoreElements() {
1656                    return (parent != null);
1657                }
1658
1659                /**
1660                 * @return next visible TreePath.
1661                 */
1662                public TreePath nextElement() {
1663                    if (!hasMoreElements())
1664                        throw new NoSuchElementException(
1665                                "No more visible paths");
1666
1667                    TreePath retObject;
1668
1669                    if (nextIndex == -1) {
1670                        retObject = parent.getTreePath();
1671                    } else {
1672                        TreeStateNode node = (TreeStateNode) parent
1673                                .getChildAt(nextIndex);
1674
1675                        retObject = node.getTreePath();
1676                    }
1677                    updateNextObject();
1678                    return retObject;
1679                }
1680
1681                /**
1682                 * Determines the next object by invoking <code>updateNextIndex</code>
1683                 * and if not succesful <code>findNextValidParent</code>.
1684                 */
1685                protected void updateNextObject() {
1686                    if (!updateNextIndex()) {
1687                        findNextValidParent();
1688                    }
1689                }
1690
1691                /**
1692                 * Finds the next valid parent, this should be called when nextIndex
1693                 * is beyond the number of children of the current parent.
1694                 */
1695                protected boolean findNextValidParent() {
1696                    if (parent == root) {
1697                        // mark as invalid!
1698                        parent = null;
1699                        return false;
1700                    }
1701                    while (parent != null) {
1702                        TreeStateNode newParent = (TreeStateNode) parent
1703                                .getParent();
1704
1705                        if (newParent != null) {
1706                            nextIndex = newParent.getIndex(parent);
1707                            parent = newParent;
1708                            childCount = parent.getChildCount();
1709                            if (updateNextIndex())
1710                                return true;
1711                        } else
1712                            parent = null;
1713                    }
1714                    return false;
1715                }
1716
1717                /**
1718                 * Updates <code>nextIndex</code> returning false if it is beyond
1719                 * the number of children of parent.
1720                 */
1721                protected boolean updateNextIndex() {
1722                    // nextIndex == -1 identifies receiver, make sure is expanded
1723                    // before descend.
1724                    if (nextIndex == -1 && !parent.isExpanded())
1725                        return false;
1726
1727                    // Check that it can have kids
1728                    if (childCount == 0)
1729                        return false;
1730                    // Make sure next index not beyond child count.
1731                    else if (++nextIndex >= childCount)
1732                        return false;
1733
1734                    TreeStateNode child = (TreeStateNode) parent
1735                            .getChildAt(nextIndex);
1736
1737                    if (child != null && child.isExpanded()) {
1738                        parent = child;
1739                        nextIndex = -1;
1740                        childCount = child.getChildCount();
1741                    }
1742                    return true;
1743                }
1744            } // VariableHeightLayoutCache.VisibleTreeStateNodeEnumeration
1745        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.