1 /*
   2  * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.util.List;
  29 
  30 /**
  31  * A package protected util class used by TreeView and TreeTableView to reduce
  32  * the level of code duplication.
  33  */
  34 class TreeUtil {
  35     
  36     static <T> int getExpandedDescendantCount(TreeItem<T> node, boolean treeItemCountDirty) {
  37         if (node == null) return 0;
  38         if (node.isLeaf()) return 1;
  39         
  40         return node.getExpandedDescendentCount(treeItemCountDirty);
  41     }
  42     
  43     static int updateExpandedItemCount(TreeItem treeItem, boolean treeItemCountDirty, boolean isShowRoot) {
  44         if (treeItem == null) {
  45             return 0;
  46         } else if (! treeItem.isExpanded()) {
  47             return 1;
  48         } else {
  49             int count = getExpandedDescendantCount(treeItem, treeItemCountDirty);
  50             if (! isShowRoot) count--;
  51 
  52             return count;
  53         }
  54     }
  55 
  56     static <T> TreeItem<T> getItem(TreeItem<T> parent, int itemIndex, boolean treeItemCountDirty) {
  57         if (parent == null) return null;
  58 
  59         // if itemIndex is 0 then our parent is what we were looking for
  60         if (itemIndex == 0) return parent;
  61 
  62         // if itemIndex is > the total item count, then it is out of range
  63         if (itemIndex >= getExpandedDescendantCount(parent, treeItemCountDirty)) return null;
  64 
  65         // if we got here, then one of our descendants is the item we're after
  66         List<TreeItem<T>> children = parent.getChildren();
  67         if (children == null) return null;
  68         
  69         int idx = itemIndex - 1;
  70 
  71         TreeItem<T> child;
  72         for (int i = 0, max = children.size(); i < max; i++) {
  73             child = children.get(i);
  74             if (idx == 0) return child;
  75             
  76             if (child.isLeaf() || ! child.isExpanded()) {
  77                 idx--;
  78                 continue;
  79             }
  80             
  81             int expandedChildCount = getExpandedDescendantCount(child, treeItemCountDirty);
  82             if (idx >= expandedChildCount) {
  83                 idx -= expandedChildCount;
  84                 continue;
  85             }
  86 
  87             TreeItem<T> result = getItem(child, idx, treeItemCountDirty);
  88             if (result != null) return result;
  89             idx--;
  90         }
  91 
  92         // We might get here if getItem(0) is called on an empty tree
  93         return null;
  94     }
  95     
  96     static <T> int getRow(TreeItem<T> item, TreeItem<T> root, boolean treeItemCountDirty, boolean isShowRoot) {
  97         if (item == null) {
  98             return -1;
  99         } else if (isShowRoot && item.equals(root)) {
 100             return 0;
 101         }
 102         
 103         int row = 0;
 104         TreeItem<T> i = item;
 105         TreeItem<T> p = item.getParent();
 106         
 107         TreeItem<T> sibling;
 108         List<TreeItem<T>> siblings;
 109         boolean parentIsCollapsed = false;
 110         
 111         while (!i.equals(root) && p != null) {
 112             if (!p.isExpanded()) {
 113                 parentIsCollapsed = true;
 114                 break;
 115             }
 116 
 117             siblings = p.children;
 118             
 119             // work up each sibling, from the current item
 120             int itemIndex = siblings.indexOf(i);
 121             for (int pos = itemIndex - 1; pos > -1; pos--) {
 122                 sibling = siblings.get(pos);
 123                 if (sibling == null) continue;
 124                 
 125                 row += getExpandedDescendantCount(sibling, treeItemCountDirty);
 126                 
 127                 if (sibling.equals(root)) {
 128                     if (! isShowRoot) {
 129                         // special case: we've found out that our sibling is 
 130                         // actually the root node AND we aren't showing root nodes.
 131                         // This means that the item shouldn't actually be shown.
 132                         return -1;
 133                     }
 134                     return row;
 135                 }
 136             }
 137             
 138             i = p;
 139             p = p.getParent();
 140             row++;
 141         }
 142         
 143         return (p == null && row == 0) || parentIsCollapsed ? -1 : isShowRoot ? row : row - 1;
 144     }
 145 }