< prev index next >

modules/javafx.controls/src/main/java/javafx/scene/control/TreeItem.java

Print this page




 116  *
 117  *                  // First getChildren() call, so we actually go off and
 118  *                  // determine the children of the File contained in this TreeItem.
 119  *                  super.getChildren().setAll(buildChildren(this));
 120  *              }
 121  *              return super.getChildren();
 122  *          }
 123  *
 124  *          @Override public boolean isLeaf() {
 125  *              if (isFirstTimeLeaf) {
 126  *                  isFirstTimeLeaf = false;
 127  *                  File f = (File) getValue();
 128  *                  isLeaf = f.isFile();
 129  *              }
 130  *
 131  *              return isLeaf;
 132  *          }
 133  *
 134  *          private ObservableList&lt;TreeItem&lt;File&gt;&gt; buildChildren(TreeItem&lt;File&gt; TreeItem) {
 135  *              File f = TreeItem.getValue();
 136  *              if (f != null && f.isDirectory()) {
 137  *                  File[] files = f.listFiles();
 138  *                  if (files != null) {
 139  *                      ObservableList&lt;TreeItem&lt;File&gt;&gt; children = FXCollections.observableArrayList();
 140  *
 141  *                      for (File childFile : files) {
 142  *                          children.add(createNode(childFile));
 143  *                      }
 144  *
 145  *                      return children;
 146  *                  }
 147  *              }
 148  *
 149  *              return FXCollections.emptyObservableList();
 150  *          }
 151  *      };
 152  *  }</code></pre>
 153  *
 154  * <strong>TreeItem Events</strong>
 155  * <p>TreeItem supports the same event bubbling concept as elsewhere in the
 156  * scenegraph. This means that it is not necessary to listen for events on all
 157  * TreeItems (and this is certainly not encouraged!). A better, and far more low
 158  * cost solution is to instead attach event listeners to the TreeView
 159  * {@link TreeView#rootProperty() root} item. As long as there is a path between
 160  * where the event occurs and the root TreeItem, the event will be bubbled to the
 161  * root item.
 162  *
 163  * <p>It is important to note however that a TreeItem is <strong>not</strong> a
 164  * Node, which means that only the event types defined in TreeItem will be
 165  * delivered. To listen to general events (for example mouse interactions), it is
 166  * necessary to add the necessary listeners to the {@link Cell cells} contained
 167  * within the TreeView (by providing a {@link TreeView#cellFactoryProperty()
 168  * cell factory}).
 169  *
 170  * <p>The TreeItem class defines a number of events, with a defined hierarchy. These
 171  * are shown below (follow the links to learn more about each event type):
 172  *
 173  * <ul>
 174  *   <li>{@link TreeItem#treeNotificationEvent() TreeItem.treeNotificationEvent()}</li>
 175  *   <ul>
 176  *     <li>{@link TreeItem#valueChangedEvent() TreeItem.valueChangedEvent()}</li>
 177  *     <li>{@link TreeItem#graphicChangedEvent() TreeItem.graphicChangedEvent()}</li>
 178  *     <li>{@link TreeItem#treeItemCountChangeEvent() TreeItem.treeItemCountChangeEvent()}</li>
 179  *     <ul>
 180  *       <li>{@link TreeItem#branchExpandedEvent() TreeItem.branchExpandedEvent()}</li>
 181  *       <li>{@link TreeItem#branchCollapsedEvent() TreeItem.branchCollapsedEvent()}</li>
 182  *       <li>{@link TreeItem#childrenModificationEvent() TreeItem.childrenModificationEvent()}</li>
 183  *     </ul>

 184  *   </ul>

 185  * </ul>
 186  *
 187  * <p>The indentation shown above signifies the relationship between event types.
 188  * For example, all TreeItem event types have
 189  * {@link TreeItem#treeNotificationEvent() treeNotificationEvent()} as their
 190  * parent event type, and the branch
 191  * {@link TreeItem#branchExpandedEvent() expand} /
 192  * {@link TreeItem#branchCollapsedEvent() collapse} event types are both
 193  * {@link TreeItem#treeNotificationEvent() treeNotificationEvent()}. For
 194  * performance reasons, it is encouraged to listen
 195  * to only the events you need to listen to. This means that it is encouraged
 196  * that it is better to listen to, for example,
 197  * {@link TreeItem#valueChangedEvent() TreeItem.valueChangedEvent()},
 198  * rather than {@link TreeItem#treeNotificationEvent() TreeItem.treeNotificationEvent()}.
 199  *
 200  * @param <T> The type of the {@link #getValue() value} property within TreeItem.
 201  * @since JavaFX 2.0
 202  */
 203 public class TreeItem<T> implements EventTarget { //, Comparable<TreeItem<T>> {
 204 
 205     /***************************************************************************
 206      *                                                                         *
 207      * Static properties and methods                                           *
 208      *                                                                         *
 209      **************************************************************************/
 210 
 211     /**
 212      * The base EventType used to indicate that an event has occurred within a
 213      * TreeItem. When an event occurs in a TreeItem, the event is fired to any
 214      * listeners on the TreeItem that the event occurs, before it 'bubbles' up the
 215      * TreeItem chain by following the TreeItem parent property. This repeats
 216      * until a TreeItem whose parent TreeItem is null is reached At this point
 217      * the event stops 'bubbling' and goes no further. This means that events
 218      * that occur on a TreeItem can be relatively cheap, as a listener needs only
 219      * be installed on the TreeView root node to be alerted of events happening
 220      * at any point in the tree.
 221      *
 222      * @param <T> The type of the value contained within the TreeItem.

 223      */
 224     @SuppressWarnings("unchecked")
 225     public static <T> EventType<TreeModificationEvent<T>> treeNotificationEvent() {
 226         return (EventType<TreeModificationEvent<T>>) TREE_NOTIFICATION_EVENT;
 227     }
 228     private static final EventType<?> TREE_NOTIFICATION_EVENT
 229             = new EventType<>(Event.ANY, "TreeNotificationEvent");
 230 
 231     /**
 232      * The general EventType used when the TreeItem receives a modification that
 233      * results in the number of children being visible changes.
 234      * This is normally achieved via one of the sub-types of this
 235      * EventType (see {@link #branchExpandedEvent()},
 236      * {@link #branchCollapsedEvent()} and {@link #childrenModificationEvent()}
 237      * for the three sub-types).
 238      *
 239      * @param <T> The type of the value contained within the TreeItem.

 240      * @since JavaFX 8.0
 241      */
 242     @SuppressWarnings("unchecked")
 243     public static <T> EventType<TreeModificationEvent<T>> expandedItemCountChangeEvent() {
 244         return (EventType<TreeModificationEvent<T>>) EXPANDED_ITEM_COUNT_CHANGE_EVENT;
 245     }
 246     private static final EventType<?> EXPANDED_ITEM_COUNT_CHANGE_EVENT
 247             = new EventType<>(treeNotificationEvent(), "ExpandedItemCountChangeEvent");
 248 
 249     /**
 250      * An EventType used when the TreeItem receives a modification to its
 251      * expanded property, such that the TreeItem is now in the expanded state.
 252      *
 253      * @param <T> The type of the value contained within the TreeItem.

 254      */
 255     @SuppressWarnings("unchecked")
 256     public static <T> EventType<TreeModificationEvent<T>> branchExpandedEvent() {
 257         return (EventType<TreeModificationEvent<T>>) BRANCH_EXPANDED_EVENT;
 258     }
 259     private static final EventType<?> BRANCH_EXPANDED_EVENT
 260             = new EventType<>(expandedItemCountChangeEvent(), "BranchExpandedEvent");
 261 
 262     /**
 263      * An EventType used when the TreeItem receives a modification to its
 264      * expanded property, such that the TreeItem is now in the collapsed state.
 265      *
 266      * @param <T> The type of the value contained within the TreeItem.

 267      */
 268     @SuppressWarnings("unchecked")
 269     public static <T> EventType<TreeModificationEvent<T>> branchCollapsedEvent() {
 270         return (EventType<TreeModificationEvent<T>>) BRANCH_COLLAPSED_EVENT;
 271     }
 272     private static final EventType<?> BRANCH_COLLAPSED_EVENT
 273             = new EventType<>(expandedItemCountChangeEvent(), "BranchCollapsedEvent");
 274 
 275     /**
 276      * An EventType used when the TreeItem receives a direct modification to its
 277      * children list.
 278      *
 279      * @param <T> The type of the value contained within the TreeItem.


 280      */
 281     @SuppressWarnings("unchecked")
 282     public static <T> EventType<TreeModificationEvent<T>> childrenModificationEvent() {
 283         return (EventType<TreeModificationEvent<T>>) CHILDREN_MODIFICATION_EVENT;
 284     }
 285     private static final EventType<?> CHILDREN_MODIFICATION_EVENT
 286             = new EventType<>(expandedItemCountChangeEvent(), "ChildrenModificationEvent");
 287 
 288     /**
 289      * An EventType used when the TreeItem receives a modification to its
 290      * value property.
 291      *
 292      * @param <T> The type of the value contained within the TreeItem.


 293      */
 294     @SuppressWarnings("unchecked")
 295     public static <T> EventType<TreeModificationEvent<T>> valueChangedEvent() {
 296         return (EventType<TreeModificationEvent<T>>) VALUE_CHANGED_EVENT;
 297     }
 298     private static final EventType<?> VALUE_CHANGED_EVENT
 299             = new EventType<>(treeNotificationEvent(), "ValueChangedEvent");
 300 
 301     /**
 302      * An EventType used when the TreeItem receives a modification to its
 303      * graphic property.
 304      *
 305      * @param <T> The type of the value contained within the TreeItem.


 306      */
 307     @SuppressWarnings("unchecked")
 308     public static <T> EventType<TreeModificationEvent<T>> graphicChangedEvent() {
 309         return (EventType<TreeModificationEvent<T>>) GRAPHIC_CHANGED_EVENT;
 310     }
 311     private static final EventType<?> GRAPHIC_CHANGED_EVENT
 312             = new EventType<>(treeNotificationEvent(), "GraphicChangedEvent");
 313 
 314 
 315 
 316     /***************************************************************************
 317      *                                                                         *
 318      * Constructors                                                            *
 319      *                                                                         *
 320      **************************************************************************/
 321 
 322     /**
 323      * Creates an empty TreeItem.
 324      */
 325     public TreeItem() {


 408 
 409     // called whenever the contents of the children sequence changes
 410     private ListChangeListener<TreeItem<T>> childrenListener = c -> {
 411         expandedDescendentCountDirty = true;
 412         updateChildren(c);
 413     };
 414 
 415 
 416 
 417     /***************************************************************************
 418      *                                                                         *
 419      * Properties                                                              *
 420      *                                                                         *
 421      **************************************************************************/
 422 
 423     // --- Value
 424     private ObjectProperty<T> value;
 425 
 426     /**
 427      * Sets the application-specific data represented by this TreeItem.

 428      */
 429     public final void setValue(T value) { valueProperty().setValue(value); }
 430 
 431     /**
 432      * Returns the application-specific data represented by this TreeItem.
 433      * @return the data represented by this TreeItem
 434      */
 435     public final T getValue() { return value == null ? null : value.getValue(); }
 436 
 437     /**
 438      * A property representing the application-specific data contained within
 439      * this TreeItem.


 440      */
 441     public final ObjectProperty<T> valueProperty() {
 442         if (value == null) {
 443             value = new ObjectPropertyBase<T>() {
 444                 @Override protected void invalidated() {
 445                     fireEvent(new TreeModificationEvent<T>(VALUE_CHANGED_EVENT, TreeItem.this, get()));
 446                 }
 447 
 448                 @Override public Object getBean() {
 449                     return TreeItem.this;
 450                 }
 451 
 452                 @Override public String getName() {
 453                     return "value";
 454                 }
 455             };
 456         }
 457         return value;
 458     }
 459 


 463 
 464     /**
 465      * Sets the node that is generally shown to the left of the value property.
 466      * For best effect, this tends to be a 16x16 image.
 467      *
 468      * @param value The graphic node that will be displayed to the user.
 469      */
 470     public final void setGraphic(Node value) { graphicProperty().setValue(value); }
 471 
 472     /**
 473      * Returns the node that is generally shown to the left of the value property.
 474      * For best effect, this tends to be a 16x16 image.
 475      *
 476      * @return The graphic node that will be displayed to the user.
 477      */
 478     public final Node getGraphic() { return graphic == null ? null : graphic.getValue(); }
 479 
 480     /**
 481      * The node that is generally shown to the left of the value property. For
 482      * best effect, this tends to be a 16x16 image.

 483      */
 484     public final ObjectProperty<Node> graphicProperty() {
 485         if (graphic == null) {
 486             graphic = new ObjectPropertyBase<Node>() {
 487                 @Override protected void invalidated() {
 488                     fireEvent(new TreeModificationEvent<T>(GRAPHIC_CHANGED_EVENT, TreeItem.this));
 489                 }
 490 
 491                 @Override
 492                 public Object getBean() {
 493                     return TreeItem.this;
 494                 }
 495 
 496                 @Override
 497                 public String getName() {
 498                     return "graphic";
 499                 }
 500             };
 501         }
 502         return graphic;


 514      *
 515      * @param value If this TreeItem has children, calling setExpanded with
 516      *      <code>true</code> will result in the children becoming visible.
 517      *      Calling setExpanded with <code>false</code> will hide any children
 518      *      belonging to the TreeItem.
 519      */
 520     public final void setExpanded(boolean value) {
 521         if (! value && expanded == null) return;
 522         expandedProperty().setValue(value);
 523     }
 524 
 525     /**
 526      * Returns the expanded state of this TreeItem.
 527      *
 528      * @return Returns the expanded state of this TreeItem.
 529      */
 530     public final boolean isExpanded() { return expanded == null ? false : expanded.getValue(); }
 531 
 532     /**
 533      * The expanded state of this TreeItem.

 534      */
 535     public final BooleanProperty expandedProperty() {
 536         if (expanded == null) {
 537             expanded = new BooleanPropertyBase() {
 538                 @Override protected void invalidated() {
 539                     // We don't fire expanded events for leaf nodes (RT-32620)
 540                     if (isLeaf()) return;
 541 
 542                     EventType<?> evtType = isExpanded() ?
 543                         BRANCH_EXPANDED_EVENT : BRANCH_COLLAPSED_EVENT;
 544 
 545                     fireEvent(new TreeModificationEvent<T>(evtType, TreeItem.this, isExpanded()));
 546                 }
 547 
 548                 @Override
 549                 public Object getBean() {
 550                     return TreeItem.this;
 551                 }
 552 
 553                 @Override


 560     }
 561 
 562 
 563     // --- Leaf
 564     private ReadOnlyBooleanWrapper leaf;
 565     private void setLeaf(boolean value) {
 566         if (value && leaf == null) {
 567             return;
 568         } else if (leaf == null) {
 569             leaf = new ReadOnlyBooleanWrapper(this, "leaf", true);
 570         }
 571         leaf.setValue(value);
 572     }
 573 
 574     /**
 575      * A TreeItem is a leaf if it has no children. The isLeaf method may of
 576      * course be overridden by subclasses to support alternate means of defining
 577      * how a TreeItem may be a leaf, but the general premise is the same: a
 578      * leaf can not be expanded by the user, and as such will not show a
 579      * disclosure node or respond to expansion requests.

 580      */
 581     public boolean isLeaf() { return leaf == null ? true : leaf.getValue(); }
 582 
 583     /**
 584      * Represents the TreeItem leaf property, which is true if the TreeItem has no children.

 585      */
 586     public final ReadOnlyBooleanProperty leafProperty() {
 587         if (leaf == null) {
 588             leaf = new ReadOnlyBooleanWrapper(this, "leaf", true);
 589         }
 590         return leaf.getReadOnlyProperty();
 591     }
 592 
 593 
 594     // --- Parent
 595     private ReadOnlyObjectWrapper<TreeItem<T>> parent = new ReadOnlyObjectWrapper<TreeItem<T>>(this, "parent");
 596     private void setParent(TreeItem<T> value) { parent.setValue(value); }
 597 
 598     /**
 599      * The parent of this TreeItem. Each TreeItem can have no more than one
 600      * parent. If a TreeItem has no parent, it represents a root in the tree model.
 601      *
 602      * @return The parent of this TreeItem, or null if the TreeItem has no parent.
 603      */
 604     public final TreeItem<T> getParent() { return parent == null ? null : parent.getValue(); }
 605 
 606     /**
 607      * A property that represents the parent of this TreeItem.

 608      */
 609     public final ReadOnlyObjectProperty<TreeItem<T>> parentProperty() { return parent.getReadOnlyProperty(); }
 610 
 611 
 612 
 613     /***********************************************************************
 614      *                                                                     *
 615      * TreeItem API                                                        *
 616      *                                                                     *
 617      **********************************************************************/
 618 
 619     /**
 620      * The children of this TreeItem. This method is called frequently, and
 621      * it is therefore recommended that the returned list be cached by
 622      * any TreeItem implementations.
 623      *
 624      * @return a list that contains the child TreeItems belonging to the TreeItem.
 625      */
 626     public ObservableList<TreeItem<T>> getChildren() {
 627         if (children == null) {


 755     /** {@inheritDoc} */
 756     @Override public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
 757         // To allow for a TreeView (and its skin) to be notified of changes in the
 758         // tree, this method recursively calls up to the root node, at which point
 759         // it fires a ROOT_NOTIFICATION_EVENT, which the TreeView may be watching for.
 760         if (getParent() != null) {
 761             getParent().buildEventDispatchChain(tail);
 762         }
 763         return tail.append(eventHandlerManager);
 764     }
 765 
 766     /**
 767      * Registers an event handler to this TreeItem. The TreeItem class allows
 768      * registration of listeners which will be notified as the
 769      * number of items changes, their position or if the values themselves change.
 770      * Note however that a TreeItem is <b>not</b> a Node, and therefore no visual
 771      * events will be fired on the TreeItem. To get these events, it is necessary to
 772      * add relevant observers to the TreeCell instances (via a custom cell factory -
 773      * see the {@link Cell} class documentation for more details).
 774      *

 775      * @param eventType the type of the events to receive by the handler
 776      * @param eventHandler the handler to register
 777      * @throws NullPointerException if the event type or handler is null
 778      */
 779     public <E extends Event> void addEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
 780         eventHandlerManager.addEventHandler(eventType, eventHandler);
 781     }
 782 
 783     /**
 784      * Unregisters a previously registered event handler from this TreeItem. One
 785      * handler might have been registered for different event types, so the
 786      * caller needs to specify the particular event type from which to
 787      * unregister the handler.
 788      *

 789      * @param eventType the event type from which to unregister
 790      * @param eventHandler the handler to unregister
 791      * @throws NullPointerException if the event type or handler is null
 792      */
 793     public <E extends Event> void removeEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
 794         eventHandlerManager.removeEventHandler(eventType, eventHandler);
 795     }
 796 
 797 
 798 
 799     /***************************************************************************
 800      *                                                                         *
 801      * private methods                                                         *
 802      *                                                                         *
 803      **************************************************************************/
 804 
 805     void sort() {
 806         sort(children, lastComparator, lastSortMode);
 807     }
 808 


 942             // the parent of that TreeItem is set to null when it should in fact
 943             // remain with the parent that it belongs to.
 944             if (treeItem.parentLinkCount == 0) {
 945                 treeItem.setParent(newParent);
 946             }
 947 
 948             boolean parentMatch = currentParent != null && currentParent.equals(newParent);
 949             if (parentMatch) {
 950                 if (newParent == null) {
 951                     treeItem.parentLinkCount--;
 952                 } else {
 953                     treeItem.parentLinkCount++;
 954                 }
 955             }
 956          }
 957     }
 958 
 959     /**
 960      * An {@link Event} that contains relevant information for all forms of
 961      * TreeItem modifications.

 962      * @since JavaFX 2.0
 963      */
 964     public static class TreeModificationEvent<T> extends Event {
 965         private static final long serialVersionUID = 4741889985221719579L;
 966 
 967         /**
 968          * Common supertype for all tree modification event types.
 969          * @since JavaFX 8.0
 970          */
 971         public static final EventType<?> ANY = TREE_NOTIFICATION_EVENT;
 972 
 973         private transient final TreeItem<T> treeItem;
 974         private final T newValue;
 975 
 976         private final List<? extends TreeItem<T>> added;
 977         private final List<? extends TreeItem<T>> removed;
 978         private final ListChangeListener.Change<? extends TreeItem<T>> change;
 979 
 980         private final boolean wasExpanded;
 981         private final boolean wasCollapsed;


1138          * @return The number of removed children items, or zero if no children
1139          *      were removed.
1140          */
1141         public int getRemovedSize() {
1142             return getRemovedChildren().size();
1143         }
1144 
1145         /**
1146          * Returns the number of children items that were added in this event,
1147          * or zero if no children were added.
1148          * @return The number of added children items, or zero if no children
1149          *      were added.
1150          */
1151         public int getAddedSize() {
1152             return getAddedChildren().size();
1153         }
1154 
1155         /**
1156          * Returns true if this event represents a TreeItem expansion event,
1157          * and false if the TreeItem was not expanded.


1158          */
1159         public boolean wasExpanded() { return wasExpanded; }
1160 
1161         /**
1162          * Returns true if this event represents a TreeItem collapse event,
1163          * and false if the TreeItem was not collapsed.


1164          */
1165         public boolean wasCollapsed() { return wasCollapsed; }
1166 
1167         /**
1168          * Returns true if this event represents a TreeItem event where children
1169          * TreeItems were added.


1170          */
1171         public boolean wasAdded() { return getAddedSize() > 0; }
1172 
1173         /**
1174          * Returns true if this event represents a TreeItem event where children
1175          * TreeItems were removed.


1176          */
1177         public boolean wasRemoved() { return getRemovedSize() > 0; }
1178 
1179         /**
1180          * Returns true if the order of the TreeItem children list has changed,
1181          * but that there have been no additions or removals.


1182          */
1183         public boolean wasPermutated() { return wasPermutated; }
1184 
1185         int getFrom() { return change == null ? -1 : change.getFrom(); }
1186         int getTo() { return change == null ? -1 : change.getTo(); }
1187         ListChangeListener.Change<? extends TreeItem<T>> getChange() { return change; }
1188     }
1189 }


 116  *
 117  *                  // First getChildren() call, so we actually go off and
 118  *                  // determine the children of the File contained in this TreeItem.
 119  *                  super.getChildren().setAll(buildChildren(this));
 120  *              }
 121  *              return super.getChildren();
 122  *          }
 123  *
 124  *          @Override public boolean isLeaf() {
 125  *              if (isFirstTimeLeaf) {
 126  *                  isFirstTimeLeaf = false;
 127  *                  File f = (File) getValue();
 128  *                  isLeaf = f.isFile();
 129  *              }
 130  *
 131  *              return isLeaf;
 132  *          }
 133  *
 134  *          private ObservableList&lt;TreeItem&lt;File&gt;&gt; buildChildren(TreeItem&lt;File&gt; TreeItem) {
 135  *              File f = TreeItem.getValue();
 136  *              if (f != null &amp;&amp; f.isDirectory()) {
 137  *                  File[] files = f.listFiles();
 138  *                  if (files != null) {
 139  *                      ObservableList&lt;TreeItem&lt;File&gt;&gt; children = FXCollections.observableArrayList();
 140  *
 141  *                      for (File childFile : files) {
 142  *                          children.add(createNode(childFile));
 143  *                      }
 144  *
 145  *                      return children;
 146  *                  }
 147  *              }
 148  *
 149  *              return FXCollections.emptyObservableList();
 150  *          }
 151  *      };
 152  *  }</code></pre>
 153  *
 154  * <strong>TreeItem Events</strong>
 155  * <p>TreeItem supports the same event bubbling concept as elsewhere in the
 156  * scenegraph. This means that it is not necessary to listen for events on all
 157  * TreeItems (and this is certainly not encouraged!). A better, and far more low
 158  * cost solution is to instead attach event listeners to the TreeView
 159  * {@link TreeView#rootProperty() root} item. As long as there is a path between
 160  * where the event occurs and the root TreeItem, the event will be bubbled to the
 161  * root item.
 162  *
 163  * <p>It is important to note however that a TreeItem is <strong>not</strong> a
 164  * Node, which means that only the event types defined in TreeItem will be
 165  * delivered. To listen to general events (for example mouse interactions), it is
 166  * necessary to add the necessary listeners to the {@link Cell cells} contained
 167  * within the TreeView (by providing a {@link TreeView#cellFactoryProperty()
 168  * cell factory}).
 169  *
 170  * <p>The TreeItem class defines a number of events, with a defined hierarchy. These
 171  * are shown below (follow the links to learn more about each event type):
 172  *
 173  * <ul>
 174  *   <li>{@link TreeItem#treeNotificationEvent() TreeItem.treeNotificationEvent()}
 175  *   <ul>
 176  *     <li>{@link TreeItem#valueChangedEvent() TreeItem.valueChangedEvent()}</li>
 177  *     <li>{@link TreeItem#graphicChangedEvent() TreeItem.graphicChangedEvent()}</li>
 178  *     <li>{@link TreeItem#expandedItemCountChangeEvent() TreeItem.expandedItemCountChangeEvent()}
 179  *     <ul>
 180  *       <li>{@link TreeItem#branchExpandedEvent() TreeItem.branchExpandedEvent()}</li>
 181  *       <li>{@link TreeItem#branchCollapsedEvent() TreeItem.branchCollapsedEvent()}</li>
 182  *       <li>{@link TreeItem#childrenModificationEvent() TreeItem.childrenModificationEvent()}</li>
 183  *     </ul>
 184  *     </li>
 185  *   </ul>
 186  *   </li>
 187  * </ul>
 188  *
 189  * <p>The indentation shown above signifies the relationship between event types.
 190  * For example, all TreeItem event types have
 191  * {@link TreeItem#treeNotificationEvent() treeNotificationEvent()} as their
 192  * parent event type, and the branch
 193  * {@link TreeItem#branchExpandedEvent() expand} /
 194  * {@link TreeItem#branchCollapsedEvent() collapse} event types are both
 195  * {@link TreeItem#treeNotificationEvent() treeNotificationEvent()}. For
 196  * performance reasons, it is encouraged to listen
 197  * to only the events you need to listen to. This means that it is encouraged
 198  * that it is better to listen to, for example,
 199  * {@link TreeItem#valueChangedEvent() TreeItem.valueChangedEvent()},
 200  * rather than {@link TreeItem#treeNotificationEvent() TreeItem.treeNotificationEvent()}.
 201  *
 202  * @param <T> The type of the {@link #getValue() value} property within TreeItem.
 203  * @since JavaFX 2.0
 204  */
 205 public class TreeItem<T> implements EventTarget { //, Comparable<TreeItem<T>> {
 206 
 207     /***************************************************************************
 208      *                                                                         *
 209      * Static properties and methods                                           *
 210      *                                                                         *
 211      **************************************************************************/
 212 
 213     /**
 214      * The base EventType used to indicate that an event has occurred within a
 215      * TreeItem. When an event occurs in a TreeItem, the event is fired to any
 216      * listeners on the TreeItem that the event occurs, before it 'bubbles' up the
 217      * TreeItem chain by following the TreeItem parent property. This repeats
 218      * until a TreeItem whose parent TreeItem is null is reached At this point
 219      * the event stops 'bubbling' and goes no further. This means that events
 220      * that occur on a TreeItem can be relatively cheap, as a listener needs only
 221      * be installed on the TreeView root node to be alerted of events happening
 222      * at any point in the tree.
 223      *
 224      * @param <T> The type of the value contained within the TreeItem.
 225      * @return The base EventType when an event has occurred a within a TreeItem
 226      */
 227     @SuppressWarnings("unchecked")
 228     public static <T> EventType<TreeModificationEvent<T>> treeNotificationEvent() {
 229         return (EventType<TreeModificationEvent<T>>) TREE_NOTIFICATION_EVENT;
 230     }
 231     private static final EventType<?> TREE_NOTIFICATION_EVENT
 232             = new EventType<>(Event.ANY, "TreeNotificationEvent");
 233 
 234     /**
 235      * The general EventType used when the TreeItem receives a modification that
 236      * results in the number of children being visible changes.
 237      * This is normally achieved via one of the sub-types of this
 238      * EventType (see {@link #branchExpandedEvent()},
 239      * {@link #branchCollapsedEvent()} and {@link #childrenModificationEvent()}
 240      * for the three sub-types).
 241      *
 242      * @param <T> The type of the value contained within the TreeItem.
 243      * @return The general EventType when the TreeItem receives a modification
 244      * @since JavaFX 8.0
 245      */
 246     @SuppressWarnings("unchecked")
 247     public static <T> EventType<TreeModificationEvent<T>> expandedItemCountChangeEvent() {
 248         return (EventType<TreeModificationEvent<T>>) EXPANDED_ITEM_COUNT_CHANGE_EVENT;
 249     }
 250     private static final EventType<?> EXPANDED_ITEM_COUNT_CHANGE_EVENT
 251             = new EventType<>(treeNotificationEvent(), "ExpandedItemCountChangeEvent");
 252 
 253     /**
 254      * An EventType used when the TreeItem receives a modification to its
 255      * expanded property, such that the TreeItem is now in the expanded state.
 256      *
 257      * @param <T> The type of the value contained within the TreeItem.
 258      * @return The EventType used when the TreeItem receives a modification
 259      */
 260     @SuppressWarnings("unchecked")
 261     public static <T> EventType<TreeModificationEvent<T>> branchExpandedEvent() {
 262         return (EventType<TreeModificationEvent<T>>) BRANCH_EXPANDED_EVENT;
 263     }
 264     private static final EventType<?> BRANCH_EXPANDED_EVENT
 265             = new EventType<>(expandedItemCountChangeEvent(), "BranchExpandedEvent");
 266 
 267     /**
 268      * An EventType used when the TreeItem receives a modification to its
 269      * expanded property, such that the TreeItem is now in the collapsed state.
 270      *
 271      * @param <T> The type of the value contained within the TreeItem.
 272      * @return The EventType when the TreeItem receives a modification
 273      */
 274     @SuppressWarnings("unchecked")
 275     public static <T> EventType<TreeModificationEvent<T>> branchCollapsedEvent() {
 276         return (EventType<TreeModificationEvent<T>>) BRANCH_COLLAPSED_EVENT;
 277     }
 278     private static final EventType<?> BRANCH_COLLAPSED_EVENT
 279             = new EventType<>(expandedItemCountChangeEvent(), "BranchCollapsedEvent");
 280 
 281     /**
 282      * An EventType used when the TreeItem receives a direct modification to its
 283      * children list.
 284      *
 285      * @param <T> The type of the value contained within the TreeItem.
 286      * @return The EventType when the TreeItem receives a direct modification to
 287      * its children list
 288      */
 289     @SuppressWarnings("unchecked")
 290     public static <T> EventType<TreeModificationEvent<T>> childrenModificationEvent() {
 291         return (EventType<TreeModificationEvent<T>>) CHILDREN_MODIFICATION_EVENT;
 292     }
 293     private static final EventType<?> CHILDREN_MODIFICATION_EVENT
 294             = new EventType<>(expandedItemCountChangeEvent(), "ChildrenModificationEvent");
 295 
 296     /**
 297      * An EventType used when the TreeItem receives a modification to its
 298      * value property.
 299      *
 300      * @param <T> The type of the value contained within the TreeItem.
 301      * @return The EventType when the TreeItem receives a modification to its
 302      * value property
 303      */
 304     @SuppressWarnings("unchecked")
 305     public static <T> EventType<TreeModificationEvent<T>> valueChangedEvent() {
 306         return (EventType<TreeModificationEvent<T>>) VALUE_CHANGED_EVENT;
 307     }
 308     private static final EventType<?> VALUE_CHANGED_EVENT
 309             = new EventType<>(treeNotificationEvent(), "ValueChangedEvent");
 310 
 311     /**
 312      * An EventType used when the TreeItem receives a modification to its
 313      * graphic property.
 314      *
 315      * @param <T> The type of the value contained within the TreeItem.
 316      * @return The EventType when the TreeItem receives a modification to its
 317      * graphic property
 318      */
 319     @SuppressWarnings("unchecked")
 320     public static <T> EventType<TreeModificationEvent<T>> graphicChangedEvent() {
 321         return (EventType<TreeModificationEvent<T>>) GRAPHIC_CHANGED_EVENT;
 322     }
 323     private static final EventType<?> GRAPHIC_CHANGED_EVENT
 324             = new EventType<>(treeNotificationEvent(), "GraphicChangedEvent");
 325 
 326 
 327 
 328     /***************************************************************************
 329      *                                                                         *
 330      * Constructors                                                            *
 331      *                                                                         *
 332      **************************************************************************/
 333 
 334     /**
 335      * Creates an empty TreeItem.
 336      */
 337     public TreeItem() {


 420 
 421     // called whenever the contents of the children sequence changes
 422     private ListChangeListener<TreeItem<T>> childrenListener = c -> {
 423         expandedDescendentCountDirty = true;
 424         updateChildren(c);
 425     };
 426 
 427 
 428 
 429     /***************************************************************************
 430      *                                                                         *
 431      * Properties                                                              *
 432      *                                                                         *
 433      **************************************************************************/
 434 
 435     // --- Value
 436     private ObjectProperty<T> value;
 437 
 438     /**
 439      * Sets the application-specific data represented by this TreeItem.
 440      * @param value the application-specific data
 441      */
 442     public final void setValue(T value) { valueProperty().setValue(value); }
 443 
 444     /**
 445      * Returns the application-specific data represented by this TreeItem.
 446      * @return the data represented by this TreeItem
 447      */
 448     public final T getValue() { return value == null ? null : value.getValue(); }
 449 
 450     /**
 451      * A property representing the application-specific data contained within
 452      * this TreeItem.
 453      * @return the property representing the application-specific data contained
 454      * within this TreeItem
 455      */
 456     public final ObjectProperty<T> valueProperty() {
 457         if (value == null) {
 458             value = new ObjectPropertyBase<T>() {
 459                 @Override protected void invalidated() {
 460                     fireEvent(new TreeModificationEvent<T>(VALUE_CHANGED_EVENT, TreeItem.this, get()));
 461                 }
 462 
 463                 @Override public Object getBean() {
 464                     return TreeItem.this;
 465                 }
 466 
 467                 @Override public String getName() {
 468                     return "value";
 469                 }
 470             };
 471         }
 472         return value;
 473     }
 474 


 478 
 479     /**
 480      * Sets the node that is generally shown to the left of the value property.
 481      * For best effect, this tends to be a 16x16 image.
 482      *
 483      * @param value The graphic node that will be displayed to the user.
 484      */
 485     public final void setGraphic(Node value) { graphicProperty().setValue(value); }
 486 
 487     /**
 488      * Returns the node that is generally shown to the left of the value property.
 489      * For best effect, this tends to be a 16x16 image.
 490      *
 491      * @return The graphic node that will be displayed to the user.
 492      */
 493     public final Node getGraphic() { return graphic == null ? null : graphic.getValue(); }
 494 
 495     /**
 496      * The node that is generally shown to the left of the value property. For
 497      * best effect, this tends to be a 16x16 image.
 498      * @return The node that is generally shown to the left of the value property
 499      */
 500     public final ObjectProperty<Node> graphicProperty() {
 501         if (graphic == null) {
 502             graphic = new ObjectPropertyBase<Node>() {
 503                 @Override protected void invalidated() {
 504                     fireEvent(new TreeModificationEvent<T>(GRAPHIC_CHANGED_EVENT, TreeItem.this));
 505                 }
 506 
 507                 @Override
 508                 public Object getBean() {
 509                     return TreeItem.this;
 510                 }
 511 
 512                 @Override
 513                 public String getName() {
 514                     return "graphic";
 515                 }
 516             };
 517         }
 518         return graphic;


 530      *
 531      * @param value If this TreeItem has children, calling setExpanded with
 532      *      <code>true</code> will result in the children becoming visible.
 533      *      Calling setExpanded with <code>false</code> will hide any children
 534      *      belonging to the TreeItem.
 535      */
 536     public final void setExpanded(boolean value) {
 537         if (! value && expanded == null) return;
 538         expandedProperty().setValue(value);
 539     }
 540 
 541     /**
 542      * Returns the expanded state of this TreeItem.
 543      *
 544      * @return Returns the expanded state of this TreeItem.
 545      */
 546     public final boolean isExpanded() { return expanded == null ? false : expanded.getValue(); }
 547 
 548     /**
 549      * The expanded state of this TreeItem.
 550      * @return The expanded state property of this TreeItem
 551      */
 552     public final BooleanProperty expandedProperty() {
 553         if (expanded == null) {
 554             expanded = new BooleanPropertyBase() {
 555                 @Override protected void invalidated() {
 556                     // We don't fire expanded events for leaf nodes (RT-32620)
 557                     if (isLeaf()) return;
 558 
 559                     EventType<?> evtType = isExpanded() ?
 560                         BRANCH_EXPANDED_EVENT : BRANCH_COLLAPSED_EVENT;
 561 
 562                     fireEvent(new TreeModificationEvent<T>(evtType, TreeItem.this, isExpanded()));
 563                 }
 564 
 565                 @Override
 566                 public Object getBean() {
 567                     return TreeItem.this;
 568                 }
 569 
 570                 @Override


 577     }
 578 
 579 
 580     // --- Leaf
 581     private ReadOnlyBooleanWrapper leaf;
 582     private void setLeaf(boolean value) {
 583         if (value && leaf == null) {
 584             return;
 585         } else if (leaf == null) {
 586             leaf = new ReadOnlyBooleanWrapper(this, "leaf", true);
 587         }
 588         leaf.setValue(value);
 589     }
 590 
 591     /**
 592      * A TreeItem is a leaf if it has no children. The isLeaf method may of
 593      * course be overridden by subclasses to support alternate means of defining
 594      * how a TreeItem may be a leaf, but the general premise is the same: a
 595      * leaf can not be expanded by the user, and as such will not show a
 596      * disclosure node or respond to expansion requests.
 597      * @return true if this TreeItem has no children
 598      */
 599     public boolean isLeaf() { return leaf == null ? true : leaf.getValue(); }
 600 
 601     /**
 602      * Represents the TreeItem leaf property, which is true if the TreeItem has no children.
 603      * @return the TreeItem leaf property
 604      */
 605     public final ReadOnlyBooleanProperty leafProperty() {
 606         if (leaf == null) {
 607             leaf = new ReadOnlyBooleanWrapper(this, "leaf", true);
 608         }
 609         return leaf.getReadOnlyProperty();
 610     }
 611 
 612 
 613     // --- Parent
 614     private ReadOnlyObjectWrapper<TreeItem<T>> parent = new ReadOnlyObjectWrapper<TreeItem<T>>(this, "parent");
 615     private void setParent(TreeItem<T> value) { parent.setValue(value); }
 616 
 617     /**
 618      * The parent of this TreeItem. Each TreeItem can have no more than one
 619      * parent. If a TreeItem has no parent, it represents a root in the tree model.
 620      *
 621      * @return The parent of this TreeItem, or null if the TreeItem has no parent.
 622      */
 623     public final TreeItem<T> getParent() { return parent == null ? null : parent.getValue(); }
 624 
 625     /**
 626      * A property that represents the parent of this TreeItem.
 627      * @return the parent property of this TreeItem
 628      */
 629     public final ReadOnlyObjectProperty<TreeItem<T>> parentProperty() { return parent.getReadOnlyProperty(); }
 630 
 631 
 632 
 633     /***********************************************************************
 634      *                                                                     *
 635      * TreeItem API                                                        *
 636      *                                                                     *
 637      **********************************************************************/
 638 
 639     /**
 640      * The children of this TreeItem. This method is called frequently, and
 641      * it is therefore recommended that the returned list be cached by
 642      * any TreeItem implementations.
 643      *
 644      * @return a list that contains the child TreeItems belonging to the TreeItem.
 645      */
 646     public ObservableList<TreeItem<T>> getChildren() {
 647         if (children == null) {


 775     /** {@inheritDoc} */
 776     @Override public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
 777         // To allow for a TreeView (and its skin) to be notified of changes in the
 778         // tree, this method recursively calls up to the root node, at which point
 779         // it fires a ROOT_NOTIFICATION_EVENT, which the TreeView may be watching for.
 780         if (getParent() != null) {
 781             getParent().buildEventDispatchChain(tail);
 782         }
 783         return tail.append(eventHandlerManager);
 784     }
 785 
 786     /**
 787      * Registers an event handler to this TreeItem. The TreeItem class allows
 788      * registration of listeners which will be notified as the
 789      * number of items changes, their position or if the values themselves change.
 790      * Note however that a TreeItem is <b>not</b> a Node, and therefore no visual
 791      * events will be fired on the TreeItem. To get these events, it is necessary to
 792      * add relevant observers to the TreeCell instances (via a custom cell factory -
 793      * see the {@link Cell} class documentation for more details).
 794      *
 795      * @param <E> The event
 796      * @param eventType the type of the events to receive by the handler
 797      * @param eventHandler the handler to register
 798      * @throws NullPointerException if the event type or handler is null
 799      */
 800     public <E extends Event> void addEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
 801         eventHandlerManager.addEventHandler(eventType, eventHandler);
 802     }
 803 
 804     /**
 805      * Unregisters a previously registered event handler from this TreeItem. One
 806      * handler might have been registered for different event types, so the
 807      * caller needs to specify the particular event type from which to
 808      * unregister the handler.
 809      *
 810      * @param <E> The event
 811      * @param eventType the event type from which to unregister
 812      * @param eventHandler the handler to unregister
 813      * @throws NullPointerException if the event type or handler is null
 814      */
 815     public <E extends Event> void removeEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
 816         eventHandlerManager.removeEventHandler(eventType, eventHandler);
 817     }
 818 
 819 
 820 
 821     /***************************************************************************
 822      *                                                                         *
 823      * private methods                                                         *
 824      *                                                                         *
 825      **************************************************************************/
 826 
 827     void sort() {
 828         sort(children, lastComparator, lastSortMode);
 829     }
 830 


 964             // the parent of that TreeItem is set to null when it should in fact
 965             // remain with the parent that it belongs to.
 966             if (treeItem.parentLinkCount == 0) {
 967                 treeItem.setParent(newParent);
 968             }
 969 
 970             boolean parentMatch = currentParent != null && currentParent.equals(newParent);
 971             if (parentMatch) {
 972                 if (newParent == null) {
 973                     treeItem.parentLinkCount--;
 974                 } else {
 975                     treeItem.parentLinkCount++;
 976                 }
 977             }
 978          }
 979     }
 980 
 981     /**
 982      * An {@link Event} that contains relevant information for all forms of
 983      * TreeItem modifications.
 984      * @param <T> The TreeModificationEvent
 985      * @since JavaFX 2.0
 986      */
 987     public static class TreeModificationEvent<T> extends Event {
 988         private static final long serialVersionUID = 4741889985221719579L;
 989 
 990         /**
 991          * Common supertype for all tree modification event types.
 992          * @since JavaFX 8.0
 993          */
 994         public static final EventType<?> ANY = TREE_NOTIFICATION_EVENT;
 995 
 996         private transient final TreeItem<T> treeItem;
 997         private final T newValue;
 998 
 999         private final List<? extends TreeItem<T>> added;
1000         private final List<? extends TreeItem<T>> removed;
1001         private final ListChangeListener.Change<? extends TreeItem<T>> change;
1002 
1003         private final boolean wasExpanded;
1004         private final boolean wasCollapsed;


1161          * @return The number of removed children items, or zero if no children
1162          *      were removed.
1163          */
1164         public int getRemovedSize() {
1165             return getRemovedChildren().size();
1166         }
1167 
1168         /**
1169          * Returns the number of children items that were added in this event,
1170          * or zero if no children were added.
1171          * @return The number of added children items, or zero if no children
1172          *      were added.
1173          */
1174         public int getAddedSize() {
1175             return getAddedChildren().size();
1176         }
1177 
1178         /**
1179          * Returns true if this event represents a TreeItem expansion event,
1180          * and false if the TreeItem was not expanded.
1181          * @return true if this event represents a TreeItem expansion event,
1182          * and false if the TreeItem was not expanded
1183          */
1184         public boolean wasExpanded() { return wasExpanded; }
1185 
1186         /**
1187          * Returns true if this event represents a TreeItem collapse event,
1188          * and false if the TreeItem was not collapsed.
1189          * @return true if this event represents a TreeItem collapse event,
1190          * and false if the TreeItem was not collapsed
1191          */
1192         public boolean wasCollapsed() { return wasCollapsed; }
1193 
1194         /**
1195          * Returns true if this event represents a TreeItem event where children
1196          * TreeItems were added.
1197          * @return true if this event represents a TreeItem event where children
1198          * TreeItems were added
1199          */
1200         public boolean wasAdded() { return getAddedSize() > 0; }
1201 
1202         /**
1203          * Returns true if this event represents a TreeItem event where children
1204          * TreeItems were removed.
1205          * @return true if this event represents a TreeItem event where children
1206          * TreeItems were removed
1207          */
1208         public boolean wasRemoved() { return getRemovedSize() > 0; }
1209 
1210         /**
1211          * Returns true if the order of the TreeItem children list has changed,
1212          * but that there have been no additions or removals.
1213          * @return true if the order of the TreeItem children list has changed,
1214          * but that there have been no additions or removals
1215          */
1216         public boolean wasPermutated() { return wasPermutated; }
1217 
1218         int getFrom() { return change == null ? -1 : change.getFrom(); }
1219         int getTo() { return change == null ? -1 : change.getTo(); }
1220         ListChangeListener.Change<? extends TreeItem<T>> getChange() { return change; }
1221     }
1222 }
< prev index next >