1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.treeview;
  33 
  34 import static com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.treeview.HierarchyTreeCell.HIERARCHY_TREE_CELL;
  35 import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
  36 import java.util.List;
  37 import java.util.Set;
  38 import javafx.scene.Node;
  39 import javafx.scene.control.TreeCell;
  40 import javafx.scene.control.TreeItem;
  41 import javafx.scene.control.TreeView;
  42 
  43 /**
  44  *
  45  * p
  46  */
  47 public abstract class HierarchyTreeViewUtils {
  48 
  49     /**
  50      * Returns the TreeCells for the specified TreeView.
  51      *
  52      * @param <T>
  53      * @param treeView the TreeView owner
  54      * @return the TreeCells for the specified TreeView.
  55      */
  56     public static <T> Set<Node> getTreeCells(final TreeView<T> treeView) {
  57         assert treeView != null;
  58         // Looks for the sub nodes which match the CSS selector
  59         return treeView.lookupAll("." + HIERARCHY_TREE_CELL); //NOI18N
  60     }
  61 
  62     /**
  63      * Returns the TreeCell object corresponding to the specified TreeItem.
  64      *
  65      * @param treeView the TreeView owner
  66      * @param treeItem the TreeItem instance
  67      * @return the TreeCell object corresponding to the specified TreeItem.
  68      */
  69     public static TreeCell<?> getTreeCell(final TreeView<?> treeView, final TreeItem<?> treeItem) {
  70         return getTreeCell(getTreeCells(treeView), treeItem);
  71     }
  72 
  73     /**
  74      * Returns the TreeCell object corresponding to the specified index.
  75      *
  76      * @param treeView the TreeView owner
  77      * @param index the TreeCell index
  78      * @return the TreeCell object corresponding to the specified index.
  79      */
  80     public static TreeCell<?> getTreeCell(final TreeView<?> treeView, final int index) {
  81         return getTreeCell(getTreeCells(treeView), index);
  82     }
  83 
  84     /**
  85      * Returns the TreeCell object corresponding to the specified TreeItem.
  86      *
  87      * @param treeCells the set of TreeCells
  88      * @param treeItem the TreeItem instance
  89      * @return the TreeCell object corresponding to the specified TreeItem.
  90      */
  91     public static TreeCell<?> getTreeCell(final Set<Node> treeCells, final TreeItem<?> treeItem) {
  92         assert treeCells != null;
  93         assert treeItem != null;
  94         for (Node node : treeCells) {
  95             assert node instanceof TreeCell;
  96             final TreeCell<?> treeCell = (TreeCell<?>) node;
  97             if (treeItem.getValue() != null
  98                     && treeItem.getValue().equals(treeCell.getItem())) {
  99                 return treeCell;
 100             }
 101         }
 102         return null;
 103     }
 104 
 105     /**
 106      * Returns the TreeCell object corresponding to the specified index.
 107      *
 108      * @param treeCells the set of TreeCells
 109      * @param index the TreeCell index
 110      * @return the TreeCell object corresponding to the specified index.
 111      */
 112     public static TreeCell<?> getTreeCell(final Set<Node> treeCells, final int index) {
 113         assert treeCells != null;
 114         for (Node node : treeCells) {
 115             assert node instanceof TreeCell;
 116             final TreeCell<?> treeCell = (TreeCell<?>) node;
 117             if (treeCell.getIndex() == index) {
 118                 return treeCell;
 119             }
 120         }
 121         return null;
 122     }
 123 
 124     /**
 125      * Return true if the specified child TreeItem can be reparented to the
 126      * specified parent TreeItem. This means that the child TreeItem object is
 127      * not in the parent chain of the parent TreeItem.
 128      *
 129      * @param <T>
 130      * @param child
 131      * @param parent
 132      * @return
 133      */
 134     public static <T> boolean canReparentTreeItem(final TreeItem<T> child, TreeItem<T> parent) {
 135         if (child == parent) {
 136             return false;
 137         }
 138         int childLevel = Deprecation.getNodeLevel(child);
 139         int parentLevel = Deprecation.getNodeLevel(parent);
 140         while (parentLevel >= childLevel) {
 141             if (parent == child) {
 142                 return false;
 143             }
 144             parent = parent.getParent();
 145             parentLevel--;
 146         }
 147         return true;
 148     }
 149 
 150     /**
 151      * Return the common parent TreeItem of the specified TreeItems.
 152      *
 153      * @param <T>
 154      * @param treeItems
 155      * @return
 156      */
 157     public static <T> TreeItem<T> getCommonParentTreeItem(
 158             final List<TreeItem<T>> treeItems) {
 159 
 160         assert treeItems != null && !treeItems.isEmpty();
 161 
 162         // TreeItems contains ROOT
 163         // => return ROOT as the common parent
 164         for (TreeItem<T> treeItem : treeItems) {
 165             if (Deprecation.getNodeLevel(treeItem) == 0) {
 166                 return treeItem;
 167             }
 168         }
 169 
 170         // TreeItem single selection
 171         // => the common parent is the single TreeItem parent
 172         if (treeItems.size() == 1) {
 173             return treeItems.get(0).getParent();
 174         } //
 175         // TreeItem multi selection
 176         else {
 177             assert treeItems.size() >= 2;
 178             TreeItem<T> parent = null;
 179             TreeItem<T> child = treeItems.get(0);
 180             for (int index = 1; index < treeItems.size(); index++) {
 181                 parent = getCommonParentTreeItem(child, treeItems.get(index));
 182                 // We reached the ROOT level
 183                 // => common parent is ROOT TreeItem
 184                 if (Deprecation.getNodeLevel(parent) == 0) {
 185                     break;
 186                 } else {
 187                     child = parent;
 188                 }
 189             }
 190             return parent;
 191         }
 192     }
 193 
 194     private static <T> TreeItem<T> getCommonParentTreeItem(
 195             final TreeItem<T> child1,
 196             final TreeItem<T> child2) {
 197 
 198         assert child1 != null && child2 != null;
 199 
 200         int child1Level = Deprecation.getNodeLevel(child1);
 201         int child2Level = Deprecation.getNodeLevel(child2);
 202         // Neither child1 nor child2 is ROOT TreeItem
 203         assert child1Level > 0 && child2Level > 0;
 204 
 205         TreeItem<T> parent1 = child1.getParent();
 206         TreeItem<T> parent2 = child2.getParent();
 207 
 208         if (child1Level < child2Level) {
 209             while (child1Level < child2Level) {
 210                 parent2 = parent2.getParent();
 211                 child2Level--;
 212             }
 213             // We reached the common parent TreeItem
 214             if (parent1 == parent2) {
 215                 return parent1;
 216             } else {
 217                 // At this step, parent1 and parent2 have same node level
 218                 // within the TreeView
 219                 while (parent1 != parent2) {
 220                     parent1 = parent1.getParent();
 221                     parent2 = parent2.getParent();
 222                 }
 223                 return parent1;
 224             }
 225         } else {
 226             while (child1Level > child2Level) {
 227                 parent1 = parent1.getParent();
 228                 child1Level--;
 229             }
 230             // We reached the common parent TreeItem
 231             if (parent1 == parent2) {
 232                 return parent1;
 233             } else {
 234                 // At this step, parent1 and parent2 have same node level
 235                 // within the TreeView
 236                 while (parent1 != parent2) {
 237                     parent1 = parent1.getParent();
 238                     parent2 = parent2.getParent();
 239                 }
 240                 return parent1;
 241             }
 242         }
 243     }
 244 }