< prev index next >

src/java.desktop/share/classes/javax/swing/tree/FixedHeightLayoutCache.java

Print this page




  26 package javax.swing.tree;
  27 
  28 import javax.swing.event.TreeModelEvent;
  29 import java.awt.Rectangle;
  30 import java.util.Enumeration;
  31 import java.util.Hashtable;
  32 import java.util.NoSuchElementException;
  33 import java.util.Stack;
  34 
  35 import sun.swing.SwingUtilities2;
  36 
  37 /**
  38  * NOTE: This will become more open in a future release.
  39  * <p>
  40  * <strong>Warning:</strong>
  41  * Serialized objects of this class will not be compatible with
  42  * future Swing releases. The current serialization support is
  43  * appropriate for short term storage or RMI between applications running
  44  * the same version of Swing.  As of 1.4, support for long term storage
  45  * of all JavaBeans&trade;
  46  * has been added to the <code>java.beans</code> package.
  47  * Please see {@link java.beans.XMLEncoder}.
  48  *
  49  * @author Scott Violet
  50  */
  51 @SuppressWarnings("serial") // Same-version serialization only
  52 public class FixedHeightLayoutCache extends AbstractLayoutCache {
  53     /** Root node. */
  54     private FHTreeStateNode    root;
  55 
  56     /** Number of rows currently visible. */
  57     private int                rowCount;
  58 
  59     /**
  60      * Used in getting sizes for nodes to avoid creating a new Rectangle
  61      * every time a size is needed.
  62      */
  63     private Rectangle          boundsBuffer;
  64 
  65     /**
  66      * Maps from TreePath to a FHTreeStateNode.


 277         if(path == null)
 278             return null;
 279 
 280         FHTreeStateNode         node = getNodeForPath(path, true, false);
 281 
 282         if(node != null) {
 283             return new VisibleFHTreeStateNodeEnumeration(node);
 284         }
 285         TreePath            parentPath = path.getParentPath();
 286 
 287         node = getNodeForPath(parentPath, true, false);
 288         if(node != null && node.isExpanded()) {
 289             return new VisibleFHTreeStateNodeEnumeration(node,
 290                   treeModel.getIndexOfChild(parentPath.getLastPathComponent(),
 291                                             path.getLastPathComponent()));
 292         }
 293         return null;
 294     }
 295 
 296     /**
 297      * Marks the path <code>path</code> expanded state to
 298      * <code>isExpanded</code>.
 299      */
 300     public void setExpandedState(TreePath path, boolean isExpanded) {
 301         if(isExpanded)
 302             ensurePathIsExpanded(path, true);
 303         else if(path != null) {
 304             TreePath              parentPath = path.getParentPath();
 305 
 306             // YECK! Make the parent expanded.
 307             if(parentPath != null) {
 308                 FHTreeStateNode     parentNode = getNodeForPath(parentPath,
 309                                                                 false, true);
 310                 if(parentNode != null)
 311                     parentNode.makeVisible();
 312             }
 313             // And collapse the child.
 314             FHTreeStateNode         childNode = getNodeForPath(path, true,
 315                                                                false);
 316 
 317             if(childNode != null)
 318                 childNode.collapse(true);


 510                     changedNode = getNodeForPath(changedPath, false, true);
 511                     changedNode.expand();
 512                 }
 513                 if(treeSelectionModel != null && wasVisible && wasExpanded)
 514                     treeSelectionModel.resetRowSelection();
 515                 if(wasVisible)
 516                     this.visibleNodesChanged();
 517             }
 518         }
 519     }
 520 
 521 
 522     //
 523     // Local methods
 524     //
 525 
 526     private void visibleNodesChanged() {
 527     }
 528 
 529     /**
 530      * Returns the bounds for the given node. If <code>childIndex</code>
 531      * is -1, the bounds of <code>parent</code> are returned, otherwise
 532      * the bounds of the node at <code>childIndex</code> are returned.
 533      */
 534     private Rectangle getBounds(FHTreeStateNode parent, int childIndex,
 535                                   Rectangle placeIn) {
 536         boolean              expanded;
 537         int                  level;
 538         int                  row;
 539         Object               value;
 540 
 541         if(childIndex == -1) {
 542             // Getting bounds for parent
 543             row = parent.getRow();
 544             value = parent.getUserObject();
 545             expanded = parent.isExpanded();
 546             level = parent.getLevel();
 547         }
 548         else {
 549             row = parent.getRowToModelIndex(childIndex);
 550             value = treeModel.getChild(parent.getUserObject(), childIndex);
 551             expanded = false;
 552             level = parent.getLevel() + 1;


 574      */
 575     private void adjustRowCountBy(int changeAmount) {
 576         rowCount += changeAmount;
 577     }
 578 
 579     /**
 580      * Adds a mapping for node.
 581      */
 582     private void addMapping(FHTreeStateNode node) {
 583         treePathMapping.put(node.getTreePath(), node);
 584     }
 585 
 586     /**
 587      * Removes the mapping for a previously added node.
 588      */
 589     private void removeMapping(FHTreeStateNode node) {
 590         treePathMapping.remove(node.getTreePath());
 591     }
 592 
 593     /**
 594      * Returns the node previously added for <code>path</code>. This may
 595      * return null, if you to create a node use getNodeForPath.
 596      */
 597     private FHTreeStateNode getMapping(TreePath path) {
 598         return treePathMapping.get(path);
 599     }
 600 
 601     /**
 602      * Sent to completely rebuild the visible tree. All nodes are collapsed.
 603      */
 604     private void rebuild(boolean clearSelection) {
 605         Object            rootUO;
 606 
 607         treePathMapping.clear();
 608         if(treeModel != null && (rootUO = treeModel.getRoot()) != null) {
 609             root = createNodeForValue(rootUO, 0);
 610             root.path = new TreePath(rootUO);
 611             addMapping(root);
 612             if(isRootVisible()) {
 613                 rowCount = 1;
 614                 root.row = 0;


 770 
 771         //
 772         // Overriden DefaultMutableTreeNode methods
 773         //
 774 
 775         /**
 776          * Messaged when this node is added somewhere, resets the path
 777          * and adds a mapping from path to this node.
 778          */
 779         public void setParent(MutableTreeNode parent) {
 780             super.setParent(parent);
 781             if(parent != null) {
 782                 path = ((FHTreeStateNode)parent).getTreePath().
 783                             pathByAddingChild(getUserObject());
 784                 addMapping(this);
 785             }
 786         }
 787 
 788         /**
 789          * Messaged when this node is removed from its parent, this messages
 790          * <code>removedFromMapping</code> to remove all the children.
 791          */
 792         public void remove(int childIndex) {
 793             FHTreeStateNode     node = (FHTreeStateNode)getChildAt(childIndex);
 794 
 795             node.removeFromMapping();
 796             super.remove(childIndex);
 797         }
 798 
 799         /**
 800          * Messaged to set the user object. This resets the path.
 801          */
 802         public void setUserObject(Object o) {
 803             super.setUserObject(o);
 804             if(path != null) {
 805                 FHTreeStateNode      parent = (FHTreeStateNode)getParent();
 806 
 807                 if(parent != null)
 808                     resetChildrenPaths(parent.getTreePath());
 809                 else
 810                     resetChildrenPaths(null);
 811             }
 812         }
 813 
 814         //
 815         //
 816 
 817         /**
 818          * Returns the index of the receiver in the model.
 819          */
 820         public int getChildIndex() {
 821             return childIndex;
 822         }
 823 
 824         /**
 825          * Returns the <code>TreePath</code> of the receiver.
 826          */
 827         public TreePath getTreePath() {
 828             return path;
 829         }
 830 
 831         /**
 832          * Returns the child for the passed in model index, this will
 833          * return <code>null</code> if the child for <code>index</code>
 834          * has not yet been created (expanded).
 835          */
 836         public FHTreeStateNode getChildAtModelIndex(int index) {
 837             // PENDING: Make this a binary search!
 838             for(int counter = getChildCount() - 1; counter >= 0; counter--)
 839                 if(((FHTreeStateNode)getChildAt(counter)).childIndex == index)
 840                     return (FHTreeStateNode)getChildAt(counter);
 841             return null;
 842         }
 843 
 844         /**
 845          * Returns true if this node is visible. This is determined by
 846          * asking all the parents if they are expanded.
 847          */
 848         public boolean isVisible() {
 849             FHTreeStateNode         parent = (FHTreeStateNode)getParent();
 850 
 851             if(parent == null)
 852                 return true;
 853             return (parent.isExpanded() && parent.isVisible());
 854         }
 855 
 856         /**
 857          * Returns the row of the receiver.
 858          */
 859         public int getRow() {
 860             return row;
 861         }
 862 
 863         /**
 864          * Returns the row of the child with a model index of
 865          * <code>index</code>.
 866          */
 867         public int getRowToModelIndex(int index) {
 868             FHTreeStateNode      child;
 869             int                  lastRow = getRow() + 1;
 870             int                  retValue = lastRow;
 871 
 872             // This too could be a binary search!
 873             for(int counter = 0, maxCounter = getChildCount();
 874                 counter < maxCounter; counter++) {
 875                 child = (FHTreeStateNode)getChildAt(counter);
 876                 if(child.childIndex >= index) {
 877                     if(child.childIndex == index)
 878                         return child.row;
 879                     if(counter == 0)
 880                         return getRow() + 1 + index;
 881                     return child.row - (child.childIndex - index);
 882                 }
 883             }
 884             // YECK!
 885             return getRow() + 1 + getTotalChildCount() -


 948                 path = parentPath.pathByAddingChild(getUserObject());
 949             addMapping(this);
 950             for(int counter = getChildCount() - 1; counter >= 0; counter--)
 951                 ((FHTreeStateNode)getChildAt(counter)).
 952                                resetChildrenPaths(path);
 953         }
 954 
 955         /**
 956          * Removes the receiver, and all its children, from the mapping
 957          * table.
 958          */
 959         protected void removeFromMapping() {
 960             if(path != null) {
 961                 removeMapping(this);
 962                 for(int counter = getChildCount() - 1; counter >= 0; counter--)
 963                     ((FHTreeStateNode)getChildAt(counter)).removeFromMapping();
 964             }
 965         }
 966 
 967         /**
 968          * Creates a new node to represent <code>userObject</code>.
 969          * This does NOT check to ensure there isn't already a child node
 970          * to manage <code>userObject</code>.
 971          */
 972         protected FHTreeStateNode createChildFor(Object userObject) {
 973             int      newChildIndex = treeModel.getIndexOfChild
 974                                      (getUserObject(), userObject);
 975 
 976             if(newChildIndex < 0)
 977                 return null;
 978 
 979             FHTreeStateNode     aNode;
 980             FHTreeStateNode     child = createNodeForValue(userObject,
 981                                                            newChildIndex);
 982             int                 childRow;
 983 
 984             if(isVisible()) {
 985                 childRow = getRowToModelIndex(newChildIndex);
 986             }
 987             else {
 988                 childRow = -1;
 989             }
 990             child.row = childRow;
 991             for(int counter = 0, maxCounter = getChildCount();
 992                 counter < maxCounter; counter++) {
 993                 aNode = (FHTreeStateNode)getChildAt(counter);
 994                 if(aNode.childIndex > newChildIndex) {
 995                     insert(child, counter);
 996                     return child;
 997                 }
 998             }
 999             add(child);
1000             return child;
1001         }
1002 
1003         /**
1004          * Adjusts the receiver, and all its children rows by
1005          * <code>amount</code>.
1006          */
1007         protected void adjustRowBy(int amount) {
1008             row += amount;
1009             if(isExpanded) {
1010                 for(int counter = getChildCount() - 1; counter >= 0;
1011                     counter--)
1012                     ((FHTreeStateNode)getChildAt(counter)).adjustRowBy(amount);
1013             }
1014         }
1015 
1016         /**
1017          * Adjusts this node, its child, and its parent starting at
1018          * an index of <code>index</code> index is the index of the child
1019          * to start adjusting from, which is not necessarily the model
1020          * index.
1021          */
1022         protected void adjustRowBy(int amount, int startIndex) {
1023             // Could check isVisible, but probably isn't worth it.
1024             if(isExpanded) {
1025                 // children following startIndex.
1026                 for(int counter = getChildCount() - 1; counter >= startIndex;
1027                     counter--)
1028                     ((FHTreeStateNode)getChildAt(counter)).adjustRowBy(amount);
1029             }
1030             // Parent
1031             FHTreeStateNode        parent = (FHTreeStateNode)getParent();
1032 
1033             if(parent != null) {
1034                 parent.adjustRowBy(amount, parent.getIndex(this) + 1);
1035             }
1036         }
1037 
1038         /**
1039          * Messaged when the node has expanded. This updates all of
1040          * the receivers children rows, as well as the total row count.
1041          */
1042         protected void didExpand() {
1043             int               nextRow = setRowAndChildren(row);
1044             FHTreeStateNode   parent = (FHTreeStateNode)getParent();
1045             int               childRowCount = nextRow - row - 1;
1046 
1047             if(parent != null) {
1048                 parent.adjustRowBy(childRowCount, parent.getIndex(this) + 1);
1049             }
1050             adjustRowCountBy(childRowCount);
1051         }
1052 
1053         /**
1054          * Sets the receivers row to <code>nextRow</code> and recursively
1055          * updates all the children of the receivers rows. The index the
1056          * next row is to be placed as is returned.
1057          */
1058         protected int setRowAndChildren(int nextRow) {
1059             row = nextRow;
1060 
1061             if(!isExpanded())
1062                 return row + 1;
1063 
1064             int              lastRow = row + 1;
1065             int              lastModelIndex = 0;
1066             FHTreeStateNode  child;
1067             int              maxCounter = getChildCount();
1068 
1069             for(int counter = 0; counter < maxCounter; counter++) {
1070                 child = (FHTreeStateNode)getChildAt(counter);
1071                 lastRow += (child.childIndex - lastModelIndex);
1072                 lastModelIndex = child.childIndex + 1;
1073                 if(child.isExpanded) {
1074                     lastRow = child.setRowAndChildren(lastRow);
1075                 }
1076                 else {
1077                     child.row = lastRow++;
1078                 }
1079             }
1080             return lastRow + childCount - lastModelIndex;
1081         }
1082 
1083         /**
1084          * Resets the receivers children's rows. Starting with the child
1085          * at <code>childIndex</code> (and <code>modelIndex</code>) to
1086          * <code>newRow</code>. This uses <code>setRowAndChildren</code>
1087          * to recursively descend children, and uses
1088          * <code>resetRowSelection</code> to ascend parents.
1089          */
1090         // This can be rather expensive, but is needed for the collapse
1091         // case this is resulting from a remove (although I could fix
1092         // that by having instances of FHTreeStateNode hold a ref to
1093         // the number of children). I prefer this though, making determing
1094         // the row of a particular node fast is very nice!
1095         protected void resetChildrenRowsFrom(int newRow, int childIndex,
1096                                             int modelIndex) {
1097             int              lastRow = newRow;
1098             int              lastModelIndex = modelIndex;
1099             FHTreeStateNode  node;
1100             int              maxCounter = getChildCount();
1101 
1102             for(int counter = childIndex; counter < maxCounter; counter++) {
1103                 node = (FHTreeStateNode)getChildAt(counter);
1104                 lastRow += (node.childIndex - lastModelIndex);
1105                 lastModelIndex = node.childIndex + 1;
1106                 if(node.isExpanded) {
1107                     lastRow = node.setRowAndChildren(lastRow);
1108                 }
1109                 else {
1110                     node.row = lastRow++;
1111                 }
1112             }
1113             lastRow += childCount - lastModelIndex;
1114             node = (FHTreeStateNode)getParent();
1115             if(node != null) {
1116                 node.resetChildrenRowsFrom(lastRow, node.getIndex(this) + 1,
1117                                            this.childIndex + 1);
1118             }
1119             else { // This is the root, reset total ROWCOUNT!
1120                 rowCount = lastRow;
1121             }
1122         }
1123 
1124         /**
1125          * Makes the receiver visible, but invoking
1126          * <code>expandParentAndReceiver</code> on the superclass.
1127          */
1128         protected void makeVisible() {
1129             FHTreeStateNode       parent = (FHTreeStateNode)getParent();
1130 
1131             if(parent != null)
1132                 parent.expandParentAndReceiver();
1133         }
1134 
1135         /**
1136          * Invokes <code>expandParentAndReceiver</code> on the parent,
1137          * and expands the receiver.
1138          */
1139         protected void expandParentAndReceiver() {
1140             FHTreeStateNode       parent = (FHTreeStateNode)getParent();
1141 
1142             if(parent != null)
1143                 parent.expandParentAndReceiver();
1144             expand();
1145         }
1146 
1147         /**
1148          * Expands the receiver.
1149          */
1150         protected void expand() {
1151             if(!isExpanded && !isLeaf()) {
1152                 boolean            visible = isVisible();
1153 
1154                 isExpanded = true;
1155                 childCount = treeModel.getChildCount(getUserObject());
1156 
1157                 if(visible) {
1158                     didExpand();
1159                 }
1160 
1161                 // Update the selection model.
1162                 if(visible && treeSelectionModel != null) {
1163                     treeSelectionModel.resetRowSelection();
1164                 }
1165             }
1166         }
1167 
1168         /**
1169          * Collapses the receiver. If <code>adjustRows</code> is true,
1170          * the rows of nodes after the receiver are adjusted.
1171          */
1172         protected void collapse(boolean adjustRows) {
1173             if(isExpanded) {
1174                 if(isVisible() && adjustRows) {
1175                     int              childCount = getTotalChildCount();
1176 
1177                     isExpanded = false;
1178                     adjustRowCountBy(-childCount);
1179                     // We can do this because adjustRowBy won't descend
1180                     // the children.
1181                     adjustRowBy(-childCount, 0);
1182                 }
1183                 else
1184                     isExpanded = false;
1185 
1186                 if(adjustRows && isVisible() && treeSelectionModel != null)
1187                     treeSelectionModel.resetRowSelection();
1188             }
1189         }


1203          * The location is determined from the childIndex of newChild.
1204          */
1205         protected void addNode(FHTreeStateNode newChild) {
1206             boolean         added = false;
1207             int             childIndex = newChild.getChildIndex();
1208 
1209             for(int counter = 0, maxCounter = getChildCount();
1210                 counter < maxCounter; counter++) {
1211                 if(((FHTreeStateNode)getChildAt(counter)).getChildIndex() >
1212                    childIndex) {
1213                     added = true;
1214                     insert(newChild, counter);
1215                     counter = maxCounter;
1216                 }
1217             }
1218             if(!added)
1219                 add(newChild);
1220         }
1221 
1222         /**
1223          * Removes the child at <code>modelIndex</code>.
1224          * <code>isChildVisible</code> should be true if the receiver
1225          * is visible and expanded.
1226          */
1227         protected void removeChildAtModelIndex(int modelIndex,
1228                                                boolean isChildVisible) {
1229             FHTreeStateNode     childNode = getChildAtModelIndex(modelIndex);
1230 
1231             if(childNode != null) {
1232                 int          row = childNode.getRow();
1233                 int          index = getIndex(childNode);
1234 
1235                 childNode.collapse(false);
1236                 remove(index);
1237                 adjustChildIndexs(index, -1);
1238                 childCount--;
1239                 if(isChildVisible) {
1240                     // Adjust the rows.
1241                     resetChildrenRowsFrom(row, index, modelIndex);
1242                 }
1243             }
1244             else {


1257                         // above.
1258                         for(; counter < maxCounter; counter++)
1259                             ((FHTreeStateNode)getChildAt(counter)).
1260                                               childIndex--;
1261                         childCount--;
1262                         return;
1263                     }
1264                 }
1265                 // No children to adjust, but it was a child, so we still need
1266                 // to adjust nodes after this one.
1267                 if(isChildVisible) {
1268                     adjustRowBy(-1, maxCounter);
1269                     adjustRowCountBy(-1);
1270                 }
1271                 childCount--;
1272             }
1273         }
1274 
1275         /**
1276          * Adjusts the child indexs of the receivers children by
1277          * <code>amount</code>, starting at <code>index</code>.
1278          */
1279         protected void adjustChildIndexs(int index, int amount) {
1280             for(int counter = index, maxCounter = getChildCount();
1281                 counter < maxCounter; counter++) {
1282                 ((FHTreeStateNode)getChildAt(counter)).childIndex += amount;
1283             }
1284         }
1285 
1286         /**
1287          * Messaged when a child has been inserted at index. For all the
1288          * children that have a childIndex &ge; index their index is incremented
1289          * by one.
1290          */
1291         protected void childInsertedAtModelIndex(int index,
1292                                                boolean isExpandedAndVisible) {
1293             FHTreeStateNode                aChild;
1294             int                            maxCounter = getChildCount();
1295 
1296             for(int counter = 0; counter < maxCounter; counter++) {
1297                 aChild = (FHTreeStateNode)getChildAt(counter);


1301                         adjustRowCountBy(1);
1302                     }
1303                     /* Since matched and children are always sorted by
1304                        index, no need to continue testing with the above. */
1305                     for(; counter < maxCounter; counter++)
1306                         ((FHTreeStateNode)getChildAt(counter)).childIndex++;
1307                     childCount++;
1308                     return;
1309                 }
1310             }
1311             // No children to adjust, but it was a child, so we still need
1312             // to adjust nodes after this one.
1313             if(isExpandedAndVisible) {
1314                 adjustRowBy(1, maxCounter);
1315                 adjustRowCountBy(1);
1316             }
1317             childCount++;
1318         }
1319 
1320         /**
1321          * Returns true if there is a row for <code>row</code>.
1322          * <code>nextRow</code> gives the bounds of the receiver.
1323          * Information about the found row is returned in <code>info</code>.
1324          * This should be invoked on root with <code>nextRow</code> set
1325          * to <code>getRowCount</code>().
1326          */
1327         protected boolean getPathForRow(int row, int nextRow,
1328                                         SearchInfo info) {
1329             if(this.row == row) {
1330                 info.node = this;
1331                 info.isNodeParentNode = false;
1332                 info.childIndex = childIndex;
1333                 return true;
1334             }
1335 
1336             FHTreeStateNode            child;
1337             FHTreeStateNode            lastChild = null;
1338 
1339             for(int counter = 0, maxCounter = getChildCount();
1340                 counter < maxCounter; counter++) {
1341                 child = (FHTreeStateNode)getChildAt(counter);
1342                 if(child.row > row) {
1343                     if(counter == 0) {
1344                         // No node exists for it, and is first.
1345                         info.node = this;


1406             int                retCount = stopIndex + 1;
1407 
1408             for(int counter = 0, maxCounter = getChildCount();
1409                 counter < maxCounter; counter++) {
1410                 aChild = (FHTreeStateNode)getChildAt(counter);
1411                 if(aChild.childIndex >= stopIndex)
1412                     counter = maxCounter;
1413                 else
1414                     retCount += aChild.getTotalChildCount();
1415             }
1416             if(parent != null)
1417                 return retCount + ((FHTreeStateNode)getParent())
1418                                    .getCountTo(childIndex);
1419             if(!isRootVisible())
1420                 return (retCount - 1);
1421             return retCount;
1422         }
1423 
1424         /**
1425          * Returns the number of children that are expanded to
1426          * <code>stopIndex</code>. This does not include the number
1427          * of children that the child at <code>stopIndex</code> might
1428          * have.
1429          */
1430         protected int getNumExpandedChildrenTo(int stopIndex) {
1431             FHTreeStateNode    aChild;
1432             int                retCount = stopIndex;
1433 
1434             for(int counter = 0, maxCounter = getChildCount();
1435                 counter < maxCounter; counter++) {
1436                 aChild = (FHTreeStateNode)getChildAt(counter);
1437                 if(aChild.childIndex >= stopIndex)
1438                     return retCount;
1439                 else {
1440                     retCount += aChild.getTotalChildCount();
1441                 }
1442             }
1443             return retCount;
1444         }
1445 
1446         /**
1447          * Messaged when this node either expands or collapses.


1517 
1518             TreePath                retObject;
1519 
1520             if(nextIndex == -1)
1521                 retObject = parent.getTreePath();
1522             else {
1523                 FHTreeStateNode  node = parent.getChildAtModelIndex(nextIndex);
1524 
1525                 if(node == null)
1526                     retObject = parent.getTreePath().pathByAddingChild
1527                                   (treeModel.getChild(parent.getUserObject(),
1528                                                       nextIndex));
1529                 else
1530                     retObject = node.getTreePath();
1531             }
1532             updateNextObject();
1533             return retObject;
1534         }
1535 
1536         /**
1537          * Determines the next object by invoking <code>updateNextIndex</code>
1538          * and if not succesful <code>findNextValidParent</code>.
1539          */
1540         protected void updateNextObject() {
1541             if(!updateNextIndex()) {
1542                 findNextValidParent();
1543             }
1544         }
1545 
1546         /**
1547          * Finds the next valid parent, this should be called when nextIndex
1548          * is beyond the number of children of the current parent.
1549          */
1550         protected boolean findNextValidParent() {
1551             if(parent == root) {
1552                 // mark as invalid!
1553                 parent = null;
1554                 return false;
1555             }
1556             while(parent != null) {
1557                 FHTreeStateNode      newParent = (FHTreeStateNode)parent.
1558                                                   getParent();
1559 
1560                 if(newParent != null) {
1561                     nextIndex = parent.childIndex;
1562                     parent = newParent;
1563                     childCount = treeModel.getChildCount
1564                                             (parent.getUserObject());
1565                     if(updateNextIndex())
1566                         return true;
1567                 }
1568                 else
1569                     parent = null;
1570             }
1571             return false;
1572         }
1573 
1574         /**
1575          * Updates <code>nextIndex</code> returning false if it is beyond
1576          * the number of children of parent.
1577          */
1578         protected boolean updateNextIndex() {
1579             // nextIndex == -1 identifies receiver, make sure is expanded
1580             // before descend.
1581             if(nextIndex == -1 && !parent.isExpanded()) {
1582                 return false;
1583             }
1584 
1585             // Check that it can have kids
1586             if(childCount == 0) {
1587                 return false;
1588             }
1589             // Make sure next index not beyond child count.
1590             else if(++nextIndex >= childCount) {
1591                 return false;
1592             }
1593 
1594             FHTreeStateNode    child = parent.getChildAtModelIndex(nextIndex);
1595 


  26 package javax.swing.tree;
  27 
  28 import javax.swing.event.TreeModelEvent;
  29 import java.awt.Rectangle;
  30 import java.util.Enumeration;
  31 import java.util.Hashtable;
  32 import java.util.NoSuchElementException;
  33 import java.util.Stack;
  34 
  35 import sun.swing.SwingUtilities2;
  36 
  37 /**
  38  * NOTE: This will become more open in a future release.
  39  * <p>
  40  * <strong>Warning:</strong>
  41  * Serialized objects of this class will not be compatible with
  42  * future Swing releases. The current serialization support is
  43  * appropriate for short term storage or RMI between applications running
  44  * the same version of Swing.  As of 1.4, support for long term storage
  45  * of all JavaBeans&trade;
  46  * has been added to the {@code java.beans} package.
  47  * Please see {@link java.beans.XMLEncoder}.
  48  *
  49  * @author Scott Violet
  50  */
  51 @SuppressWarnings("serial") // Same-version serialization only
  52 public class FixedHeightLayoutCache extends AbstractLayoutCache {
  53     /** Root node. */
  54     private FHTreeStateNode    root;
  55 
  56     /** Number of rows currently visible. */
  57     private int                rowCount;
  58 
  59     /**
  60      * Used in getting sizes for nodes to avoid creating a new Rectangle
  61      * every time a size is needed.
  62      */
  63     private Rectangle          boundsBuffer;
  64 
  65     /**
  66      * Maps from TreePath to a FHTreeStateNode.


 277         if(path == null)
 278             return null;
 279 
 280         FHTreeStateNode         node = getNodeForPath(path, true, false);
 281 
 282         if(node != null) {
 283             return new VisibleFHTreeStateNodeEnumeration(node);
 284         }
 285         TreePath            parentPath = path.getParentPath();
 286 
 287         node = getNodeForPath(parentPath, true, false);
 288         if(node != null && node.isExpanded()) {
 289             return new VisibleFHTreeStateNodeEnumeration(node,
 290                   treeModel.getIndexOfChild(parentPath.getLastPathComponent(),
 291                                             path.getLastPathComponent()));
 292         }
 293         return null;
 294     }
 295 
 296     /**
 297      * Marks the path {@code path} expanded state to
 298      * {@code isExpanded}.
 299      */
 300     public void setExpandedState(TreePath path, boolean isExpanded) {
 301         if(isExpanded)
 302             ensurePathIsExpanded(path, true);
 303         else if(path != null) {
 304             TreePath              parentPath = path.getParentPath();
 305 
 306             // YECK! Make the parent expanded.
 307             if(parentPath != null) {
 308                 FHTreeStateNode     parentNode = getNodeForPath(parentPath,
 309                                                                 false, true);
 310                 if(parentNode != null)
 311                     parentNode.makeVisible();
 312             }
 313             // And collapse the child.
 314             FHTreeStateNode         childNode = getNodeForPath(path, true,
 315                                                                false);
 316 
 317             if(childNode != null)
 318                 childNode.collapse(true);


 510                     changedNode = getNodeForPath(changedPath, false, true);
 511                     changedNode.expand();
 512                 }
 513                 if(treeSelectionModel != null && wasVisible && wasExpanded)
 514                     treeSelectionModel.resetRowSelection();
 515                 if(wasVisible)
 516                     this.visibleNodesChanged();
 517             }
 518         }
 519     }
 520 
 521 
 522     //
 523     // Local methods
 524     //
 525 
 526     private void visibleNodesChanged() {
 527     }
 528 
 529     /**
 530      * Returns the bounds for the given node. If {@code childIndex}
 531      * is -1, the bounds of {@code parent} are returned, otherwise
 532      * the bounds of the node at {@code childIndex} are returned.
 533      */
 534     private Rectangle getBounds(FHTreeStateNode parent, int childIndex,
 535                                   Rectangle placeIn) {
 536         boolean              expanded;
 537         int                  level;
 538         int                  row;
 539         Object               value;
 540 
 541         if(childIndex == -1) {
 542             // Getting bounds for parent
 543             row = parent.getRow();
 544             value = parent.getUserObject();
 545             expanded = parent.isExpanded();
 546             level = parent.getLevel();
 547         }
 548         else {
 549             row = parent.getRowToModelIndex(childIndex);
 550             value = treeModel.getChild(parent.getUserObject(), childIndex);
 551             expanded = false;
 552             level = parent.getLevel() + 1;


 574      */
 575     private void adjustRowCountBy(int changeAmount) {
 576         rowCount += changeAmount;
 577     }
 578 
 579     /**
 580      * Adds a mapping for node.
 581      */
 582     private void addMapping(FHTreeStateNode node) {
 583         treePathMapping.put(node.getTreePath(), node);
 584     }
 585 
 586     /**
 587      * Removes the mapping for a previously added node.
 588      */
 589     private void removeMapping(FHTreeStateNode node) {
 590         treePathMapping.remove(node.getTreePath());
 591     }
 592 
 593     /**
 594      * Returns the node previously added for {@code path}. This may
 595      * return null, if you to create a node use getNodeForPath.
 596      */
 597     private FHTreeStateNode getMapping(TreePath path) {
 598         return treePathMapping.get(path);
 599     }
 600 
 601     /**
 602      * Sent to completely rebuild the visible tree. All nodes are collapsed.
 603      */
 604     private void rebuild(boolean clearSelection) {
 605         Object            rootUO;
 606 
 607         treePathMapping.clear();
 608         if(treeModel != null && (rootUO = treeModel.getRoot()) != null) {
 609             root = createNodeForValue(rootUO, 0);
 610             root.path = new TreePath(rootUO);
 611             addMapping(root);
 612             if(isRootVisible()) {
 613                 rowCount = 1;
 614                 root.row = 0;


 770 
 771         //
 772         // Overriden DefaultMutableTreeNode methods
 773         //
 774 
 775         /**
 776          * Messaged when this node is added somewhere, resets the path
 777          * and adds a mapping from path to this node.
 778          */
 779         public void setParent(MutableTreeNode parent) {
 780             super.setParent(parent);
 781             if(parent != null) {
 782                 path = ((FHTreeStateNode)parent).getTreePath().
 783                             pathByAddingChild(getUserObject());
 784                 addMapping(this);
 785             }
 786         }
 787 
 788         /**
 789          * Messaged when this node is removed from its parent, this messages
 790          * {@code removedFromMapping} to remove all the children.
 791          */
 792         public void remove(int childIndex) {
 793             FHTreeStateNode     node = (FHTreeStateNode)getChildAt(childIndex);
 794 
 795             node.removeFromMapping();
 796             super.remove(childIndex);
 797         }
 798 
 799         /**
 800          * Messaged to set the user object. This resets the path.
 801          */
 802         public void setUserObject(Object o) {
 803             super.setUserObject(o);
 804             if(path != null) {
 805                 FHTreeStateNode      parent = (FHTreeStateNode)getParent();
 806 
 807                 if(parent != null)
 808                     resetChildrenPaths(parent.getTreePath());
 809                 else
 810                     resetChildrenPaths(null);
 811             }
 812         }
 813 
 814         //
 815         //
 816 
 817         /**
 818          * Returns the index of the receiver in the model.
 819          */
 820         public int getChildIndex() {
 821             return childIndex;
 822         }
 823 
 824         /**
 825          * Returns the {@code TreePath} of the receiver.
 826          */
 827         public TreePath getTreePath() {
 828             return path;
 829         }
 830 
 831         /**
 832          * Returns the child for the passed in model index, this will
 833          * return {@code null} if the child for {@code index}
 834          * has not yet been created (expanded).
 835          */
 836         public FHTreeStateNode getChildAtModelIndex(int index) {
 837             // PENDING: Make this a binary search!
 838             for(int counter = getChildCount() - 1; counter >= 0; counter--)
 839                 if(((FHTreeStateNode)getChildAt(counter)).childIndex == index)
 840                     return (FHTreeStateNode)getChildAt(counter);
 841             return null;
 842         }
 843 
 844         /**
 845          * Returns true if this node is visible. This is determined by
 846          * asking all the parents if they are expanded.
 847          */
 848         public boolean isVisible() {
 849             FHTreeStateNode         parent = (FHTreeStateNode)getParent();
 850 
 851             if(parent == null)
 852                 return true;
 853             return (parent.isExpanded() && parent.isVisible());
 854         }
 855 
 856         /**
 857          * Returns the row of the receiver.
 858          */
 859         public int getRow() {
 860             return row;
 861         }
 862 
 863         /**
 864          * Returns the row of the child with a model index of
 865          * {@code index}.
 866          */
 867         public int getRowToModelIndex(int index) {
 868             FHTreeStateNode      child;
 869             int                  lastRow = getRow() + 1;
 870             int                  retValue = lastRow;
 871 
 872             // This too could be a binary search!
 873             for(int counter = 0, maxCounter = getChildCount();
 874                 counter < maxCounter; counter++) {
 875                 child = (FHTreeStateNode)getChildAt(counter);
 876                 if(child.childIndex >= index) {
 877                     if(child.childIndex == index)
 878                         return child.row;
 879                     if(counter == 0)
 880                         return getRow() + 1 + index;
 881                     return child.row - (child.childIndex - index);
 882                 }
 883             }
 884             // YECK!
 885             return getRow() + 1 + getTotalChildCount() -


 948                 path = parentPath.pathByAddingChild(getUserObject());
 949             addMapping(this);
 950             for(int counter = getChildCount() - 1; counter >= 0; counter--)
 951                 ((FHTreeStateNode)getChildAt(counter)).
 952                                resetChildrenPaths(path);
 953         }
 954 
 955         /**
 956          * Removes the receiver, and all its children, from the mapping
 957          * table.
 958          */
 959         protected void removeFromMapping() {
 960             if(path != null) {
 961                 removeMapping(this);
 962                 for(int counter = getChildCount() - 1; counter >= 0; counter--)
 963                     ((FHTreeStateNode)getChildAt(counter)).removeFromMapping();
 964             }
 965         }
 966 
 967         /**
 968          * Creates a new node to represent {@code userObject}.
 969          * This does NOT check to ensure there isn't already a child node
 970          * to manage {@code userObject}.
 971          */
 972         protected FHTreeStateNode createChildFor(Object userObject) {
 973             int      newChildIndex = treeModel.getIndexOfChild
 974                                      (getUserObject(), userObject);
 975 
 976             if(newChildIndex < 0)
 977                 return null;
 978 
 979             FHTreeStateNode     aNode;
 980             FHTreeStateNode     child = createNodeForValue(userObject,
 981                                                            newChildIndex);
 982             int                 childRow;
 983 
 984             if(isVisible()) {
 985                 childRow = getRowToModelIndex(newChildIndex);
 986             }
 987             else {
 988                 childRow = -1;
 989             }
 990             child.row = childRow;
 991             for(int counter = 0, maxCounter = getChildCount();
 992                 counter < maxCounter; counter++) {
 993                 aNode = (FHTreeStateNode)getChildAt(counter);
 994                 if(aNode.childIndex > newChildIndex) {
 995                     insert(child, counter);
 996                     return child;
 997                 }
 998             }
 999             add(child);
1000             return child;
1001         }
1002 
1003         /**
1004          * Adjusts the receiver, and all its children rows by
1005          * {@code amount}.
1006          */
1007         protected void adjustRowBy(int amount) {
1008             row += amount;
1009             if(isExpanded) {
1010                 for(int counter = getChildCount() - 1; counter >= 0;
1011                     counter--)
1012                     ((FHTreeStateNode)getChildAt(counter)).adjustRowBy(amount);
1013             }
1014         }
1015 
1016         /**
1017          * Adjusts this node, its child, and its parent starting at
1018          * an index of {@code index} index is the index of the child
1019          * to start adjusting from, which is not necessarily the model
1020          * index.
1021          */
1022         protected void adjustRowBy(int amount, int startIndex) {
1023             // Could check isVisible, but probably isn't worth it.
1024             if(isExpanded) {
1025                 // children following startIndex.
1026                 for(int counter = getChildCount() - 1; counter >= startIndex;
1027                     counter--)
1028                     ((FHTreeStateNode)getChildAt(counter)).adjustRowBy(amount);
1029             }
1030             // Parent
1031             FHTreeStateNode        parent = (FHTreeStateNode)getParent();
1032 
1033             if(parent != null) {
1034                 parent.adjustRowBy(amount, parent.getIndex(this) + 1);
1035             }
1036         }
1037 
1038         /**
1039          * Messaged when the node has expanded. This updates all of
1040          * the receivers children rows, as well as the total row count.
1041          */
1042         protected void didExpand() {
1043             int               nextRow = setRowAndChildren(row);
1044             FHTreeStateNode   parent = (FHTreeStateNode)getParent();
1045             int               childRowCount = nextRow - row - 1;
1046 
1047             if(parent != null) {
1048                 parent.adjustRowBy(childRowCount, parent.getIndex(this) + 1);
1049             }
1050             adjustRowCountBy(childRowCount);
1051         }
1052 
1053         /**
1054          * Sets the receivers row to {@code nextRow} and recursively
1055          * updates all the children of the receivers rows. The index the
1056          * next row is to be placed as is returned.
1057          */
1058         protected int setRowAndChildren(int nextRow) {
1059             row = nextRow;
1060 
1061             if(!isExpanded())
1062                 return row + 1;
1063 
1064             int              lastRow = row + 1;
1065             int              lastModelIndex = 0;
1066             FHTreeStateNode  child;
1067             int              maxCounter = getChildCount();
1068 
1069             for(int counter = 0; counter < maxCounter; counter++) {
1070                 child = (FHTreeStateNode)getChildAt(counter);
1071                 lastRow += (child.childIndex - lastModelIndex);
1072                 lastModelIndex = child.childIndex + 1;
1073                 if(child.isExpanded) {
1074                     lastRow = child.setRowAndChildren(lastRow);
1075                 }
1076                 else {
1077                     child.row = lastRow++;
1078                 }
1079             }
1080             return lastRow + childCount - lastModelIndex;
1081         }
1082 
1083         /**
1084          * Resets the receivers children's rows. Starting with the child
1085          * at {@code childIndex} (and {@code modelIndex}) to
1086          * {@code newRow}. This uses {@code setRowAndChildren}
1087          * to recursively descend children, and uses
1088          * {@code resetRowSelection} to ascend parents.
1089          */
1090         // This can be rather expensive, but is needed for the collapse
1091         // case this is resulting from a remove (although I could fix
1092         // that by having instances of FHTreeStateNode hold a ref to
1093         // the number of children). I prefer this though, making determing
1094         // the row of a particular node fast is very nice!
1095         protected void resetChildrenRowsFrom(int newRow, int childIndex,
1096                                             int modelIndex) {
1097             int              lastRow = newRow;
1098             int              lastModelIndex = modelIndex;
1099             FHTreeStateNode  node;
1100             int              maxCounter = getChildCount();
1101 
1102             for(int counter = childIndex; counter < maxCounter; counter++) {
1103                 node = (FHTreeStateNode)getChildAt(counter);
1104                 lastRow += (node.childIndex - lastModelIndex);
1105                 lastModelIndex = node.childIndex + 1;
1106                 if(node.isExpanded) {
1107                     lastRow = node.setRowAndChildren(lastRow);
1108                 }
1109                 else {
1110                     node.row = lastRow++;
1111                 }
1112             }
1113             lastRow += childCount - lastModelIndex;
1114             node = (FHTreeStateNode)getParent();
1115             if(node != null) {
1116                 node.resetChildrenRowsFrom(lastRow, node.getIndex(this) + 1,
1117                                            this.childIndex + 1);
1118             }
1119             else { // This is the root, reset total ROWCOUNT!
1120                 rowCount = lastRow;
1121             }
1122         }
1123 
1124         /**
1125          * Makes the receiver visible, but invoking
1126          * {@code expandParentAndReceiver} on the superclass.
1127          */
1128         protected void makeVisible() {
1129             FHTreeStateNode       parent = (FHTreeStateNode)getParent();
1130 
1131             if(parent != null)
1132                 parent.expandParentAndReceiver();
1133         }
1134 
1135         /**
1136          * Invokes {@code expandParentAndReceiver} on the parent,
1137          * and expands the receiver.
1138          */
1139         protected void expandParentAndReceiver() {
1140             FHTreeStateNode       parent = (FHTreeStateNode)getParent();
1141 
1142             if(parent != null)
1143                 parent.expandParentAndReceiver();
1144             expand();
1145         }
1146 
1147         /**
1148          * Expands the receiver.
1149          */
1150         protected void expand() {
1151             if(!isExpanded && !isLeaf()) {
1152                 boolean            visible = isVisible();
1153 
1154                 isExpanded = true;
1155                 childCount = treeModel.getChildCount(getUserObject());
1156 
1157                 if(visible) {
1158                     didExpand();
1159                 }
1160 
1161                 // Update the selection model.
1162                 if(visible && treeSelectionModel != null) {
1163                     treeSelectionModel.resetRowSelection();
1164                 }
1165             }
1166         }
1167 
1168         /**
1169          * Collapses the receiver. If {@code adjustRows} is true,
1170          * the rows of nodes after the receiver are adjusted.
1171          */
1172         protected void collapse(boolean adjustRows) {
1173             if(isExpanded) {
1174                 if(isVisible() && adjustRows) {
1175                     int              childCount = getTotalChildCount();
1176 
1177                     isExpanded = false;
1178                     adjustRowCountBy(-childCount);
1179                     // We can do this because adjustRowBy won't descend
1180                     // the children.
1181                     adjustRowBy(-childCount, 0);
1182                 }
1183                 else
1184                     isExpanded = false;
1185 
1186                 if(adjustRows && isVisible() && treeSelectionModel != null)
1187                     treeSelectionModel.resetRowSelection();
1188             }
1189         }


1203          * The location is determined from the childIndex of newChild.
1204          */
1205         protected void addNode(FHTreeStateNode newChild) {
1206             boolean         added = false;
1207             int             childIndex = newChild.getChildIndex();
1208 
1209             for(int counter = 0, maxCounter = getChildCount();
1210                 counter < maxCounter; counter++) {
1211                 if(((FHTreeStateNode)getChildAt(counter)).getChildIndex() >
1212                    childIndex) {
1213                     added = true;
1214                     insert(newChild, counter);
1215                     counter = maxCounter;
1216                 }
1217             }
1218             if(!added)
1219                 add(newChild);
1220         }
1221 
1222         /**
1223          * Removes the child at {@code modelIndex}.
1224          * {@code isChildVisible} should be true if the receiver
1225          * is visible and expanded.
1226          */
1227         protected void removeChildAtModelIndex(int modelIndex,
1228                                                boolean isChildVisible) {
1229             FHTreeStateNode     childNode = getChildAtModelIndex(modelIndex);
1230 
1231             if(childNode != null) {
1232                 int          row = childNode.getRow();
1233                 int          index = getIndex(childNode);
1234 
1235                 childNode.collapse(false);
1236                 remove(index);
1237                 adjustChildIndexs(index, -1);
1238                 childCount--;
1239                 if(isChildVisible) {
1240                     // Adjust the rows.
1241                     resetChildrenRowsFrom(row, index, modelIndex);
1242                 }
1243             }
1244             else {


1257                         // above.
1258                         for(; counter < maxCounter; counter++)
1259                             ((FHTreeStateNode)getChildAt(counter)).
1260                                               childIndex--;
1261                         childCount--;
1262                         return;
1263                     }
1264                 }
1265                 // No children to adjust, but it was a child, so we still need
1266                 // to adjust nodes after this one.
1267                 if(isChildVisible) {
1268                     adjustRowBy(-1, maxCounter);
1269                     adjustRowCountBy(-1);
1270                 }
1271                 childCount--;
1272             }
1273         }
1274 
1275         /**
1276          * Adjusts the child indexs of the receivers children by
1277          * {@code amount}, starting at {@code index}.
1278          */
1279         protected void adjustChildIndexs(int index, int amount) {
1280             for(int counter = index, maxCounter = getChildCount();
1281                 counter < maxCounter; counter++) {
1282                 ((FHTreeStateNode)getChildAt(counter)).childIndex += amount;
1283             }
1284         }
1285 
1286         /**
1287          * Messaged when a child has been inserted at index. For all the
1288          * children that have a childIndex &ge; index their index is incremented
1289          * by one.
1290          */
1291         protected void childInsertedAtModelIndex(int index,
1292                                                boolean isExpandedAndVisible) {
1293             FHTreeStateNode                aChild;
1294             int                            maxCounter = getChildCount();
1295 
1296             for(int counter = 0; counter < maxCounter; counter++) {
1297                 aChild = (FHTreeStateNode)getChildAt(counter);


1301                         adjustRowCountBy(1);
1302                     }
1303                     /* Since matched and children are always sorted by
1304                        index, no need to continue testing with the above. */
1305                     for(; counter < maxCounter; counter++)
1306                         ((FHTreeStateNode)getChildAt(counter)).childIndex++;
1307                     childCount++;
1308                     return;
1309                 }
1310             }
1311             // No children to adjust, but it was a child, so we still need
1312             // to adjust nodes after this one.
1313             if(isExpandedAndVisible) {
1314                 adjustRowBy(1, maxCounter);
1315                 adjustRowCountBy(1);
1316             }
1317             childCount++;
1318         }
1319 
1320         /**
1321          * Returns true if there is a row for {@code row}.
1322          * {@code nextRow} gives the bounds of the receiver.
1323          * Information about the found row is returned in {@code info}.
1324          * This should be invoked on root with {@code nextRow} set
1325          * to {@code getRowCount}().
1326          */
1327         protected boolean getPathForRow(int row, int nextRow,
1328                                         SearchInfo info) {
1329             if(this.row == row) {
1330                 info.node = this;
1331                 info.isNodeParentNode = false;
1332                 info.childIndex = childIndex;
1333                 return true;
1334             }
1335 
1336             FHTreeStateNode            child;
1337             FHTreeStateNode            lastChild = null;
1338 
1339             for(int counter = 0, maxCounter = getChildCount();
1340                 counter < maxCounter; counter++) {
1341                 child = (FHTreeStateNode)getChildAt(counter);
1342                 if(child.row > row) {
1343                     if(counter == 0) {
1344                         // No node exists for it, and is first.
1345                         info.node = this;


1406             int                retCount = stopIndex + 1;
1407 
1408             for(int counter = 0, maxCounter = getChildCount();
1409                 counter < maxCounter; counter++) {
1410                 aChild = (FHTreeStateNode)getChildAt(counter);
1411                 if(aChild.childIndex >= stopIndex)
1412                     counter = maxCounter;
1413                 else
1414                     retCount += aChild.getTotalChildCount();
1415             }
1416             if(parent != null)
1417                 return retCount + ((FHTreeStateNode)getParent())
1418                                    .getCountTo(childIndex);
1419             if(!isRootVisible())
1420                 return (retCount - 1);
1421             return retCount;
1422         }
1423 
1424         /**
1425          * Returns the number of children that are expanded to
1426          * {@code stopIndex}. This does not include the number
1427          * of children that the child at {@code stopIndex} might
1428          * have.
1429          */
1430         protected int getNumExpandedChildrenTo(int stopIndex) {
1431             FHTreeStateNode    aChild;
1432             int                retCount = stopIndex;
1433 
1434             for(int counter = 0, maxCounter = getChildCount();
1435                 counter < maxCounter; counter++) {
1436                 aChild = (FHTreeStateNode)getChildAt(counter);
1437                 if(aChild.childIndex >= stopIndex)
1438                     return retCount;
1439                 else {
1440                     retCount += aChild.getTotalChildCount();
1441                 }
1442             }
1443             return retCount;
1444         }
1445 
1446         /**
1447          * Messaged when this node either expands or collapses.


1517 
1518             TreePath                retObject;
1519 
1520             if(nextIndex == -1)
1521                 retObject = parent.getTreePath();
1522             else {
1523                 FHTreeStateNode  node = parent.getChildAtModelIndex(nextIndex);
1524 
1525                 if(node == null)
1526                     retObject = parent.getTreePath().pathByAddingChild
1527                                   (treeModel.getChild(parent.getUserObject(),
1528                                                       nextIndex));
1529                 else
1530                     retObject = node.getTreePath();
1531             }
1532             updateNextObject();
1533             return retObject;
1534         }
1535 
1536         /**
1537          * Determines the next object by invoking {@code updateNextIndex}
1538          * and if not succesful {@code findNextValidParent}.
1539          */
1540         protected void updateNextObject() {
1541             if(!updateNextIndex()) {
1542                 findNextValidParent();
1543             }
1544         }
1545 
1546         /**
1547          * Finds the next valid parent, this should be called when nextIndex
1548          * is beyond the number of children of the current parent.
1549          */
1550         protected boolean findNextValidParent() {
1551             if(parent == root) {
1552                 // mark as invalid!
1553                 parent = null;
1554                 return false;
1555             }
1556             while(parent != null) {
1557                 FHTreeStateNode      newParent = (FHTreeStateNode)parent.
1558                                                   getParent();
1559 
1560                 if(newParent != null) {
1561                     nextIndex = parent.childIndex;
1562                     parent = newParent;
1563                     childCount = treeModel.getChildCount
1564                                             (parent.getUserObject());
1565                     if(updateNextIndex())
1566                         return true;
1567                 }
1568                 else
1569                     parent = null;
1570             }
1571             return false;
1572         }
1573 
1574         /**
1575          * Updates {@code nextIndex} returning false if it is beyond
1576          * the number of children of parent.
1577          */
1578         protected boolean updateNextIndex() {
1579             // nextIndex == -1 identifies receiver, make sure is expanded
1580             // before descend.
1581             if(nextIndex == -1 && !parent.isExpanded()) {
1582                 return false;
1583             }
1584 
1585             // Check that it can have kids
1586             if(childCount == 0) {
1587                 return false;
1588             }
1589             // Make sure next index not beyond child count.
1590             else if(++nextIndex >= childCount) {
1591                 return false;
1592             }
1593 
1594             FHTreeStateNode    child = parent.getChildAtModelIndex(nextIndex);
1595 
< prev index next >