1 /*
   2  * Copyright (c) 2010, 2017, 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;
  27 
  28 
  29 import com.sun.javafx.geometry.BoundsUtils;
  30 import javafx.application.Platform;
  31 import javafx.beans.InvalidationListener;
  32 import javafx.beans.Observable;
  33 import javafx.beans.binding.BooleanExpression;
  34 import javafx.beans.property.BooleanProperty;
  35 import javafx.beans.property.BooleanPropertyBase;
  36 import javafx.beans.property.DoubleProperty;
  37 import javafx.beans.property.DoublePropertyBase;
  38 import javafx.beans.property.IntegerProperty;
  39 import javafx.beans.property.ObjectProperty;
  40 import javafx.beans.property.ObjectPropertyBase;
  41 import javafx.beans.property.ReadOnlyBooleanProperty;
  42 import javafx.beans.property.ReadOnlyBooleanPropertyBase;
  43 import javafx.beans.property.ReadOnlyBooleanWrapper;
  44 import javafx.beans.property.ReadOnlyObjectProperty;
  45 import javafx.beans.property.ReadOnlyObjectPropertyBase;
  46 import javafx.beans.property.ReadOnlyObjectWrapper;
  47 import javafx.beans.property.SimpleBooleanProperty;
  48 import javafx.beans.property.SimpleObjectProperty;
  49 import javafx.beans.property.StringProperty;
  50 import javafx.beans.property.StringPropertyBase;
  51 import javafx.beans.value.ChangeListener;
  52 import javafx.collections.FXCollections;
  53 import javafx.collections.ListChangeListener.Change;
  54 import javafx.collections.ObservableList;
  55 import javafx.collections.ObservableMap;
  56 import javafx.collections.ObservableSet;
  57 import javafx.css.CssMetaData;
  58 import javafx.css.ParsedValue;
  59 import javafx.css.PseudoClass;
  60 import javafx.css.StyleConverter;
  61 import javafx.css.Styleable;
  62 import javafx.css.StyleableBooleanProperty;
  63 import javafx.css.StyleableDoubleProperty;
  64 import javafx.css.StyleableObjectProperty;
  65 import javafx.css.StyleableProperty;
  66 import javafx.event.Event;
  67 import javafx.event.EventDispatchChain;
  68 import javafx.event.EventDispatcher;
  69 import javafx.event.EventHandler;
  70 import javafx.event.EventTarget;
  71 import javafx.event.EventType;
  72 import javafx.geometry.BoundingBox;
  73 import javafx.geometry.Bounds;
  74 import javafx.geometry.NodeOrientation;
  75 import javafx.geometry.Orientation;
  76 import javafx.geometry.Point2D;
  77 import javafx.geometry.Point3D;
  78 import javafx.geometry.Rectangle2D;
  79 import javafx.scene.effect.BlendMode;
  80 import javafx.scene.effect.Effect;
  81 import javafx.scene.image.WritableImage;
  82 import javafx.scene.input.ContextMenuEvent;
  83 import javafx.scene.input.DragEvent;
  84 import javafx.scene.input.Dragboard;
  85 import javafx.scene.input.InputEvent;
  86 import javafx.scene.input.InputMethodEvent;
  87 import javafx.scene.input.InputMethodRequests;
  88 import javafx.scene.input.KeyEvent;
  89 import javafx.scene.input.MouseDragEvent;
  90 import javafx.scene.input.MouseEvent;
  91 import javafx.scene.input.PickResult;
  92 import javafx.scene.input.RotateEvent;
  93 import javafx.scene.input.ScrollEvent;
  94 import javafx.scene.input.SwipeEvent;
  95 import javafx.scene.input.TouchEvent;
  96 import javafx.scene.input.TransferMode;
  97 import javafx.scene.input.ZoomEvent;
  98 import javafx.scene.text.Font;
  99 import javafx.scene.transform.Rotate;
 100 import javafx.scene.transform.Transform;
 101 import javafx.stage.Window;
 102 import javafx.util.Callback;
 103 import java.security.AccessControlContext;
 104 
 105 import java.util.ArrayList;
 106 import java.util.Collections;
 107 import java.util.HashMap;
 108 import java.util.LinkedList;
 109 import java.util.List;
 110 import java.util.Map;
 111 import java.util.Set;
 112 
 113 import com.sun.glass.ui.Accessible;
 114 import com.sun.glass.ui.Application;
 115 import com.sun.javafx.util.Logging;
 116 import com.sun.javafx.util.TempState;
 117 import com.sun.javafx.util.Utils;
 118 import com.sun.javafx.beans.IDProperty;
 119 import com.sun.javafx.beans.event.AbstractNotifyListener;
 120 import com.sun.javafx.binding.ExpressionHelper;
 121 import com.sun.javafx.collections.TrackableObservableList;
 122 import com.sun.javafx.collections.UnmodifiableListSet;
 123 import com.sun.javafx.css.PseudoClassState;
 124 import javafx.css.Selector;
 125 import javafx.css.Style;
 126 import javafx.css.converter.BooleanConverter;
 127 import javafx.css.converter.CursorConverter;
 128 import javafx.css.converter.EffectConverter;
 129 import javafx.css.converter.EnumConverter;
 130 import javafx.css.converter.SizeConverter;
 131 import com.sun.javafx.effect.EffectDirtyBits;
 132 import com.sun.javafx.geom.BaseBounds;
 133 import com.sun.javafx.geom.BoxBounds;
 134 import com.sun.javafx.geom.PickRay;
 135 import com.sun.javafx.geom.RectBounds;
 136 import com.sun.javafx.geom.Vec3d;
 137 import com.sun.javafx.geom.transform.Affine3D;
 138 import com.sun.javafx.geom.transform.BaseTransform;
 139 import com.sun.javafx.geom.transform.GeneralTransform3D;
 140 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
 141 import com.sun.javafx.perf.PerformanceTracker;
 142 import com.sun.javafx.scene.BoundsAccessor;
 143 import com.sun.javafx.scene.CameraHelper;
 144 import com.sun.javafx.scene.CssFlags;
 145 import com.sun.javafx.scene.DirtyBits;
 146 import com.sun.javafx.scene.EventHandlerProperties;
 147 import com.sun.javafx.scene.LayoutFlags;
 148 import com.sun.javafx.scene.NodeEventDispatcher;
 149 import com.sun.javafx.scene.NodeHelper;
 150 import com.sun.javafx.scene.SceneHelper;
 151 import com.sun.javafx.scene.SceneUtils;
 152 import com.sun.javafx.scene.input.PickResultChooser;
 153 import com.sun.javafx.scene.transform.TransformHelper;
 154 import com.sun.javafx.scene.transform.TransformUtils;
 155 import com.sun.javafx.scene.traversal.Direction;
 156 import com.sun.javafx.sg.prism.NGNode;
 157 import com.sun.javafx.tk.Toolkit;
 158 import com.sun.prism.impl.PrismSettings;
 159 import com.sun.scenario.effect.EffectHelper;
 160 
 161 import javafx.scene.shape.Shape3D;
 162 import sun.util.logging.PlatformLogger;
 163 import sun.util.logging.PlatformLogger.Level;
 164 
 165 /**
 166  * Base class for scene graph nodes. A scene graph is a set of tree data structures
 167  * where every item has zero or one parent, and each item is either
 168  * a "leaf" with zero sub-items or a "branch" with zero or more sub-items.
 169  * <p>
 170  * Each item in the scene graph is called a {@code Node}. Branch nodes are
 171  * of type {@link Parent}, whose concrete subclasses are {@link Group},
 172  * {@link javafx.scene.layout.Region}, and {@link javafx.scene.control.Control},
 173  * or subclasses thereof.
 174  * <p>
 175  * Leaf nodes are classes such as
 176  * {@link javafx.scene.shape.Rectangle}, {@link javafx.scene.text.Text},
 177  * {@link javafx.scene.image.ImageView}, {@link javafx.scene.media.MediaView},
 178  * or other such leaf classes which cannot have children. Only a single node within
 179  * each scene graph tree will have no parent, which is referred to as the "root" node.
 180  * <p>
 181  * There may be several trees in the scene graph. Some trees may be part of
 182  * a {@link Scene}, in which case they are eligible to be displayed.
 183  * Other trees might not be part of any {@link Scene}.
 184  * <p>
 185  * A node may occur at most once anywhere in the scene graph. Specifically,
 186  * a node must appear no more than once in all of the following:
 187  * as the root node of a {@link Scene},
 188  * the children ObservableList of a {@link Parent},
 189  * or as the clip of a {@link Node}.
 190  * <p>
 191  * The scene graph must not have cycles. A cycle would exist if a node is
 192  * an ancestor of itself in the tree, considering the {@link Group} content
 193  * ObservableList, {@link Parent} children ObservableList, and {@link Node} clip relationships
 194  * mentioned above.
 195  * <p>
 196  * If a program adds a child node to a Parent (including Group, Region, etc)
 197  * and that node is already a child of a different Parent or the root of a Scene,
 198  * the node is automatically (and silently) removed from its former parent.
 199  * If a program attempts to modify the scene graph in any other way that violates
 200  * the above rules, an exception is thrown, the modification attempt is ignored
 201  * and the scene graph is restored to its previous state.
 202  * <p>
 203  * It is possible to rearrange the structure of the scene graph, for
 204  * example, to move a subtree from one location in the scene graph to
 205  * another. In order to do this, one would normally remove the subtree from
 206  * its old location before inserting it at the new location. However, the
 207  * subtree will be automatically removed as described above if the application
 208  * doesn't explicitly remove it.
 209  * <p>
 210  * Node objects may be constructed and modified on any thread as long they are
 211  * not yet attached to a {@link Scene} in a {@link Window} that is
 212  * {@link Window#isShowing showing}.
 213  * An application must attach nodes to such a Scene or modify them on the JavaFX
 214  * Application Thread.
 215  *
 216  * <p>
 217  * The JavaFX Application Thread is created as part of the startup process for
 218  * the JavaFX runtime. See the {@link javafx.application.Application} class and
 219  * the {@link Platform#startup(Runnable)} method for more information.
 220  * </p>
 221  *
 222  * <p>
 223  * An application should not extend the Node class directly. Doing so may lead to
 224  * an UnsupportedOperationException being thrown.
 225  * </p>
 226  *
 227  * <h3>String ID</h3>
 228  * <p>
 229  * Each node in the scene graph can be given a unique {@link #idProperty id}. This id is
 230  * much like the "id" attribute of an HTML tag in that it is up to the designer
 231  * and developer to ensure that the {@code id} is unique within the scene graph.
 232  * A convenience function called {@link #lookup(String)} can be used to find
 233  * a node with a unique id within the scene graph, or within a subtree of the
 234  * scene graph. The id can also be used identify nodes for applying styles; see
 235  * the CSS section below.
 236  *
 237  * <h4>Coordinate System</h4>
 238  * <p>
 239  * The {@code Node} class defines a traditional computer graphics "local"
 240  * coordinate system in which the {@code x} axis increases to the right and the
 241  * {@code y} axis increases downwards.  The concrete node classes for shapes
 242  * provide variables for defining the geometry and location of the shape
 243  * within this local coordinate space.  For example,
 244  * {@link javafx.scene.shape.Rectangle} provides {@code x}, {@code y},
 245  * {@code width}, {@code height} variables while
 246  * {@link javafx.scene.shape.Circle} provides {@code centerX}, {@code centerY},
 247  * and {@code radius}.
 248  * <p>
 249  * At the device pixel level, integer coordinates map onto the corners and
 250  * cracks between the pixels and the centers of the pixels appear at the
 251  * midpoints between integer pixel locations.  Because all coordinate values
 252  * are specified with floating point numbers, coordinates can precisely
 253  * point to these corners (when the floating point values have exact integer
 254  * values) or to any location on the pixel.  For example, a coordinate of
 255  * {@code (0.5, 0.5)} would point to the center of the upper left pixel on the
 256  * {@code Stage}.  Similarly, a rectangle at {@code (0, 0)} with dimensions
 257  * of {@code 10} by {@code 10} would span from the upper left corner of the
 258  * upper left pixel on the {@code Stage} to the lower right corner of the
 259  * 10th pixel on the 10th scanline.  The pixel center of the last pixel
 260  * inside that rectangle would be at the coordinates {@code (9.5, 9.5)}.
 261  * <p>
 262  * In practice, most nodes have transformations applied to their coordinate
 263  * system as mentioned below.  As a result, the information above describing
 264  * the alignment of device coordinates to the pixel grid is relative to
 265  * the transformed coordinates, not the local coordinates of the nodes.
 266  * The {@link javafx.scene.shape.Shape Shape} class describes some additional
 267  * important context-specific information about coordinate mapping and how
 268  * it can affect rendering.
 269  *
 270  * <h4>Transformations</h4>
 271  * <p>
 272  * Any {@code Node} can have transformations applied to it. These include
 273  * translation, rotation, scaling, or shearing.
 274  * <p>
 275  * A <b>translation</b> transformation is one which shifts the origin of the
 276  * node's coordinate space along either the x or y axis. For example, if you
 277  * create a {@link javafx.scene.shape.Rectangle} which is drawn at the origin
 278  * (x=0, y=0) and has a width of 100 and a height of 50, and then apply a
 279  * {@link javafx.scene.transform.Translate} with a shift of 10 along the x axis
 280  * (x=10), then the rectangle will appear drawn at (x=10, y=0) and remain
 281  * 100 points wide and 50 tall. Note that the origin was shifted, not the
 282  * {@code x} variable of the rectangle.
 283  * <p>
 284  * A common node transform is a translation by an integer distance, most often
 285  * used to lay out nodes on the stage.  Such integer translations maintain the
 286  * device pixel mapping so that local coordinates that are integers still
 287  * map to the cracks between pixels.
 288  * <p>
 289  * A <b>rotation</b> transformation is one which rotates the coordinate space of
 290  * the node about a specified "pivot" point, causing the node to appear rotated.
 291  * For example, if you create a {@link javafx.scene.shape.Rectangle} which is
 292  * drawn at the origin (x=0, y=0) and has a width of 100 and height of 30 and
 293  * you apply a {@link javafx.scene.transform.Rotate} with a 90 degree rotation
 294  * (angle=90) and a pivot at the origin (pivotX=0, pivotY=0), then
 295  * the rectangle will be drawn as if its x and y were zero but its height was
 296  * 100 and its width -30. That is, it is as if a pin is being stuck at the top
 297  * left corner and the rectangle is rotating 90 degrees clockwise around that
 298  * pin. If the pivot point is instead placed in the center of the rectangle
 299  * (at point x=50, y=15) then the rectangle will instead appear to rotate about
 300  * its center.
 301  * <p>
 302  * Note that as with all transformations, the x, y, width, and height variables
 303  * of the rectangle (which remain relative to the local coordinate space) have
 304  * not changed, but rather the transformation alters the entire coordinate space
 305  * of the rectangle.
 306  * <p>
 307  * A <b>scaling</b> transformation causes a node to either appear larger or
 308  * smaller depending on the scaling factor. Scaling alters the coordinate space
 309  * of the node such that each unit of distance along the axis in local
 310  * coordinates is multipled by the scale factor. As with rotation
 311  * transformations, scaling transformations are applied about a "pivot" point.
 312  * You can think of this as the point in the Node around which you "zoom".  For
 313  * example, if you create a {@link javafx.scene.shape.Rectangle} with a
 314  * {@code strokeWidth} of 5, and a width and height of 50, and you apply a
 315  * {@link javafx.scene.transform.Scale} with scale factors (x=2.0, y=2.0) and
 316  * a pivot at the origin (pivotX=0, pivotY=0), the entire rectangle
 317  * (including the stroke) will double in size, growing to the right and
 318  * downwards from the origin.
 319  * <p>
 320  * A <b>shearing</b> transformation, sometimes called a skew, effectively
 321  * rotates one axis so that the x and y axes are no longer perpendicular.
 322  * <p>
 323  * Multiple transformations may be applied to a node by specifying an ordered
 324  * chain of transforms.  The order in which the transforms are applied is
 325  * defined by the ObservableList specified in the {@link #getTransforms transforms} variable.
 326  *
 327  * <h3>Bounding Rectangles</h3>
 328  * <p>
 329  * Since every {@code Node} has transformations, every Node's geometric
 330  * bounding rectangle can be described differently depending on whether
 331  * transformations are accounted for or not.
 332  * <p>
 333  * Each {@code Node} has a read-only {@link #boundsInLocalProperty boundsInLocal}
 334  * variable which specifies the bounding rectangle of the {@code Node} in
 335  * untransformed local coordinates. {@code boundsInLocal} includes the
 336  * Node's shape geometry, including any space required for a
 337  * non-zero stroke that may fall outside the local position/size variables,
 338  * and its {@link #clipProperty clip} and {@link #effectProperty effect} variables.
 339  * <p>
 340  * Each {@code Node} also has a read-only {@link #boundsInParentProperty boundsInParent} variable which
 341  * specifies the bounding rectangle of the {@code Node} after all transformations
 342  * have been applied, including those set in {@link #getTransforms transforms},
 343  * {@link #scaleXProperty scaleX}/{@link #scaleYProperty scaleY}, {@link #rotateProperty rotate},
 344  * {@link #translateXProperty translateX}/{@link #translateYProperty translateY}, and {@link #layoutXProperty layoutX}/{@link #layoutYProperty layoutY}.
 345  * It is called "boundsInParent" because the rectangle will be relative to the
 346  * parent's coordinate system.  This is the 'visual' bounds of the node.
 347  * <p>
 348  * Finally, the {@link #layoutBoundsProperty layoutBounds} variable defines the rectangular bounds of
 349  * the {@code Node} that should be used as the basis for layout calculations and
 350  * may differ from the visual bounds of the node.  For shapes, Text, and ImageView,
 351  * layoutBounds by default includes only the shape geometry, including space required
 352  * for a non-zero {@code strokeWidth}, but does <i>not</i> include the effect,
 353  * clip, or any transforms. For resizable classes (Regions and Controls)
 354  * layoutBounds will always map to {@code 0,0 width x height}.
 355  *
 356  * <p> The image shows a node without any transformation and its {@code boundsInLocal}:
 357  * <p> <img src="doc-files/boundsLocal.png" alt="A sine wave shape enclosed by
 358  * an axis-aligned rectangular bounds"> </p>
 359  * If we rotate the image by 20 degrees we get following result:
 360  * <p> <img src="doc-files/boundsParent.png" alt="An axis-aligned rectangular
 361  * bounds that encloses the shape rotated by 20 degrees"> </p>
 362  * The red rectangle represents {@code boundsInParent} in the
 363  * coordinate space of the Node's parent. The {@code boundsInLocal} stays the same
 364  * as in the first image, the green rectangle in this image represents {@code boundsInLocal}
 365  * in the coordinate space of the Node.
 366  *
 367  * <p> The images show a filled and stroked rectangle and their bounds. The
 368  * first rectangle {@code [x:10.0 y:10.0 width:100.0 height:100.0 strokeWidth:0]}
 369  * has the following bounds bounds: {@code [x:10.0 y:10.0 width:100.0 height:100.0]}.
 370  *
 371  * The second rectangle {@code [x:10.0 y:10.0 width:100.0 height:100.0 strokeWidth:5]}
 372  * has the following bounds: {@code [x:7.5 y:7.5 width:105 height:105]}
 373  * (the stroke is centered by default, so only half of it is outside
 374  * of the original bounds; it is also possible to create inside or outside
 375  * stroke).
 376  *
 377  * Since neither of the rectangles has any transformation applied,
 378  * {@code boundsInParent} and {@code boundsInLocal} are the same. </p>
 379  * <p> <img src="doc-files/bounds.png" alt="The rectangles are enclosed by their
 380  * respective bounds"> </p>
 381  *
 382  *
 383  * <h3>CSS</h3>
 384  * <p>
 385  * The {@code Node} class contains {@code id}, {@code styleClass}, and
 386  * {@code style} variables that are used in styling this node from
 387  * CSS. The {@code id} and {@code styleClass} variables are used in
 388  * CSS style sheets to identify nodes to which styles should be
 389  * applied. The {@code style} variable contains style properties and
 390  * values that are applied directly to this node.
 391  * <p>
 392  * For further information about CSS and how to apply CSS styles
 393  * to nodes, see the <a href="doc-files/cssref.html">CSS Reference
 394  * Guide</a>.
 395  * @since JavaFX 2.0
 396  */
 397 @IDProperty("id")
 398 public abstract class Node implements EventTarget, Styleable {
 399 
 400     /*
 401      * Store the singleton instance of the NodeHelper subclass corresponding
 402      * to the subclass of this instance of Node
 403      */
 404     private NodeHelper nodeHelper = null;
 405 
 406     static {
 407         PerformanceTracker.logEvent("Node class loaded");
 408 
 409         // This is used by classes in different packages to get access to
 410         // private and package private methods.
 411         NodeHelper.setNodeAccessor(new NodeHelper.NodeAccessor() {
 412             @Override
 413             public NodeHelper getHelper(Node node) {
 414                 return node.nodeHelper;
 415             }
 416 
 417             @Override
 418             public void setHelper(Node node, NodeHelper nodeHelper) {
 419                 node.nodeHelper = nodeHelper;
 420             }
 421 
 422             @Override
 423             public void doMarkDirty(Node node, DirtyBits dirtyBit) {
 424                 node.doMarkDirty(dirtyBit);
 425             }
 426 
 427             @Override
 428             public void doUpdatePeer(Node node) {
 429                 node.doUpdatePeer();
 430             }
 431 
 432             @Override
 433             public BaseTransform getLeafTransform(Node node) {
 434                 return node.getLeafTransform();
 435             }
 436 
 437             @Override
 438             public Bounds doComputeLayoutBounds(Node node) {
 439                 return node.doComputeLayoutBounds();
 440             }
 441 
 442             @Override
 443             public void doTransformsChanged(Node node) {
 444                 node.doTransformsChanged();
 445             }
 446 
 447             @Override
 448             public void doPickNodeLocal(Node node, PickRay localPickRay,
 449                     PickResultChooser result) {
 450                 node.doPickNodeLocal(localPickRay, result);
 451             }
 452 
 453             @Override
 454             public boolean doComputeIntersects(Node node, PickRay pickRay,
 455                     PickResultChooser pickResult) {
 456                 return node.doComputeIntersects(pickRay, pickResult);
 457             }
 458 
 459             @Override
 460             public void doGeomChanged(Node node) {
 461                 node.doGeomChanged();
 462             }
 463 
 464             @Override
 465             public void doNotifyLayoutBoundsChanged(Node node) {
 466                 node.doNotifyLayoutBoundsChanged();
 467             }
 468 
 469             @Override
 470             public void doProcessCSS(Node node) {
 471                 node.doProcessCSS();
 472             }
 473 
 474             @Override
 475             public boolean isDirty(Node node, DirtyBits dirtyBit) {
 476                 return node.isDirty(dirtyBit);
 477             }
 478 
 479             @Override
 480             public boolean isDirtyEmpty(Node node) {
 481                 return node.isDirtyEmpty();
 482             }
 483 
 484             @Override
 485             public void syncPeer(Node node) {
 486                 node.syncPeer();
 487             }
 488 
 489             @Override
 490             public void layoutBoundsChanged(Node node) {
 491                 node.layoutBoundsChanged();
 492             }
 493 
 494             @Override
 495             public <P extends NGNode> P getPeer(Node node) {
 496                 return node.getPeer();
 497             }
 498 
 499             @Override
 500             public void setShowMnemonics(Node node, boolean value) {
 501                 node.setShowMnemonics(value);
 502             }
 503 
 504             @Override
 505             public boolean isShowMnemonics(Node node) {
 506                 return node.isShowMnemonics();
 507             }
 508 
 509             @Override
 510             public BooleanProperty showMnemonicsProperty(Node node) {
 511                 return node.showMnemonicsProperty();
 512             }
 513 
 514             @Override
 515             public boolean traverse(Node node, Direction direction) {
 516                 return node.traverse(direction);
 517             }
 518 
 519             @Override
 520             public double getPivotX(Node node) {
 521                 return node.getPivotX();
 522             }
 523 
 524             @Override
 525             public double getPivotY(Node node) {
 526                 return node.getPivotY();
 527             }
 528 
 529             @Override
 530             public double getPivotZ(Node node) {
 531                 return node.getPivotZ();
 532             }
 533 
 534             @Override
 535             public void pickNode(Node node,PickRay pickRay,
 536                     PickResultChooser result) {
 537                 node.pickNode(pickRay, result);
 538             }
 539 
 540             @Override
 541             public boolean intersects(Node node, PickRay pickRay,
 542                     PickResultChooser pickResult) {
 543                 return node.intersects(pickRay, pickResult);
 544             }
 545 
 546             @Override
 547             public double intersectsBounds(Node node, PickRay pickRay) {
 548                 return node.intersectsBounds(pickRay);
 549             }
 550 
 551             @Override
 552             public void layoutNodeForPrinting(Node node) {
 553                 node.doCSSLayoutSyncForSnapshot();
 554             }
 555 
 556             @Override
 557             public boolean isDerivedDepthTest(Node node) {
 558                 return node.isDerivedDepthTest();
 559             }
 560 
 561             @Override
 562             public SubScene getSubScene(Node node) {
 563                 return node.getSubScene();
 564             }
 565 
 566             @Override
 567             public void setLabeledBy(Node node, Node labeledBy) {
 568                 node.labeledBy = labeledBy;
 569             }
 570 
 571             @Override
 572             public Accessible getAccessible(Node node) {
 573                 return node.getAccessible();
 574             }
 575 
 576             @Override
 577             public void reapplyCSS(Node node) {
 578                 node.reapplyCSS();
 579             }
 580 
 581             @Override
 582             public boolean isTreeVisible(Node node) {
 583                 return node.isTreeVisible();
 584             }
 585 
 586             @Override
 587             public BooleanExpression treeVisibleProperty(Node node) {
 588                 return node.treeVisibleProperty();
 589             }
 590 
 591             @Override
 592             public boolean isTreeShowing(Node node) {
 593                 return node.isTreeShowing();
 594             }
 595 
 596             @Override
 597             public BooleanExpression treeShowingProperty(Node node) {
 598                 return node.treeShowingProperty();
 599             }
 600 
 601             @Override
 602             public List<Style> getMatchingStyles(CssMetaData cssMetaData,
 603                     Styleable styleable) {
 604                 return Node.getMatchingStyles(cssMetaData, styleable);
 605             }
 606 
 607             @Override
 608             public Map<StyleableProperty<?>, List<Style>> findStyles(Node node,
 609                     Map<StyleableProperty<?>, List<Style>> styleMap) {
 610                 return node.findStyles(styleMap);
 611             }
 612         });
 613     }
 614 
 615     /**************************************************************************
 616      *                                                                        *
 617      * Methods and state for managing the dirty bits of a Node. The dirty     *
 618      * bits are flags used to keep track of what things are dirty on the      *
 619      * node and therefore need processing on the next pulse. Since the pulse  *
 620      * happens asynchronously to the change that made the node dirty (for     *
 621      * performance reasons), we need to keep track of what things have        *
 622      * changed.                                                               *
 623      *                                                                        *
 624      *************************************************************************/
 625 
 626     /*
 627      * Set of dirty bits that are set when state is invalidated and cleared by
 628      * the updateState method, which is called from the synchronizer.
 629      */
 630     private int dirtyBits;
 631 
 632     /*
 633      * Mark the specified bit as dirty, and add this node to the scene's dirty list.
 634      *
 635      * Note: This method MUST only be called via its accessor method.
 636      */
 637     private void doMarkDirty(DirtyBits dirtyBit) {
 638         if (isDirtyEmpty()) {
 639             addToSceneDirtyList();
 640         }
 641 
 642         dirtyBits |= dirtyBit.getMask();
 643     }
 644 
 645     private void addToSceneDirtyList() {
 646         Scene s = getScene();
 647         if (s != null) {
 648             s.addToDirtyList(this);
 649             if (getSubScene() != null) {
 650                 getSubScene().setDirty(this);
 651             }
 652         }
 653     }
 654 
 655     /*
 656      * Test whether the specified dirty bit is set
 657      */
 658     final boolean isDirty(DirtyBits dirtyBit) {
 659         return (dirtyBits & dirtyBit.getMask()) != 0;
 660     }
 661 
 662     /*
 663      * Clear the specified dirty bit
 664      */
 665     final void clearDirty(DirtyBits dirtyBit) {
 666         dirtyBits &= ~dirtyBit.getMask();
 667     }
 668 
 669     /*
 670      * Set all dirty bits
 671      */
 672     private void setDirty() {
 673         dirtyBits = ~0;
 674     }
 675 
 676     /*
 677      * Clear all dirty bits
 678      */
 679     private void clearDirty() {
 680         dirtyBits = 0;
 681     }
 682 
 683     /*
 684      * Test whether the set of dirty bits is empty
 685      */
 686     final boolean isDirtyEmpty() {
 687         return dirtyBits == 0;
 688     }
 689 
 690     /**************************************************************************
 691      *                                                                        *
 692      * Methods for synchronizing state from this Node to its PG peer. This    *
 693      * should only *ever* be called during synchronization initialized as a   *
 694      * result of a pulse. Any attempt to synchronize at any other time may    *
 695      * cause rendering artifacts.                                             *
 696      *                                                                        *
 697      *************************************************************************/
 698 
 699     /*
 700      * Called by the synchronizer to update the state and
 701      * clear dirtybits of this node in the PG graph
 702      */
 703     final void syncPeer() {
 704         // Do not synchronize invisible nodes unless their visibility has changed
 705         // or they have requested a forced synchronization
 706         if (!isDirtyEmpty() && (treeVisible
 707                                      || isDirty(DirtyBits.NODE_VISIBLE)
 708                                      || isDirty(DirtyBits.NODE_FORCE_SYNC)))
 709         {
 710             NodeHelper.updatePeer(this);
 711             clearDirty();
 712         }
 713     }
 714 
 715     /**
 716      * A temporary rect used for computing bounds by the various bounds
 717      * variables. This bounds starts life as a RectBounds, but may be promoted
 718      * to a BoxBounds if there is a 3D transform mixed into its computation.
 719      * These two fields were held in a thread local, but were then pulled
 720      * out of it so that we could compute bounds before holding the
 721      * synchronization lock. These objects have to be per-instance so
 722      * that we can pass the right data down to the PG side later during
 723      * synchronization (rather than statics as they were before).
 724      */
 725     private BaseBounds _geomBounds = new RectBounds(0, 0, -1, -1);
 726     private BaseBounds _txBounds = new RectBounds(0, 0, -1, -1);
 727 
 728     private boolean pendingUpdateBounds = false;
 729 
 730     // Happens before we hold the sync lock
 731     void updateBounds() {
 732         // Note: the clip must be handled before the visibility is checked. This is because the visiblity might be
 733         // changing in the clip and it is going to be synchronized, so it needs to recompute the bounds.
 734         Node n = getClip();
 735         if (n != null) {
 736             n.updateBounds();
 737         }
 738 
 739         // See syncPeer()
 740         if (!treeVisible && !isDirty(DirtyBits.NODE_VISIBLE)) {
 741 
 742             // Need to save the dirty bits since they will be cleared even for the
 743             // case of short circuiting dirty bit processing.
 744             if (isDirty(DirtyBits.NODE_TRANSFORM)
 745                     || isDirty(DirtyBits.NODE_TRANSFORMED_BOUNDS)
 746                     || isDirty(DirtyBits.NODE_BOUNDS)) {
 747                 pendingUpdateBounds = true;
 748             }
 749 
 750             return;
 751         }
 752 
 753         // Set transform and bounds dirty bits when this node becomes visible
 754         if (pendingUpdateBounds) {
 755             NodeHelper.markDirty(this, DirtyBits.NODE_TRANSFORM);
 756             NodeHelper.markDirty(this, DirtyBits.NODE_TRANSFORMED_BOUNDS);
 757             NodeHelper.markDirty(this, DirtyBits.NODE_BOUNDS);
 758 
 759             pendingUpdateBounds = false;
 760         }
 761 
 762         if (isDirty(DirtyBits.NODE_TRANSFORM) || isDirty(DirtyBits.NODE_TRANSFORMED_BOUNDS)) {
 763             if (isDirty(DirtyBits.NODE_TRANSFORM)) {
 764                 updateLocalToParentTransform();
 765             }
 766             _txBounds = getTransformedBounds(_txBounds,
 767                                              BaseTransform.IDENTITY_TRANSFORM);
 768         }
 769 
 770         if (isDirty(DirtyBits.NODE_BOUNDS)) {
 771             _geomBounds = getGeomBounds(_geomBounds,
 772                     BaseTransform.IDENTITY_TRANSFORM);
 773         }
 774 
 775     }
 776 
 777     /*
 778      * This function is called during synchronization to update the state of the
 779      * NG Node from the FX Node. Subclasses of Node should override this method
 780      * and must call NodeHelper.updatePeer(this)
 781      *
 782      * Note: This method MUST only be called via its accessor method.
 783      */
 784     private void doUpdatePeer() {
 785         final NGNode peer = getPeer();
 786 
 787         // For debug / diagnostic purposes, we will copy across a name for this node down to
 788         // the NG layer, where we can use the name to figure out what the NGNode represents.
 789         // An alternative would be to have a back-reference from the NGNode back to the Node it
 790         // is a peer to, however it was felt that this would make it too easy to communicate back
 791         // to the Node and possibly violate thread invariants. But of course, we only need to do this
 792         // if we're going to print the render graph (otherwise all the work we'd do to keep the name
 793         // properly updated would be a waste).
 794         if (PrismSettings.printRenderGraph && isDirty(DirtyBits.DEBUG)) {
 795             final String id = getId();
 796             String className = getClass().getSimpleName();
 797             if (className.isEmpty()) {
 798                 className = getClass().getName();
 799             }
 800             peer.setName(id == null ? className : id + "(" + className + ")");
 801         }
 802 
 803         if (isDirty(DirtyBits.NODE_TRANSFORM)) {
 804             peer.setTransformMatrix(localToParentTx);
 805         }
 806 
 807         if (isDirty(DirtyBits.NODE_VIEW_ORDER)) {
 808             peer.setViewOrder(getViewOrder());
 809         }
 810 
 811         if (isDirty(DirtyBits.NODE_BOUNDS)) {
 812             peer.setContentBounds(_geomBounds);
 813         }
 814 
 815         if (isDirty(DirtyBits.NODE_TRANSFORMED_BOUNDS)) {
 816             peer.setTransformedBounds(_txBounds, !isDirty(DirtyBits.NODE_BOUNDS));
 817         }
 818 
 819         if (isDirty(DirtyBits.NODE_OPACITY)) {
 820             peer.setOpacity((float)Utils.clamp(0, getOpacity(), 1));
 821         }
 822 
 823         if (isDirty(DirtyBits.NODE_CACHE)) {
 824             peer.setCachedAsBitmap(isCache(), getCacheHint());
 825         }
 826 
 827         if (isDirty(DirtyBits.NODE_CLIP)) {
 828             peer.setClipNode(getClip() != null ? getClip().getPeer() : null);
 829         }
 830 
 831         if (isDirty(DirtyBits.EFFECT_EFFECT)) {
 832             if (getEffect() != null) {
 833                 EffectHelper.sync(getEffect());
 834                 peer.effectChanged();
 835             }
 836         }
 837 
 838         if (isDirty(DirtyBits.NODE_EFFECT)) {
 839             peer.setEffect(getEffect() != null ? EffectHelper.getPeer(getEffect()) : null);
 840         }
 841 
 842         if (isDirty(DirtyBits.NODE_VISIBLE)) {
 843             peer.setVisible(isVisible());
 844         }
 845 
 846         if (isDirty(DirtyBits.NODE_DEPTH_TEST)) {
 847             peer.setDepthTest(isDerivedDepthTest());
 848         }
 849 
 850         if (isDirty(DirtyBits.NODE_BLENDMODE)) {
 851             BlendMode mode = getBlendMode();
 852             peer.setNodeBlendMode((mode == null)
 853                                   ? null
 854                                   : EffectHelper.getToolkitBlendMode(mode));
 855         }
 856     }
 857 
 858     /*************************************************************************
 859     *                                                                        *
 860     *                                                                        *
 861     *                                                                        *
 862     *************************************************************************/
 863 
 864     private static final Object USER_DATA_KEY = new Object();
 865     // A map containing a set of properties for this node
 866     private ObservableMap<Object, Object> properties;
 867 
 868     /**
 869       * Returns an observable map of properties on this node for use primarily
 870       * by application developers.
 871       *
 872       * @return an observable map of properties on this node for use primarily
 873       * by application developers
 874       */
 875      public final ObservableMap<Object, Object> getProperties() {
 876         if (properties == null) {
 877             properties = FXCollections.observableMap(new HashMap<Object, Object>());
 878         }
 879         return properties;
 880     }
 881 
 882     /**
 883      * Tests if Node has properties.
 884      * @return true if node has properties.
 885      */
 886      public boolean hasProperties() {
 887         return properties != null && !properties.isEmpty();
 888     }
 889 
 890     /**
 891      * Convenience method for setting a single Object property that can be
 892      * retrieved at a later date. This is functionally equivalent to calling
 893      * the getProperties().put(Object key, Object value) method. This can later
 894      * be retrieved by calling {@link Node#getUserData()}.
 895      *
 896      * @param value The value to be stored - this can later be retrieved by calling
 897      *          {@link Node#getUserData()}.
 898      */
 899     public void setUserData(Object value) {
 900         getProperties().put(USER_DATA_KEY, value);
 901     }
 902 
 903     /**
 904      * Returns a previously set Object property, or null if no such property
 905      * has been set using the {@link Node#setUserData(java.lang.Object)} method.
 906      *
 907      * @return The Object that was previously set, or null if no property
 908      *          has been set or if null was set.
 909      */
 910     public Object getUserData() {
 911         return getProperties().get(USER_DATA_KEY);
 912     }
 913 
 914     /**************************************************************************
 915      *                                                                        *
 916      *
 917      *                                                                        *
 918      *************************************************************************/
 919 
 920     /**
 921      * The parent of this {@code Node}. If this {@code Node} has not been added
 922      * to a scene graph, then parent will be null.
 923      *
 924      * @defaultValue null
 925      */
 926     private ReadOnlyObjectWrapper<Parent> parent;
 927 
 928     final void setParent(Parent value) {
 929         parentPropertyImpl().set(value);
 930     }
 931 
 932     public final Parent getParent() {
 933         return parent == null ? null : parent.get();
 934     }
 935 
 936     public final ReadOnlyObjectProperty<Parent> parentProperty() {
 937         return parentPropertyImpl().getReadOnlyProperty();
 938     }
 939 
 940     private ReadOnlyObjectWrapper<Parent> parentPropertyImpl() {
 941         if (parent == null) {
 942             parent = new ReadOnlyObjectWrapper<Parent>() {
 943                 private Parent oldParent;
 944 
 945                 @Override
 946                 protected void invalidated() {
 947                     if (oldParent != null) {
 948                         oldParent.disabledProperty().removeListener(parentDisabledChangedListener);
 949                         oldParent.treeVisibleProperty().removeListener(parentTreeVisibleChangedListener);
 950                         if (nodeTransformation != null && nodeTransformation.listenerReasons > 0) {
 951                             ((Node) oldParent).localToSceneTransformProperty().removeListener(
 952                                     nodeTransformation.getLocalToSceneInvalidationListener());
 953                         }
 954                     }
 955                     updateDisabled();
 956                     computeDerivedDepthTest();
 957                     final Parent newParent = get();
 958                     if (newParent != null) {
 959                         newParent.disabledProperty().addListener(parentDisabledChangedListener);
 960                         newParent.treeVisibleProperty().addListener(parentTreeVisibleChangedListener);
 961                         if (nodeTransformation != null && nodeTransformation.listenerReasons > 0) {
 962                             ((Node) newParent).localToSceneTransformProperty().addListener(
 963                                     nodeTransformation.getLocalToSceneInvalidationListener());
 964                         }
 965                         //
 966                         // if parent changed, then CSS needs to be reapplied so
 967                         // that this node will get the right styles. This used
 968                         // to be done from Parent.children's onChanged method.
 969                         // See the comments there, also.
 970                         //
 971                         reapplyCSS();
 972                     } else {
 973                         // RT-31168: reset CssFlag to clean so css will be reapplied if the node is added back later.
 974                         // If flag is REAPPLY, then reapplyCSS() will just return and the call to
 975                         // notifyParentsOfInvalidatedCSS() will be skipped thus leaving the node un-styled.
 976                         cssFlag = CssFlags.CLEAN;
 977                     }
 978                     updateTreeVisible(true);
 979                     oldParent = newParent;
 980                     invalidateLocalToSceneTransform();
 981                     parentResolvedOrientationInvalidated();
 982                     notifyAccessibleAttributeChanged(AccessibleAttribute.PARENT);
 983                 }
 984 
 985                 @Override
 986                 public Object getBean() {
 987                     return Node.this;
 988                 }
 989 
 990                 @Override
 991                 public String getName() {
 992                     return "parent";
 993                 }
 994             };
 995         }
 996         return parent;
 997     }
 998 
 999     private final InvalidationListener parentDisabledChangedListener = valueModel -> updateDisabled();
1000 
1001     private final InvalidationListener parentTreeVisibleChangedListener = valueModel -> updateTreeVisible(true);
1002 
1003     private final ChangeListener<Boolean> windowShowingChangedListener
1004             = (win, oldVal, newVal) -> updateTreeShowing();
1005 
1006     private final ChangeListener<Window> sceneWindowChangedListener = (scene, oldWindow, newWindow) -> {
1007         // Replace the windowShowingListener and call updateTreeShowing()
1008         if (oldWindow != null) {
1009             oldWindow.showingProperty().removeListener(windowShowingChangedListener);
1010         }
1011         if (newWindow != null) {
1012             newWindow.showingProperty().addListener(windowShowingChangedListener);
1013         }
1014         updateTreeShowing();
1015     };
1016 
1017     private SubScene subScene = null;
1018 
1019     /**
1020      * The {@link Scene} that this {@code Node} is part of. If the Node is not
1021      * part of a scene, then this variable will be null.
1022      *
1023      * @defaultValue null
1024      */
1025     private ReadOnlyObjectWrapperManualFire<Scene> scene = new ReadOnlyObjectWrapperManualFire<Scene>();
1026 
1027     private class ReadOnlyObjectWrapperManualFire<T> extends ReadOnlyObjectWrapper<T> {
1028         @Override
1029         public Object getBean() {
1030             return Node.this;
1031         }
1032 
1033         @Override
1034         public String getName() {
1035             return "scene";
1036         }
1037 
1038         @Override
1039         protected void fireValueChangedEvent() {
1040             /*
1041              * Note: This method has been intentionally made into a no-op. In
1042              * order to override the default set behavior. By default calling
1043              * set(...) on a different scene will trigger:
1044              * - invalidated();
1045              * - fireValueChangedEvent();
1046              * Both of the above are no-ops, but are handled manually via
1047              * - Node.this.setScenes(...)
1048              * - Node.this.invalidatedScenes(...)
1049              * - forceValueChangedEvent()
1050              */
1051         }
1052 
1053         public void fireSuperValueChangedEvent() {
1054             super.fireValueChangedEvent();
1055         }
1056     }
1057 
1058     // reapplyCSS should be true for root elements when they are added, and is false for children
1059     // of the root element. This prevents CSS being reapplied recursively, as noted in JDK-8151756.
1060     private void invalidatedScenes(Scene oldScene, SubScene oldSubScene, boolean reapplyCSS) {
1061         Scene newScene = sceneProperty().get();
1062         boolean sceneChanged = oldScene != newScene;
1063         SubScene newSubScene = subScene;
1064 
1065         if (getClip() != null) {
1066             getClip().setScenes(newScene, newSubScene, reapplyCSS);
1067         }
1068         if (sceneChanged) {
1069             updateCanReceiveFocus();
1070             if (isFocusTraversable()) {
1071                 if (newScene != null) {
1072                     newScene.initializeInternalEventDispatcher();
1073                 }
1074             }
1075             focusSetDirty(oldScene);
1076             focusSetDirty(newScene);
1077         }
1078         scenesChanged(newScene, newSubScene, oldScene, oldSubScene);
1079 
1080         // isTreeShowing needs to take into account of Window's showing
1081         if (oldScene != null) {
1082             oldScene.windowProperty().removeListener(sceneWindowChangedListener);
1083         }
1084         if (newScene != null) {
1085             newScene.windowProperty().addListener(sceneWindowChangedListener);
1086         }
1087         updateTreeShowing();
1088 
1089         if (sceneChanged && reapplyCSS) reapplyCSS();
1090 
1091         if (sceneChanged && !isDirtyEmpty()) {
1092             //Note: no need to remove from scene's dirty list
1093             //Scene's is checking if the node's scene is correct
1094             /* TODO: looks like an existing bug when a node is moved from one
1095              * location to another, setScenes will be called twice by
1096              * Parent.VetoableListDecorator onProposedChange and onChanged
1097              * respectively. Removing the node and setting setScense(null,null)
1098              * then adding it back to potentially the same scene. Causing the
1099              * same node to being added twice to the same scene.
1100              */
1101             addToSceneDirtyList();
1102         }
1103 
1104         if (newScene == null && peer != null) {
1105             peer.release();
1106         }
1107 
1108         if (oldScene != null) {
1109             oldScene.clearNodeMnemonics(this);
1110         }
1111         if (getParent() == null) {
1112             // if we are the root we need to handle scene change
1113             parentResolvedOrientationInvalidated();
1114         }
1115 
1116         if (sceneChanged) { scene.fireSuperValueChangedEvent(); }
1117 
1118         /* Dispose the accessible peer, if any. If AT ever needs this node again
1119          * a new accessible peer is created. */
1120         if (accessible != null) {
1121             /* Generally accessibility does not retain any state, therefore deleting objects
1122              * generally does not cause problems (AT just asks everything back).
1123              * The exception to this rule is when the object sends a notifications to the AT,
1124              * in which case it is expected to be around to answer request for the new values.
1125              * It is possible that a object is reparented (within the scene) in the middle of
1126              * this process. For example, when a tree item is expanded, the notification is
1127              * sent to the AT by the cell. But when the TreeView relayouts the cell can be
1128              * reparented before AT can query the relevant information about the expand event.
1129              * If the accessible was disposed, AT can't properly report the event.
1130              *
1131              * The fix is to defer the disposal of the accessible to the next pulse.
1132              * If at that time the node is placed back to the scene, then the accessible is hooked
1133              * to Node and AT requests are processed. Otherwise the accessible is disposed.
1134              */
1135             if (oldScene != null && oldScene != newScene && newScene == null) {
1136                 // Strictly speaking we need some type of accessible.thaw() at this point.
1137                 oldScene.addAccessible(Node.this, accessible);
1138             } else {
1139                 accessible.dispose();
1140             }
1141             /* Always set to null to ensure this accessible is never on more than one
1142              * Scene#accMap at the same time (At lest not with the same accessible).
1143              */
1144             accessible = null;
1145         }
1146     }
1147 
1148     final void setScenes(Scene newScene, SubScene newSubScene, boolean reapplyCSS) {
1149         Scene oldScene = sceneProperty().get();
1150         if (newScene != oldScene || newSubScene != subScene) {
1151             scene.set(newScene);
1152             SubScene oldSubScene = subScene;
1153             subScene = newSubScene;
1154             invalidatedScenes(oldScene, oldSubScene, reapplyCSS);
1155             if (this instanceof SubScene) { // TODO: find better solution
1156                 SubScene thisSubScene = (SubScene)this;
1157                 thisSubScene.getRoot().setScenes(newScene, thisSubScene, reapplyCSS);
1158             }
1159         }
1160     }
1161 
1162     final SubScene getSubScene() {
1163         return subScene;
1164     }
1165 
1166     public final Scene getScene() {
1167         return scene.get();
1168     }
1169 
1170     public final ReadOnlyObjectProperty<Scene> sceneProperty() {
1171         return scene.getReadOnlyProperty();
1172     }
1173 
1174     /**
1175      * Exists for Parent and LightBase
1176      */
1177     void scenesChanged(final Scene newScene, final SubScene newSubScene,
1178                        final Scene oldScene, final SubScene oldSubScene) { }
1179 
1180 
1181     /**
1182      * The id of this {@code Node}. This simple string identifier is useful for
1183      * finding a specific Node within the scene graph. While the id of a Node
1184      * should be unique within the scene graph, this uniqueness is not enforced.
1185      * This is analogous to the "id" attribute on an HTML element
1186      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
1187      * <p>
1188      *     For example, if a Node is given the id of "myId", then the lookup method can
1189      *     be used to find this node as follows: <code>scene.lookup("#myId");</code>.
1190      * </p>
1191      *
1192      * @defaultValue null
1193      * @see <a href="doc-files/cssref.html">CSS Reference Guide</a>.
1194      */
1195     private StringProperty id;
1196 
1197     public final void setId(String value) {
1198         idProperty().set(value);
1199     }
1200 
1201     //TODO: this is copied from the property in order to add the @return statement.
1202     //      We should have a better, general solution without the need to copy it.
1203     /**
1204      * The id of this {@code Node}. This simple string identifier is useful for
1205      * finding a specific Node within the scene graph. While the id of a Node
1206      * should be unique within the scene graph, this uniqueness is not enforced.
1207      * This is analogous to the "id" attribute on an HTML element
1208      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
1209      *
1210      * @return the id assigned to this {@code Node} using the {@code setId}
1211      *         method or {@code null}, if no id has been assigned.
1212      * @defaultValue null
1213      * @see <a href="doc-files/cssref.html">CSS Reference Guide</a>
1214      */
1215     public final String getId() {
1216         return id == null ? null : id.get();
1217     }
1218 
1219     public final StringProperty idProperty() {
1220         if (id == null) {
1221             id = new StringPropertyBase() {
1222 
1223                 @Override
1224                 protected void invalidated() {
1225                     reapplyCSS();
1226                     if (PrismSettings.printRenderGraph) {
1227                         NodeHelper.markDirty(Node.this, DirtyBits.DEBUG);
1228                     }
1229                 }
1230 
1231                 @Override
1232                 public Object getBean() {
1233                     return Node.this;
1234                 }
1235 
1236                 @Override
1237                 public String getName() {
1238                     return "id";
1239                 }
1240             };
1241         }
1242         return id;
1243     }
1244 
1245     /**
1246      * A list of String identifiers which can be used to logically group
1247      * Nodes, specifically for an external style engine. This variable is
1248      * analogous to the "class" attribute on an HTML element and, as such,
1249      * each element of the list is a style class to which this Node belongs.
1250      *
1251      * @see <a href="http://www.w3.org/TR/css3-selectors/#class-html">CSS3 class selectors</a>
1252      * @see <a href="doc-files/cssref.html">CSS Reference Guide</a>.
1253      * @defaultValue null
1254      */
1255     private ObservableList<String> styleClass = new TrackableObservableList<String>() {
1256         @Override
1257         protected void onChanged(Change<String> c) {
1258             reapplyCSS();
1259         }
1260 
1261         @Override
1262         public String toString() {
1263             if (size() == 0) {
1264                 return "";
1265             } else if (size() == 1) {
1266                 return get(0);
1267             } else {
1268                 StringBuilder buf = new StringBuilder();
1269                 for (int i = 0; i < size(); i++) {
1270                     buf.append(get(i));
1271                     if (i + 1 < size()) {
1272                         buf.append(' ');
1273                     }
1274                 }
1275                 return buf.toString();
1276             }
1277         }
1278     };
1279 
1280     @Override
1281     public final ObservableList<String> getStyleClass() {
1282         return styleClass;
1283     }
1284 
1285     /**
1286      * A string representation of the CSS style associated with this
1287      * specific {@code Node}. This is analogous to the "style" attribute of an
1288      * HTML element. Note that, like the HTML style attribute, this
1289      * variable contains style properties and values and not the
1290      * selector portion of a style rule.
1291      * @defaultValue empty string
1292      * @see <a href="doc-files/cssref.html">CSS Reference Guide</a>.
1293      */
1294     private StringProperty style;
1295 
1296     /**
1297      * A string representation of the CSS style associated with this
1298      * specific {@code Node}. This is analogous to the "style" attribute of an
1299      * HTML element. Note that, like the HTML style attribute, this
1300      * variable contains style properties and values and not the
1301      * selector portion of a style rule.
1302      * @param value The inline CSS style to use for this {@code Node}.
1303      *         {@code null} is implicitly converted to an empty String.
1304      * @defaultValue empty string
1305      * @see <a href="doc-files/cssref.html">CSS Reference Guide</a>
1306      */
1307     public final void setStyle(String value) {
1308         styleProperty().set(value);
1309     }
1310 
1311     // TODO: javadoc copied from property for the sole purpose of providing a return tag
1312     /**
1313      * A string representation of the CSS style associated with this
1314      * specific {@code Node}. This is analogous to the "style" attribute of an
1315      * HTML element. Note that, like the HTML style attribute, this
1316      * variable contains style properties and values and not the
1317      * selector portion of a style rule.
1318      * @defaultValue empty string
1319      * @return The inline CSS style associated with this {@code Node}.
1320      *         If this {@code Node} does not have an inline style,
1321      *         an empty String is returned.
1322      * @see <a href="doc-files/cssref.html">CSS Reference Guide</a>
1323      */
1324     public final String getStyle() {
1325         return style == null ? "" : style.get();
1326     }
1327 
1328     public final StringProperty styleProperty() {
1329         if (style == null) {
1330             style = new StringPropertyBase("") {
1331 
1332                 @Override public void set(String value) {
1333                     // getStyle returns an empty string if the style property
1334                     // is null. To be consistent, getStyle should also return
1335                     // an empty string when the style property's value is null.
1336                     super.set((value != null) ? value : "");
1337                 }
1338 
1339                 @Override
1340                 protected void invalidated() {
1341                     // If the style has changed, then styles of this node
1342                     // and child nodes might be affected.
1343                     reapplyCSS();
1344                 }
1345 
1346                 @Override
1347                 public Object getBean() {
1348                     return Node.this;
1349                 }
1350 
1351                 @Override
1352                 public String getName() {
1353                     return "style";
1354                 }
1355             };
1356         }
1357         return style;
1358     }
1359 
1360     /**
1361      * Specifies whether this {@code Node} and any subnodes should be rendered
1362      * as part of the scene graph. A node may be visible and yet not be shown
1363      * in the rendered scene if, for instance, it is off the screen or obscured
1364      * by another Node. Invisible nodes never receive mouse events or
1365      * keyboard focus and never maintain keyboard focus when they become
1366      * invisible.
1367      *
1368      * @defaultValue true
1369      */
1370     private BooleanProperty visible;
1371 
1372     public final void setVisible(boolean value) {
1373         visibleProperty().set(value);
1374     }
1375 
1376     public final boolean isVisible() {
1377         return visible == null ? true : visible.get();
1378     }
1379 
1380     public final BooleanProperty visibleProperty() {
1381         if (visible == null) {
1382             visible = new StyleableBooleanProperty(true) {
1383                 boolean oldValue = true;
1384                 @Override
1385                 protected void invalidated() {
1386                     if (oldValue != get()) {
1387                         NodeHelper.markDirty(Node.this, DirtyBits.NODE_VISIBLE);
1388                         NodeHelper.geomChanged(Node.this);
1389                         updateTreeVisible(false);
1390                         if (getParent() != null) {
1391                             // notify the parent of the potential change in visibility
1392                             // of this node, since visibility affects bounds of the
1393                             // parent node
1394                             getParent().childVisibilityChanged(Node.this);
1395                         }
1396                         oldValue = get();
1397                     }
1398                 }
1399 
1400                 @Override
1401                 public CssMetaData getCssMetaData() {
1402                     return StyleableProperties.VISIBILITY;
1403                 }
1404 
1405                 @Override
1406                 public Object getBean() {
1407                     return Node.this;
1408                 }
1409 
1410                 @Override
1411                 public String getName() {
1412                     return "visible";
1413                 }
1414             };
1415         }
1416         return visible;
1417     }
1418 
1419     public final void setCursor(Cursor value) {
1420         cursorProperty().set(value);
1421     }
1422 
1423     public final Cursor getCursor() {
1424         return (miscProperties == null) ? DEFAULT_CURSOR
1425                                         : miscProperties.getCursor();
1426     }
1427 
1428     /**
1429      * Defines the mouse cursor for this {@code Node} and subnodes. If null,
1430      * then the cursor of the first parent node with a non-null cursor will be
1431      * used. If no Node in the scene graph defines a cursor, then the cursor
1432      * of the {@code Scene} will be used.
1433      *
1434      * @return the mouse cursor for this {@code Node} and subnodes
1435      * @defaultValue null
1436      */
1437     public final ObjectProperty<Cursor> cursorProperty() {
1438         return getMiscProperties().cursorProperty();
1439     }
1440 
1441     /**
1442      * Specifies how opaque (that is, solid) the {@code Node} appears. A Node
1443      * with 0% opacity is fully translucent. That is, while it is still
1444      * {@link #visibleProperty visible} and rendered, you generally won't be able to see it. The
1445      * exception to this rule is when the {@code Node} is combined with a
1446      * blending mode and blend effect in which case a translucent Node may still
1447      * have an impact in rendering. An opacity of 50% will render the node as
1448      * being 50% transparent.
1449      * <p>
1450      * A {@link #visibleProperty visible} node with any opacity setting still receives mouse
1451      * events and can receive keyboard focus. For example, if you want to have
1452      * a large invisible rectangle overlay all {@code Node}s in the scene graph
1453      * in order to intercept mouse events but not be visible to the user, you could
1454      * create a large {@code Rectangle} that had an opacity of 0%.
1455      * <p>
1456      * Opacity is specified as a value between 0 and 1. Values less than 0 are
1457      * treated as 0, values greater than 1 are treated as 1.
1458      * <p>
1459      * On some platforms ImageView might not support opacity variable.
1460      *
1461      * <p>
1462      * There is a known limitation of mixing opacity &lt; 1.0 with a 3D Transform.
1463      * Opacity/Blending is essentially a 2D image operation. The result of
1464      * an opacity &lt; 1.0 set on a {@link Group} node with 3D transformed children
1465      * will cause its children to be rendered in order without Z-buffering
1466      * applied between those children.
1467      *
1468      * @defaultValue 1.0
1469      */
1470     private DoubleProperty opacity;
1471 
1472     public final void setOpacity(double value) {
1473         opacityProperty().set(value);
1474     }
1475     public final double getOpacity() {
1476         return opacity == null ? 1 : opacity.get();
1477     }
1478 
1479     public final DoubleProperty opacityProperty() {
1480         if (opacity == null) {
1481             opacity = new StyleableDoubleProperty(1) {
1482 
1483                 @Override
1484                 public void invalidated() {
1485                     NodeHelper.markDirty(Node.this, DirtyBits.NODE_OPACITY);
1486                 }
1487 
1488                 @Override
1489                 public CssMetaData getCssMetaData() {
1490                     return StyleableProperties.OPACITY;
1491                 }
1492 
1493                 @Override
1494                 public Object getBean() {
1495                     return Node.this;
1496                 }
1497 
1498                 @Override
1499                 public String getName() {
1500                     return "opacity";
1501                 }
1502             };
1503         }
1504         return opacity;
1505     }
1506 
1507     /**
1508      * The {@link javafx.scene.effect.BlendMode} used to blend this individual node
1509      * into the scene behind it. If this node happens to be a Group then all of the
1510      * children will be composited individually into a temporary buffer using their
1511      * own blend modes and then that temporary buffer will be composited into the
1512      * scene using the specified blend mode.
1513      *
1514      * A value of {@code null} is treated as pass-though this means no effect on a
1515      * parent such as a Group and the equivalent of SRC_OVER for a single Node.
1516      *
1517      * @defaultValue null
1518      */
1519     private javafx.beans.property.ObjectProperty<BlendMode> blendMode;
1520 
1521     public final void setBlendMode(BlendMode value) {
1522         blendModeProperty().set(value);
1523     }
1524     public final BlendMode getBlendMode() {
1525         return blendMode == null ? null : blendMode.get();
1526     }
1527 
1528     public final ObjectProperty<BlendMode> blendModeProperty() {
1529         if (blendMode == null) {
1530             blendMode = new StyleableObjectProperty<BlendMode>(null) {
1531                 @Override public void invalidated() {
1532                     NodeHelper.markDirty(Node.this, DirtyBits.NODE_BLENDMODE);
1533                 }
1534 
1535                 @Override
1536                 public CssMetaData getCssMetaData() {
1537                     return StyleableProperties.BLEND_MODE;
1538                 }
1539 
1540                 @Override
1541                 public Object getBean() {
1542                     return Node.this;
1543                 }
1544 
1545                 @Override
1546                 public String getName() {
1547                     return "blendMode";
1548                 }
1549             };
1550         }
1551         return blendMode;
1552     }
1553 
1554     public final void setClip(Node value) {
1555         clipProperty().set(value);
1556     }
1557 
1558     public final Node getClip() {
1559         return (miscProperties == null) ? DEFAULT_CLIP
1560                                         : miscProperties.getClip();
1561     }
1562 
1563     /**
1564      * Specifies a {@code Node} to use to define the the clipping shape for this
1565      * Node. This clipping Node is not a child of this {@code Node} in the scene
1566      * graph sense. Rather, it is used to define the clip for this {@code Node}.
1567      * <p>
1568      * For example, you can use an {@link javafx.scene.image.ImageView} Node as
1569      * a mask to represent the Clip. Or you could use one of the geometric shape
1570      * Nodes such as {@link javafx.scene.shape.Rectangle} or
1571      * {@link javafx.scene.shape.Circle}. Or you could use a
1572      * {@link javafx.scene.text.Text} node to represent the Clip.
1573      * <p>
1574      * See the class documentation for {@link Node} for scene graph structure
1575      * restrictions on setting the clip. If these restrictions are violated by
1576      * a change to the clip variable, the change is ignored and the
1577      * previous value of the clip variable is restored.
1578      * <p>
1579      * Note that this is a conditional feature. See
1580      * {@link javafx.application.ConditionalFeature#SHAPE_CLIP ConditionalFeature.SHAPE_CLIP}
1581      * for more information.
1582      * <p>
1583      * There is a known limitation of mixing Clip with a 3D Transform.
1584      * Clipping is essentially a 2D image operation. The result of
1585      * a Clip set on a {@link Group} node with 3D transformed children
1586      * will cause its children to be rendered in order without Z-buffering
1587      * applied between those children.
1588      *
1589      * @return the the clipping shape for this {@code Node}
1590      * @defaultValue null
1591      */
1592     public final ObjectProperty<Node> clipProperty() {
1593         return getMiscProperties().clipProperty();
1594     }
1595 
1596     public final void setCache(boolean value) {
1597         cacheProperty().set(value);
1598     }
1599 
1600     public final boolean isCache() {
1601         return (miscProperties == null) ? DEFAULT_CACHE
1602                                         : miscProperties.isCache();
1603     }
1604 
1605     /**
1606      * A performance hint to the system to indicate that this {@code Node}
1607      * should be cached as a bitmap. Rendering a bitmap representation of a node
1608      * will be faster than rendering primitives in many cases, especially in the
1609      * case of primitives with effects applied (such as a blur). However, it
1610      * also increases memory usage. This hint indicates whether that trade-off
1611      * (increased memory usage for increased performance) is worthwhile. Also
1612      * note that on some platforms such as GPU accelerated platforms there is
1613      * little benefit to caching Nodes as bitmaps when blurs and other effects
1614      * are used since they are very fast to render on the GPU.
1615      *
1616      * The {@link #cacheHintProperty} variable provides additional options for enabling
1617      * more aggressive bitmap caching.
1618      *
1619      * <p>
1620      * Caching may be disabled for any node that has a 3D transform on itself,
1621      * any of its ancestors, or any of its descendants.
1622      *
1623      * @return the hint to cache for this {@code Node}
1624      * @see #cacheHintProperty
1625      * @defaultValue false
1626      */
1627     public final BooleanProperty cacheProperty() {
1628         return getMiscProperties().cacheProperty();
1629     }
1630 
1631     public final void setCacheHint(CacheHint value) {
1632         cacheHintProperty().set(value);
1633     }
1634 
1635     public final CacheHint getCacheHint() {
1636         return (miscProperties == null) ? DEFAULT_CACHE_HINT
1637                                         : miscProperties.getCacheHint();
1638     }
1639 
1640     /**
1641      * Additional hint for controlling bitmap caching.
1642      * <p>
1643      * Under certain circumstances, such as animating nodes that are very
1644      * expensive to render, it is desirable to be able to perform
1645      * transformations on the node without having to regenerate the cached
1646      * bitmap.  An option in such cases is to perform the transforms on the
1647      * cached bitmap itself.
1648      * <p>
1649      * This technique can provide a dramatic improvement to animation
1650      * performance, though may also result in a reduction in visual quality.
1651      * The {@code cacheHint} variable provides a hint to the system about how
1652      * and when that trade-off (visual quality for animation performance) is
1653      * acceptable.
1654      * <p>
1655      * It is possible to enable the cacheHint only at times when your node is
1656      * animating.  In this way, expensive nodes can appear on screen with full
1657      * visual quality, yet still animate smoothly.
1658      * <p>
1659      * Example:
1660      * <pre>{@code
1661         expensiveNode.setCache(true);
1662         expensiveNode.setCacheHint(CacheHint.QUALITY);
1663         ...
1664         // Do an animation
1665         expensiveNode.setCacheHint(CacheHint.SPEED);
1666         new Timeline(
1667             new KeyFrame(Duration.seconds(2),
1668                 new KeyValue(expensiveNode.scaleXProperty(), 2.0),
1669                 new KeyValue(expensiveNode.scaleYProperty(), 2.0),
1670                 new KeyValue(expensiveNode.rotateProperty(), 360),
1671                 new KeyValue(expensiveNode.cacheHintProperty(), CacheHint.QUALITY)
1672             )
1673         ).play();
1674      }</pre>
1675      *
1676      * Note that {@code cacheHint} is only a hint to the system.  Depending on
1677      * the details of the node or the transform, this hint may be ignored.
1678      *
1679      * <p>
1680      * If {@code Node.cache} is false, cacheHint is ignored.
1681      * Caching may be disabled for any node that has a 3D transform on itself,
1682      * any of its ancestors, or any of its descendants.
1683      *
1684      * @return the {@code CacheHint} for this {@code Node}
1685      * @see #cacheProperty
1686      * @defaultValue CacheHint.DEFAULT
1687      */
1688     public final ObjectProperty<CacheHint> cacheHintProperty() {
1689         return getMiscProperties().cacheHintProperty();
1690     }
1691 
1692     public final void setEffect(Effect value) {
1693         effectProperty().set(value);
1694     }
1695 
1696     public final Effect getEffect() {
1697         return (miscProperties == null) ? DEFAULT_EFFECT
1698                                         : miscProperties.getEffect();
1699     }
1700 
1701     /**
1702      * Specifies an effect to apply to this {@code Node}.
1703      * <p>
1704      * Note that this is a conditional feature. See
1705      * {@link javafx.application.ConditionalFeature#EFFECT ConditionalFeature.EFFECT}
1706      * for more information.
1707      *
1708      * <p>
1709      * There is a known limitation of mixing Effect with a 3D Transform. Effect is
1710      * essentially a 2D image operation. The result of an Effect set on
1711      * a {@link Group} node with 3D transformed children will cause its children
1712      * to be rendered in order without Z-buffering applied between those
1713      * children.
1714      *
1715      * @return the effect for this {@code Node}
1716      * @defaultValue null
1717      */
1718     public final ObjectProperty<Effect> effectProperty() {
1719         return getMiscProperties().effectProperty();
1720     }
1721 
1722     public final void setDepthTest(DepthTest value) {
1723         depthTestProperty().set(value);
1724     }
1725 
1726     public final DepthTest getDepthTest() {
1727         return (miscProperties == null) ? DEFAULT_DEPTH_TEST
1728                                         : miscProperties.getDepthTest();
1729     }
1730 
1731     /**
1732      * Indicates whether depth testing is used when rendering this node.
1733      * If the depthTest flag is {@code DepthTest.DISABLE}, then depth testing
1734      * is disabled for this node.
1735      * If the depthTest flag is {@code DepthTest.ENABLE}, then depth testing
1736      * is enabled for this node.
1737      * If the depthTest flag is {@code DepthTest.INHERIT}, then depth testing
1738      * is enabled for this node if it is enabled for the parent node or the
1739      * parent node is null.
1740      * <p>
1741      * The depthTest flag is only used when the depthBuffer flag for
1742      * the {@link Scene} is true (meaning that the
1743      * {@link Scene} has an associated depth buffer)
1744      * <p>
1745      * Depth test comparison is only done among nodes with depthTest enabled.
1746      * A node with depthTest disabled does not read, test, or write the depth buffer,
1747      * that is to say its Z value will not be considered for depth testing
1748      * with other nodes.
1749      * <p>
1750      * Note that this is a conditional feature. See
1751      * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
1752      * for more information.
1753      *<p>
1754      * See the constructor in Scene with depthBuffer as one of its input
1755      * arguments.
1756      *
1757      * @return the depth test setting for this {@code Node}
1758      * @see javafx.scene.Scene
1759      * @defaultValue INHERIT
1760      */
1761     public final ObjectProperty<DepthTest> depthTestProperty() {
1762         return getMiscProperties().depthTestProperty();
1763     }
1764 
1765     /**
1766      * Recompute the derived depth test flag. This flag is true
1767      * if the depthTest flag for this node is true and the
1768      * depth test flag for each ancestor node is true. It is false
1769      * otherwise. Equivalently, the derived depth flag is true
1770      * if the depthTest flag for this node is true and the derivedDepthTest
1771      * flag for its parent is true.
1772      */
1773     void computeDerivedDepthTest() {
1774         boolean newDDT;
1775         if (getDepthTest() == DepthTest.INHERIT) {
1776             if (getParent() != null) {
1777                 newDDT = getParent().isDerivedDepthTest();
1778             } else {
1779                 newDDT = true;
1780             }
1781         } else if (getDepthTest() == DepthTest.ENABLE) {
1782             newDDT = true;
1783         } else {
1784             newDDT = false;
1785         }
1786 
1787         if (isDerivedDepthTest() != newDDT) {
1788             NodeHelper.markDirty(this, DirtyBits.NODE_DEPTH_TEST);
1789             setDerivedDepthTest(newDDT);
1790         }
1791     }
1792 
1793     // This is the derived depthTest value to pass to PG level
1794     private boolean derivedDepthTest = true;
1795 
1796     void setDerivedDepthTest(boolean value) {
1797         derivedDepthTest = value;
1798     }
1799 
1800     boolean isDerivedDepthTest() {
1801         return derivedDepthTest;
1802     }
1803 
1804     public final void setDisable(boolean value) {
1805         disableProperty().set(value);
1806     }
1807 
1808     public final boolean isDisable() {
1809         return (miscProperties == null) ? DEFAULT_DISABLE
1810                                         : miscProperties.isDisable();
1811     }
1812 
1813     /**
1814      * Defines the individual disabled state of this {@code Node}. Setting
1815      * {@code disable} to true will cause this {@code Node} and any subnodes to
1816      * become disabled. This property should be used only to set the disabled
1817      * state of a {@code Node}.  For querying the disabled state of a
1818      * {@code Node}, the {@link #disabledProperty disabled} property should instead be used,
1819      * since it is possible that a {@code Node} was disabled as a result of an
1820      * ancestor being disabled even if the individual {@code disable} state on
1821      * this {@code Node} is {@code false}.
1822      *
1823      * @return the disabled state for this {@code Node}
1824      * @defaultValue false
1825      */
1826     public final BooleanProperty disableProperty() {
1827         return getMiscProperties().disableProperty();
1828     }
1829 
1830     /**
1831      * Indicates whether this {@code Node} is sensitive to CSS processing.<p>
1832      * Nodes with {@code cssSensitive} set to {@code false} and
1833      * their subtrees will be skipped during CSS processing.
1834      *
1835      * @return is this {@code Node} sensitive to CSS processing
1836      * @defaultValue true
1837      * @since 10
1838      */
1839     public final BooleanProperty cssSensitiveProperty() {
1840         return getMiscProperties().cssSensitiveProperty();
1841     }
1842 
1843     public final void setCssSensitive(boolean value) {
1844         cssSensitiveProperty().set(value);
1845     }
1846 
1847     public final boolean isCssSensitive() {
1848         return (miscProperties == null) ? true
1849                 : miscProperties.isCssSensitive();
1850     }
1851 
1852 //    /**
1853 //     * TODO document - null by default, could be non-null in subclasses (e.g. Control)
1854 //     */
1855 //    public final ObjectProperty<InputMap<?>> inputMapProperty() {
1856 //        if (inputMap == null) {
1857 //            inputMap = new SimpleObjectProperty<InputMap<?>>(this, "inputMap") {
1858 //                private InputMap<?> currentMap = get();
1859 //                @Override protected void invalidated() {
1860 //                    if (currentMap != null) {
1861 //                        currentMap.dispose();
1862 //                    }
1863 //                    currentMap = get();
1864 //                }
1865 //            };
1866 //        }
1867 //        return inputMap;
1868 //    }
1869 //    public final void setInputMap(InputMap<?> value) { inputMapProperty().set(value); }
1870 //    public final InputMap<?> getInputMap() { return inputMapProperty().getValue(); }
1871 //    private ObjectProperty<InputMap<?>> inputMap;
1872 
1873 
1874     /**************************************************************************
1875      *                                                                        *
1876      *
1877      *                                                                        *
1878      *************************************************************************/
1879     /**
1880      * Defines how the picking computation is done for this node when
1881      * triggered by a {@code MouseEvent} or a {@code contains} function call.
1882      *
1883      * If {@code pickOnBounds} is true, then picking is computed by
1884      * intersecting with the bounds of this node, else picking is computed
1885      * by intersecting with the geometric shape of this node.
1886      *
1887      * @defaultValue false
1888      */
1889     private BooleanProperty pickOnBounds;
1890 
1891     public final void setPickOnBounds(boolean value) {
1892         pickOnBoundsProperty().set(value);
1893     }
1894 
1895     public final boolean isPickOnBounds() {
1896         return pickOnBounds == null ? false : pickOnBounds.get();
1897     }
1898 
1899     public final BooleanProperty pickOnBoundsProperty() {
1900         if (pickOnBounds == null) {
1901             pickOnBounds = new SimpleBooleanProperty(this, "pickOnBounds");
1902         }
1903         return pickOnBounds;
1904     }
1905 
1906     /**
1907      * Indicates whether or not this {@code Node} is disabled.  A {@code Node}
1908      * will become disabled if {@link #disableProperty disable} is set to {@code true} on either
1909      * itself or one of its ancestors in the scene graph.
1910      * <p>
1911      * A disabled {@code Node} should render itself differently to indicate its
1912      * disabled state to the user.
1913      * Such disabled rendering is dependent on the implementation of the
1914      * {@code Node}. The shape classes contained in {@code javafx.scene.shape}
1915      * do not implement such rendering by default, therefore applications using
1916      * shapes for handling input must implement appropriate disabled rendering
1917      * themselves. The user-interface controls defined in
1918      * {@code javafx.scene.control} will implement disabled-sensitive rendering,
1919      * however.
1920      * <p>
1921      * A disabled {@code Node} does not receive mouse or key events.
1922      *
1923      * @defaultValue false
1924      */
1925     private ReadOnlyBooleanWrapper disabled;
1926 
1927     protected final void setDisabled(boolean value) {
1928         disabledPropertyImpl().set(value);
1929     }
1930 
1931     public final boolean isDisabled() {
1932         return disabled == null ? false : disabled.get();
1933     }
1934 
1935     public final ReadOnlyBooleanProperty disabledProperty() {
1936         return disabledPropertyImpl().getReadOnlyProperty();
1937     }
1938 
1939     private ReadOnlyBooleanWrapper disabledPropertyImpl() {
1940         if (disabled == null) {
1941             disabled = new ReadOnlyBooleanWrapper() {
1942 
1943                 @Override
1944                 protected void invalidated() {
1945                     pseudoClassStateChanged(DISABLED_PSEUDOCLASS_STATE, get());
1946                     updateCanReceiveFocus();
1947                     focusSetDirty(getScene());
1948                 }
1949 
1950                 @Override
1951                 public Object getBean() {
1952                     return Node.this;
1953                 }
1954 
1955                 @Override
1956                 public String getName() {
1957                     return "disabled";
1958                 }
1959             };
1960         }
1961         return disabled;
1962     }
1963 
1964     private void updateDisabled() {
1965         boolean isDisabled = isDisable();
1966         if (!isDisabled) {
1967             isDisabled = getParent() != null ? getParent().isDisabled() :
1968                     getSubScene() != null && getSubScene().isDisabled();
1969         }
1970         setDisabled(isDisabled);
1971         if (this instanceof SubScene) {
1972             ((SubScene)this).getRoot().setDisabled(isDisabled);
1973         }
1974     }
1975 
1976     /**
1977      * Finds this {@code Node}, or the first sub-node, based on the given CSS selector.
1978      * If this node is a {@code Parent}, then this function will traverse down
1979      * into the branch until it finds a match. If more than one sub-node matches the
1980      * specified selector, this function returns the first of them.
1981      * <p>
1982      *     For example, if a Node is given the id of "myId", then the lookup method can
1983      *     be used to find this node as follows: <code>scene.lookup("#myId");</code>.
1984      * </p>
1985      *
1986      * @param selector The css selector of the node to find
1987      * @return The first node, starting from this {@code Node}, which matches
1988      *         the CSS {@code selector}, null if none is found.
1989      */
1990     public Node lookup(String selector) {
1991         if (selector == null) return null;
1992         Selector s = Selector.createSelector(selector);
1993         return s != null && s.applies(this) ? this : null;
1994     }
1995 
1996     /**
1997      * Finds all {@code Node}s, including this one and any children, which match
1998      * the given CSS selector. If no matches are found, an empty unmodifiable set is
1999      * returned. The set is explicitly unordered.
2000      *
2001      * @param selector The css selector of the nodes to find
2002      * @return All nodes, starting from and including this {@code Node}, which match
2003      *         the CSS {@code selector}. The returned set is always unordered and
2004      *         unmodifiable, and never null.
2005      */
2006     public Set<Node> lookupAll(String selector) {
2007         final Selector s = Selector.createSelector(selector);
2008         final Set<Node> empty = Collections.emptySet();
2009         if (s == null) return empty;
2010         List<Node> results = lookupAll(s, null);
2011         return results == null ? empty : new UnmodifiableListSet<Node>(results);
2012     }
2013 
2014     /**
2015      * Used by Node and Parent for traversing the tree and adding all nodes which
2016      * match the given selector.
2017      *
2018      * @param selector The Selector. This will never be null.
2019      * @param results The results. This will never be null.
2020      */
2021     List<Node> lookupAll(Selector selector, List<Node> results) {
2022         if (selector.applies(this)) {
2023             // Lazily create the set to reduce some trash.
2024             if (results == null) {
2025                 results = new LinkedList<Node>();
2026             }
2027             results.add(this);
2028         }
2029         return results;
2030     }
2031 
2032     /**
2033      * Moves this {@code Node} to the back of its sibling nodes in terms of
2034      * z-order.  This is accomplished by moving this {@code Node} to the
2035      * first position in its parent's {@code content} ObservableList.
2036      * This function has no effect if this {@code Node} is not part of a group.
2037      */
2038     public void toBack() {
2039         if (getParent() != null) {
2040             getParent().toBack(this);
2041         }
2042     }
2043 
2044     /**
2045      * Moves this {@code Node} to the front of its sibling nodes in terms of
2046      * z-order.  This is accomplished by moving this {@code Node} to the
2047      * last position in its parent's {@code content} ObservableList.
2048      * This function has no effect if this {@code Node} is not part of a group.
2049      */
2050     public void toFront() {
2051         if (getParent() != null) {
2052             getParent().toFront(this);
2053         }
2054     }
2055 
2056     // TODO: need to verify whether this is OK to do starting from a node in
2057     // the scene graph other than the root.
2058     private void doCSSPass() {
2059         if (this.cssFlag != CssFlags.CLEAN) {
2060             // The dirty bit isn't checked but we must ensure it is cleared.
2061             // The cssFlag is set to clean in either Node.processCSS or
2062             // NodeHelper.processCSS
2063 
2064             // Don't clear the dirty bit in case it will cause problems
2065             // with a full CSS pass on the scene.
2066             // TODO: is this the right thing to do?
2067             // this.clearDirty(com.sun.javafx.scene.DirtyBits.NODE_CSS);
2068 
2069             this.processCSS();
2070         }
2071     }
2072 
2073     /**
2074      * Recursive function for synchronizing a node and all descendents
2075      */
2076     private static void syncAll(Node node) {
2077         node.syncPeer();
2078         if (node instanceof Parent) {
2079             Parent p = (Parent) node;
2080             final int childrenCount = p.getChildren().size();
2081 
2082             for (int i = 0; i < childrenCount; i++) {
2083                 Node n = p.getChildren().get(i);
2084                 if (n != null) {
2085                     syncAll(n);
2086                 }
2087             }
2088         }
2089         if (node.getClip() != null) {
2090             syncAll(node.getClip());
2091         }
2092     }
2093 
2094     private void doLayoutPass() {
2095         if (this instanceof Parent) {
2096             // TODO: As an optimization we only need to layout those dirty
2097             // roots that are descendents of this node
2098             Parent p = (Parent)this;
2099             for (int i = 0; i < 3; i++) {
2100                 p.layout();
2101             }
2102         }
2103     }
2104 
2105     private void doCSSLayoutSyncForSnapshot() {
2106         doCSSPass();
2107         doLayoutPass();
2108         updateBounds();
2109         Scene.setAllowPGAccess(true);
2110         syncAll(this);
2111         Scene.setAllowPGAccess(false);
2112     }
2113 
2114     private WritableImage doSnapshot(SnapshotParameters params, WritableImage img) {
2115         if (getScene() != null) {
2116             getScene().doCSSLayoutSyncForSnapshot(this);
2117         } else {
2118             doCSSLayoutSyncForSnapshot();
2119         }
2120 
2121         BaseTransform transform = BaseTransform.IDENTITY_TRANSFORM;
2122         if (params.getTransform() != null) {
2123             Affine3D tempTx = new Affine3D();
2124             TransformHelper.apply(params.getTransform(), tempTx);
2125             transform = tempTx;
2126         }
2127         double x;
2128         double y;
2129         double w;
2130         double h;
2131         Rectangle2D viewport = params.getViewport();
2132         if (viewport != null) {
2133             // Use the specified viewport
2134             x = viewport.getMinX();
2135             y = viewport.getMinY();
2136             w = viewport.getWidth();
2137             h = viewport.getHeight();
2138         } else {
2139             // Get the bounds in parent of this node, transformed by the
2140             // specified transform.
2141             BaseBounds tempBounds = TempState.getInstance().bounds;
2142             tempBounds = getTransformedBounds(tempBounds, transform);
2143             x = tempBounds.getMinX();
2144             y = tempBounds.getMinY();
2145             w = tempBounds.getWidth();
2146             h = tempBounds.getHeight();
2147         }
2148         WritableImage result = Scene.doSnapshot(getScene(), x, y, w, h,
2149                 this, transform, params.isDepthBufferInternal(),
2150                 params.getFill(), params.getEffectiveCamera(), img);
2151 
2152         return result;
2153     }
2154 
2155     /**
2156      * Takes a snapshot of this node and returns the rendered image when
2157      * it is ready.
2158      * CSS and layout processing will be done for the node, and any of its
2159      * children, prior to rendering it.
2160      * The entire destination image is cleared to the fill {@code Paint}
2161      * specified by the SnapshotParameters. This node is then rendered to
2162      * the image.
2163      * If the viewport specified by the SnapshotParameters is null, the
2164      * upper-left pixel of the {@code boundsInParent} of this
2165      * node, after first applying the transform specified by the
2166      * SnapshotParameters,
2167      * is mapped to the upper-left pixel (0,0) in the image.
2168      * If a non-null viewport is specified,
2169      * the upper-left pixel of the viewport is mapped to upper-left pixel
2170      * (0,0) in the image.
2171      * In both cases, this mapping to (0,0) of the image is done with an integer
2172      * translation. The portion of the node that is outside of the rendered
2173      * image will be clipped by the image.
2174      *
2175      * <p>
2176      * When taking a snapshot of a scene that is being animated, either
2177      * explicitly by the application or implicitly (such as chart animation),
2178      * the snapshot will be rendered based on the state of the scene graph at
2179      * the moment the snapshot is taken and will not reflect any subsequent
2180      * animation changes.
2181      * </p>
2182      *
2183      * <p>
2184      * NOTE: In order for CSS and layout to function correctly, the node
2185      * must be part of a Scene (the Scene may be attached to a Stage, but need
2186      * not be).
2187      * </p>
2188      *
2189      * @param params the snapshot parameters containing attributes that
2190      * will control the rendering. If the SnapshotParameters object is null,
2191      * then the Scene's attributes will be used if this node is part of a scene,
2192      * or default attributes will be used if this node is not part of a scene.
2193      *
2194      * @param image the writable image that will be used to hold the rendered node.
2195      * It may be null in which case a new WritableImage will be constructed.
2196      * The new image is constructed using integer width and
2197      * height values that are derived either from the transformed bounds of this
2198      * Node or from the size of the viewport as specified in the
2199      * SnapShotParameters. These integer values are chosen such that the image
2200      * will wholly contain the bounds of this Node or the specified viewport.
2201      * If the image is non-null, the node will be rendered into the
2202      * existing image.
2203      * In this case, the width and height of the image determine the area
2204      * that is rendered instead of the width and height of the bounds or
2205      * viewport.
2206      *
2207      * @throws IllegalStateException if this method is called on a thread
2208      *     other than the JavaFX Application Thread.
2209      *
2210      * @return the rendered image
2211      * @since JavaFX 2.2
2212      */
2213     public WritableImage snapshot(SnapshotParameters params, WritableImage image) {
2214         Toolkit.getToolkit().checkFxUserThread();
2215 
2216         if (params == null) {
2217             params = new SnapshotParameters();
2218             Scene s = getScene();
2219             if (s != null) {
2220                 params.setCamera(s.getEffectiveCamera());
2221                 params.setDepthBuffer(s.isDepthBufferInternal());
2222                 params.setFill(s.getFill());
2223             }
2224         }
2225 
2226         return doSnapshot(params, image);
2227     }
2228 
2229     /**
2230      * Takes a snapshot of this node at the next frame and calls the
2231      * specified callback method when the image is ready.
2232      * CSS and layout processing will be done for the node, and any of its
2233      * children, prior to rendering it.
2234      * The entire destination image is cleared to the fill {@code Paint}
2235      * specified by the SnapshotParameters. This node is then rendered to
2236      * the image.
2237      * If the viewport specified by the SnapshotParameters is null, the
2238      * upper-left pixel of the {@code boundsInParent} of this
2239      * node, after first applying the transform specified by the
2240      * SnapshotParameters,
2241      * is mapped to the upper-left pixel (0,0) in the image.
2242      * If a non-null viewport is specified,
2243      * the upper-left pixel of the viewport is mapped to upper-left pixel
2244      * (0,0) in the image.
2245      * In both cases, this mapping to (0,0) of the image is done with an integer
2246      * translation. The portion of the node that is outside of the rendered
2247      * image will be clipped by the image.
2248      *
2249      * <p>
2250      * This is an asynchronous call, which means that other
2251      * events or animation might be processed before the node is rendered.
2252      * If any such events modify the node, or any of its children, that
2253      * modification will be reflected in the rendered image (just like it
2254      * will also be reflected in the frame rendered to the Stage, if this node
2255      * is part of a live scene graph).
2256      * </p>
2257      *
2258      * <p>
2259      * When taking a snapshot of a node that is being animated, either
2260      * explicitly by the application or implicitly (such as chart animation),
2261      * the snapshot will be rendered based on the state of the scene graph at
2262      * the moment the snapshot is taken and will not reflect any subsequent
2263      * animation changes.
2264      * </p>
2265      *
2266      * <p>
2267      * NOTE: In order for CSS and layout to function correctly, the node
2268      * must be part of a Scene (the Scene may be attached to a Stage, but need
2269      * not be).
2270      * </p>
2271      *
2272      * @param callback a class whose call method will be called when the image
2273      * is ready. The SnapshotResult that is passed into the call method of
2274      * the callback will contain the rendered image, the source node
2275      * that was rendered, and a copy of the SnapshotParameters.
2276      * The callback parameter must not be null.
2277      *
2278      * @param params the snapshot parameters containing attributes that
2279      * will control the rendering. If the SnapshotParameters object is null,
2280      * then the Scene's attributes will be used if this node is part of a scene,
2281      * or default attributes will be used if this node is not part of a scene.
2282      *
2283      * @param image the writable image that will be used to hold the rendered node.
2284      * It may be null in which case a new WritableImage will be constructed.
2285      * The new image is constructed using integer width and
2286      * height values that are derived either from the transformed bounds of this
2287      * Node or from the size of the viewport as specified in the
2288      * SnapShotParameters. These integer values are chosen such that the image
2289      * will wholly contain the bounds of this Node or the specified viewport.
2290      * If the image is non-null, the node will be rendered into the
2291      * existing image.
2292      * In this case, the width and height of the image determine the area
2293      * that is rendered instead of the width and height of the bounds or
2294      * viewport.
2295      *
2296      * @throws IllegalStateException if this method is called on a thread
2297      *     other than the JavaFX Application Thread.
2298      *
2299      * @throws NullPointerException if the callback parameter is null.
2300      * @since JavaFX 2.2
2301      */
2302     public void snapshot(Callback<SnapshotResult, Void> callback,
2303             SnapshotParameters params, WritableImage image) {
2304 
2305         Toolkit.getToolkit().checkFxUserThread();
2306         if (callback == null) {
2307             throw new NullPointerException("The callback must not be null");
2308         }
2309 
2310         if (params == null) {
2311             params = new SnapshotParameters();
2312             Scene s = getScene();
2313             if (s != null) {
2314                 params.setCamera(s.getEffectiveCamera());
2315                 params.setDepthBuffer(s.isDepthBufferInternal());
2316                 params.setFill(s.getFill());
2317             }
2318         } else {
2319             params = params.copy();
2320         }
2321 
2322         final SnapshotParameters theParams = params;
2323         final Callback<SnapshotResult, Void> theCallback = callback;
2324         final WritableImage theImage = image;
2325 
2326         // Create a deferred runnable that will be run from a pulse listener
2327         // that is called after all of the scenes have been synced but before
2328         // any of them have been rendered.
2329         final Runnable snapshotRunnable = () -> {
2330             WritableImage img = doSnapshot(theParams, theImage);
2331             SnapshotResult result = new SnapshotResult(img, Node.this, theParams);
2332 //                System.err.println("Calling snapshot callback");
2333             try {
2334                 Void v = theCallback.call(result);
2335             } catch (Throwable th) {
2336                 System.err.println("Exception in snapshot callback");
2337                 th.printStackTrace(System.err);
2338             }
2339         };
2340 
2341 //        System.err.println("Schedule a snapshot in the future");
2342         Scene.addSnapshotRunnable(snapshotRunnable);
2343     }
2344 
2345     /* ************************************************************************
2346      *                                                                        *
2347      *
2348      *                                                                        *
2349      *************************************************************************/
2350 
2351     public final void setOnDragEntered(
2352             EventHandler<? super DragEvent> value) {
2353         onDragEnteredProperty().set(value);
2354     }
2355 
2356     public final EventHandler<? super DragEvent> getOnDragEntered() {
2357         return (eventHandlerProperties == null)
2358                 ? null : eventHandlerProperties.getOnDragEntered();
2359     }
2360 
2361     /**
2362      * Defines a function to be called when drag gesture
2363      * enters this {@code Node}.
2364      * @return the event handler that is called when drag gesture enters this
2365      * {@code Node}
2366      */
2367     public final ObjectProperty<EventHandler<? super DragEvent>>
2368             onDragEnteredProperty() {
2369         return getEventHandlerProperties().onDragEnteredProperty();
2370     }
2371 
2372     public final void setOnDragExited(
2373             EventHandler<? super DragEvent> value) {
2374         onDragExitedProperty().set(value);
2375     }
2376 
2377     public final EventHandler<? super DragEvent> getOnDragExited() {
2378         return (eventHandlerProperties == null)
2379                 ? null : eventHandlerProperties.getOnDragExited();
2380     }
2381 
2382     /**
2383      * Defines a function to be called when drag gesture
2384      * exits this {@code Node}.
2385      * @return the event handler that is called when drag gesture exits this
2386      * {@code Node}
2387      */
2388     public final ObjectProperty<EventHandler<? super DragEvent>>
2389             onDragExitedProperty() {
2390         return getEventHandlerProperties().onDragExitedProperty();
2391     }
2392 
2393     public final void setOnDragOver(
2394             EventHandler<? super DragEvent> value) {
2395         onDragOverProperty().set(value);
2396     }
2397 
2398     public final EventHandler<? super DragEvent> getOnDragOver() {
2399         return (eventHandlerProperties == null)
2400                 ? null : eventHandlerProperties.getOnDragOver();
2401     }
2402 
2403     /**
2404      * Defines a function to be called when drag gesture progresses within
2405      * this {@code Node}.
2406      * @return the event handler that is called when drag gesture progresses
2407      * within this {@code Node}
2408      */
2409     public final ObjectProperty<EventHandler<? super DragEvent>>
2410             onDragOverProperty() {
2411         return getEventHandlerProperties().onDragOverProperty();
2412     }
2413 
2414     // Do we want DRAG_TRANSFER_MODE_CHANGED event?
2415 //    public final void setOnDragTransferModeChanged(
2416 //            EventHandler<? super DragEvent> value) {
2417 //        onDragTransferModeChangedProperty().set(value);
2418 //    }
2419 //
2420 //    public final EventHandler<? super DragEvent> getOnDragTransferModeChanged() {
2421 //        return (eventHandlerProperties == null)
2422 //                ? null : eventHandlerProperties.getOnDragTransferModeChanged();
2423 //    }
2424 //
2425 //    /**
2426 //     * Defines a function to be called this {@code Node} if it is a potential
2427 //     * drag-and-drop target when the user takes action to change the intended
2428 //     * {@code TransferMode}.
2429 //     * The user can change the intended {@link TransferMode} by holding down
2430 //     * or releasing key modifiers.
2431 //     */
2432 //    public final ObjectProperty<EventHandler<? super DragEvent>>
2433 //            onDragTransferModeChangedProperty() {
2434 //        return getEventHandlerProperties().onDragTransferModeChangedProperty();
2435 //    }
2436 
2437     public final void setOnDragDropped(
2438             EventHandler<? super DragEvent> value) {
2439         onDragDroppedProperty().set(value);
2440     }
2441 
2442     public final EventHandler<? super DragEvent> getOnDragDropped() {
2443         return (eventHandlerProperties == null)
2444                 ? null : eventHandlerProperties.getOnDragDropped();
2445     }
2446 
2447     /**
2448      * Defines a function to be called when the mouse button is released
2449      * on this {@code Node} during drag and drop gesture. Transfer of data from
2450      * the {@link DragEvent}'s {@link DragEvent#getDragboard() dragboard} should
2451      * happen in this function.
2452      * @return the event handler that is called when the mouse button is
2453      * released on this {@code Node}
2454      */
2455     public final ObjectProperty<EventHandler<? super DragEvent>>
2456             onDragDroppedProperty() {
2457         return getEventHandlerProperties().onDragDroppedProperty();
2458     }
2459 
2460     public final void setOnDragDone(
2461             EventHandler<? super DragEvent> value) {
2462         onDragDoneProperty().set(value);
2463     }
2464 
2465     public final EventHandler<? super DragEvent> getOnDragDone() {
2466         return (eventHandlerProperties == null)
2467                 ? null : eventHandlerProperties.getOnDragDone();
2468     }
2469 
2470     /**
2471      * Defines a function to be called when this {@code Node} is a
2472      * drag and drop gesture source after its data has
2473      * been dropped on a drop target. The {@code transferMode} of the
2474      * event shows what just happened at the drop target.
2475      * If {@code transferMode} has the value {@code MOVE}, then the source can
2476      * clear out its data. Clearing the source's data gives the appropriate
2477      * appearance to a user that the data has been moved by the drag and drop
2478      * gesture. A {@code transferMode} that has the value {@code NONE}
2479      * indicates that no data was transferred during the drag and drop gesture.
2480      * @return the event handler that is called when this {@code Node} is a drag
2481      * and drop gesture source after its data has been dropped on a drop target
2482      */
2483     public final ObjectProperty<EventHandler<? super DragEvent>>
2484             onDragDoneProperty() {
2485         return getEventHandlerProperties().onDragDoneProperty();
2486     }
2487 
2488     /**
2489      * Confirms a potential drag and drop gesture that is recognized over this
2490      * {@code Node}.
2491      * Can be called only from a DRAG_DETECTED event handler. The returned
2492      * {@link Dragboard} is used to transfer data during
2493      * the drag and drop gesture. Placing this {@code Node}'s data on the
2494      * {@link Dragboard} also identifies this {@code Node} as the source of
2495      * the drag and drop gesture.
2496      * More detail about drag and drop gestures is described in the overivew
2497      * of {@link DragEvent}.
2498      *
2499      * @see DragEvent
2500      * @param transferModes The supported {@code TransferMode}(s) of this {@code Node}
2501      * @return A {@code Dragboard} to place this {@code Node}'s data on
2502      * @throws IllegalStateException if drag and drop cannot be started at this
2503      * moment (it's called outside of {@code DRAG_DETECTED} event handling or
2504      * this node is not in scene).
2505      */
2506     public Dragboard startDragAndDrop(TransferMode... transferModes) {
2507         if (getScene() != null) {
2508             return getScene().startDragAndDrop(this, transferModes);
2509         }
2510 
2511         throw new IllegalStateException("Cannot start drag and drop on node "
2512                 + "that is not in scene");
2513     }
2514 
2515     /**
2516      * Starts a full press-drag-release gesture with this node as gesture
2517      * source. This method can be called only from a {@code DRAG_DETECTED} mouse
2518      * event handler. More detail about dragging gestures can be found
2519      * in the overview of {@link MouseEvent} and {@link MouseDragEvent}.
2520      *
2521      * @see MouseEvent
2522      * @see MouseDragEvent
2523      * @throws IllegalStateException if the full press-drag-release gesture
2524      * cannot be started at this moment (it's called outside of
2525      * {@code DRAG_DETECTED} event handling or this node is not in scene).
2526      * @since JavaFX 2.1
2527      */
2528     public void startFullDrag() {
2529         if (getScene() != null) {
2530             getScene().startFullDrag(this);
2531             return;
2532         }
2533 
2534         throw new IllegalStateException("Cannot start full drag on node "
2535                 + "that is not in scene");
2536     }
2537 
2538     ////////////////////////////
2539     //  Private Implementation
2540     ////////////////////////////
2541 
2542     /**
2543      * If this Node is being used as the clip of another Node, that other node
2544      * is referred to as the clipParent. If the boundsInParent of this Node
2545      * changes, it must update the clipParent's bounds as well.
2546      */
2547     private Node clipParent;
2548     // Use a getter function instead of giving clipParent package access,
2549     // so that clipParent doesn't get turned into a Location.
2550     final Node getClipParent() {
2551         return clipParent;
2552     }
2553 
2554     /**
2555      * Determines whether this node is connected anywhere in the scene graph.
2556      */
2557     boolean isConnected() {
2558         // don't need to check scene, because if scene is non-null
2559         // parent must also be non-null
2560         return getParent() != null || clipParent != null;
2561     }
2562 
2563     /**
2564      * Tests whether creating a parent-child relationship between these
2565      * nodes would cause a cycle. The parent relationship includes not only
2566      * the "real" parent (child of Group) but also the clipParent.
2567      */
2568     boolean wouldCreateCycle(Node parent, Node child) {
2569         if (child != null && child.getClip() == null && (!(child instanceof Parent))) {
2570             return false;
2571     }
2572 
2573         Node n = parent;
2574         while (n != child) {
2575             if (n.getParent() != null) {
2576                 n = n.getParent();
2577             } else if (n.getSubScene() != null) {
2578                 n = n.getSubScene();
2579             } else if (n.clipParent != null) {
2580                 n = n.clipParent;
2581             } else {
2582                 return false;
2583             }
2584         }
2585         return true;
2586     }
2587 
2588     /**
2589      * The peer node created by the graphics Toolkit/Pipeline implementation
2590      */
2591     private NGNode peer;
2592 
2593     @SuppressWarnings("CallToPrintStackTrace")
2594     <P extends NGNode> P getPeer() {
2595         if (Utils.assertionEnabled()) {
2596             // Assertion checking code
2597             if (getScene() != null && !Scene.isPGAccessAllowed()) {
2598                 java.lang.System.err.println();
2599                 java.lang.System.err.println("*** unexpected PG access");
2600                 java.lang.Thread.dumpStack();
2601             }
2602         }
2603 
2604         if (peer == null) {
2605             //if (PerformanceTracker.isLoggingEnabled()) {
2606             //    PerformanceTracker.logEvent("Creating NGNode for [{this}, id=\"{id}\"]");
2607             //}
2608             peer = NodeHelper.createPeer(this);
2609             //if (PerformanceTracker.isLoggingEnabled()) {
2610             //    PerformanceTracker.logEvent("NGNode created");
2611             //}
2612         }
2613         return (P) peer;
2614     }
2615 
2616     /***************************************************************************
2617      *                                                                         *
2618      *                              Initialization                             *
2619      *                                                                         *
2620      *  To Note limit the number of bounds computations and improve startup    *
2621      *  performance.                                                           *
2622      *                                                                         *
2623      **************************************************************************/
2624 
2625     /**
2626      * Creates a new instance of Node.
2627      */
2628     protected Node() {
2629         //if (PerformanceTracker.isLoggingEnabled()) {
2630         //    PerformanceTracker.logEvent("Node.init for [{this}, id=\"{id}\"]");
2631         //}
2632         setDirty();
2633         updateTreeVisible(false);
2634         //if (PerformanceTracker.isLoggingEnabled()) {
2635         //    PerformanceTracker.logEvent("Node.postinit " +
2636         //                                "for [{this}, id=\"{id}\"] finished");
2637         //}
2638     }
2639 
2640     /***************************************************************************
2641      *                                                                         *
2642      * Layout related APIs.                                                    *
2643      *                                                                         *
2644      **************************************************************************/
2645     /**
2646      * Defines whether or not this node's layout will be managed by it's parent.
2647      * If the node is managed, it's parent will factor the node's geometry
2648      * into its own preferred size and {@link #layoutBoundsProperty layoutBounds}
2649      * calculations and will lay it
2650      * out during the scene's layout pass.  If a managed node's layoutBounds
2651      * changes, it will automatically trigger relayout up the scene-graph
2652      * to the nearest layout root (which is typically the scene's root node).
2653      * <p>
2654      * If the node is unmanaged, its parent will ignore the child in both preferred
2655      * size computations and layout.   Changes in layoutBounds will not trigger
2656      * relayout above it.   If an unmanaged node is of type {@link javafx.scene.Parent Parent},
2657      * it will act as a "layout root", meaning that calls to {@link Parent#requestLayout()}
2658      * beneath it will cause only the branch rooted by the node to be relayed out,
2659      * thereby isolating layout changes to that root and below.  It's the application's
2660      * responsibility to set the size and position of an unmanaged node.
2661      * <p>
2662      * By default all nodes are managed.
2663      * </p>
2664      *
2665      * @see #isResizable()
2666      * @see #layoutBoundsProperty()
2667      * @see Parent#requestLayout()
2668      *
2669      */
2670     private BooleanProperty managed;
2671 
2672     public final void setManaged(boolean value) {
2673         managedProperty().set(value);
2674     }
2675 
2676     public final boolean isManaged() {
2677         return managed == null ? true : managed.get();
2678     }
2679 
2680     public final BooleanProperty managedProperty() {
2681         if (managed == null) {
2682             managed = new BooleanPropertyBase(true) {
2683 
2684                 @Override
2685                 protected void invalidated() {
2686                     final Parent parent = getParent();
2687                     if (parent != null) {
2688                         parent.managedChildChanged();
2689                     }
2690                     notifyManagedChanged();
2691                 }
2692 
2693                 @Override
2694                 public Object getBean() {
2695                     return Node.this;
2696                 }
2697 
2698                 @Override
2699                 public String getName() {
2700                     return "managed";
2701                 }
2702 
2703             };
2704         }
2705         return managed;
2706     }
2707 
2708     /**
2709      * Called whenever the "managed" flag has changed. This is only
2710      * used by Parent as an optimization to keep track of whether a
2711      * Parent node is a layout root or not.
2712      */
2713     void notifyManagedChanged() { }
2714 
2715     /**
2716      * Defines the x coordinate of the translation that is added to this {@code Node}'s
2717      * transform for the purpose of layout.  The value should be computed as the
2718      * offset required to adjust the position of the node from its current
2719      * {@link #layoutBoundsProperty() layoutBounds minX} position (which might not be 0) to the desired location.
2720      *
2721      * <p>For example, if {@code textnode} should be positioned at {@code finalX}
2722      * <pre>{@code
2723      *     textnode.setLayoutX(finalX - textnode.getLayoutBounds().getMinX());
2724      * }</pre>
2725      * <p>
2726      * Failure to subtract {@code layoutBounds minX} may result in misplacement
2727      * of the node.  The {@link #relocate(double, double) relocate(x, y)} method will automatically do the
2728      * correct computation and should generally be used over setting layoutX directly.
2729      * <p>
2730      * The node's final translation will be computed as {@code layoutX} + {@link #translateXProperty translateX},
2731      * where {@code layoutX} establishes the node's stable position
2732      * and {@code translateX} optionally makes dynamic adjustments to that
2733      * position.
2734      * <p>
2735      * If the node is managed and has a {@link javafx.scene.layout.Region}
2736      * as its parent, then the layout region will set {@code layoutX} according to its
2737      * own layout policy.   If the node is unmanaged or parented by a {@link Group},
2738      * then the application may set {@code layoutX} directly to position it.
2739      *
2740      * @see #relocate(double, double)
2741      * @see #layoutBoundsProperty()
2742      *
2743      */
2744     private DoubleProperty layoutX;
2745 
2746     public final void setLayoutX(double value) {
2747         layoutXProperty().set(value);
2748     }
2749 
2750     public final double getLayoutX() {
2751         return layoutX == null ? 0.0 : layoutX.get();
2752     }
2753 
2754     public final DoubleProperty layoutXProperty() {
2755         if (layoutX == null) {
2756             layoutX = new DoublePropertyBase(0.0) {
2757 
2758                 @Override
2759                 protected void invalidated() {
2760                     NodeHelper.transformsChanged(Node.this);
2761                     final Parent p = getParent();
2762 
2763                     // Propagate layout if this change isn't triggered by its parent
2764                     if (p != null && !p.isCurrentLayoutChild(Node.this)) {
2765                         if (isManaged()) {
2766                             // Force its parent to fix the layout since it is a managed child.
2767                             p.requestLayout(true);
2768                         } else {
2769                             // Parent size changed, parent's parent might need to re-layout
2770                             p.clearSizeCache();
2771                             p.requestParentLayout();
2772                         }
2773                     }
2774                 }
2775 
2776                 @Override
2777                 public Object getBean() {
2778                     return Node.this;
2779                 }
2780 
2781                 @Override
2782                 public String getName() {
2783                     return "layoutX";
2784                 }
2785             };
2786         }
2787         return layoutX;
2788     }
2789 
2790     /**
2791      * Defines the y coordinate of the translation that is added to this {@code Node}'s
2792      * transform for the purpose of layout.  The value should be computed as the
2793      * offset required to adjust the position of the node from its current
2794      * {@link #layoutBoundsProperty() layoutBounds minY} position (which might not be 0) to the desired location.
2795      *
2796      * <p>For example, if {@code textnode} should be positioned at {@code finalY}
2797      * <pre>{@code
2798      *     textnode.setLayoutY(finalY - textnode.getLayoutBounds().getMinY());
2799      * }</pre>
2800      * <p>
2801      * Failure to subtract {@code layoutBounds minY} may result in misplacement
2802      * of the node.  The {@link #relocate(double, double) relocate(x, y)} method will automatically do the
2803      * correct computation and should generally be used over setting layoutY directly.
2804      * <p>
2805      * The node's final translation will be computed as {@code layoutY} + {@link #translateYProperty translateY},
2806      * where {@code layoutY} establishes the node's stable position
2807      * and {@code translateY} optionally makes dynamic adjustments to that
2808      * position.
2809      * <p>
2810      * If the node is managed and has a {@link javafx.scene.layout.Region}
2811      * as its parent, then the region will set {@code layoutY} according to its
2812      * own layout policy.   If the node is unmanaged or parented by a {@link Group},
2813      * then the application may set {@code layoutY} directly to position it.
2814      *
2815      * @see #relocate(double, double)
2816      * @see #layoutBoundsProperty()
2817      */
2818     private DoubleProperty layoutY;
2819 
2820     public final void setLayoutY(double value) {
2821         layoutYProperty().set(value);
2822     }
2823 
2824     public final double getLayoutY() {
2825         return layoutY == null ? 0.0 : layoutY.get();
2826     }
2827 
2828     public final DoubleProperty layoutYProperty() {
2829         if (layoutY == null) {
2830             layoutY = new DoublePropertyBase(0.0) {
2831 
2832                 @Override
2833                 protected void invalidated() {
2834                     NodeHelper.transformsChanged(Node.this);
2835                     final Parent p = getParent();
2836 
2837                     // Propagate layout if this change isn't triggered by its parent
2838                     if (p != null && !p.isCurrentLayoutChild(Node.this)) {
2839                         if (isManaged()) {
2840                             // Force its parent to fix the layout since it is a managed child.
2841                             p.requestLayout(true);
2842                         } else {
2843                             // Parent size changed, parent's parent might need to re-layout
2844                             p.clearSizeCache();
2845                             p.requestParentLayout();
2846                         }
2847                     }
2848                 }
2849 
2850                 @Override
2851                 public Object getBean() {
2852                     return Node.this;
2853                 }
2854 
2855                 @Override
2856                 public String getName() {
2857                     return "layoutY";
2858                 }
2859 
2860             };
2861         }
2862         return layoutY;
2863     }
2864 
2865     /**
2866      * Sets the node's layoutX and layoutY translation properties in order to
2867      * relocate this node to the x,y location in the parent.
2868      * <p>
2869      * This method does not alter translateX or translateY, which if also set
2870      * will be added to layoutX and layoutY, adjusting the final location by
2871      * corresponding amounts.
2872      *
2873      * @param x the target x coordinate location
2874      * @param y the target y coordinate location
2875      */
2876     public void relocate(double x, double y) {
2877         setLayoutX(x - getLayoutBounds().getMinX());
2878         setLayoutY(y - getLayoutBounds().getMinY());
2879 
2880         PlatformLogger logger = Logging.getLayoutLogger();
2881         if (logger.isLoggable(Level.FINER)) {
2882             logger.finer(this.toString()+" moved to ("+x+","+y+")");
2883         }
2884     }
2885 
2886     /**
2887      * Indicates whether this node is a type which can be resized by its parent.
2888      * If this method returns true, then the parent will resize the node (ideally
2889      * within its size range) by calling node.resize(width,height) during the
2890      * layout pass.  All Regions, Controls, and WebView are resizable classes
2891      * which depend on their parents resizing them during layout once all sizing
2892      * and CSS styling information has been applied.
2893      * <p>
2894      * If this method returns false, then the parent cannot resize it during
2895      * layout (resize() is a no-op) and it should return its layoutBounds for
2896      * minimum, preferred, and maximum sizes.  Group, Text, and all Shapes are not
2897      * resizable and hence depend on the application to establish their sizing
2898      * by setting appropriate properties (e.g.  width/height for Rectangle,
2899      * text on Text, and so on).  Non-resizable nodes may still be relocated
2900      * during layout.
2901      *
2902      * @see #getContentBias()
2903      * @see #minWidth(double)
2904      * @see #minHeight(double)
2905      * @see #prefWidth(double)
2906      * @see #prefHeight(double)
2907      * @see #maxWidth(double)
2908      * @see #maxHeight(double)
2909      * @see #resize(double, double)
2910      * @see #getLayoutBounds()
2911      *
2912      * @return whether or not this node type can be resized by its parent during layout
2913      */
2914     public boolean isResizable() {
2915         return false;
2916     }
2917 
2918     /**
2919      * Returns the orientation of a node's resizing bias for layout purposes.
2920      * If the node type has no bias, returns null.  If the node is resizable and
2921      * it's height depends on its width, returns HORIZONTAL, else if its width
2922      * depends on its height, returns VERTICAL.
2923      * <p>
2924      * Resizable subclasses should override this method to return an
2925      * appropriate value.
2926      *
2927      * @see #isResizable()
2928      * @see #minWidth(double)
2929      * @see #minHeight(double)
2930      * @see #prefWidth(double)
2931      * @see #prefHeight(double)
2932      * @see #maxWidth(double)
2933      * @see #maxHeight(double)
2934      *
2935      * @return orientation of width/height dependency or null if there is none
2936      */
2937     public Orientation getContentBias() {
2938         return null;
2939     }
2940 
2941     /**
2942      * Returns the node's minimum width for use in layout calculations.
2943      * If the node is resizable, its parent should not resize its width any
2944      * smaller than this value.  If the node is not resizable, returns its
2945      * layoutBounds width.
2946      * <p>
2947      * Layout code which calls this method should first check the content-bias
2948      * of the node.  If the node has a vertical content-bias, then callers
2949      * should pass in a height value that the minimum width should be based on.
2950      * If the node has either a horizontal or null content-bias, then the caller
2951      * should pass in -1.
2952      * <p>
2953      * Node subclasses with a vertical content-bias should honor the height
2954      * parameter whether -1 or a positive value.   All other subclasses may ignore
2955      * the height parameter (which will likely be -1).
2956      * <p>
2957      * If Node's {@link #maxWidth(double)} is lower than this number,
2958      * {@code minWidth} takes precedence. This means the Node should never be resized below {@code minWidth}.
2959      * <p>
2960      * @see #isResizable()
2961      * @see #getContentBias()
2962      *
2963      * @param height the height that should be used if minimum width depends on it
2964      * @return the minimum width that the node should be resized to during layout.
2965      *         The result will never be NaN, nor will it ever be negative.
2966      */
2967     public double minWidth(double height) {
2968         return prefWidth(height);
2969     }
2970 
2971     /**
2972      * Returns the node's minimum height for use in layout calculations.
2973      * If the node is resizable, its parent should not resize its height any
2974      * smaller than this value.  If the node is not resizable, returns its
2975      * layoutBounds height.
2976      * <p>
2977      * Layout code which calls this method should first check the content-bias
2978      * of the node.  If the node has a horizontal content-bias, then callers
2979      * should pass in a width value that the minimum height should be based on.
2980      * If the node has either a vertical or null content-bias, then the caller
2981      * should pass in -1.
2982      * <p>
2983      * Node subclasses with a horizontal content-bias should honor the width
2984      * parameter whether -1 or a positive value.   All other subclasses may ignore
2985      * the width parameter (which will likely be -1).
2986      * <p>
2987      * If Node's {@link #maxHeight(double)} is lower than this number,
2988      * {@code minHeight} takes precedence. This means the Node should never be resized below {@code minHeight}.
2989      * <p>
2990      * @see #isResizable()
2991      * @see #getContentBias()
2992      *
2993      * @param width the width that should be used if minimum height depends on it
2994      * @return the minimum height that the node should be resized to during layout
2995      *         The result will never be NaN, nor will it ever be negative.
2996      */
2997     public double minHeight(double width) {
2998         return prefHeight(width);
2999     }
3000 
3001     /**
3002      * Returns the node's preferred width for use in layout calculations.
3003      * If the node is resizable, its parent should treat this value as the
3004      * node's ideal width within its range.  If the node is not resizable,
3005      * just returns its layoutBounds width, which should be treated as the rigid
3006      * width of the node.
3007      * <p>
3008      * Layout code which calls this method should first check the content-bias
3009      * of the node.  If the node has a vertical content-bias, then callers
3010      * should pass in a height value that the preferred width should be based on.
3011      * If the node has either a horizontal or null content-bias, then the caller
3012      * should pass in -1.
3013      * <p>
3014      * Node subclasses with a vertical content-bias should honor the height
3015      * parameter whether -1 or a positive value.   All other subclasses may ignore
3016      * the height parameter (which will likely be -1).
3017      * <p>
3018      * @see #isResizable()
3019      * @see #getContentBias()
3020      * @see #autosize()
3021      *
3022      * @param height the height that should be used if preferred width depends on it
3023      * @return the preferred width that the node should be resized to during layout
3024      *         The result will never be NaN, nor will it ever be negative.
3025      */
3026     public double prefWidth(double height) {
3027         final double result = getLayoutBounds().getWidth();
3028         return Double.isNaN(result) || result < 0 ? 0 : result;
3029     }
3030 
3031     /**
3032      * Returns the node's preferred height for use in layout calculations.
3033      * If the node is resizable, its parent should treat this value as the
3034      * node's ideal height within its range.  If the node is not resizable,
3035      * just returns its layoutBounds height, which should be treated as the rigid
3036      * height of the node.
3037      * <p>
3038      * Layout code which calls this method should first check the content-bias
3039      * of the node.  If the node has a horizontal content-bias, then callers
3040      * should pass in a width value that the preferred height should be based on.
3041      * If the node has either a vertical or null content-bias, then the caller
3042      * should pass in -1.
3043      * <p>
3044      * Node subclasses with a horizontal content-bias should honor the height
3045      * parameter whether -1 or a positive value.   All other subclasses may ignore
3046      * the height parameter (which will likely be -1).
3047      * <p>
3048      * @see #getContentBias()
3049      * @see #autosize()
3050      *
3051      * @param width the width that should be used if preferred height depends on it
3052      * @return the preferred height that the node should be resized to during layout
3053      *         The result will never be NaN, nor will it ever be negative.
3054      */
3055     public double prefHeight(double width) {
3056         final double result = getLayoutBounds().getHeight();
3057         return Double.isNaN(result) || result < 0 ? 0 : result;
3058     }
3059 
3060     /**
3061      * Returns the node's maximum width for use in layout calculations.
3062      * If the node is resizable, its parent should not resize its width any
3063      * larger than this value.  A value of Double.MAX_VALUE indicates the
3064      * parent may expand the node's width beyond its preferred without limits.
3065      * <p>
3066      * If the node is not resizable, returns its layoutBounds width.
3067      * <p>
3068      * Layout code which calls this method should first check the content-bias
3069      * of the node.  If the node has a vertical content-bias, then callers
3070      * should pass in a height value that the maximum width should be based on.
3071      * If the node has either a horizontal or null content-bias, then the caller
3072      * should pass in -1.
3073      * <p>
3074      * Node subclasses with a vertical content-bias should honor the height
3075      * parameter whether -1 or a positive value.   All other subclasses may ignore
3076      * the height parameter (which will likely be -1).
3077      * <p>
3078      * If Node's {@link #minWidth(double)} is greater, it should take precedence
3079      * over the {@code maxWidth}. This means the Node should never be resized below {@code minWidth}.
3080      * <p>
3081      * @see #isResizable()
3082      * @see #getContentBias()
3083      *
3084      * @param height the height that should be used if maximum width depends on it
3085      * @return the maximum width that the node should be resized to during layout
3086      *         The result will never be NaN, nor will it ever be negative.
3087      */
3088     public double maxWidth(double height) {
3089         return prefWidth(height);
3090     }
3091 
3092     /**
3093      * Returns the node's maximum height for use in layout calculations.
3094      * If the node is resizable, its parent should not resize its height any
3095      * larger than this value.  A value of Double.MAX_VALUE indicates the
3096      * parent may expand the node's height beyond its preferred without limits.
3097      * <p>
3098      * If the node is not resizable, returns its layoutBounds height.
3099      * <p>
3100      * Layout code which calls this method should first check the content-bias
3101      * of the node.  If the node has a horizontal content-bias, then callers
3102      * should pass in a width value that the maximum height should be based on.
3103      * If the node has either a vertical or null content-bias, then the caller
3104      * should pass in -1.
3105      * <p>
3106      * Node subclasses with a horizontal content-bias should honor the width
3107      * parameter whether -1 or a positive value.   All other subclasses may ignore
3108      * the width parameter (which will likely be -1).
3109      * <p>
3110      * If Node's {@link #minHeight(double)} is greater, it should take precedence
3111      * over the {@code maxHeight}.  This means the Node should never be resized below {@code minHeight}.
3112      * <p>
3113      * @see #isResizable()
3114      * @see #getContentBias()
3115      *
3116      * @param width the width that should be used if maximum height depends on it
3117      * @return the maximum height that the node should be resized to during layout
3118      *         The result will never be NaN, nor will it ever be negative.
3119      */
3120     public double maxHeight(double width) {
3121         return prefHeight(width);
3122     }
3123 
3124     /**
3125      * If the node is resizable, will set its layout bounds to the specified
3126      * width and height.   If the node is not resizable, this method is a no-op.
3127      * <p>
3128      * This method should generally only be called by parent nodes from their
3129      * layoutChildren() methods.   All Parent classes will automatically resize
3130      * resizable children, so resizing done directly by the application will be
3131      * overridden by the node's parent, unless the child is unmanaged.
3132      * <p>
3133      * Parents are responsible for ensuring the width and height values fall
3134      * within the resizable node's preferred range.  The autosize() method may
3135      * be used if the parent just needs to resize the node to its preferred size.
3136      *
3137      * <p>
3138      * @see #isResizable()
3139      * @see #getContentBias()
3140      * @see #autosize()
3141      * @see #minWidth(double)
3142      * @see #minHeight(double)
3143      * @see #prefWidth(double)
3144      * @see #prefHeight(double)
3145      * @see #maxWidth(double)
3146      * @see #maxHeight(double)
3147      * @see #getLayoutBounds()
3148      *
3149      * @param width the target layout bounds width
3150      * @param height the target layout bounds height
3151      */
3152     public void resize(double width, double height) {
3153     }
3154 
3155     /**
3156      * If the node is resizable, will set its layout bounds to its current preferred
3157      * width and height. If the node is not resizable, this method is a no-op.
3158      * <p>
3159      * This method automatically queries the node's content-bias and if it's
3160      * horizontal, will pass in the node's preferred width to get the preferred
3161      * height; if vertical, will pass in the node's preferred height to get the width,
3162      * and if null, will compute the preferred width/height independently.
3163      * </p>
3164      *
3165      * @see #isResizable()
3166      * @see #getContentBias()
3167      *
3168      */
3169     public final void autosize() {
3170         if (isResizable()) {
3171             Orientation contentBias = getContentBias();
3172             double w, h;
3173             if (contentBias == null) {
3174                 w = boundedSize(prefWidth(-1), minWidth(-1), maxWidth(-1));
3175                 h = boundedSize(prefHeight(-1), minHeight(-1), maxHeight(-1));
3176             } else if (contentBias == Orientation.HORIZONTAL) {
3177                 w = boundedSize(prefWidth(-1), minWidth(-1), maxWidth(-1));
3178                 h = boundedSize(prefHeight(w), minHeight(w), maxHeight(w));
3179             } else { // bias == VERTICAL
3180                 h = boundedSize(prefHeight(-1), minHeight(-1), maxHeight(-1));
3181                 w = boundedSize(prefWidth(h), minWidth(h), maxWidth(h));
3182             }
3183             resize(w,h);
3184         }
3185     }
3186 
3187     double boundedSize(double value, double min, double max) {
3188         // if max < value, return max
3189         // if min > value, return min
3190         // if min > max, return min
3191         return Math.min(Math.max(value, min), Math.max(min,max));
3192     }
3193 
3194     /**
3195      * If the node is resizable, will set its layout bounds to the specified
3196      * width and height.   If the node is not resizable, the resize step is skipped.
3197      * <p>
3198      * Once the node has been resized (if resizable) then sets the node's layoutX
3199      * and layoutY translation properties in order to relocate it to x,y in the
3200      * parent's coordinate space.
3201      * <p>
3202      * This method should generally only be called by parent nodes from their
3203      * layoutChildren() methods.   All Parent classes will automatically resize
3204      * resizable children, so resizing done directly by the application will be
3205      * overridden by the node's parent, unless the child is unmanaged.
3206      * <p>
3207      * Parents are responsible for ensuring the width and height values fall
3208      * within the resizable node's preferred range.  The autosize() and relocate()
3209      * methods may be used if the parent just needs to resize the node to its
3210      * preferred size and reposition it.
3211      * <p>
3212      * @see #isResizable()
3213      * @see #getContentBias()
3214      * @see #autosize()
3215      * @see #minWidth(double)
3216      * @see #minHeight(double)
3217      * @see #prefWidth(double)
3218      * @see #prefHeight(double)
3219      * @see #maxWidth(double)
3220      * @see #maxHeight(double)
3221      *
3222      * @param x the target x coordinate location
3223      * @param y the target y coordinate location
3224      * @param width the target layout bounds width
3225      * @param height the target layout bounds height
3226      *
3227      */
3228     public void resizeRelocate(double x, double y, double width, double height) {
3229         resize(width, height);
3230         relocate(x,y);
3231     }
3232 
3233     /**
3234      * This is a special value that might be returned by {@link #getBaselineOffset()}.
3235      * This means that the Parent (layout Pane) of this Node should use the height of this Node as a baseline.
3236      */
3237     public static final double BASELINE_OFFSET_SAME_AS_HEIGHT = Double.NEGATIVE_INFINITY;
3238 
3239     /**
3240      * The 'alphabetic' (or 'roman') baseline offset from the node's layoutBounds.minY location
3241      * that should be used when this node is being vertically aligned by baseline with
3242      * other nodes.  By default this returns {@link #BASELINE_OFFSET_SAME_AS_HEIGHT} for resizable Nodes
3243      * and layoutBounds height for non-resizable.  Subclasses
3244      * which contain text should override this method to return their actual text baseline offset.
3245      *
3246      * @return offset of text baseline from layoutBounds.minY for non-resizable Nodes or {@link #BASELINE_OFFSET_SAME_AS_HEIGHT} otherwise
3247      */
3248     public double getBaselineOffset() {
3249         if (isResizable()) {
3250             return BASELINE_OFFSET_SAME_AS_HEIGHT;
3251         } else {
3252             return getLayoutBounds().getHeight();
3253         }
3254     }
3255 
3256     /**
3257      * Returns the area of this {@code Node} projected onto the
3258      * physical screen in pixel units.
3259      * @return the area of this {@code Node} projected onto the physical screen
3260      * @since JavaFX 8.0
3261      */
3262     public double computeAreaInScreen() {
3263         return doComputeAreaInScreen();
3264     }
3265 
3266     /*
3267      * Help application or utility to implement LOD support by returning the
3268      * projected area of a Node in pixel unit. The projected area is not clipped.
3269      *
3270      * For perspective camera, this method first exams node's bounds against
3271      * camera's clipping plane to cut off those out of viewing frustrum. After
3272      * computing areaInScreen, it applys a tight viewing frustrum check using
3273      * canonical view volume.
3274      *
3275      * The result of areaInScreen comes from the product of
3276      * (projViewTx x localToSceneTransform x localBounds).
3277      *
3278      * Returns 0 for those fall outside viewing frustrum.
3279      */
3280     private double doComputeAreaInScreen() {
3281         Scene tmpScene = getScene();
3282         if (tmpScene != null) {
3283             Bounds bounds = getBoundsInLocal();
3284             Camera camera = tmpScene.getEffectiveCamera();
3285             boolean isPerspective = camera instanceof PerspectiveCamera ? true : false;
3286             Transform localToSceneTx = getLocalToSceneTransform();
3287             Affine3D tempTx = TempState.getInstance().tempTx;
3288             BaseBounds localBounds = new BoxBounds((float) bounds.getMinX(),
3289                                                    (float) bounds.getMinY(),
3290                                                    (float) bounds.getMinZ(),
3291                                                    (float) bounds.getMaxX(),
3292                                                    (float) bounds.getMaxY(),
3293                                                    (float) bounds.getMaxZ());
3294 
3295             // NOTE: Viewing frustrum check on camera's clipping plane is now only
3296             // for perspective camera.
3297             // TODO: Need to hook up parallel camera's nearClip and farClip.
3298             if (isPerspective) {
3299                 Transform cameraL2STx = camera.getLocalToSceneTransform();
3300 
3301                 // If camera transform only contains translate, compare in scene
3302                 // coordinate. Otherwise, compare in camera coordinate.
3303                 if (cameraL2STx.getMxx() == 1.0
3304                         && cameraL2STx.getMxy() == 0.0
3305                         && cameraL2STx.getMxz() == 0.0
3306                         && cameraL2STx.getMyx() == 0.0
3307                         && cameraL2STx.getMyy() == 1.0
3308                         && cameraL2STx.getMyz() == 0.0
3309                         && cameraL2STx.getMzx() == 0.0
3310                         && cameraL2STx.getMzy() == 0.0
3311                         && cameraL2STx.getMzz() == 1.0) {
3312 
3313                     double minZ, maxZ;
3314 
3315                     // If node transform only contains translate, only convert
3316                     // minZ and maxZ to scene coordinate. Otherwise, convert
3317                     // node bounds to scene coordinate.
3318                     if (localToSceneTx.getMxx() == 1.0
3319                             && localToSceneTx.getMxy() == 0.0
3320                             && localToSceneTx.getMxz() == 0.0
3321                             && localToSceneTx.getMyx() == 0.0
3322                             && localToSceneTx.getMyy() == 1.0
3323                             && localToSceneTx.getMyz() == 0.0
3324                             && localToSceneTx.getMzx() == 0.0
3325                             && localToSceneTx.getMzy() == 0.0
3326                             && localToSceneTx.getMzz() == 1.0) {
3327 
3328                         Vec3d tempV3D = TempState.getInstance().vec3d;
3329                         tempV3D.set(0, 0, bounds.getMinZ());
3330                         localToScene(tempV3D);
3331                         minZ = tempV3D.z;
3332 
3333                         tempV3D.set(0, 0, bounds.getMaxZ());
3334                         localToScene(tempV3D);
3335                         maxZ = tempV3D.z;
3336                     } else {
3337                         Bounds nodeInSceneBounds = localToScene(bounds);
3338                         minZ = nodeInSceneBounds.getMinZ();
3339                         maxZ = nodeInSceneBounds.getMaxZ();
3340                     }
3341 
3342                     if (minZ > camera.getFarClipInScene()
3343                             || maxZ < camera.getNearClipInScene()) {
3344                         return 0;
3345                     }
3346 
3347                 } else {
3348                     BaseBounds nodeInCameraBounds = new BoxBounds();
3349 
3350                     // We need to set tempTx to identity since it is a recycled transform.
3351                     // This is because TransformHelper.apply() is a matrix concatenation operation.
3352                     tempTx.setToIdentity();
3353                     TransformHelper.apply(localToSceneTx, tempTx);
3354 
3355                     // Convert node from local coordinate to camera coordinate
3356                     tempTx.preConcatenate(camera.getSceneToLocalTransform());
3357                     tempTx.transform(localBounds, nodeInCameraBounds);
3358 
3359                     // Compare in camera coornidate
3360                     if (nodeInCameraBounds.getMinZ() > camera.getFarClip()
3361                             || nodeInCameraBounds.getMaxZ() < camera.getNearClip()) {
3362                         return 0;
3363                     }
3364                 }
3365             }
3366 
3367             GeneralTransform3D projViewTx = TempState.getInstance().projViewTx;
3368             projViewTx.set(camera.getProjViewTransform());
3369 
3370             // We need to set tempTx to identity since it is a recycled transform.
3371             // This is because TransformHelper.apply() is a matrix concatenation operation.
3372             tempTx.setToIdentity();
3373             TransformHelper.apply(localToSceneTx, tempTx);
3374 
3375             // The product of projViewTx * localToSceneTransform
3376             GeneralTransform3D tx = projViewTx.mul(tempTx);
3377 
3378             // Transform localBounds to projected bounds
3379             localBounds = tx.transform(localBounds, localBounds);
3380             double area = localBounds.getWidth() * localBounds.getHeight();
3381 
3382             // Use canonical view volume to check whether object is outside the
3383             // viewing frustrum
3384             if (isPerspective) {
3385                 localBounds.intersectWith(-1, -1, 0, 1, 1, 1);
3386                 area = (localBounds.getWidth() < 0 || localBounds.getHeight() < 0) ? 0 : area;
3387             }
3388             return area * (camera.getViewWidth() / 2 * camera.getViewHeight() / 2);
3389         }
3390         return 0;
3391     }
3392 
3393     /* *************************************************************************
3394      *                                                                         *
3395      * Bounds related APIs                                                     *
3396      *                                                                         *
3397      **************************************************************************/
3398 
3399     public final Bounds getBoundsInParent() {
3400         return boundsInParentProperty().get();
3401     }
3402 
3403     /**
3404      * The rectangular bounds of this {@code Node} which include its transforms.
3405      * {@code boundsInParent} is calculated by
3406      * taking the local bounds (defined by {@link #boundsInLocalProperty boundsInLocal}) and applying
3407      * the transform created by setting the following additional variables
3408      * <ol>
3409      * <li>{@link #getTransforms transforms} ObservableList</li>
3410      * <li>{@link #scaleXProperty scaleX}, {@link #scaleYProperty scaleY}</li>
3411      * <li>{@link #rotateProperty rotate}</li>
3412      * <li>{@link #layoutXProperty layoutX}, {@link #layoutYProperty layoutY}</li>
3413      * <li>{@link #translateXProperty translateX}, {@link #translateYProperty translateY}</li>
3414      * </ol>
3415      * <p>
3416      * The resulting bounds will be conceptually in the coordinate space of the
3417      * {@code Node}'s parent, however the node need not have a parent to calculate
3418      * these bounds.
3419      * <p>
3420      * Note that this method does not take the node's visibility into account;
3421      * the computation is based on the geometry of this {@code Node} only.
3422      * <p>
3423      * This property will always have a non-null value.
3424      * <p>
3425      * Note that boundsInParent is automatically recomputed whenever the
3426      * geometry of a node changes, or when any of the following the change:
3427      * transforms ObservableList, translateX, translateY, layoutX, layoutY,
3428      * scaleX, scaleY, or the rotate variable. For this reason, it is an error
3429      * to bind any of these values in a node to an expression that depends upon
3430      * this variable. For example, the x or y variables of a shape, or
3431      * translateX, translateY should never be bound to boundsInParent
3432      * for the purpose of positioning the node.
3433      * @return the boundsInParent for this {@code Node}
3434      */
3435     public final ReadOnlyObjectProperty<Bounds> boundsInParentProperty() {
3436         return getMiscProperties().boundsInParentProperty();
3437     }
3438 
3439     private void invalidateBoundsInParent() {
3440         if (miscProperties != null) {
3441             miscProperties.invalidateBoundsInParent();
3442         }
3443     }
3444 
3445     public final Bounds getBoundsInLocal() {
3446         return boundsInLocalProperty().get();
3447     }
3448 
3449     /**
3450      * The rectangular bounds of this {@code Node} in the node's
3451      * untransformed local coordinate space.  For nodes that extend
3452      * {@link javafx.scene.shape.Shape}, the local bounds will also include
3453      * space required for a non-zero stroke that may fall outside the shape's
3454      * geometry that is defined by position and size attributes.
3455      * The local bounds will also include any clipping set with {@link #clipProperty clip}
3456      * as well as effects set with {@link #effectProperty effect}.
3457      *
3458      * <p>
3459      * Note that this method does not take the node's visibility into account;
3460      * the computation is based on the geometry of this {@code Node} only.
3461      * <p>
3462      * This property will always have a non-null value.
3463      * <p>
3464      * Note that boundsInLocal is automatically recomputed whenever the
3465      * geometry of a node changes. For this reason, it is an error to bind any
3466      * of these values in a node to an expression that depends upon this variable.
3467      * For example, the x or y variables of a shape should never be bound
3468      * to boundsInLocal for the purpose of positioning the node.
3469      * @return the boundsInLocal for this {@code Node}
3470      */
3471     public final ReadOnlyObjectProperty<Bounds> boundsInLocalProperty() {
3472         return getMiscProperties().boundsInLocalProperty();
3473     }
3474 
3475     private void invalidateBoundsInLocal() {
3476         if (miscProperties != null) {
3477             miscProperties.invalidateBoundsInLocal();
3478         }
3479     }
3480 
3481     /**
3482      * The rectangular bounds that should be used for layout calculations for
3483      * this node. {@code layoutBounds} may differ from the visual bounds
3484      * of the node and is computed differently depending on the node type.
3485      * <p>
3486      * If the node type is resizable ({@link javafx.scene.layout.Region Region},
3487      * {@link javafx.scene.control.Control Control}, or {@link javafx.scene.web.WebView WebView})
3488      * then the layoutBounds will always be {@code 0,0 width x height}.
3489      * If the node type is not resizable ({@link javafx.scene.shape.Shape Shape},
3490      * {@link javafx.scene.text.Text Text}, or {@link Group}), then the layoutBounds
3491      * are computed based on the node's geometric properties and does not include the
3492      * node's clip, effect, or transforms.  See individual class documentation
3493      * for details.
3494      * <p>
3495      * Note that the {@link #layoutXProperty layoutX}, {@link #layoutYProperty layoutY}, {@link #translateXProperty translateX}, and
3496      * {@link #translateYProperty translateY} variables are not included in the layoutBounds.
3497      * This is important because layout code must first determine the current
3498      * size and location of the node (using layoutBounds) and then set
3499      * {@code layoutX} and {@code layoutY} to adjust the translation of the
3500      * node so that it will have the desired layout position.
3501      * <p>
3502      * Because the computation of layoutBounds is often tied to a node's
3503      * geometric variables, it is an error to bind any such variables to an
3504      * expression that depends upon {@code layoutBounds}. For example, the
3505      * x or y variables of a shape should never be bound to layoutBounds
3506      * for the purpose of positioning the node.
3507      * <p>
3508      * The layoutBounds will never be null.
3509      *
3510      */
3511     private LazyBoundsProperty layoutBounds = new LazyBoundsProperty() {
3512         @Override
3513         protected Bounds computeBounds() {
3514             return NodeHelper.computeLayoutBounds(Node.this);
3515         }
3516 
3517         @Override
3518         public Object getBean() {
3519             return Node.this;
3520         }
3521 
3522         @Override
3523         public String getName() {
3524             return "layoutBounds";
3525         }
3526     };
3527 
3528     public final Bounds getLayoutBounds() {
3529         return layoutBoundsProperty().get();
3530     }
3531 
3532     public final ReadOnlyObjectProperty<Bounds> layoutBoundsProperty() {
3533         return layoutBounds;
3534     }
3535 
3536     /*
3537      *                  Bounds And Transforms Computation
3538      *
3539      *  This section of the code is responsible for computing and caching
3540      *  various bounds and transforms. For optimal performance and minimal
3541      *  recomputation of bounds (which can be quite expensive), we cache
3542      *  values on two different levels. We expose two public immutable
3543      *  Bounds boundsInParent objects and boundsInLocal. Because they are
3544      *  immutable and because they may change quite frequently (especially
3545      *  in the case of a Parent whose children are animated), it is
3546      *  important that the system does not rely on these variables, because
3547      *  doing so would produce a large amount of garbage. Rather, these
3548      *  variables are provided solely for the convenience of application
3549      *  developers and, being lazily bound, should generally be created at
3550      *  most once per frame.
3551      *
3552      *  The second level of caching are within local Bounds2D variables.
3553      *  These variables, txBounds and geomBounds, are mutable and as such
3554      *  can be cached and updated as frequently as necessary without creating
3555      *  excessive garbage. However, since the computation of bounds is still
3556      *  expensive, it is desirable to cache both the geometric bounds and
3557      *  the "complete" transformed bounds (essentially, boundsInParent).
3558      *  Cached txBounds is particularly useful when computing the geometric
3559      *  bounds of a Parent since it would not require complete or partial
3560      *  recomputation of each child.
3561      *
3562      *  Finally, we cache the complete transform for this node which converts
3563      *  its coord system from local to parent coords. This is useful both for
3564      *  minimizing bounds recomputations in the case of the geometry having
3565      *  changed but the transform not having changed, and also because the tx
3566      *  is required for several different computations (for example, it must
3567      *  be computed once during state synchronization with the PG peer, and
3568      *  must also be computed when the pivot point changes, and also when
3569      *  deriving the txBounds of the Node).
3570      *
3571      *  As with any caching system, a subtle and non-trivial amount of code
3572      *  is devoted to invalidating the bounds / transforms at appropriate
3573      *  times and in appropriate places to make sure bounds / transforms
3574      *  are recomputed at all necessary times.
3575      *
3576      *  There are three computeXXX functions. One is for computing the
3577      *  boundsInParent, the second for computing boundsInLocal, and the
3578      *  third for computing the default layout bounds (which, by default,
3579      *  is based on the geometric bounds). These functions are all prefixed
3580      *  with "compute" because they create and return new immutable
3581      *  Bounds objects.
3582      *
3583      *  There are three getXXXBounds functions. One is for returning the
3584      *  complete transformed bounds. The second is for returning the
3585      *  local bounds. The last is for returning the geometric bounds. These
3586      *  functions are all prefixed with "get" because they may well return
3587      *  a cached value, or may actually compute the bounds if necessary. These
3588      *  functions all have the same signature. They take a Bounds2D and
3589      *  BaseTransform, and return a Bounds2D (the same as they took). These
3590      *  functions essentially populate the supplied bounds2D with the
3591      *  appropriate bounds information, leveraging cached bounds if possible.
3592      *
3593      *  There is a single NodeHelper.computeGeomBoundsImpl function which is abstract.
3594      *  This must be implemented in each subclass, and is responsible for
3595      *  computing the actual geometric bounds for the Node. For example, Parent
3596      *  is written such that this function is the union of the transformed
3597      *  bounds of each child. Rectangle is written such that this takes into
3598      *  account the size and stroke. Text is written such that it is computed
3599      *  based on the actual glyphs.
3600      *
3601      *  There are two updateXXX functions, updateGeomBounds and updateTxBounds.
3602      *  These functions are for ensuring that geomBounds and txBounds are
3603      *  valid. They only execute in the case of the cached value being invalid,
3604      *  so the function call is very cheap in cases where the cached bounds
3605      *  values are still valid.
3606      */
3607 
3608     /**
3609      * An affine transform that holds the computed local-to-parent transform.
3610      * This is the concatenation of all transforms in this node, including all
3611      * of the convenience transforms.
3612      */
3613     private BaseTransform localToParentTx = BaseTransform.IDENTITY_TRANSFORM;
3614 
3615     /**
3616      * This flag is used to indicate that localToParentTx is dirty and needs
3617      * to be recomputed.
3618      */
3619     private boolean transformDirty = true;
3620 
3621     /**
3622      * The cached transformed bounds. This is never null, but is frequently set
3623      * to be invalid whenever the bounds for the node have changed. These are
3624      * "complete" bounds, that is, with transforms and effect and clip applied.
3625      * Note that this is equivalent to boundsInParent
3626      */
3627     private BaseBounds txBounds = new RectBounds();
3628 
3629     /**
3630      * The cached bounds. This is never null, but is frequently set to be
3631      * invalid whenever the bounds for the node have changed. These are the
3632      * "content" bounds, that is, without transforms or effects applied.
3633      */
3634     private BaseBounds geomBounds = new RectBounds();
3635 
3636     /**
3637      * The cached local bounds (without transforms, with clip and effects).
3638      * If there is neither clip nor effect
3639      * local bounds are equal to geom bounds, so in this case we don't keep
3640      * the extra instance and set null to this variable.
3641      */
3642     private BaseBounds localBounds = null;
3643 
3644     /**
3645      * This special flag is used only by Parent to flag whether or not
3646      * the *parent* has processed the fact that bounds have changed for this
3647      * child Node. We need some way of flagging this on a per-node basis to
3648      * enable the significant performance optimizations and fast paths that
3649      * are in the Parent code.
3650      * <p>
3651      * To reduce confusion, although this variable is defined on Node, it
3652      * really belongs to the Parent of the node and should *only* be modified
3653      * by the parent.
3654      */
3655     boolean boundsChanged;
3656 
3657     /*
3658      * Returns geometric bounds, but may be over-ridden by a subclass.
3659      */
3660     private Bounds doComputeLayoutBounds() {
3661         BaseBounds tempBounds = TempState.getInstance().bounds;
3662         tempBounds = getGeomBounds(tempBounds,
3663                                    BaseTransform.IDENTITY_TRANSFORM);
3664         return new BoundingBox(tempBounds.getMinX(),
3665                                tempBounds.getMinY(),
3666                                tempBounds.getMinZ(),
3667                                tempBounds.getWidth(),
3668                                tempBounds.getHeight(),
3669                                tempBounds.getDepth());
3670     }
3671 
3672     /*
3673      * Subclasses may customize the layoutBounds by means of overriding the
3674      * NodeHelper.computeLayoutBoundsImpl method. If the layout bounds need to be
3675      * recomputed, the subclass must notify the Node implementation of this
3676      * fact so that appropriate notifications and internal state can be
3677      * kept in sync. Subclasses must call NodeHelper.layoutBoundsChanged to
3678      * let Node know that the layout bounds are invalid and need to be
3679      * recomputed.
3680      */
3681     final void layoutBoundsChanged() {
3682         if (!layoutBounds.valid) {
3683             return;
3684         }
3685         layoutBounds.invalidate();
3686         if ((nodeTransformation != null && nodeTransformation.hasScaleOrRotate()) || hasMirroring()) {
3687             // if either the scale or rotate convenience variables are used,
3688             // then we need a valid pivot point. Since the layoutBounds
3689             // affects the pivot we need to invalidate the transform
3690             NodeHelper.transformsChanged(this);
3691         }
3692     }
3693 
3694     /**
3695      * Loads the given bounds object with the transformed bounds relative to,
3696      * and based on, the given transform. That is, this is the local bounds
3697      * with the local-to-parent transform applied.
3698      *
3699      * We *never* pass null in as a bounds. This method will
3700      * NOT take a null bounds object. The returned value may be
3701      * the same bounds object passed in, or it may be a new object.
3702      * The reason for this object promotion is in the case of needing
3703      * to promote from a RectBounds to a BoxBounds (3D).
3704      */
3705     BaseBounds getTransformedBounds(BaseBounds bounds, BaseTransform tx) {
3706         updateLocalToParentTransform();
3707         if (tx.isTranslateOrIdentity()) {
3708             updateTxBounds();
3709             bounds = bounds.deriveWithNewBounds(txBounds);
3710             if (!tx.isIdentity()) {
3711                 final double translateX = tx.getMxt();
3712                 final double translateY = tx.getMyt();
3713                 final double translateZ = tx.getMzt();
3714                 bounds = bounds.deriveWithNewBounds(
3715                                     (float) (bounds.getMinX() + translateX),
3716                                     (float) (bounds.getMinY() + translateY),
3717                                     (float) (bounds.getMinZ() + translateZ),
3718                                     (float) (bounds.getMaxX() + translateX),
3719                                     (float) (bounds.getMaxY() + translateY),
3720                                     (float) (bounds.getMaxZ() + translateZ));
3721             }
3722             return bounds;
3723         } else if (localToParentTx.isIdentity()) {
3724             return getLocalBounds(bounds, tx);
3725         } else {
3726             double mxx = tx.getMxx();
3727             double mxy = tx.getMxy();
3728             double mxz = tx.getMxz();
3729             double mxt = tx.getMxt();
3730             double myx = tx.getMyx();
3731             double myy = tx.getMyy();
3732             double myz = tx.getMyz();
3733             double myt = tx.getMyt();
3734             double mzx = tx.getMzx();
3735             double mzy = tx.getMzy();
3736             double mzz = tx.getMzz();
3737             double mzt = tx.getMzt();
3738             BaseTransform boundsTx = tx.deriveWithConcatenation(localToParentTx);
3739             bounds = getLocalBounds(bounds, boundsTx);
3740             if (boundsTx == tx) {
3741                 tx.restoreTransform(mxx, mxy, mxz, mxt,
3742                                     myx, myy, myz, myt,
3743                                     mzx, mzy, mzz, mzt);
3744             }
3745             return bounds;
3746         }
3747     }
3748 
3749     /**
3750      * Loads the given bounds object with the local bounds relative to,
3751      * and based on, the given transform. That is, these are the geometric
3752      * bounds + clip and effect.
3753      *
3754      * We *never* pass null in as a bounds. This method will
3755      * NOT take a null bounds object. The returned value may be
3756      * the same bounds object passed in, or it may be a new object.
3757      * The reason for this object promotion is in the case of needing
3758      * to promote from a RectBounds to a BoxBounds (3D).
3759      */
3760     BaseBounds getLocalBounds(BaseBounds bounds, BaseTransform tx) {
3761         if (getEffect() == null && getClip() == null) {
3762             return getGeomBounds(bounds, tx);
3763         }
3764 
3765         if (tx.isTranslateOrIdentity()) {
3766             // we can take a fast path since we know tx is either a simple
3767             // translation or is identity
3768             updateLocalBounds();
3769             bounds = bounds.deriveWithNewBounds(localBounds);
3770             if (!tx.isIdentity()) {
3771                 double translateX = tx.getMxt();
3772                 double translateY = tx.getMyt();
3773                 double translateZ = tx.getMzt();
3774                 bounds = bounds.deriveWithNewBounds((float) (bounds.getMinX() + translateX),
3775                         (float) (bounds.getMinY() + translateY),
3776                         (float) (bounds.getMinZ() + translateZ),
3777                         (float) (bounds.getMaxX() + translateX),
3778                         (float) (bounds.getMaxY() + translateY),
3779                         (float) (bounds.getMaxZ() + translateZ));
3780             }
3781             return bounds;
3782         } else if (tx.is2D()
3783                 && (tx.getType()
3784                 & ~(BaseTransform.TYPE_UNIFORM_SCALE | BaseTransform.TYPE_TRANSLATION
3785                 | BaseTransform.TYPE_FLIP | BaseTransform.TYPE_QUADRANT_ROTATION)) != 0) {
3786             // this is a non-uniform scale / non-quadrant rotate / skew transform
3787             return computeLocalBounds(bounds, tx);
3788         } else {
3789             // 3D transformations and
3790             // selected 2D transformations (unifrom transform, flip, quadrant rotation).
3791             // These 2D transformation will yield tight bounds when applied on the pre-computed
3792             // geomBounds
3793             // Note: Transforming the local bounds into a 3D space will yield a bounds
3794             // that isn't as tight as transforming its geometry and compute it bounds.
3795             updateLocalBounds();
3796             return tx.transform(localBounds, bounds);
3797         }
3798     }
3799 
3800     /**
3801      * Loads the given bounds object with the geometric bounds relative to,
3802      * and based on, the given transform.
3803      *
3804      * We *never* pass null in as a bounds. This method will
3805      * NOT take a null bounds object. The returned value may be
3806      * the same bounds object passed in, or it may be a new object.
3807      * The reason for this object promotion is in the case of needing
3808      * to promote from a RectBounds to a BoxBounds (3D).
3809      */
3810     BaseBounds getGeomBounds(BaseBounds bounds, BaseTransform tx) {
3811         if (tx.isTranslateOrIdentity()) {
3812             // we can take a fast path since we know tx is either a simple
3813             // translation or is identity
3814             updateGeomBounds();
3815             bounds = bounds.deriveWithNewBounds(geomBounds);
3816             if (!tx.isIdentity()) {
3817                 double translateX = tx.getMxt();
3818                 double translateY = tx.getMyt();
3819                 double translateZ = tx.getMzt();
3820                 bounds = bounds.deriveWithNewBounds((float) (bounds.getMinX() + translateX),
3821                         (float) (bounds.getMinY() + translateY),
3822                         (float) (bounds.getMinZ() + translateZ),
3823                         (float) (bounds.getMaxX() + translateX),
3824                         (float) (bounds.getMaxY() + translateY),
3825                         (float) (bounds.getMaxZ() + translateZ));
3826             }
3827             return bounds;
3828         } else if (tx.is2D()
3829                 && (tx.getType()
3830                 & ~(BaseTransform.TYPE_UNIFORM_SCALE | BaseTransform.TYPE_TRANSLATION
3831                 | BaseTransform.TYPE_FLIP | BaseTransform.TYPE_QUADRANT_ROTATION)) != 0) {
3832             // this is a non-uniform scale / non-quadrant rotate / skew transform
3833             return NodeHelper.computeGeomBounds(this, bounds, tx);
3834         } else {
3835             // 3D transformations and
3836             // selected 2D transformations (unifrom transform, flip, quadrant rotation).
3837             // These 2D transformation will yield tight bounds when applied on the pre-computed
3838             // geomBounds
3839             // Note: Transforming the local geomBounds into a 3D space will yield a bounds
3840             // that isn't as tight as transforming its geometry and compute it bounds.
3841             updateGeomBounds();
3842             return tx.transform(geomBounds, bounds);
3843         }
3844     }
3845 
3846     /**
3847      * If necessary, recomputes the cached geom bounds. If the bounds are not
3848      * invalid, then this method is a no-op.
3849      */
3850     void updateGeomBounds() {
3851         if (geomBoundsInvalid) {
3852             geomBounds = NodeHelper.computeGeomBounds(this, geomBounds, BaseTransform.IDENTITY_TRANSFORM);
3853             geomBoundsInvalid = false;
3854         }
3855     }
3856 
3857     /**
3858      * Computes the local bounds of this Node.
3859      */
3860     private BaseBounds computeLocalBounds(BaseBounds bounds, BaseTransform tx) {
3861         // We either get the bounds of the effect (if it isn't null)
3862         // or we get the geom bounds (if effect is null). We will then
3863         // intersect this with the clip.
3864         if (getEffect() != null) {
3865             BaseBounds b = EffectHelper.getBounds(getEffect(), bounds, tx, this, boundsAccessor);
3866             bounds = bounds.deriveWithNewBounds(b);
3867         } else {
3868             bounds = getGeomBounds(bounds, tx);
3869         }
3870         // intersect with the clip. Take care with "bounds" as it may
3871         // actually be TEMP_BOUNDS, so we save off state
3872         if (getClip() != null
3873                 // FIXME: All 3D picking is currently ignored by rendering.
3874                 // Until this is fixed or defined differently (RT-28510),
3875                 // we follow this behavior.
3876                 && !(this instanceof Shape3D) && !(getClip() instanceof Shape3D)) {
3877             double x1 = bounds.getMinX();
3878             double y1 = bounds.getMinY();
3879             double x2 = bounds.getMaxX();
3880             double y2 = bounds.getMaxY();
3881             double z1 = bounds.getMinZ();
3882             double z2 = bounds.getMaxZ();
3883             bounds = getClip().getTransformedBounds(bounds, tx);
3884             bounds.intersectWith((float)x1, (float)y1, (float)z1,
3885                     (float)x2, (float)y2, (float)z2);
3886         }
3887         return bounds;
3888     }
3889 
3890 
3891     /**
3892      * If necessary, recomputes the cached local bounds. If the bounds are not
3893      * invalid, then this method is a no-op.
3894      */
3895     private void updateLocalBounds() {
3896         if (localBoundsInvalid) {
3897             if (getClip() != null || getEffect() != null) {
3898                 localBounds = computeLocalBounds(
3899                         localBounds == null ? new RectBounds() : localBounds,
3900                         BaseTransform.IDENTITY_TRANSFORM);
3901             } else {
3902                 localBounds = null;
3903             }
3904             localBoundsInvalid = false;
3905         }
3906     }
3907 
3908     /**
3909      * If necessary, recomputes the cached transformed bounds.
3910      * If the cached transformed bounds are not invalid, then
3911      * this method is a no-op.
3912      */
3913     void updateTxBounds() {
3914         if (txBoundsInvalid) {
3915             updateLocalToParentTransform();
3916             txBounds = getLocalBounds(txBounds, localToParentTx);
3917             txBoundsInvalid = false;
3918         }
3919     }
3920 
3921     /*
3922      *                   Bounds Invalidation And Notification
3923      *
3924      *  The goal of this section is to efficiently propagate bounds
3925      *  invalidation through the scenegraph while also being semantically
3926      *  correct.
3927      *
3928      *  The code path for invalidation of layout bounds is somewhat confusing
3929      *  primarily due to performance enhancements and the desire to reduce the
3930      *  number of requestLayout() calls that are performed when layout bounds
3931      *  change. Before diving into layout bounds, I will first describe how
3932      *  normal bounds invalidation occurs.
3933      *
3934      *  When a node's geometry changes (for example, if the width of a
3935      *  Rectangle is changed) then the Node must call NodeHelper.geomChanged().
3936      *  Invoking this function will eventually clear all cached bounds and
3937      *  notify to each parent up the tree that their bounds may have changed.
3938      *
3939      *  After invalidating geomBounds (and after kicking off layout bounds
3940      *  notification), NodeHelper.geomChanged calls localBoundsChanged(). It should
3941      *  be noted that NodeHelper.geomChanged should only be called when the geometry
3942      *  of the node has changed such that it may result in the geom bounds
3943      *  actually changing.
3944      *
3945      *  localBoundsChanged() simply invalidates boundsInLocal and then calls
3946      *  transformedBoundsChanged().
3947      *
3948      *  transformedBoundsChanged() is responsible for invalidating
3949      *  boundsInParent and txBounds. If the Node is not visible, then there is
3950      *  no need to notify the parent of the bounds change because the parent's
3951      *  bounds do not include invisible nodes. If the node is visible, then
3952      *  it must tell the parent that this child node's bounds have changed.
3953      *  It is up to the parent to eventually invoke its own NodeHelper.geomChanged
3954      *  function. If instead of a parent this node has a clipParent, then the
3955      *  clipParent's localBoundsChanged() is called instead.
3956      *
3957      *  There are a few other ways in which we enter the invalidate steps
3958      *  beyond just the geometry changes. If the visibility of a Node changes,
3959      *  its own bounds are not affected but its parent's bounds are. So a
3960      *  special call to parent.childVisibilityChanged is made so the parent
3961      *  can react accordingly.
3962      *
3963      *  If a transform is changed (layoutX, layoutY, rotate, transforms, etc)
3964      *  then the transform must be invalidated. When a transform is invalidated,
3965      *  it must also invalidate the txBounds by invoking
3966      *  transformedBoundsChanged, which will in turn notify the parent as
3967      *  before.
3968      *
3969      *  If an effect is changed or replaced then the local bounds must be
3970      *  invalidated, as well as the transformedBounds and the parent notified
3971      *  of the change in bounds.
3972      *
3973      *  layoutBound is somewhat unique in that it can be redefined in
3974      *  subclasses. By default, the layoutBounds is the geomBounds, and so
3975      *  whenever the geomBounds() function is called the layoutBounds
3976      *  must be invalidated. However in subclasses, especially Resizables,
3977      *  the layout bounds may not be defined to be the same as the geometric
3978      *  bounds. This is both useful and provides a very nice performance
3979      *  optimization for regions and controls. In this case, subclasses
3980      *  need some way to interpose themselves such that a call to
3981      *  NodeHelper.geomChanged() *does not* invalidate the layout bounds.
3982      *
3983      *  This interposition happens by providing the
3984      *  NodeHelper.notifyLayoutBoundsChanged function. The default implementation
3985      *  simply invalidates boundsInLocal. Subclasses (such as Region and
3986      *  Control) can override this function so that it does not invalidate
3987      *  the layout bounds.
3988      *
3989      *  An on invalidate trigger on layoutBounds handles kicking off the rest
3990      *  of the invalidate process for layoutBounds. Because the layout bounds
3991      *  define the pivot point, if scaleX, scaleY, or rotate contain
3992      *  non-identity values then whenever the layoutBounds change the
3993      *  transformed bounds also change. Finally, if this node's parent is
3994      *  a Region and if the Node is being managed by the Region, then
3995      *  we must call requestLayout on the Region whenever the layout bounds
3996      *  have changed.
3997      */
3998 
3999     /*
4000      * Invoked by subclasses whenever their geometric bounds have changed.
4001      * Because the default layout bounds is based on the node geometry, this
4002      * function will invoke NodeHelper.notifyLayoutBoundsChanged. The default
4003      * implementation of NodeHelper.notifyLayoutBoundsChanged() will simply invalidate
4004      * layoutBounds. Resizable subclasses will want to override this function
4005      * in most cases to be a no-op.
4006      *
4007      * This function will also invalidate the cached geom bounds, and then
4008      * invoke localBoundsChanged() which will eventually end up invoking a
4009      * chain of functions up the tree to ensure that each parent of this
4010      * Node is notified that its bounds may have also changed.
4011      *
4012      * This function should be treated as though it were final. It is not
4013      * intended to be overridden by subclasses.
4014      *
4015      * Note: This method MUST only be called via its accessor method.
4016      */
4017     private void doGeomChanged() {
4018         if (geomBoundsInvalid) {
4019             // GeomBoundsInvalid is false when node geometry changed and
4020             // the untransformed node bounds haven't been recalculated yet.
4021             // Most of the time, the recalculation of layout and transformed
4022             // node bounds don't require validation of untransformed bounds
4023             // and so we can not skip the following notifications.
4024             NodeHelper.notifyLayoutBoundsChanged(this);
4025             transformedBoundsChanged();
4026             return;
4027         }
4028         geomBounds.makeEmpty();
4029         geomBoundsInvalid = true;
4030         NodeHelper.markDirty(this, DirtyBits.NODE_BOUNDS);
4031         NodeHelper.notifyLayoutBoundsChanged(this);
4032         localBoundsChanged();
4033     }
4034 
4035     private boolean geomBoundsInvalid = true;
4036     private boolean localBoundsInvalid = true;
4037     private boolean txBoundsInvalid = true;
4038 
4039     /**
4040      * Responds to changes in the local bounds by invalidating boundsInLocal
4041      * and notifying this node that its transformed bounds have changed.
4042      */
4043     void localBoundsChanged() {
4044         localBoundsInvalid = true;
4045         invalidateBoundsInLocal();
4046         transformedBoundsChanged();
4047     }
4048 
4049     /**
4050      * Responds to changes in the transformed bounds by invalidating txBounds
4051      * and boundsInParent. If this Node is not visible, then we have no need
4052      * to walk further up the tree but can instead simply invalidate state.
4053      * Otherwise, this function will notify parents (either the parent or the
4054      * clipParent) that this child Node's bounds have changed.
4055      */
4056     void transformedBoundsChanged() {
4057         if (!txBoundsInvalid) {
4058             txBounds.makeEmpty();
4059             txBoundsInvalid = true;
4060             invalidateBoundsInParent();
4061             NodeHelper.markDirty(this, DirtyBits.NODE_TRANSFORMED_BOUNDS);
4062         }
4063         if (isVisible()) {
4064             notifyParentOfBoundsChange();
4065         }
4066     }
4067 
4068     /*
4069      * Invoked by geomChanged(). Since layoutBounds is by default based
4070      * on the geometric bounds, the default implementation of this function will
4071      * invalidate the layoutBounds. Resizable Node subclasses generally base
4072      * layoutBounds on the width/height instead of the geometric bounds, and so
4073      * will generally want to override this function to be a no-op.
4074      *
4075      * Note: This method MUST only be called via its accessor method.
4076      */
4077     private void doNotifyLayoutBoundsChanged() {
4078         layoutBoundsChanged();
4079         // notify the parent
4080         // Group instanceof check a little hoaky, but it allows us to disable
4081         // unnecessary layout for the case of a non-resizable within a group
4082         Parent p = getParent();
4083 
4084         // Need to propagate layout if parent isn't part of performing layout
4085         if (isManaged() && (p != null) && !(p instanceof Group && !isResizable())
4086                 && !p.isPerformingLayout()) {
4087             // Force its parent to fix the layout since it is a managed child.
4088             p.requestLayout(true);
4089         }
4090     }
4091 
4092     /**
4093      * Notifies both the real parent and the clip parent (if they exist) that
4094      * the bounds of the child has changed. Note that since FX doesn't throw
4095      * NPE's, things actually are faster if we don't check twice for Null
4096      * (we check once, the compiler checks again)
4097      */
4098     void notifyParentOfBoundsChange() {
4099         // let the parent know which node has changed and the parent will
4100         // deal with marking itself invalid correctly
4101         Parent p = getParent();
4102         if (p != null) {
4103             p.childBoundsChanged(this);
4104         }
4105         // since the clip is used to compute the local bounds (and not the
4106         // geom bounds), we just need to notify that local bounds on the
4107         // clip parent have changed
4108         if (clipParent != null) {
4109             clipParent.localBoundsChanged();
4110         }
4111     }
4112 
4113     /***************************************************************************
4114      *                                                                         *
4115      * Geometry and coordinate system related APIs. For example, methods       *
4116      * related to containment, intersection, coordinate space conversion, etc. *
4117      *                                                                         *
4118      **************************************************************************/
4119 
4120     /**
4121      * Returns {@code true} if the given point (specified in the local
4122      * coordinate space of this {@code Node}) is contained within the shape of
4123      * this {@code Node}. Note that this method does not take visibility into
4124      * account; the test is based on the geometry of this {@code Node} only.
4125      * @param localX the x coordinate of the point in Node's space
4126      * @param localY the y coordinate of the point in Node's space
4127      * @return the result of contains for this {@code Node}
4128      */
4129     public boolean contains(double localX, double localY) {
4130         if (containsBounds(localX, localY)) {
4131             return (isPickOnBounds() || NodeHelper.computeContains(this, localX, localY));
4132         }
4133         return false;
4134     }
4135 
4136     /*
4137      * This method only does the contains check based on the bounds, clip and
4138      * effect of this node, excluding its shape (or geometry).
4139      *
4140      * Returns true if the given point (specified in the local
4141      * coordinate space of this {@code Node}) is contained within the bounds,
4142      * clip and effect of this node.
4143      */
4144     private boolean containsBounds(double localX, double localY) {
4145         final TempState tempState = TempState.getInstance();
4146         BaseBounds tempBounds = tempState.bounds;
4147 
4148         // first, we do a quick test to see if the point is contained in
4149         // our local bounds. If so, then we will go the next step and check
4150         // the clip, effect, and geometry for containment.
4151         tempBounds = getLocalBounds(tempBounds,
4152                                     BaseTransform.IDENTITY_TRANSFORM);
4153         if (tempBounds.contains((float)localX, (float)localY)) {
4154             // if the clip is defined, then check it for containment, being
4155             // sure to convert from this node's local coordinate system
4156             // to the local coordinate system of the clip node
4157             if (getClip() != null) {
4158                 tempState.point.x = (float)localX;
4159                 tempState.point.y = (float)localY;
4160                 try {
4161                     getClip().parentToLocal(tempState.point);
4162                 } catch (NoninvertibleTransformException e) {
4163                     return false;
4164                 }
4165                 if (!getClip().contains(tempState.point.x, tempState.point.y)) {
4166                     return false;
4167                 }
4168             }
4169             return true;
4170         }
4171         return false;
4172     }
4173 
4174     /**
4175      * Returns {@code true} if the given point (specified in the local
4176      * coordinate space of this {@code Node}) is contained within the shape of
4177      * this {@code Node}. Note that this method does not take visibility into
4178      * account; the test is based on the geometry of this {@code Node} only.
4179      * @param localPoint the 2D point in Node's space
4180      * @return the result of contains for this {@code Node}
4181      */
4182     public boolean contains(Point2D localPoint) {
4183         return contains(localPoint.getX(), localPoint.getY());
4184     }
4185 
4186     /**
4187      * Returns {@code true} if the given rectangle (specified in the local
4188      * coordinate space of this {@code Node}) intersects the shape of this
4189      * {@code Node}. Note that this method does not take visibility into
4190      * account; the test is based on the geometry of this {@code Node} only.
4191      * The default behavior of this function is simply to check if the
4192      * given coordinates intersect with the local bounds.
4193      * @param localX the x coordinate of a rectangle in Node's space
4194      * @param localY the y coordinate of a rectangle in Node's space
4195      * @param localWidth the width of a rectangle in Node's space
4196      * @param localHeight the height of a rectangle in Node's space
4197      * @return the result of intersects for this {@code Node}
4198      */
4199     public boolean intersects(double localX, double localY, double localWidth, double localHeight) {
4200         BaseBounds tempBounds = TempState.getInstance().bounds;
4201         tempBounds = getLocalBounds(tempBounds,
4202                                     BaseTransform.IDENTITY_TRANSFORM);
4203         return tempBounds.intersects((float)localX,
4204                                      (float)localY,
4205                                      (float)localWidth,
4206                                      (float)localHeight);
4207     }
4208 
4209     /**
4210      * Returns {@code true} if the given bounds (specified in the local
4211      * coordinate space of this {@code Node}) intersects the shape of this
4212      * {@code Node}. Note that this method does not take visibility into
4213      * account; the test is based on the geometry of this {@code Node} only.
4214      * The default behavior of this function is simply to check if the
4215      * given coordinates intersect with the local bounds.
4216      * @param localBounds the bounds
4217      * @return the result of intersects for this {@code Node}
4218      */
4219     public boolean intersects(Bounds localBounds) {
4220         return intersects(localBounds.getMinX(), localBounds.getMinY(), localBounds.getWidth(), localBounds.getHeight());
4221     }
4222 
4223     /**
4224      * Transforms a point from the coordinate space of the {@link javafx.stage.Screen}
4225      * into the local coordinate space of this {@code Node}.
4226      * @param screenX x coordinate of a point on a Screen
4227      * @param screenY y coordinate of a point on a Screen
4228      * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
4229      * Null is also returned if the transformation from local to Scene is not invertible.
4230      * @since JavaFX 8.0
4231      */
4232     public Point2D screenToLocal(double screenX, double screenY) {
4233         Scene scene = getScene();
4234         if (scene == null) return null;
4235         Window window = scene.getWindow();
4236         if (window == null) return null;
4237 
4238         final com.sun.javafx.geom.Point2D tempPt =
4239                 TempState.getInstance().point;
4240 
4241         tempPt.setLocation((float)(screenX - scene.getX() - window.getX()),
4242                            (float)(screenY - scene.getY() - window.getY()));
4243 
4244         final SubScene subScene = getSubScene();
4245         if (subScene != null) {
4246             final Point2D ssCoord = SceneUtils.sceneToSubScenePlane(subScene,
4247                     new Point2D(tempPt.x, tempPt.y));
4248             if (ssCoord == null) {
4249                 return null;
4250             }
4251             tempPt.setLocation((float) ssCoord.getX(), (float) ssCoord.getY());
4252         }
4253 
4254         final Point3D ppIntersect =
4255                 scene.getEffectiveCamera().pickProjectPlane(tempPt.x, tempPt.y);
4256         tempPt.setLocation((float) ppIntersect.getX(), (float) ppIntersect.getY());
4257 
4258         try {
4259             sceneToLocal(tempPt);
4260         } catch (NoninvertibleTransformException e) {
4261             return null;
4262         }
4263         return new Point2D(tempPt.x, tempPt.y);
4264     }
4265 
4266     /**
4267      * Transforms a point from the coordinate space of the {@link javafx.stage.Screen}
4268      * into the local coordinate space of this {@code Node}.
4269      * @param screenPoint a point on a Screen
4270      * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
4271      * Null is also returned if the transformation from local to Scene is not invertible.
4272      * @since JavaFX 8.0
4273      */
4274     public Point2D screenToLocal(Point2D screenPoint) {
4275         return screenToLocal(screenPoint.getX(), screenPoint.getY());
4276     }
4277 
4278     /**
4279      * Transforms a rectangle from the coordinate space of the
4280      * {@link javafx.stage.Screen} into the local coordinate space of this
4281      * {@code Node}. Returns reasonable result only in 2D space.
4282      * @param screenBounds bounds on a Screen
4283      * @return bounds in the local Node'space or null if Node is not in a {@link Window}.
4284      * Null is also returned if the transformation from local to Scene is not invertible.
4285      * @since JavaFX 8.0
4286      */
4287     public Bounds screenToLocal(Bounds screenBounds) {
4288         final Point2D p1 = screenToLocal(screenBounds.getMinX(), screenBounds.getMinY());
4289         final Point2D p2 = screenToLocal(screenBounds.getMinX(), screenBounds.getMaxY());
4290         final Point2D p3 = screenToLocal(screenBounds.getMaxX(), screenBounds.getMinY());
4291         final Point2D p4 = screenToLocal(screenBounds.getMaxX(), screenBounds.getMaxY());
4292 
4293         return BoundsUtils.createBoundingBox(p1, p2, p3, p4);
4294     }
4295 
4296 
4297     /**
4298      * Transforms a point from the coordinate space of the scene
4299      * into the local coordinate space of this {@code Node}.
4300      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the arguments are in {@link Scene} coordinates
4301      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4302      * {@link #sceneToLocal(double, double)}
4303      * @param x the x coordinate
4304      * @param y the y coordinate
4305      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4306      * @return local coordinates of the point
4307      * @since JavaFX 8u40
4308      */
4309     public Point2D sceneToLocal(double x, double y, boolean rootScene) {
4310         if (!rootScene) {
4311             return sceneToLocal(x, y);
4312         }
4313         final com.sun.javafx.geom.Point2D tempPt =
4314                 TempState.getInstance().point;
4315 
4316         tempPt.setLocation((float)(x), (float)y);
4317 
4318         final SubScene subScene = getSubScene();
4319         if (subScene != null) {
4320             final Point2D ssCoord = SceneUtils.sceneToSubScenePlane(subScene,
4321                     new Point2D(tempPt.x, tempPt.y));
4322             if (ssCoord == null) {
4323                 return null;
4324             }
4325             tempPt.setLocation((float) ssCoord.getX(), (float) ssCoord.getY());
4326         }
4327 
4328         try {
4329             sceneToLocal(tempPt);
4330             return new Point2D(tempPt.x, tempPt.y);
4331         } catch (NoninvertibleTransformException e) {
4332             return null;
4333         }
4334     }
4335 
4336     /**
4337      * Transforms a point from the coordinate space of the scene
4338      * into the local coordinate space of this {@code Node}.
4339      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the arguments are in {@link Scene} coordinates
4340      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4341      * {@link #sceneToLocal(javafx.geometry.Point2D)}
4342      * @param point the point
4343      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4344      * @return local coordinates of the point
4345      * @since JavaFX 8u40
4346      */
4347     public Point2D sceneToLocal(Point2D point, boolean rootScene) {
4348         return sceneToLocal(point.getX(), point.getY(), rootScene);
4349     }
4350 
4351     /**
4352      * Transforms a bounds from the coordinate space of the scene
4353      * into the local coordinate space of this {@code Node}.
4354      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the arguments are in {@link Scene} coordinates
4355      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4356      * {@link #sceneToLocal(javafx.geometry.Bounds)}.
4357      * <p>
4358      *     Since 3D bounds cannot be converted with {@code rootScene} set to {@code true}, trying to convert 3D bounds will yield {@code null}.
4359      * </p>
4360      * @param bounds the bounds
4361      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4362      * @return local coordinates of the bounds
4363      * @since JavaFX 8u40
4364      */
4365     public Bounds sceneToLocal(Bounds bounds, boolean rootScene) {
4366         if (!rootScene) {
4367             return sceneToLocal(bounds);
4368         }
4369         if (bounds.getMinZ() != 0 || bounds.getMaxZ() != 0) {
4370             return null;
4371         }
4372         final Point2D p1 = sceneToLocal(bounds.getMinX(), bounds.getMinY(), true);
4373         final Point2D p2 = sceneToLocal(bounds.getMinX(), bounds.getMaxY(), true);
4374         final Point2D p3 = sceneToLocal(bounds.getMaxX(), bounds.getMinY(), true);
4375         final Point2D p4 = sceneToLocal(bounds.getMaxX(), bounds.getMaxY(), true);
4376 
4377         return BoundsUtils.createBoundingBox(p1, p2, p3, p4);
4378     }
4379 
4380     /**
4381      * Transforms a point from the coordinate space of the scene
4382      * into the local coordinate space of this {@code Node}.
4383      *
4384      * Note that if this node is in a {@link SubScene}, the arguments should be in the subscene coordinates,
4385      * not that of {@link javafx.scene.Scene}.
4386      *
4387      * @param sceneX x coordinate of a point on a Scene
4388      * @param sceneY y coordinate of a point on a Scene
4389      * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
4390      * Null is also returned if the transformation from local to Scene is not invertible.
4391      */
4392     public Point2D sceneToLocal(double sceneX, double sceneY) {
4393         final com.sun.javafx.geom.Point2D tempPt =
4394                 TempState.getInstance().point;
4395         tempPt.setLocation((float)sceneX, (float)sceneY);
4396         try {
4397             sceneToLocal(tempPt);
4398         } catch (NoninvertibleTransformException e) {
4399             return null;
4400         }
4401         return new Point2D(tempPt.x, tempPt.y);
4402     }
4403 
4404     /**
4405      * Transforms a point from the coordinate space of the scene
4406      * into the local coordinate space of this {@code Node}.
4407      *
4408      * Note that if this node is in a {@link SubScene}, the arguments should be in the subscene coordinates,
4409      * not that of {@link javafx.scene.Scene}.
4410      *
4411      * @param scenePoint a point on a Scene
4412      * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
4413      * Null is also returned if the transformation from local to Scene is not invertible.
4414      */
4415     public Point2D sceneToLocal(Point2D scenePoint) {
4416         return sceneToLocal(scenePoint.getX(), scenePoint.getY());
4417     }
4418 
4419     /**
4420      * Transforms a point from the coordinate space of the scene
4421      * into the local coordinate space of this {@code Node}.
4422      *
4423      * Note that if this node is in a {@link SubScene}, the arguments should be in the subscene coordinates,
4424      * not that of {@link javafx.scene.Scene}.
4425      *
4426      * @param scenePoint a point on a Scene
4427      * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
4428      * Null is also returned if the transformation from local to Scene is not invertible.
4429      * @since JavaFX 8.0
4430      */
4431     public Point3D sceneToLocal(Point3D scenePoint) {
4432         return sceneToLocal(scenePoint.getX(), scenePoint.getY(), scenePoint.getZ());
4433     }
4434 
4435     /**
4436      * Transforms a point from the coordinate space of the scene
4437      * into the local coordinate space of this {@code Node}.
4438      *
4439      * Note that if this node is in a {@link SubScene}, the arguments should be in the subscene coordinates,
4440      * not that of {@link javafx.scene.Scene}.
4441      *
4442      * @param sceneX x coordinate of a point on a Scene
4443      * @param sceneY y coordinate of a point on a Scene
4444      * @param sceneZ z coordinate of a point on a Scene
4445      * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
4446      * Null is also returned if the transformation from local to Scene is not invertible.
4447      * @since JavaFX 8.0
4448      */
4449     public Point3D sceneToLocal(double sceneX, double sceneY, double sceneZ) {
4450         try {
4451             return sceneToLocal0(sceneX, sceneY, sceneZ);
4452         } catch (NoninvertibleTransformException ex) {
4453             return null;
4454         }
4455     }
4456 
4457     /**
4458      * Internal method to transform a point from scene to local coordinates.
4459      */
4460     private Point3D sceneToLocal0(double x, double y, double z) throws NoninvertibleTransformException {
4461         final com.sun.javafx.geom.Vec3d tempV3D =
4462                 TempState.getInstance().vec3d;
4463         tempV3D.set(x, y, z);
4464         sceneToLocal(tempV3D);
4465         return new Point3D(tempV3D.x, tempV3D.y, tempV3D.z);
4466     }
4467 
4468     /**
4469      * Transforms a rectangle from the coordinate space of the
4470      * scene into the local coordinate space of this
4471      * {@code Node}.
4472      *
4473      * Note that if this node is in a {@link SubScene}, the arguments should be in the subscene coordinates,
4474      * not that of {@link javafx.scene.Scene}.
4475      *
4476      * @param sceneBounds bounds on a Scene
4477      * @return bounds in the local Node'space or null if Node is not in a {@link Window}.
4478      * Null is also returned if the transformation from local to Scene is not invertible.
4479      */
4480     public Bounds sceneToLocal(Bounds sceneBounds) {
4481         // Do a quick update of localToParentTransform so that we can determine
4482         // if this tx is 2D transform
4483         updateLocalToParentTransform();
4484         if (localToParentTx.is2D() && (sceneBounds.getMinZ() == 0) && (sceneBounds.getMaxZ() == 0)) {
4485             Point2D p1 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY());
4486             Point2D p2 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY());
4487             Point2D p3 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY());
4488             Point2D p4 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY());
4489 
4490             return BoundsUtils.createBoundingBox(p1, p2, p3, p4);
4491         }
4492         try {
4493             Point3D p1 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
4494             Point3D p2 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
4495             Point3D p3 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
4496             Point3D p4 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
4497             Point3D p5 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
4498             Point3D p6 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
4499             Point3D p7 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
4500             Point3D p8 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
4501             return BoundsUtils.createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
4502         } catch (NoninvertibleTransformException e) {
4503             return null;
4504         }
4505     }
4506 
4507     /**
4508      * Transforms a point from the local coordinate space of this {@code Node}
4509      * into the coordinate space of its {@link javafx.stage.Screen}.
4510      * @param localX x coordinate of a point in Node's space
4511      * @param localY y coordinate of a point in Node's space
4512      * @return screen coordinates of the point or null if Node is not in a {@link Window}
4513      * @since JavaFX 8.0
4514      */
4515     public Point2D localToScreen(double localX, double localY) {
4516         return localToScreen(localX, localY, 0.0);
4517     }
4518 
4519     /**
4520      * Transforms a point from the local coordinate space of this {@code Node}
4521      * into the coordinate space of its {@link javafx.stage.Screen}.
4522      * @param localPoint a point in Node's space
4523      * @return screen coordinates of the point or null if Node is not in a {@link Window}
4524      * @since JavaFX 8.0
4525      */
4526     public Point2D localToScreen(Point2D localPoint) {
4527         return localToScreen(localPoint.getX(), localPoint.getY());
4528     }
4529 
4530     /**
4531      * Transforms a point from the local coordinate space of this {@code Node}
4532      * into the coordinate space of its {@link javafx.stage.Screen}.
4533      * @param localX x coordinate of a point in Node's space
4534      * @param localY y coordinate of a point in Node's space
4535      * @param localZ z coordinate of a point in Node's space
4536      * @return screen coordinates of the point or null if Node is not in a {@link Window}
4537      * @since JavaFX 8.0
4538      */
4539     public Point2D localToScreen(double localX, double localY, double localZ) {
4540         Scene scene = getScene();
4541         if (scene == null) return null;
4542         Window window = scene.getWindow();
4543         if (window == null) return null;
4544 
4545         Point3D pt = localToScene(localX, localY, localZ);
4546         final SubScene subScene = getSubScene();
4547         if (subScene != null) {
4548             pt = SceneUtils.subSceneToScene(subScene, pt);
4549         }
4550         final Point2D projection = CameraHelper.project(
4551                 SceneHelper.getEffectiveCamera(getScene()), pt);
4552 
4553         return new Point2D(projection.getX() + scene.getX() + window.getX(),
4554                            projection.getY() + scene.getY() + window.getY());
4555     }
4556 
4557     /**
4558      * Transforms a point from the local coordinate space of this {@code Node}
4559      * into the coordinate space of its {@link javafx.stage.Screen}.
4560      * @param localPoint a point in Node's space
4561      * @return screen coordinates of the point or null if Node is not in a {@link Window}
4562      * @since JavaFX 8.0
4563      */
4564     public Point2D localToScreen(Point3D localPoint) {
4565         return localToScreen(localPoint.getX(), localPoint.getY(), localPoint.getZ());
4566     }
4567 
4568     /**
4569      * Transforms a bounds from the local coordinate space of this
4570      * {@code Node} into the coordinate space of its {@link javafx.stage.Screen}.
4571      * @param localBounds bounds in Node's space
4572      * @return the bounds in screen coordinates or null if Node is not in a {@link Window}
4573      * @since JavaFX 8.0
4574      */
4575     public Bounds localToScreen(Bounds localBounds) {
4576         final Point2D p1 = localToScreen(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMinZ());
4577         final Point2D p2 = localToScreen(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMaxZ());
4578         final Point2D p3 = localToScreen(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMinZ());
4579         final Point2D p4 = localToScreen(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMaxZ());
4580         final Point2D p5 = localToScreen(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMinZ());
4581         final Point2D p6 = localToScreen(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMaxZ());
4582         final Point2D p7 = localToScreen(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMinZ());
4583         final Point2D p8 = localToScreen(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMaxZ());
4584 
4585         return BoundsUtils.createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
4586     }
4587 
4588     /**
4589      * Transforms a point from the local coordinate space of this {@code Node}
4590      * into the coordinate space of its scene.
4591      * Note that if this node is in a {@link SubScene}, the result is in the subscene coordinates,
4592      * not that of {@link javafx.scene.Scene}.
4593      * @param localX x coordinate of a point in Node's space
4594      * @param localY y coordinate of a point in Node's space
4595      * @return scene coordinates of the point or null if Node is not in a {@link Window}
4596      */
4597     public Point2D localToScene(double localX, double localY) {
4598         final com.sun.javafx.geom.Point2D tempPt =
4599                 TempState.getInstance().point;
4600         tempPt.setLocation((float)localX, (float)localY);
4601         localToScene(tempPt);
4602         return new Point2D(tempPt.x, tempPt.y);
4603     }
4604 
4605     /**
4606      * Transforms a point from the local coordinate space of this {@code Node}
4607      * into the coordinate space of its scene.
4608      * Note that if this node is in a {@link SubScene}, the result is in the subscene coordinates,
4609      * not that of {@link javafx.scene.Scene}.
4610      * @param localPoint a point in Node's space
4611      * @return scene coordinates of the point or null if Node is not in a {@link Window}
4612      */
4613     public Point2D localToScene(Point2D localPoint) {
4614         return localToScene(localPoint.getX(), localPoint.getY());
4615     }
4616 
4617     /**
4618      * Transforms a point from the local coordinate space of this {@code Node}
4619      * into the coordinate space of its scene.
4620      * Note that if this node is in a {@link SubScene}, the result is in the subscene coordinates,
4621      * not that of {@link javafx.scene.Scene}.
4622      * @param localPoint a 3D point in Node's space
4623      * @return the transformed 3D point in Scene's space
4624      * @see #localToScene(javafx.geometry.Point3D, boolean)
4625      * @since JavaFX 8.0
4626      */
4627     public Point3D localToScene(Point3D localPoint) {
4628         return localToScene(localPoint.getX(), localPoint.getY(), localPoint.getZ());
4629     }
4630 
4631     /**
4632      * Transforms a point from the local coordinate space of this {@code Node}
4633      * into the coordinate space of its scene.
4634      * Note that if this node is in a {@link SubScene}, the result is in the subscene coordinates,
4635      * not that of {@link javafx.scene.Scene}.
4636      * @param x the x coordinate of a point in Node's space
4637      * @param y the y coordinate of a point in Node's space
4638      * @param z the z coordinate of a point in Node's space
4639      * @return the transformed 3D point in Scene's space
4640      * @see #localToScene(double, double, double, boolean)
4641      * @since JavaFX 8.0
4642      */
4643     public Point3D localToScene(double x, double y, double z) {
4644         final com.sun.javafx.geom.Vec3d tempV3D =
4645                 TempState.getInstance().vec3d;
4646         tempV3D.set(x, y, z);
4647         localToScene(tempV3D);
4648         return new Point3D(tempV3D.x, tempV3D.y, tempV3D.z);
4649     }
4650 
4651     /**
4652      * Transforms a point from the local coordinate space of this {@code Node}
4653      * into the coordinate space of its scene.
4654      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the result point is in {@link Scene} coordinates
4655      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4656      * {@link #localToScene(javafx.geometry.Point3D)}
4657      *
4658      * @param localPoint the point in local coordinates
4659      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4660      * @return transformed point
4661      *
4662      * @see #localToScene(javafx.geometry.Point3D)
4663      * @since JavaFX 8u40
4664      */
4665     public Point3D localToScene(Point3D localPoint, boolean rootScene) {
4666         Point3D pt = localToScene(localPoint);
4667         if (rootScene) {
4668             final SubScene subScene = getSubScene();
4669             if (subScene != null) {
4670                 pt = SceneUtils.subSceneToScene(subScene, pt);
4671             }
4672         }
4673         return pt;
4674     }
4675 
4676     /**
4677      * Transforms a point from the local coordinate space of this {@code Node}
4678      * into the coordinate space of its scene.
4679      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the result point is in {@link Scene} coordinates
4680      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4681      * {@link #localToScene(double, double, double)}
4682      *
4683      * @param x the x coordinate of the point in local coordinates
4684      * @param y the y coordinate of the point in local coordinates
4685      * @param z the z coordinate of the point in local coordinates
4686      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4687      * @return transformed point
4688      *
4689      * @see #localToScene(double, double, double)
4690      * @since JavaFX 8u40
4691      */
4692     public Point3D localToScene(double x, double y, double z, boolean rootScene) {
4693         return localToScene(new Point3D(x, y, z), rootScene);
4694     }
4695 
4696     /**
4697      * Transforms a point from the local coordinate space of this {@code Node}
4698      * into the coordinate space of its scene.
4699      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the result point is in {@link Scene} coordinates
4700      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4701      * {@link #localToScene(javafx.geometry.Point2D)}
4702      *
4703      * @param localPoint the point in local coordinates
4704      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4705      * @return transformed point
4706      *
4707      * @see #localToScene(javafx.geometry.Point2D)
4708      * @since JavaFX 8u40
4709      */
4710     public Point2D localToScene(Point2D localPoint, boolean rootScene) {
4711         if (!rootScene) {
4712             return localToScene(localPoint);
4713         }
4714         Point3D pt = localToScene(localPoint.getX(), localPoint.getY(), 0, rootScene);
4715         return new Point2D(pt.getX(), pt.getY());
4716     }
4717 
4718     /**
4719      * Transforms a point from the local coordinate space of this {@code Node}
4720      * into the coordinate space of its scene.
4721      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the result point is in {@link Scene} coordinates
4722      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4723      * {@link #localToScene(double, double)}
4724      *
4725      * @param x the x coordinate of the point in local coordinates
4726      * @param y the y coordinate of the point in local coordinates
4727      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4728      * @return transformed point
4729      *
4730      * @see #localToScene(double, double)
4731      * @since JavaFX 8u40
4732      */
4733     public Point2D localToScene(double x, double y, boolean rootScene) {
4734         return localToScene(new Point2D(x, y), rootScene);
4735     }
4736 
4737     /**
4738      * Transforms a bounds from the local coordinate space of this {@code Node}
4739      * into the coordinate space of its scene.
4740      * If the Node does not have any {@link SubScene} or {@code rootScene} is set to true, the result bounds are in {@link Scene} coordinates
4741      * of the Node returned by {@link #getScene()}. Othwerwise, the subscene coordinates are used, which is equivalent to calling
4742      * {@link #localToScene(javafx.geometry.Bounds)}
4743      *
4744      * @param localBounds the bounds in local coordinates
4745      * @param rootScene whether Scene coordinates should be used even if the Node is in a SubScene
4746      * @return transformed bounds
4747      *
4748      * @see #localToScene(javafx.geometry.Bounds)
4749      * @since JavaFX 8u40
4750      */
4751     public Bounds localToScene(Bounds localBounds, boolean rootScene) {
4752         if (!rootScene) {
4753             return localToScene(localBounds);
4754         }
4755         Point3D p1 = localToScene(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMinZ(), true);
4756         Point3D p2 = localToScene(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMaxZ(), true);
4757         Point3D p3 = localToScene(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMinZ(), true);
4758         Point3D p4 = localToScene(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMaxZ(), true);
4759         Point3D p5 = localToScene(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMinZ(), true);
4760         Point3D p6 = localToScene(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMaxZ(), true);
4761         Point3D p7 = localToScene(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMinZ(), true);
4762         Point3D p8 = localToScene(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMaxZ(), true);
4763         return BoundsUtils.createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
4764     }
4765 
4766     /**
4767      * Transforms a bounds from the local coordinate space of this
4768      * {@code Node} into the coordinate space of its scene.
4769      * Note that if this node is in a {@link SubScene}, the result is in the subscene coordinates,
4770      * not that of {@link javafx.scene.Scene}.
4771      * @param localBounds bounds in Node's space
4772      * @return the bounds in the scene coordinates or null if Node is not in a {@link Window}
4773      * @see #localToScene(javafx.geometry.Bounds, boolean)
4774      */
4775     public Bounds localToScene(Bounds localBounds) {
4776         // Do a quick update of localToParentTransform so that we can determine
4777         // if this tx is 2D transform
4778         updateLocalToParentTransform();
4779         if (localToParentTx.is2D() && (localBounds.getMinZ() == 0) && (localBounds.getMaxZ() == 0)) {
4780             Point2D p1 = localToScene(localBounds.getMinX(), localBounds.getMinY());
4781             Point2D p2 = localToScene(localBounds.getMaxX(), localBounds.getMinY());
4782             Point2D p3 = localToScene(localBounds.getMaxX(), localBounds.getMaxY());
4783             Point2D p4 = localToScene(localBounds.getMinX(), localBounds.getMaxY());
4784 
4785             return BoundsUtils.createBoundingBox(p1, p2, p3, p4);
4786         }
4787         Point3D p1 = localToScene(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMinZ());
4788         Point3D p2 = localToScene(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMaxZ());
4789         Point3D p3 = localToScene(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMinZ());
4790         Point3D p4 = localToScene(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMaxZ());
4791         Point3D p5 = localToScene(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMinZ());
4792         Point3D p6 = localToScene(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMaxZ());
4793         Point3D p7 = localToScene(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMinZ());
4794         Point3D p8 = localToScene(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMaxZ());
4795         return BoundsUtils.createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
4796 
4797     }
4798 
4799     /**
4800      * Transforms a point from the coordinate space of the parent into the
4801      * local coordinate space of this {@code Node}.
4802      * @param parentX the x coordinate in Parent's space
4803      * @param parentY the y coordinate in Parent's space
4804      * @return the transformed 2D point in Node's space
4805      */
4806     public Point2D parentToLocal(double parentX, double parentY) {
4807         final com.sun.javafx.geom.Point2D tempPt =
4808                 TempState.getInstance().point;
4809         tempPt.setLocation((float)parentX, (float)parentY);
4810         try {
4811             parentToLocal(tempPt);
4812         } catch (NoninvertibleTransformException e) {
4813             return null;
4814         }
4815         return new Point2D(tempPt.x, tempPt.y);
4816     }
4817 
4818     /**
4819      * Transforms a point from the coordinate space of the parent into the
4820      * local coordinate space of this {@code Node}.
4821      * @param parentPoint the 2D point in Parent's space
4822      * @return the transformed 2D point in Node's space
4823      */
4824     public Point2D parentToLocal(Point2D parentPoint) {
4825         return parentToLocal(parentPoint.getX(), parentPoint.getY());
4826     }
4827 
4828     /**
4829      * Transforms a point from the coordinate space of the parent into the
4830      * local coordinate space of this {@code Node}.
4831      * @param parentPoint parentPoint the 3D point in Parent's space
4832      * @return the transformed 3D point in Node's space
4833      * @since JavaFX 8.0
4834      */
4835     public Point3D parentToLocal(Point3D parentPoint) {
4836         return parentToLocal(parentPoint.getX(), parentPoint.getY(), parentPoint.getZ());
4837     }
4838 
4839     /**
4840      * Transforms a point from the coordinate space of the parent into the
4841      * local coordinate space of this {@code Node}.
4842      * @param parentX the x coordinate in Parent's space
4843      * @param parentY the y coordinate in Parent's space
4844      * @param parentZ the z coordinate in Parent's space
4845      * @return the transformed 3D point in Node's space
4846      * @since JavaFX 8.0
4847      */
4848     public Point3D parentToLocal(double parentX, double parentY, double parentZ) {
4849         final com.sun.javafx.geom.Vec3d tempV3D =
4850                 TempState.getInstance().vec3d;
4851         tempV3D.set(parentX, parentY, parentZ);
4852         try {
4853             parentToLocal(tempV3D);
4854         } catch (NoninvertibleTransformException e) {
4855             return null;
4856         }
4857         return new Point3D(tempV3D.x, tempV3D.y, tempV3D.z);
4858     }
4859 
4860     /**
4861      * Transforms a rectangle from the coordinate space of the parent into the
4862      * local coordinate space of this {@code Node}.
4863      * @param parentBounds the bounds in Parent's space
4864      * @return the transformed bounds in Node's space
4865      */
4866     public Bounds parentToLocal(Bounds parentBounds) {
4867         // Do a quick update of localToParentTransform so that we can determine
4868         // if this tx is 2D transform
4869         updateLocalToParentTransform();
4870         if (localToParentTx.is2D() && (parentBounds.getMinZ() == 0) && (parentBounds.getMaxZ() == 0)) {
4871             Point2D p1 = parentToLocal(parentBounds.getMinX(), parentBounds.getMinY());
4872             Point2D p2 = parentToLocal(parentBounds.getMaxX(), parentBounds.getMinY());
4873             Point2D p3 = parentToLocal(parentBounds.getMaxX(), parentBounds.getMaxY());
4874             Point2D p4 = parentToLocal(parentBounds.getMinX(), parentBounds.getMaxY());
4875 
4876             return BoundsUtils.createBoundingBox(p1, p2, p3, p4);
4877         }
4878         Point3D p1 = parentToLocal(parentBounds.getMinX(), parentBounds.getMinY(), parentBounds.getMinZ());
4879         Point3D p2 = parentToLocal(parentBounds.getMinX(), parentBounds.getMinY(), parentBounds.getMaxZ());
4880         Point3D p3 = parentToLocal(parentBounds.getMinX(), parentBounds.getMaxY(), parentBounds.getMinZ());
4881         Point3D p4 = parentToLocal(parentBounds.getMinX(), parentBounds.getMaxY(), parentBounds.getMaxZ());
4882         Point3D p5 = parentToLocal(parentBounds.getMaxX(), parentBounds.getMaxY(), parentBounds.getMinZ());
4883         Point3D p6 = parentToLocal(parentBounds.getMaxX(), parentBounds.getMaxY(), parentBounds.getMaxZ());
4884         Point3D p7 = parentToLocal(parentBounds.getMaxX(), parentBounds.getMinY(), parentBounds.getMinZ());
4885         Point3D p8 = parentToLocal(parentBounds.getMaxX(), parentBounds.getMinY(), parentBounds.getMaxZ());
4886         return BoundsUtils.createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
4887     }
4888 
4889     /**
4890      * Transforms a point from the local coordinate space of this {@code Node}
4891      * into the coordinate space of its parent.
4892      * @param localX the x coordinate of the point in Node's space
4893      * @param localY the y coordinate of the point in Node's space
4894      * @return the transformed 2D point in Parent's space
4895      */
4896     public Point2D localToParent(double localX, double localY) {
4897         final com.sun.javafx.geom.Point2D tempPt =
4898                 TempState.getInstance().point;
4899         tempPt.setLocation((float)localX, (float)localY);
4900         localToParent(tempPt);
4901         return new Point2D(tempPt.x, tempPt.y);
4902     }
4903 
4904     /**
4905      * Transforms a point from the local coordinate space of this {@code Node}
4906      * into the coordinate space of its parent.
4907      * @param localPoint the 2D point in Node's space
4908      * @return the transformed 2D point in Parent's space
4909      */
4910     public Point2D localToParent(Point2D localPoint) {
4911         return localToParent(localPoint.getX(), localPoint.getY());
4912     }
4913 
4914     /**
4915      * Transforms a point from the local coordinate space of this {@code Node}
4916      * into the coordinate space of its parent.
4917      * @param localPoint the 3D point in Node's space
4918      * @return the transformed 3D point in Parent's space
4919      * @since JavaFX 8.0
4920      */
4921     public Point3D localToParent(Point3D localPoint) {
4922         return localToParent(localPoint.getX(), localPoint.getY(), localPoint.getZ());
4923     }
4924 
4925     /**
4926      * Transforms a point from the local coordinate space of this {@code Node}
4927      * into the coordinate space of its parent.
4928      * @param x the x coordinate of the point in Node's space
4929      * @param y the y coordinate of the point in Node's space
4930      * @param z the z coordinate of the point in Node's space
4931      * @return the transformed 3D point in Parent's space
4932      * @since JavaFX 8.0
4933      */
4934     public Point3D localToParent(double x, double y, double z) {
4935         final com.sun.javafx.geom.Vec3d tempV3D =
4936                 TempState.getInstance().vec3d;
4937         tempV3D.set(x, y, z);
4938         localToParent(tempV3D);
4939         return new Point3D(tempV3D.x, tempV3D.y, tempV3D.z);
4940     }
4941 
4942     /**
4943      * Transforms a bounds from the local coordinate space of this
4944      * {@code Node} into the coordinate space of its parent.
4945      * @param localBounds the bounds in Node's space
4946      * @return the transformed bounds in Parent's space
4947      */
4948     public Bounds localToParent(Bounds localBounds) {
4949         // Do a quick update of localToParentTransform so that we can determine
4950         // if this tx is 2D transform
4951         updateLocalToParentTransform();
4952         if (localToParentTx.is2D() && (localBounds.getMinZ() == 0) && (localBounds.getMaxZ() == 0)) {
4953             Point2D p1 = localToParent(localBounds.getMinX(), localBounds.getMinY());
4954             Point2D p2 = localToParent(localBounds.getMaxX(), localBounds.getMinY());
4955             Point2D p3 = localToParent(localBounds.getMaxX(), localBounds.getMaxY());
4956             Point2D p4 = localToParent(localBounds.getMinX(), localBounds.getMaxY());
4957 
4958             return BoundsUtils.createBoundingBox(p1, p2, p3, p4);
4959         }
4960         Point3D p1 = localToParent(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMinZ());
4961         Point3D p2 = localToParent(localBounds.getMinX(), localBounds.getMinY(), localBounds.getMaxZ());
4962         Point3D p3 = localToParent(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMinZ());
4963         Point3D p4 = localToParent(localBounds.getMinX(), localBounds.getMaxY(), localBounds.getMaxZ());
4964         Point3D p5 = localToParent(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMinZ());
4965         Point3D p6 = localToParent(localBounds.getMaxX(), localBounds.getMaxY(), localBounds.getMaxZ());
4966         Point3D p7 = localToParent(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMinZ());
4967         Point3D p8 = localToParent(localBounds.getMaxX(), localBounds.getMinY(), localBounds.getMaxZ());
4968         return BoundsUtils.createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
4969     }
4970 
4971     /**
4972      * Copy the localToParent transform into specified transform.
4973      */
4974     BaseTransform getLocalToParentTransform(BaseTransform tx) {
4975         updateLocalToParentTransform();
4976         tx.setTransform(localToParentTx);
4977         return tx;
4978     }
4979 
4980     /*
4981      * Currently used only by PathTransition
4982      */
4983     final BaseTransform getLeafTransform() {
4984         return getLocalToParentTransform(TempState.getInstance().leafTx);
4985     }
4986 
4987     /*
4988      * Invoked whenever the transforms[] ObservableList changes, or by the transforms
4989      * in that ObservableList whenever they are changed.
4990      *
4991      * Note: This method MUST only be called via its accessor method.
4992      */
4993     private void doTransformsChanged() {
4994         if (!transformDirty) {
4995             NodeHelper.markDirty(this, DirtyBits.NODE_TRANSFORM);
4996             transformDirty = true;
4997             transformedBoundsChanged();
4998         }
4999         invalidateLocalToParentTransform();
5000         invalidateLocalToSceneTransform();
5001     }
5002 
5003     final double getPivotX() {
5004         final Bounds bounds = getLayoutBounds();
5005         return bounds.getMinX() + bounds.getWidth()/2;
5006     }
5007 
5008     final double getPivotY() {
5009         final Bounds bounds = getLayoutBounds();
5010         return bounds.getMinY() + bounds.getHeight()/2;
5011     }
5012 
5013     final double getPivotZ() {
5014         final Bounds bounds = getLayoutBounds();
5015         return bounds.getMinZ() + bounds.getDepth()/2;
5016     }
5017 
5018     /**
5019      * This helper function will update the transform matrix on the peer based
5020      * on the "complete" transform for this node.
5021      */
5022     void updateLocalToParentTransform() {
5023         if (transformDirty) {
5024             localToParentTx.setToIdentity();
5025 
5026             boolean mirror = false;
5027             double mirroringCenter = 0;
5028             if (hasMirroring()) {
5029                 final Scene sceneValue = getScene();
5030                 if ((sceneValue != null) && (sceneValue.getRoot() == this)) {
5031                     // handle scene mirroring in this branch
5032                     // (must be the last transformation)
5033                     mirroringCenter = sceneValue.getWidth() / 2;
5034                     if (mirroringCenter == 0.0) {
5035                         mirroringCenter = getPivotX();
5036                     }
5037 
5038                     localToParentTx = localToParentTx.deriveWithTranslation(
5039                             mirroringCenter, 0.0);
5040                     localToParentTx = localToParentTx.deriveWithScale(
5041                             -1.0, 1.0, 1.0);
5042                     localToParentTx = localToParentTx.deriveWithTranslation(
5043                             -mirroringCenter, 0.0);
5044                 } else {
5045                     // mirror later
5046                     mirror = true;
5047                     mirroringCenter = getPivotX();
5048                 }
5049             }
5050 
5051             if (getScaleX() != 1 || getScaleY() != 1 || getScaleZ() != 1 || getRotate() != 0) {
5052                 // recompute pivotX, pivotY and pivotZ
5053                 double pivotX = getPivotX();
5054                 double pivotY = getPivotY();
5055                 double pivotZ = getPivotZ();
5056 
5057                 localToParentTx = localToParentTx.deriveWithTranslation(
5058                         getTranslateX() + getLayoutX() + pivotX,
5059                         getTranslateY() + getLayoutY() + pivotY,
5060                         getTranslateZ() + pivotZ);
5061                 localToParentTx = localToParentTx.deriveWithRotation(
5062                         Math.toRadians(getRotate()), getRotationAxis().getX(),
5063                         getRotationAxis().getY(), getRotationAxis().getZ());
5064                 localToParentTx = localToParentTx.deriveWithScale(
5065                         getScaleX(), getScaleY(), getScaleZ());
5066                 localToParentTx = localToParentTx.deriveWithTranslation(
5067                         -pivotX, -pivotY, -pivotZ);
5068             } else {
5069                 localToParentTx = localToParentTx.deriveWithTranslation(
5070                         getTranslateX() + getLayoutX(),
5071                         getTranslateY() + getLayoutY(),
5072                         getTranslateZ());
5073             }
5074 
5075             if (hasTransforms()) {
5076                 for (Transform t : getTransforms()) {
5077                     localToParentTx = TransformHelper.derive(t, localToParentTx);
5078                 }
5079             }
5080 
5081             // Check to see whether the node requires mirroring
5082             if (mirror) {
5083                 localToParentTx = localToParentTx.deriveWithTranslation(
5084                         mirroringCenter, 0);
5085                 localToParentTx = localToParentTx.deriveWithScale(
5086                         -1.0, 1.0, 1.0);
5087                 localToParentTx = localToParentTx.deriveWithTranslation(
5088                         -mirroringCenter, 0);
5089             }
5090 
5091             transformDirty = false;
5092         }
5093     }
5094 
5095     /**
5096      * Transforms in place the specified point from parent coords to local
5097      * coords. Made package private for the sake of testing.
5098      */
5099     void parentToLocal(com.sun.javafx.geom.Point2D pt) throws NoninvertibleTransformException {
5100         updateLocalToParentTransform();
5101         localToParentTx.inverseTransform(pt, pt);
5102     }
5103 
5104     void parentToLocal(com.sun.javafx.geom.Vec3d pt) throws NoninvertibleTransformException {
5105         updateLocalToParentTransform();
5106         localToParentTx.inverseTransform(pt, pt);
5107     }
5108 
5109     void sceneToLocal(com.sun.javafx.geom.Point2D pt) throws NoninvertibleTransformException {
5110         if (getParent() != null) {
5111             getParent().sceneToLocal(pt);
5112         }
5113         parentToLocal(pt);
5114     }
5115 
5116     void sceneToLocal(com.sun.javafx.geom.Vec3d pt) throws NoninvertibleTransformException {
5117         if (getParent() != null) {
5118             getParent().sceneToLocal(pt);
5119         }
5120         parentToLocal(pt);
5121     }
5122 
5123     void localToScene(com.sun.javafx.geom.Point2D pt) {
5124         localToParent(pt);
5125         if (getParent() != null) {
5126             getParent().localToScene(pt);
5127         }
5128     }
5129 
5130     void localToScene(com.sun.javafx.geom.Vec3d pt) {
5131         localToParent(pt);
5132         if (getParent() != null) {
5133             getParent().localToScene(pt);
5134         }
5135     }
5136 
5137     /***************************************************************************
5138      *                                                                         *
5139      * Mouse event related APIs                                                *
5140      *                                                                         *
5141      **************************************************************************/
5142 
5143     /**
5144      * Transforms in place the specified point from local coords to parent
5145      * coords. Made package private for the sake of testing.
5146      */
5147     void localToParent(com.sun.javafx.geom.Point2D pt) {
5148         updateLocalToParentTransform();
5149         localToParentTx.transform(pt, pt);
5150     }
5151 
5152     void localToParent(com.sun.javafx.geom.Vec3d pt) {
5153         updateLocalToParentTransform();
5154         localToParentTx.transform(pt, pt);
5155     }
5156 
5157     /*
5158      * Finds a top-most child node that contains the given local coordinates.
5159      *
5160      * The result argument is used for storing the picking result.
5161      *
5162      * Note: This method MUST only be called via its accessor method.
5163      */
5164     private void doPickNodeLocal(PickRay localPickRay, PickResultChooser result) {
5165         intersects(localPickRay, result);
5166     }
5167 
5168     /*
5169      * Finds a top-most child node that intersects the given ray.
5170      *
5171      * The result argument is used for storing the picking result.
5172      */
5173     final void pickNode(PickRay pickRay, PickResultChooser result) {
5174 
5175         // In some conditions we can omit picking this node or subgraph
5176         if (!isVisible() || isDisable() || isMouseTransparent()) {
5177             return;
5178         }
5179 
5180         final Vec3d o = pickRay.getOriginNoClone();
5181         final double ox = o.x;
5182         final double oy = o.y;
5183         final double oz = o.z;
5184         final Vec3d d = pickRay.getDirectionNoClone();
5185         final double dx = d.x;
5186         final double dy = d.y;
5187         final double dz = d.z;
5188 
5189         updateLocalToParentTransform();
5190         try {
5191             localToParentTx.inverseTransform(o, o);
5192             localToParentTx.inverseDeltaTransform(d, d);
5193 
5194             // Delegate to a function which can be overridden by subclasses which
5195             // actually does the pick. The implementation is markedly different
5196             // for leaf nodes vs. parent nodes vs. region nodes.
5197             NodeHelper.pickNodeLocal(this, pickRay, result);
5198         } catch (NoninvertibleTransformException e) {
5199             // in this case we just don't pick anything
5200         }
5201 
5202         pickRay.setOrigin(ox, oy, oz);
5203         pickRay.setDirection(dx, dy, dz);
5204     }
5205 
5206     /*
5207      * Returns {@code true} if the given ray (start, dir), specified in the
5208      * local coordinate space of this {@code Node}, intersects the
5209      * shape of this {@code Node}. Note that this method does not take visibility
5210      * into account; the test is based on the geometry of this {@code Node} only.
5211      * <p>
5212      * The pickResult is updated if the found intersection is closer than
5213      * the currently held one.
5214      * <p>
5215      * Note that this is a conditional feature. See
5216      * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
5217      * for more information.
5218      */
5219     final boolean intersects(PickRay pickRay, PickResultChooser pickResult) {
5220         double boundsDistance = intersectsBounds(pickRay);
5221         if (!Double.isNaN(boundsDistance)) {
5222             if (isPickOnBounds()) {
5223                 if (pickResult != null) {
5224                     pickResult.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
5225                 }
5226                 return true;
5227             } else {
5228                 return NodeHelper.computeIntersects(this, pickRay, pickResult);
5229             }
5230         }
5231         return false;
5232     }
5233 
5234     /*
5235      * Computes the intersection of the pickRay with this node.
5236      * The pickResult argument is updated if the found intersection
5237      * is closer than the passed one. On the other hand, the return value
5238      * specifies whether the intersection exists, regardless of its comparison
5239      * with the given pickResult.
5240      */
5241     private boolean doComputeIntersects(PickRay pickRay, PickResultChooser pickResult) {
5242         double origZ = pickRay.getOriginNoClone().z;
5243         double dirZ = pickRay.getDirectionNoClone().z;
5244         // Handle the case where pickRay is almost parallel to the Z-plane
5245         if (almostZero(dirZ)) {
5246             return false;
5247         }
5248         double t = -origZ / dirZ;
5249         if (t < pickRay.getNearClip() || t > pickRay.getFarClip()) {
5250             return false;
5251         }
5252         double x = pickRay.getOriginNoClone().x + (pickRay.getDirectionNoClone().x * t);
5253         double y = pickRay.getOriginNoClone().y + (pickRay.getDirectionNoClone().y * t);
5254 
5255         if (contains((float) x, (float) y)) {
5256             if (pickResult != null) {
5257                 pickResult.offer(this, t, PickResultChooser.computePoint(pickRay, t));
5258             }
5259             return true;
5260         }
5261         return false;
5262     }
5263 
5264     /*
5265      * Computes the intersection of the pickRay with the bounds of this node.
5266      * The return value is the distance between the camera and the intersection
5267      * point, measured in pickRay direction magnitudes. If there is
5268      * no intersection, it returns NaN.
5269      *
5270      * @param pickRay The pick ray
5271      * @return Distance of the intersection point, a NaN if there
5272      *         is no intersection
5273      */
5274     final double intersectsBounds(PickRay pickRay) {
5275 
5276         final Vec3d dir = pickRay.getDirectionNoClone();
5277         double tmin, tmax;
5278 
5279         final Vec3d origin = pickRay.getOriginNoClone();
5280         final double originX = origin.x;
5281         final double originY = origin.y;
5282         final double originZ = origin.z;
5283 
5284         final TempState tempState = TempState.getInstance();
5285         BaseBounds tempBounds = tempState.bounds;
5286 
5287         tempBounds = getLocalBounds(tempBounds,
5288                                     BaseTransform.IDENTITY_TRANSFORM);
5289 
5290         if (dir.x == 0.0 && dir.y == 0.0) {
5291             // fast path for the usual 2D picking
5292 
5293             if (dir.z == 0.0) {
5294                 return Double.NaN;
5295             }
5296 
5297             if (originX < tempBounds.getMinX() ||
5298                     originX > tempBounds.getMaxX() ||
5299                     originY < tempBounds.getMinY() ||
5300                     originY > tempBounds.getMaxY()) {
5301                 return Double.NaN;
5302             }
5303 
5304             final double invDirZ = 1.0 / dir.z;
5305             final boolean signZ = invDirZ < 0.0;
5306 
5307             final double minZ = tempBounds.getMinZ();
5308             final double maxZ = tempBounds.getMaxZ();
5309             tmin = ((signZ ? maxZ : minZ) - originZ) * invDirZ;
5310             tmax = ((signZ ? minZ : maxZ) - originZ) * invDirZ;
5311 
5312         } else if (tempBounds.getDepth() == 0.0) {
5313             // fast path for 3D picking of 2D bounds
5314 
5315             if (almostZero(dir.z)) {
5316                 return Double.NaN;
5317             }
5318 
5319             final double t = (tempBounds.getMinZ() - originZ) / dir.z;
5320             final double x = originX + (dir.x * t);
5321             final double y = originY + (dir.y * t);
5322 
5323             if (x < tempBounds.getMinX() ||
5324                     x > tempBounds.getMaxX() ||
5325                     y < tempBounds.getMinY() ||
5326                     y > tempBounds.getMaxY()) {
5327                 return Double.NaN;
5328             }
5329 
5330             tmin = tmax = t;
5331 
5332         } else {
5333 
5334             final double invDirX = dir.x == 0.0 ? Double.POSITIVE_INFINITY : (1.0 / dir.x);
5335             final double invDirY = dir.y == 0.0 ? Double.POSITIVE_INFINITY : (1.0 / dir.y);
5336             final double invDirZ = dir.z == 0.0 ? Double.POSITIVE_INFINITY : (1.0 / dir.z);
5337             final boolean signX = invDirX < 0.0;
5338             final boolean signY = invDirY < 0.0;
5339             final boolean signZ = invDirZ < 0.0;
5340             final double minX = tempBounds.getMinX();
5341             final double minY = tempBounds.getMinY();
5342             final double maxX = tempBounds.getMaxX();
5343             final double maxY = tempBounds.getMaxY();
5344 
5345             tmin = Double.NEGATIVE_INFINITY;
5346             tmax = Double.POSITIVE_INFINITY;
5347             if (Double.isInfinite(invDirX)) {
5348                 if (minX <= originX && maxX >= originX) {
5349                     // move on, we are inside for the whole length
5350                 } else {
5351                     return Double.NaN;
5352                 }
5353             } else {
5354                 tmin = ((signX ? maxX : minX) - originX) * invDirX;
5355                 tmax = ((signX ? minX : maxX) - originX) * invDirX;
5356             }
5357 
5358             if (Double.isInfinite(invDirY)) {
5359                 if (minY <= originY && maxY >= originY) {
5360                     // move on, we are inside for the whole length
5361                 } else {
5362                     return Double.NaN;
5363                 }
5364             } else {
5365                 final double tymin = ((signY ? maxY : minY) - originY) * invDirY;
5366                 final double tymax = ((signY ? minY : maxY) - originY) * invDirY;
5367 
5368                 if ((tmin > tymax) || (tymin > tmax)) {
5369                     return Double.NaN;
5370                 }
5371                 if (tymin > tmin) {
5372                     tmin = tymin;
5373                 }
5374                 if (tymax < tmax) {
5375                     tmax = tymax;
5376                 }
5377             }
5378 
5379             final double minZ = tempBounds.getMinZ();
5380             final double maxZ = tempBounds.getMaxZ();
5381             if (Double.isInfinite(invDirZ)) {
5382                 if (minZ <= originZ && maxZ >= originZ) {
5383                     // move on, we are inside for the whole length
5384                 } else {
5385                     return Double.NaN;
5386                 }
5387             } else {
5388                 final double tzmin = ((signZ ? maxZ : minZ) - originZ) * invDirZ;
5389                 final double tzmax = ((signZ ? minZ : maxZ) - originZ) * invDirZ;
5390 
5391                 if ((tmin > tzmax) || (tzmin > tmax)) {
5392                     return Double.NaN;
5393                 }
5394                 if (tzmin > tmin) {
5395                     tmin = tzmin;
5396                 }
5397                 if (tzmax < tmax) {
5398                     tmax = tzmax;
5399                 }
5400             }
5401         }
5402 
5403         // For clip we use following semantics: pick the node normally
5404         // if there is an intersection with the clip node. We don't consider
5405         // clip node distance.
5406         Node clip = getClip();
5407         if (clip != null
5408                 // FIXME: All 3D picking is currently ignored by rendering.
5409                 // Until this is fixed or defined differently (RT-28510),
5410                 // we follow this behavior.
5411                 && !(this instanceof Shape3D) && !(clip instanceof Shape3D)) {
5412             final double dirX = dir.x;
5413             final double dirY = dir.y;
5414             final double dirZ = dir.z;
5415 
5416             clip.updateLocalToParentTransform();
5417 
5418             boolean hitClip = true;
5419             try {
5420                 clip.localToParentTx.inverseTransform(origin, origin);
5421                 clip.localToParentTx.inverseDeltaTransform(dir, dir);
5422             } catch (NoninvertibleTransformException e) {
5423                 hitClip = false;
5424             }
5425             hitClip = hitClip && clip.intersects(pickRay, null);
5426             pickRay.setOrigin(originX, originY, originZ);
5427             pickRay.setDirection(dirX, dirY, dirZ);
5428 
5429             if (!hitClip) {
5430                 return Double.NaN;
5431             }
5432         }
5433 
5434         if (Double.isInfinite(tmin) || Double.isNaN(tmin)) {
5435             // We've got a nonsense pick ray or bounds.
5436             return Double.NaN;
5437         }
5438 
5439         final double minDistance = pickRay.getNearClip();
5440         final double maxDistance = pickRay.getFarClip();
5441         if (tmin < minDistance) {
5442             if (tmax >= minDistance) {
5443                 // we are inside bounds
5444                 return 0.0;
5445             } else {
5446                 return Double.NaN;
5447             }
5448         } else if (tmin > maxDistance) {
5449             return Double.NaN;
5450         }
5451 
5452         return tmin;
5453     }
5454 
5455 
5456     // Good to find a home for commonly use util. code such as EPS.
5457     // and almostZero. This code currently defined in multiple places,
5458     // such as Affine3D and GeneralTransform3D.
5459     private static final double EPSILON_ABSOLUTE = 1.0e-5;
5460 
5461     static boolean almostZero(double a) {
5462         return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE));
5463     }
5464 
5465     /***************************************************************************
5466      *                                                                         *
5467      *                      viewOrder property handling                        *
5468      *                                                                         *
5469      **************************************************************************/
5470 
5471     /**
5472      * Defines the rendering and picking order of this {@code Node} within its
5473      * parent.
5474      * <p>
5475      * This property is used to alter the rendering and picking order of a node
5476      * within its parent without reordering the parent's {@code children} list.
5477      * For example, this can be used as a more efficient way to implement
5478      * transparency sorting. To do this, an application can assign the viewOrder
5479      * value of each node to the computed distance between that node and the
5480      * viewer.
5481      * </p>
5482      * <p>
5483      * The parent will traverse its {@code children} in decreasing
5484      * {@code viewOrder} order. This means that a child with a lower
5485      * {@code viewOrder} will be in front of a child with a higher
5486      * {@code viewOrder}. If two children have the same {@code viewOrder}, the
5487      * parent will traverse them in the order they appear in the parent's
5488      * {@code children} list.
5489      * </p>
5490      * <p>
5491      * However, {@code viewOrder} does not alter the layout and focus traversal
5492      * order of this Node within its parent. A parent always traverses its
5493      * {@code children} list in order when doing layout or focus traversal.
5494      * </p>
5495      *
5496      * @return the view order for this {@code Node}
5497      * @defaultValue 0.0
5498      *
5499      * @since 9
5500      */
5501     public final DoubleProperty viewOrderProperty() {
5502         return getMiscProperties().viewOrderProperty();
5503     }
5504 
5505     public final void setViewOrder(double value) {
5506         viewOrderProperty().set(value);
5507     }
5508 
5509     public final double getViewOrder() {
5510         return (miscProperties == null) ? DEFAULT_VIEW_ORDER
5511                 : miscProperties.getViewOrder();
5512     }
5513 
5514     /***************************************************************************
5515      *                                                                         *
5516      *                             Transformations                             *
5517      *                                                                         *
5518      **************************************************************************/
5519     /**
5520      * Defines the ObservableList of {@link javafx.scene.transform.Transform} objects
5521      * to be applied to this {@code Node}. This ObservableList of transforms is applied
5522      * before {@link #translateXProperty translateX}, {@link #translateYProperty translateY}, {@link #scaleXProperty scaleX}, and
5523      * {@link #scaleYProperty scaleY}, {@link #rotateProperty rotate} transforms.
5524      *
5525      * @return the transforms for this {@code Node}
5526      * @defaultValue empty
5527      */
5528     public final ObservableList<Transform> getTransforms() {
5529         return transformsProperty();
5530     }
5531 
5532     private ObservableList<Transform> transformsProperty() {
5533         return getNodeTransformation().getTransforms();
5534     }
5535 
5536     public final void setTranslateX(double value) {
5537         translateXProperty().set(value);
5538     }
5539 
5540     public final double getTranslateX() {
5541         return (nodeTransformation == null)
5542                 ? DEFAULT_TRANSLATE_X
5543                 : nodeTransformation.getTranslateX();
5544     }
5545 
5546     /**
5547      * Defines the x coordinate of the translation that is added to this {@code Node}'s
5548      * transform.
5549      * <p>
5550      * The node's final translation will be computed as {@link #layoutXProperty layoutX} + {@code translateX},
5551      * where {@code layoutX} establishes the node's stable position and {@code translateX}
5552      * optionally makes dynamic adjustments to that position.
5553      *<p>
5554      * This variable can be used to alter the location of a node without disturbing
5555      * its {@link #layoutBoundsProperty layoutBounds}, which makes it useful for animating a node's location.
5556      *
5557      * @return the translateX for this {@code Node}
5558      * @defaultValue 0
5559      */
5560     public final DoubleProperty translateXProperty() {
5561         return getNodeTransformation().translateXProperty();
5562     }
5563 
5564     public final void setTranslateY(double value) {
5565         translateYProperty().set(value);
5566     }
5567 
5568     public final double getTranslateY() {
5569         return (nodeTransformation == null)
5570                 ? DEFAULT_TRANSLATE_Y
5571                 : nodeTransformation.getTranslateY();
5572     }
5573 
5574     /**
5575      * Defines the y coordinate of the translation that is added to this {@code Node}'s
5576      * transform.
5577      * <p>
5578      * The node's final translation will be computed as {@link #layoutYProperty layoutY} + {@code translateY},
5579      * where {@code layoutY} establishes the node's stable position and {@code translateY}
5580      * optionally makes dynamic adjustments to that position.
5581      *<p>
5582      * This variable can be used to alter the location of a node without disturbing
5583      * its {@link #layoutBoundsProperty layoutBounds}, which makes it useful for animating a node's location.
5584      *
5585      * @return the translateY for this {@code Node}
5586      * @defaultValue 0
5587      */
5588     public final DoubleProperty translateYProperty() {
5589         return getNodeTransformation().translateYProperty();
5590     }
5591 
5592     public final void setTranslateZ(double value) {
5593         translateZProperty().set(value);
5594     }
5595 
5596     public final double getTranslateZ() {
5597         return (nodeTransformation == null)
5598                 ? DEFAULT_TRANSLATE_Z
5599                 : nodeTransformation.getTranslateZ();
5600     }
5601 
5602     /**
5603      * Defines the Z coordinate of the translation that is added to the
5604      * transformed coordinates of this {@code Node}.  This value will be added
5605      * to any translation defined by the {@code transforms} ObservableList and
5606      * {@code layoutZ}.
5607      *<p>
5608      * This variable can be used to alter the location of a Node without
5609      * disturbing its layout bounds, which makes it useful for animating a
5610      * node's location.
5611      * <p>
5612      * Note that this is a conditional feature. See
5613      * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
5614      * for more information.
5615      *
5616      * @return the translateZ for this {@code Node}
5617      * @defaultValue 0
5618      */
5619     public final DoubleProperty translateZProperty() {
5620         return getNodeTransformation().translateZProperty();
5621     }
5622 
5623     public final void setScaleX(double value) {
5624         scaleXProperty().set(value);
5625     }
5626 
5627     public final double getScaleX() {
5628         return (nodeTransformation == null) ? DEFAULT_SCALE_X
5629                                             : nodeTransformation.getScaleX();
5630     }
5631 
5632     /**
5633      * Defines the factor by which coordinates are scaled about the center of the
5634      * object along the X axis of this {@code Node}. This is used to stretch or
5635      * animate the node either manually or by using an animation.
5636      * <p>
5637      * This scale factor is not included in {@link #layoutBoundsProperty layoutBounds} by
5638      * default, which makes it ideal for scaling the entire node after
5639      * all effects and transforms have been taken into account.
5640      * <p>
5641      * The pivot point about which the scale occurs is the center of the
5642      * untransformed {@link #layoutBoundsProperty layoutBounds}.
5643      *
5644      * @return the scaleX for this {@code Node}
5645      * @defaultValue 1.0
5646      */
5647     public final DoubleProperty scaleXProperty() {
5648         return getNodeTransformation().scaleXProperty();
5649     }
5650 
5651     public final void setScaleY(double value) {
5652         scaleYProperty().set(value);
5653     }
5654 
5655     public final double getScaleY() {
5656         return (nodeTransformation == null) ? DEFAULT_SCALE_Y
5657                                             : nodeTransformation.getScaleY();
5658     }
5659 
5660     /**
5661      * Defines the factor by which coordinates are scaled about the center of the
5662      * object along the Y axis of this {@code Node}. This is used to stretch or
5663      * animate the node either manually or by using an animation.
5664      * <p>
5665      * This scale factor is not included in {@link #layoutBoundsProperty layoutBounds} by
5666      * default, which makes it ideal for scaling the entire node after
5667      * all effects and transforms have been taken into account.
5668      * <p>
5669      * The pivot point about which the scale occurs is the center of the
5670      * untransformed {@link #layoutBoundsProperty layoutBounds}.
5671      *
5672      * @return the scaleY for this {@code Node}
5673      * @defaultValue 1.0
5674      */
5675     public final DoubleProperty scaleYProperty() {
5676         return getNodeTransformation().scaleYProperty();
5677     }
5678 
5679     public final void setScaleZ(double value) {
5680         scaleZProperty().set(value);
5681     }
5682 
5683     public final double getScaleZ() {
5684         return (nodeTransformation == null) ? DEFAULT_SCALE_Z
5685                                             : nodeTransformation.getScaleZ();
5686     }
5687 
5688     /**
5689      * Defines the factor by which coordinates are scaled about the center of the
5690      * object along the Z axis of this {@code Node}. This is used to stretch or
5691      * animate the node either manually or by using an animation.
5692      * <p>
5693      * This scale factor is not included in {@link #layoutBoundsProperty layoutBounds} by
5694      * default, which makes it ideal for scaling the entire node after
5695      * all effects and transforms have been taken into account.
5696      * <p>
5697      * The pivot point about which the scale occurs is the center of the
5698      * rectangular bounds formed by taking {@link #boundsInLocalProperty boundsInLocal} and applying
5699      * all the transforms in the {@link #getTransforms transforms} ObservableList.
5700      * <p>
5701      * Note that this is a conditional feature. See
5702      * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
5703      * for more information.
5704      *
5705      * @return the scaleZ for this {@code Node}
5706      * @defaultValue 1.0
5707      */
5708     public final DoubleProperty scaleZProperty() {
5709         return getNodeTransformation().scaleZProperty();
5710     }
5711 
5712     public final void setRotate(double value) {
5713         rotateProperty().set(value);
5714     }
5715 
5716     public final double getRotate() {
5717         return (nodeTransformation == null) ? DEFAULT_ROTATE
5718                                             : nodeTransformation.getRotate();
5719     }
5720 
5721     /**
5722      * Defines the angle of rotation about the {@code Node}'s center, measured in
5723      * degrees. This is used to rotate the {@code Node}.
5724      * <p>
5725      * This rotation factor is not included in {@link #layoutBoundsProperty layoutBounds} by
5726      * default, which makes it ideal for rotating the entire node after
5727      * all effects and transforms have been taken into account.
5728      * <p>
5729      * The pivot point about which the rotation occurs is the center of the
5730      * untransformed {@link #layoutBoundsProperty layoutBounds}.
5731      * <p>
5732      * Note that because the pivot point is computed as the center of this
5733      * {@code Node}'s layout bounds, any change to the layout bounds will cause
5734      * the pivot point to change, which can move the object. For a leaf node,
5735      * any change to the geometry will cause the layout bounds to change.
5736      * For a group node, any change to any of its children, including a
5737      * change in a child's geometry, clip, effect, position, orientation, or
5738      * scale, will cause the group's layout bounds to change. If this movement
5739      * of the pivot point is not
5740      * desired, applications should instead use the Node's {@link #getTransforms transforms}
5741      * ObservableList, and add a {@link javafx.scene.transform.Rotate} transform,
5742      * which has a user-specifiable pivot point.
5743      *
5744      * @return the rotate for this {@code Node}
5745      * @defaultValue 0.0
5746      */
5747     public final DoubleProperty rotateProperty() {
5748         return getNodeTransformation().rotateProperty();
5749     }
5750 
5751     public final void setRotationAxis(Point3D value) {
5752         rotationAxisProperty().set(value);
5753     }
5754 
5755     public final Point3D getRotationAxis() {
5756         return (nodeTransformation == null)
5757                 ? DEFAULT_ROTATION_AXIS
5758                 : nodeTransformation.getRotationAxis();
5759     }
5760 
5761     /**
5762      * Defines the axis of rotation of this {@code Node}.
5763      * <p>
5764      * Note that this is a conditional feature. See
5765      * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
5766      * for more information.
5767      *
5768      * @return the rotationAxis for this {@code Node}
5769      * @defaultValue Rotate.Z_AXIS
5770      */
5771     public final ObjectProperty<Point3D> rotationAxisProperty() {
5772         return getNodeTransformation().rotationAxisProperty();
5773     }
5774 
5775     /**
5776      * An affine transform that holds the computed local-to-parent transform.
5777      * This is the concatenation of all transforms in this node, including all
5778      * of the convenience transforms.
5779      * @return the localToParent transform for this {@code Node}
5780      * @since JavaFX 2.2
5781      */
5782     public final ReadOnlyObjectProperty<Transform> localToParentTransformProperty() {
5783         return getNodeTransformation().localToParentTransformProperty();
5784     }
5785 
5786     private void invalidateLocalToParentTransform() {
5787         if (nodeTransformation != null) {
5788             nodeTransformation.invalidateLocalToParentTransform();
5789         }
5790     }
5791 
5792     public final Transform getLocalToParentTransform() {
5793         return localToParentTransformProperty().get();
5794     }
5795 
5796     /**
5797      * An affine transform that holds the computed local-to-scene transform.
5798      * This is the concatenation of all transforms in this node's parents and
5799      * in this node, including all of the convenience transforms up to the root.
5800      * If this node is in a {@link javafx.scene.SubScene}, this property represents
5801      * transforms up to the subscene, not the root scene.
5802      *
5803      * <p>
5804      * Note that when you register a listener or a binding to this property,
5805      * it needs to listen for invalidation on all its parents to the root node.
5806      * This means that registering a listener on this
5807      * property on many nodes may negatively affect performance of
5808      * transformation changes in their common parents.
5809      * </p>
5810      *
5811      * @return the localToScene transform for this {@code Node}
5812      * @since JavaFX 2.2
5813      */
5814     public final ReadOnlyObjectProperty<Transform> localToSceneTransformProperty() {
5815         return getNodeTransformation().localToSceneTransformProperty();
5816     }
5817 
5818     private void invalidateLocalToSceneTransform() {
5819         if (nodeTransformation != null) {
5820             nodeTransformation.invalidateLocalToSceneTransform();
5821         }
5822     }
5823 
5824     public final Transform getLocalToSceneTransform() {
5825         return localToSceneTransformProperty().get();
5826     }
5827 
5828     private NodeTransformation nodeTransformation;
5829 
5830     private NodeTransformation getNodeTransformation() {
5831         if (nodeTransformation == null) {
5832             nodeTransformation = new NodeTransformation();
5833         }
5834 
5835         return nodeTransformation;
5836     }
5837 
5838     private boolean hasTransforms() {
5839         return (nodeTransformation != null)
5840                 && nodeTransformation.hasTransforms();
5841     }
5842 
5843     // for tests only
5844     Transform getCurrentLocalToSceneTransformState() {
5845         if (nodeTransformation == null ||
5846                 nodeTransformation.localToSceneTransform == null) {
5847             return null;
5848         }
5849 
5850         return nodeTransformation.localToSceneTransform.transform;
5851     }
5852 
5853     private static final double DEFAULT_TRANSLATE_X = 0;
5854     private static final double DEFAULT_TRANSLATE_Y = 0;
5855     private static final double DEFAULT_TRANSLATE_Z = 0;
5856     private static final double DEFAULT_SCALE_X = 1;
5857     private static final double DEFAULT_SCALE_Y = 1;
5858     private static final double DEFAULT_SCALE_Z = 1;
5859     private static final double DEFAULT_ROTATE = 0;
5860     private static final Point3D DEFAULT_ROTATION_AXIS = Rotate.Z_AXIS;
5861 
5862     private final class NodeTransformation {
5863         private DoubleProperty translateX;
5864         private DoubleProperty translateY;
5865         private DoubleProperty translateZ;
5866         private DoubleProperty scaleX;
5867         private DoubleProperty scaleY;
5868         private DoubleProperty scaleZ;
5869         private DoubleProperty rotate;
5870         private ObjectProperty<Point3D> rotationAxis;
5871         private ObservableList<Transform> transforms;
5872         private LazyTransformProperty localToParentTransform;
5873         private LazyTransformProperty localToSceneTransform;
5874         private int listenerReasons = 0;
5875         private InvalidationListener localToSceneInvLstnr;
5876 
5877         private InvalidationListener getLocalToSceneInvalidationListener() {
5878             if (localToSceneInvLstnr == null) {
5879                 localToSceneInvLstnr = observable -> invalidateLocalToSceneTransform();
5880             }
5881             return localToSceneInvLstnr;
5882         }
5883 
5884         public void incListenerReasons() {
5885             if (listenerReasons == 0) {
5886                 Node n = Node.this.getParent();
5887                 if (n != null) {
5888                     n.localToSceneTransformProperty().addListener(
5889                             getLocalToSceneInvalidationListener());
5890                 }
5891             }
5892             listenerReasons++;
5893         }
5894 
5895         public void decListenerReasons() {
5896             listenerReasons--;
5897             if (listenerReasons == 0) {
5898                 Node n = Node.this.getParent();
5899                 if (n != null) {
5900                     n.localToSceneTransformProperty().removeListener(
5901                             getLocalToSceneInvalidationListener());
5902                 }
5903                 if (localToSceneTransform != null) {
5904                     localToSceneTransform.validityUnknown();
5905                 }
5906             }
5907         }
5908 
5909         public final Transform getLocalToParentTransform() {
5910             return localToParentTransformProperty().get();
5911         }
5912 
5913         public final ReadOnlyObjectProperty<Transform> localToParentTransformProperty() {
5914             if (localToParentTransform == null) {
5915                 localToParentTransform = new LazyTransformProperty() {
5916                     @Override
5917                     protected Transform computeTransform(Transform reuse) {
5918                         updateLocalToParentTransform();
5919                         return TransformUtils.immutableTransform(reuse,
5920                                 localToParentTx.getMxx(), localToParentTx.getMxy(), localToParentTx.getMxz(), localToParentTx.getMxt(),
5921                                 localToParentTx.getMyx(), localToParentTx.getMyy(), localToParentTx.getMyz(), localToParentTx.getMyt(),
5922                                 localToParentTx.getMzx(), localToParentTx.getMzy(), localToParentTx.getMzz(), localToParentTx.getMzt());
5923                     }
5924 
5925                     @Override
5926                     protected boolean validityKnown() {
5927                         return true;
5928                     }
5929 
5930                     @Override
5931                     protected int computeValidity() {
5932                         return valid;
5933                     }
5934 
5935                     @Override
5936                     public Object getBean() {
5937                         return Node.this;
5938                     }
5939 
5940                     @Override
5941                     public String getName() {
5942                         return "localToParentTransform";
5943                     }
5944                 };
5945             }
5946 
5947             return localToParentTransform;
5948         }
5949 
5950         public void invalidateLocalToParentTransform() {
5951             if (localToParentTransform != null) {
5952                 localToParentTransform.invalidate();
5953             }
5954         }
5955 
5956         public final Transform getLocalToSceneTransform() {
5957             return localToSceneTransformProperty().get();
5958         }
5959 
5960         class LocalToSceneTransformProperty extends LazyTransformProperty {
5961             // need this to track number of listeners
5962             private List localToSceneListeners;
5963             // stamps to watch for parent changes when the listeners
5964             // are not present
5965             private long stamp, parentStamp;
5966 
5967             @Override
5968             protected Transform computeTransform(Transform reuse) {
5969                 stamp++;
5970                 updateLocalToParentTransform();
5971 
5972                 Node parentNode = Node.this.getParent();
5973                 if (parentNode != null) {
5974                     final LocalToSceneTransformProperty parentProperty =
5975                             (LocalToSceneTransformProperty) parentNode.localToSceneTransformProperty();
5976                     final Transform parentTransform = parentProperty.getInternalValue();
5977 
5978                     parentStamp = parentProperty.stamp;
5979 
5980                     return TransformUtils.immutableTransform(reuse,
5981                             parentTransform,
5982                             ((LazyTransformProperty) localToParentTransformProperty()).getInternalValue());
5983                 } else {
5984                     return TransformUtils.immutableTransform(reuse,
5985                             ((LazyTransformProperty) localToParentTransformProperty()).getInternalValue());
5986                 }
5987             }
5988 
5989             @Override
5990             public Object getBean() {
5991                 return Node.this;
5992             }
5993 
5994             @Override
5995             public String getName() {
5996                 return "localToSceneTransform";
5997             }
5998 
5999             @Override
6000             protected boolean validityKnown() {
6001                 return listenerReasons > 0;
6002             }
6003 
6004             @Override
6005             protected int computeValidity() {
6006                 if (valid != VALIDITY_UNKNOWN) {
6007                     return valid;
6008                 }
6009 
6010                 Node n = (Node) getBean();
6011                 Node parent = n.getParent();
6012 
6013                 if (parent != null) {
6014                     final LocalToSceneTransformProperty parentProperty =
6015                             (LocalToSceneTransformProperty) parent.localToSceneTransformProperty();
6016 
6017                     if (parentStamp != parentProperty.stamp) {
6018                         valid = INVALID;
6019                         return INVALID;
6020                     }
6021 
6022                     int parentValid = parentProperty.computeValidity();
6023                     if (parentValid == INVALID) {
6024                         valid = INVALID;
6025                     }
6026                     return parentValid;
6027                 }
6028 
6029                 // Validity unknown for root means it is valid
6030                 return VALID;
6031             }
6032 
6033             @Override
6034             public void addListener(InvalidationListener listener) {
6035                 incListenerReasons();
6036                 if (localToSceneListeners == null) {
6037                     localToSceneListeners = new LinkedList<Object>();
6038                 }
6039                 localToSceneListeners.add(listener);
6040                 super.addListener(listener);
6041             }
6042 
6043             @Override
6044             public void addListener(ChangeListener<? super Transform> listener) {
6045                 incListenerReasons();
6046                 if (localToSceneListeners == null) {
6047                     localToSceneListeners = new LinkedList<Object>();
6048                 }
6049                 localToSceneListeners.add(listener);
6050                 super.addListener(listener);
6051             }
6052 
6053             @Override
6054             public void removeListener(InvalidationListener listener) {
6055                 if (localToSceneListeners != null &&
6056                         localToSceneListeners.remove(listener)) {
6057                     decListenerReasons();
6058                 }
6059                 super.removeListener(listener);
6060             }
6061 
6062             @Override
6063             public void removeListener(ChangeListener<? super Transform> listener) {
6064                 if (localToSceneListeners != null &&
6065                         localToSceneListeners.remove(listener)) {
6066                     decListenerReasons();
6067                 }
6068                 super.removeListener(listener);
6069             }
6070         }
6071 
6072         public final ReadOnlyObjectProperty<Transform> localToSceneTransformProperty() {
6073             if (localToSceneTransform == null) {
6074                 localToSceneTransform = new LocalToSceneTransformProperty();
6075             }
6076 
6077             return localToSceneTransform;
6078         }
6079 
6080         public void invalidateLocalToSceneTransform() {
6081             if (localToSceneTransform != null) {
6082                 localToSceneTransform.invalidate();
6083             }
6084         }
6085 
6086         public double getTranslateX() {
6087             return (translateX == null) ? DEFAULT_TRANSLATE_X
6088                                         : translateX.get();
6089         }
6090 
6091         public final DoubleProperty translateXProperty() {
6092             if (translateX == null) {
6093                 translateX = new StyleableDoubleProperty(DEFAULT_TRANSLATE_X) {
6094                     @Override
6095                     public void invalidated() {
6096                         NodeHelper.transformsChanged(Node.this);
6097                     }
6098 
6099                     @Override
6100                     public CssMetaData getCssMetaData() {
6101                         return StyleableProperties.TRANSLATE_X;
6102                     }
6103 
6104                     @Override
6105                     public Object getBean() {
6106                         return Node.this;
6107                     }
6108 
6109                     @Override
6110                     public String getName() {
6111                         return "translateX";
6112                     }
6113                 };
6114             }
6115             return translateX;
6116         }
6117 
6118         public double getTranslateY() {
6119             return (translateY == null) ? DEFAULT_TRANSLATE_Y : translateY.get();
6120         }
6121 
6122         public final DoubleProperty translateYProperty() {
6123             if (translateY == null) {
6124                 translateY = new StyleableDoubleProperty(DEFAULT_TRANSLATE_Y) {
6125                     @Override
6126                     public void invalidated() {
6127                         NodeHelper.transformsChanged(Node.this);
6128                     }
6129 
6130                     @Override
6131                     public CssMetaData getCssMetaData() {
6132                         return StyleableProperties.TRANSLATE_Y;
6133                     }
6134 
6135                     @Override
6136                     public Object getBean() {
6137                         return Node.this;
6138                     }
6139 
6140                     @Override
6141                     public String getName() {
6142                         return "translateY";
6143                     }
6144                 };
6145             }
6146             return translateY;
6147         }
6148 
6149         public double getTranslateZ() {
6150             return (translateZ == null) ? DEFAULT_TRANSLATE_Z : translateZ.get();
6151         }
6152 
6153         public final DoubleProperty translateZProperty() {
6154             if (translateZ == null) {
6155                 translateZ = new StyleableDoubleProperty(DEFAULT_TRANSLATE_Z) {
6156                     @Override
6157                     public void invalidated() {
6158                         NodeHelper.transformsChanged(Node.this);
6159                     }
6160 
6161                     @Override
6162                     public CssMetaData getCssMetaData() {
6163                         return StyleableProperties.TRANSLATE_Z;
6164                     }
6165 
6166                     @Override
6167                     public Object getBean() {
6168                         return Node.this;
6169                     }
6170 
6171                     @Override
6172                     public String getName() {
6173                         return "translateZ";
6174                     }
6175                 };
6176             }
6177             return translateZ;
6178         }
6179 
6180         public double getScaleX() {
6181             return (scaleX == null) ? DEFAULT_SCALE_X : scaleX.get();
6182         }
6183 
6184         public final DoubleProperty scaleXProperty() {
6185             if (scaleX == null) {
6186                 scaleX = new StyleableDoubleProperty(DEFAULT_SCALE_X) {
6187                     @Override
6188                     public void invalidated() {
6189                         NodeHelper.transformsChanged(Node.this);
6190                     }
6191 
6192                     @Override
6193                     public CssMetaData getCssMetaData() {
6194                         return StyleableProperties.SCALE_X;
6195                     }
6196 
6197                     @Override
6198                     public Object getBean() {
6199                         return Node.this;
6200                     }
6201 
6202                     @Override
6203                     public String getName() {
6204                         return "scaleX";
6205                     }
6206                 };
6207             }
6208             return scaleX;
6209         }
6210 
6211         public double getScaleY() {
6212             return (scaleY == null) ? DEFAULT_SCALE_Y : scaleY.get();
6213         }
6214 
6215         public final DoubleProperty scaleYProperty() {
6216             if (scaleY == null) {
6217                 scaleY = new StyleableDoubleProperty(DEFAULT_SCALE_Y) {
6218                     @Override
6219                     public void invalidated() {
6220                         NodeHelper.transformsChanged(Node.this);
6221                     }
6222 
6223                     @Override
6224                     public CssMetaData getCssMetaData() {
6225                         return StyleableProperties.SCALE_Y;
6226                     }
6227 
6228                     @Override
6229                     public Object getBean() {
6230                         return Node.this;
6231                     }
6232 
6233                     @Override
6234                     public String getName() {
6235                         return "scaleY";
6236                     }
6237                 };
6238             }
6239             return scaleY;
6240         }
6241 
6242         public double getScaleZ() {
6243             return (scaleZ == null) ? DEFAULT_SCALE_Z : scaleZ.get();
6244         }
6245 
6246         public final DoubleProperty scaleZProperty() {
6247             if (scaleZ == null) {
6248                 scaleZ = new StyleableDoubleProperty(DEFAULT_SCALE_Z) {
6249                     @Override
6250                     public void invalidated() {
6251                         NodeHelper.transformsChanged(Node.this);
6252                     }
6253 
6254                     @Override
6255                     public CssMetaData getCssMetaData() {
6256                         return StyleableProperties.SCALE_Z;
6257                     }
6258 
6259                     @Override
6260                     public Object getBean() {
6261                         return Node.this;
6262                     }
6263 
6264                     @Override
6265                     public String getName() {
6266                         return "scaleZ";
6267                     }
6268                 };
6269             }
6270             return scaleZ;
6271         }
6272 
6273         public double getRotate() {
6274             return (rotate == null) ? DEFAULT_ROTATE : rotate.get();
6275         }
6276 
6277         public final DoubleProperty rotateProperty() {
6278             if (rotate == null) {
6279                 rotate = new StyleableDoubleProperty(DEFAULT_ROTATE) {
6280                     @Override
6281                     public void invalidated() {
6282                         NodeHelper.transformsChanged(Node.this);
6283                     }
6284 
6285                     @Override
6286                     public CssMetaData getCssMetaData() {
6287                         return StyleableProperties.ROTATE;
6288                     }
6289 
6290                     @Override
6291                     public Object getBean() {
6292                         return Node.this;
6293                     }
6294 
6295                     @Override
6296                     public String getName() {
6297                         return "rotate";
6298                     }
6299                 };
6300             }
6301             return rotate;
6302         }
6303 
6304         public Point3D getRotationAxis() {
6305             return (rotationAxis == null) ? DEFAULT_ROTATION_AXIS
6306                                           : rotationAxis.get();
6307         }
6308 
6309         public final ObjectProperty<Point3D> rotationAxisProperty() {
6310             if (rotationAxis == null) {
6311                 rotationAxis = new ObjectPropertyBase<Point3D>(
6312                                            DEFAULT_ROTATION_AXIS) {
6313                     @Override
6314                     protected void invalidated() {
6315                         NodeHelper.transformsChanged(Node.this);
6316                     }
6317 
6318                     @Override
6319                     public Object getBean() {
6320                         return Node.this;
6321                     }
6322 
6323                     @Override
6324                     public String getName() {
6325                         return "rotationAxis";
6326                     }
6327                 };
6328             }
6329             return rotationAxis;
6330         }
6331 
6332         public ObservableList<Transform> getTransforms() {
6333             if (transforms == null) {
6334                 transforms = new TrackableObservableList<Transform>() {
6335                     @Override
6336                     protected void onChanged(Change<Transform> c) {
6337                         while (c.next()) {
6338                             for (Transform t : c.getRemoved()) {
6339                                 TransformHelper.remove(t, Node.this);
6340                             }
6341                             for (Transform t : c.getAddedSubList()) {
6342                                 TransformHelper.add(t, Node.this);
6343                             }
6344                         }
6345 
6346                         NodeHelper.transformsChanged(Node.this);
6347                     }
6348                 };
6349             }
6350 
6351             return transforms;
6352         }
6353 
6354         public boolean canSetTranslateX() {
6355             return (translateX == null) || !translateX.isBound();
6356         }
6357 
6358         public boolean canSetTranslateY() {
6359             return (translateY == null) || !translateY.isBound();
6360         }
6361 
6362         public boolean canSetTranslateZ() {
6363             return (translateZ == null) || !translateZ.isBound();
6364         }
6365 
6366         public boolean canSetScaleX() {
6367             return (scaleX == null) || !scaleX.isBound();
6368         }
6369 
6370         public boolean canSetScaleY() {
6371             return (scaleY == null) || !scaleY.isBound();
6372         }
6373 
6374         public boolean canSetScaleZ() {
6375             return (scaleZ == null) || !scaleZ.isBound();
6376         }
6377 
6378         public boolean canSetRotate() {
6379             return (rotate == null) || !rotate.isBound();
6380         }
6381 
6382         public boolean hasTransforms() {
6383             return (transforms != null && !transforms.isEmpty());
6384         }
6385 
6386         public boolean hasScaleOrRotate() {
6387             if (scaleX != null && scaleX.get() != DEFAULT_SCALE_X) {
6388                 return true;
6389             }
6390             if (scaleY != null && scaleY.get() != DEFAULT_SCALE_Y) {
6391                 return true;
6392             }
6393             if (scaleZ != null && scaleZ.get() != DEFAULT_SCALE_Z) {
6394                 return true;
6395             }
6396             if (rotate != null && rotate.get() != DEFAULT_ROTATE) {
6397                 return true;
6398             }
6399             return false;
6400         }
6401 
6402     }
6403 
6404     ////////////////////////////
6405     //  Private Implementation
6406     ////////////////////////////
6407 
6408     /***************************************************************************
6409      *                                                                         *
6410      *                        Event Handler Properties                         *
6411      *                                                                         *
6412      **************************************************************************/
6413 
6414     private EventHandlerProperties eventHandlerProperties;
6415 
6416     private EventHandlerProperties getEventHandlerProperties() {
6417         if (eventHandlerProperties == null) {
6418             eventHandlerProperties =
6419                     new EventHandlerProperties(
6420                         getInternalEventDispatcher().getEventHandlerManager(),
6421                         this);
6422         }
6423 
6424         return eventHandlerProperties;
6425     }
6426 
6427     /***************************************************************************
6428      *                                                                         *
6429      *                       Component Orientation Properties                  *
6430      *                                                                         *
6431      **************************************************************************/
6432 
6433     private ObjectProperty<NodeOrientation> nodeOrientation;
6434     private EffectiveOrientationProperty effectiveNodeOrientationProperty;
6435 
6436     private static final byte EFFECTIVE_ORIENTATION_LTR = 0;
6437     private static final byte EFFECTIVE_ORIENTATION_RTL = 1;
6438     private static final byte EFFECTIVE_ORIENTATION_MASK = 1;
6439     private static final byte AUTOMATIC_ORIENTATION_LTR = 0;
6440     private static final byte AUTOMATIC_ORIENTATION_RTL = 2;
6441     private static final byte AUTOMATIC_ORIENTATION_MASK = 2;
6442 
6443     private byte resolvedNodeOrientation =
6444             EFFECTIVE_ORIENTATION_LTR | AUTOMATIC_ORIENTATION_LTR;
6445 
6446     public final void setNodeOrientation(NodeOrientation orientation) {
6447         nodeOrientationProperty().set(orientation);
6448     }
6449 
6450     public final NodeOrientation getNodeOrientation() {
6451         return nodeOrientation == null ? NodeOrientation.INHERIT : nodeOrientation.get();
6452     }
6453     /**
6454      * Property holding NodeOrientation.
6455      * <p>
6456      * Node orientation describes the flow of visual data within a node.
6457      * In the English speaking world, visual data normally flows from
6458      * left-to-right. In an Arabic or Hebrew world, visual data flows
6459      * from right-to-left.  This is consistent with the reading order
6460      * of text in both worlds.  The default value is left-to-right.
6461      * </p>
6462      *
6463      * @return NodeOrientation
6464      * @since JavaFX 8.0
6465      */
6466     public final ObjectProperty<NodeOrientation> nodeOrientationProperty() {
6467         if (nodeOrientation == null) {
6468             nodeOrientation = new StyleableObjectProperty<NodeOrientation>(NodeOrientation.INHERIT) {
6469                 @Override
6470                 protected void invalidated() {
6471                     nodeResolvedOrientationInvalidated();
6472                 }
6473 
6474                 @Override
6475                 public Object getBean() {
6476                     return Node.this;
6477                 }
6478 
6479                 @Override
6480                 public String getName() {
6481                     return "nodeOrientation";
6482                 }
6483 
6484                 @Override
6485                 public CssMetaData getCssMetaData() {
6486                     //TODO - not supported
6487                     throw new UnsupportedOperationException("Not supported yet.");
6488                 }
6489 
6490             };
6491         }
6492         return nodeOrientation;
6493     }
6494 
6495     public final NodeOrientation getEffectiveNodeOrientation() {
6496         return (getEffectiveOrientation(resolvedNodeOrientation)
6497                     == EFFECTIVE_ORIENTATION_LTR)
6498                        ? NodeOrientation.LEFT_TO_RIGHT
6499                        : NodeOrientation.RIGHT_TO_LEFT;
6500     }
6501 
6502     /**
6503      * The effective orientation of a node resolves the inheritance of
6504      * node orientation, returning either left-to-right or right-to-left.
6505      * @return the node orientation for this {@code Node}
6506      * @since JavaFX 8.0
6507      */
6508     public final ReadOnlyObjectProperty<NodeOrientation>
6509             effectiveNodeOrientationProperty() {
6510         if (effectiveNodeOrientationProperty == null) {
6511             effectiveNodeOrientationProperty =
6512                     new EffectiveOrientationProperty();
6513         }
6514 
6515         return effectiveNodeOrientationProperty;
6516     }
6517 
6518     /**
6519      * Determines whether a node should be mirrored when node orientation
6520      * is right-to-left.
6521      * <p>
6522      * When a node is mirrored, the origin is automatically moved to the
6523      * top right corner causing the node to layout children and draw from
6524      * right to left using a mirroring transformation.  Some nodes may wish
6525      * to draw from right to left without using a transformation.  These
6526      * nodes will will answer {@code false} and implement right-to-left
6527      * orientation without using the automatic transformation.
6528      * </p>
6529      * @return true if this {@code Node} should be mirrored
6530      * @since JavaFX 8.0
6531      */
6532     public boolean usesMirroring() {
6533         return true;
6534     }
6535 
6536     final void parentResolvedOrientationInvalidated() {
6537         if (getNodeOrientation() == NodeOrientation.INHERIT) {
6538             nodeResolvedOrientationInvalidated();
6539         } else {
6540             // mirroring changed
6541             NodeHelper.transformsChanged(this);
6542         }
6543     }
6544 
6545     final void nodeResolvedOrientationInvalidated() {
6546         final byte oldResolvedNodeOrientation =
6547                 resolvedNodeOrientation;
6548 
6549         resolvedNodeOrientation =
6550                 (byte) (calcEffectiveNodeOrientation()
6551                             | calcAutomaticNodeOrientation());
6552 
6553         if ((effectiveNodeOrientationProperty != null)
6554                 && (getEffectiveOrientation(resolvedNodeOrientation)
6555                         != getEffectiveOrientation(
6556                                oldResolvedNodeOrientation))) {
6557             effectiveNodeOrientationProperty.invalidate();
6558         }
6559 
6560         // mirroring changed
6561         NodeHelper.transformsChanged(this);
6562 
6563         if (resolvedNodeOrientation != oldResolvedNodeOrientation) {
6564             nodeResolvedOrientationChanged();
6565         }
6566     }
6567 
6568     void nodeResolvedOrientationChanged() {
6569         // overriden in Parent
6570     }
6571 
6572     private Node getMirroringOrientationParent() {
6573         Node parentValue = getParent();
6574         while (parentValue != null) {
6575             if (parentValue.usesMirroring()) {
6576                 return parentValue;
6577             }
6578             parentValue = parentValue.getParent();
6579         }
6580 
6581         final Node subSceneValue = getSubScene();
6582         if (subSceneValue != null) {
6583             return subSceneValue;
6584         }
6585 
6586         return null;
6587     }
6588 
6589     private Node getOrientationParent() {
6590         final Node parentValue = getParent();
6591         if (parentValue != null) {
6592             return parentValue;
6593         }
6594 
6595         final Node subSceneValue = getSubScene();
6596         if (subSceneValue != null) {
6597             return subSceneValue;
6598         }
6599 
6600         return null;
6601     }
6602 
6603     private byte calcEffectiveNodeOrientation() {
6604         final NodeOrientation nodeOrientationValue = getNodeOrientation();
6605         if (nodeOrientationValue != NodeOrientation.INHERIT) {
6606             return (nodeOrientationValue == NodeOrientation.LEFT_TO_RIGHT)
6607                        ? EFFECTIVE_ORIENTATION_LTR
6608                        : EFFECTIVE_ORIENTATION_RTL;
6609         }
6610 
6611         final Node parentValue = getOrientationParent();
6612         if (parentValue != null) {
6613             return getEffectiveOrientation(parentValue.resolvedNodeOrientation);
6614         }
6615 
6616         final Scene sceneValue = getScene();
6617         if (sceneValue != null) {
6618             return (sceneValue.getEffectiveNodeOrientation()
6619                         == NodeOrientation.LEFT_TO_RIGHT)
6620                            ? EFFECTIVE_ORIENTATION_LTR
6621                            : EFFECTIVE_ORIENTATION_RTL;
6622         }
6623 
6624         return EFFECTIVE_ORIENTATION_LTR;
6625     }
6626 
6627     private byte calcAutomaticNodeOrientation() {
6628         if (!usesMirroring()) {
6629             return AUTOMATIC_ORIENTATION_LTR;
6630         }
6631 
6632         final NodeOrientation nodeOrientationValue = getNodeOrientation();
6633         if (nodeOrientationValue != NodeOrientation.INHERIT) {
6634             return (nodeOrientationValue == NodeOrientation.LEFT_TO_RIGHT)
6635                        ? AUTOMATIC_ORIENTATION_LTR
6636                        : AUTOMATIC_ORIENTATION_RTL;
6637         }
6638 
6639         final Node parentValue = getMirroringOrientationParent();
6640         if (parentValue != null) {
6641             // automatic node orientation is inherited
6642             return getAutomaticOrientation(parentValue.resolvedNodeOrientation);
6643         }
6644 
6645         final Scene sceneValue = getScene();
6646         if (sceneValue != null) {
6647             return (sceneValue.getEffectiveNodeOrientation()
6648                         == NodeOrientation.LEFT_TO_RIGHT)
6649                            ? AUTOMATIC_ORIENTATION_LTR
6650                            : AUTOMATIC_ORIENTATION_RTL;
6651         }
6652 
6653         return AUTOMATIC_ORIENTATION_LTR;
6654     }
6655 
6656     // Return true if the node needs to be mirrored.
6657     // A node has mirroring if the orientation differs from the parent
6658     // package private for testing
6659     final boolean hasMirroring() {
6660         final Node parentValue = getOrientationParent();
6661 
6662         final byte thisOrientation =
6663                 getAutomaticOrientation(resolvedNodeOrientation);
6664         final byte parentOrientation =
6665                 (parentValue != null)
6666                     ? getAutomaticOrientation(
6667                           parentValue.resolvedNodeOrientation)
6668                     : AUTOMATIC_ORIENTATION_LTR;
6669 
6670         return thisOrientation != parentOrientation;
6671     }
6672 
6673     private static byte getEffectiveOrientation(
6674             final byte resolvedNodeOrientation) {
6675         return (byte) (resolvedNodeOrientation & EFFECTIVE_ORIENTATION_MASK);
6676     }
6677 
6678     private static byte getAutomaticOrientation(
6679             final byte resolvedNodeOrientation) {
6680         return (byte) (resolvedNodeOrientation & AUTOMATIC_ORIENTATION_MASK);
6681     }
6682 
6683     private final class EffectiveOrientationProperty
6684             extends ReadOnlyObjectPropertyBase<NodeOrientation> {
6685         @Override
6686         public NodeOrientation get() {
6687             return getEffectiveNodeOrientation();
6688         }
6689 
6690         @Override
6691         public Object getBean() {
6692             return Node.this;
6693         }
6694 
6695         @Override
6696         public String getName() {
6697             return "effectiveNodeOrientation";
6698         }
6699 
6700         public void invalidate() {
6701             fireValueChangedEvent();
6702         }
6703     }
6704 
6705     /***************************************************************************
6706      *                                                                         *
6707      *                       Misc Seldom Used Properties                       *
6708      *                                                                         *
6709      **************************************************************************/
6710 
6711     private MiscProperties miscProperties;
6712 
6713     private MiscProperties getMiscProperties() {
6714         if (miscProperties == null) {
6715             miscProperties = new MiscProperties();
6716         }
6717 
6718         return miscProperties;
6719     }
6720 
6721     private static final double DEFAULT_VIEW_ORDER = 0;
6722     private static final boolean DEFAULT_CACHE = false;
6723     private static final CacheHint DEFAULT_CACHE_HINT = CacheHint.DEFAULT;
6724     private static final Node DEFAULT_CLIP = null;
6725     private static final Cursor DEFAULT_CURSOR = null;
6726     private static final DepthTest DEFAULT_DEPTH_TEST = DepthTest.INHERIT;
6727     private static final boolean DEFAULT_DISABLE = false;
6728     private static final Effect DEFAULT_EFFECT = null;
6729     private static final InputMethodRequests DEFAULT_INPUT_METHOD_REQUESTS =
6730             null;
6731     private static final boolean DEFAULT_MOUSE_TRANSPARENT = false;
6732 
6733     private final class MiscProperties {
6734         private LazyBoundsProperty boundsInParent;
6735         private LazyBoundsProperty boundsInLocal;
6736         private BooleanProperty cache;
6737         private ObjectProperty<CacheHint> cacheHint;
6738         private ObjectProperty<Node> clip;
6739         private ObjectProperty<Cursor> cursor;
6740         private ObjectProperty<DepthTest> depthTest;
6741         private BooleanProperty disable;
6742         private BooleanProperty cssSensitive;
6743         private ObjectProperty<Effect> effect;
6744         private ObjectProperty<InputMethodRequests> inputMethodRequests;
6745         private BooleanProperty mouseTransparent;
6746         private DoubleProperty viewOrder;
6747 
6748         public double getViewOrder() {
6749             return (viewOrder == null) ? DEFAULT_VIEW_ORDER : viewOrder.get();
6750         }
6751 
6752         public final DoubleProperty viewOrderProperty() {
6753             if (viewOrder == null) {
6754                 viewOrder = new StyleableDoubleProperty(DEFAULT_VIEW_ORDER) {
6755                     @Override
6756                     public void invalidated() {
6757                         Parent p = getParent();
6758                         if (p != null) {
6759                             // Parent will be responsible to update sorted children list
6760                             p.markViewOrderChildrenDirty();
6761                         }
6762                         NodeHelper.markDirty(Node.this, DirtyBits.NODE_VIEW_ORDER);
6763                     }
6764 
6765                     @Override
6766                     public CssMetaData getCssMetaData() {
6767                         return StyleableProperties.VIEW_ORDER;
6768                     }
6769 
6770                     @Override
6771                     public Object getBean() {
6772                         return Node.this;
6773                     }
6774 
6775                     @Override
6776                     public String getName() {
6777                         return "viewOrder";
6778                     }
6779                 };
6780             }
6781             return viewOrder;
6782         }
6783 
6784         public final Bounds getBoundsInParent() {
6785             return boundsInParentProperty().get();
6786         }
6787 
6788         public final ReadOnlyObjectProperty<Bounds> boundsInParentProperty() {
6789             if (boundsInParent == null) {
6790                 boundsInParent = new LazyBoundsProperty() {
6791                     /**
6792                      * Computes the bounds including the clip, effects, and all
6793                      * transforms. This function is essentially how to compute
6794                      * the boundsInParent. Optimizations are made to compute as
6795                      * little as possible and create as little trash as
6796                      * possible.
6797                      */
6798                     @Override
6799                     protected Bounds computeBounds() {
6800                         BaseBounds tempBounds = TempState.getInstance().bounds;
6801                         tempBounds = getTransformedBounds(
6802                                              tempBounds,
6803                                              BaseTransform.IDENTITY_TRANSFORM);
6804                         return new BoundingBox(tempBounds.getMinX(),
6805                                                tempBounds.getMinY(),
6806                                                tempBounds.getMinZ(),
6807                                                tempBounds.getWidth(),
6808                                                tempBounds.getHeight(),
6809                                                tempBounds.getDepth());
6810                     }
6811 
6812                     @Override
6813                     public Object getBean() {
6814                         return Node.this;
6815                     }
6816 
6817                     @Override
6818                     public String getName() {
6819                         return "boundsInParent";
6820                     }
6821                 };
6822             }
6823 
6824             return boundsInParent;
6825         }
6826 
6827         public void invalidateBoundsInParent() {
6828             if (boundsInParent != null) {
6829                 boundsInParent.invalidate();
6830             }
6831         }
6832 
6833         public final Bounds getBoundsInLocal() {
6834             return boundsInLocalProperty().get();
6835         }
6836 
6837         public final ReadOnlyObjectProperty<Bounds> boundsInLocalProperty() {
6838             if (boundsInLocal == null) {
6839                 boundsInLocal = new LazyBoundsProperty() {
6840                     @Override
6841                     protected Bounds computeBounds() {
6842                         BaseBounds tempBounds = TempState.getInstance().bounds;
6843                         tempBounds = getLocalBounds(
6844                                              tempBounds,
6845                                              BaseTransform.IDENTITY_TRANSFORM);
6846                         return new BoundingBox(tempBounds.getMinX(),
6847                                                tempBounds.getMinY(),
6848                                                tempBounds.getMinZ(),
6849                                                tempBounds.getWidth(),
6850                                                tempBounds.getHeight(),
6851                                                tempBounds.getDepth());
6852                     }
6853 
6854                     @Override
6855                     public Object getBean() {
6856                         return Node.this;
6857                     }
6858 
6859                     @Override
6860                     public String getName() {
6861                         return "boundsInLocal";
6862                     }
6863                 };
6864             }
6865 
6866             return boundsInLocal;
6867         }
6868 
6869         public void invalidateBoundsInLocal() {
6870             if (boundsInLocal != null) {
6871                 boundsInLocal.invalidate();
6872             }
6873         }
6874 
6875         public final boolean isCache() {
6876             return (cache == null) ? DEFAULT_CACHE
6877                                    : cache.get();
6878         }
6879 
6880         public final BooleanProperty cacheProperty() {
6881             if (cache == null) {
6882                 cache = new BooleanPropertyBase(DEFAULT_CACHE) {
6883                     @Override
6884                     protected void invalidated() {
6885                         NodeHelper.markDirty(Node.this, DirtyBits.NODE_CACHE);
6886                     }
6887 
6888                     @Override
6889                     public Object getBean() {
6890                         return Node.this;
6891                     }
6892 
6893                     @Override
6894                     public String getName() {
6895                         return "cache";
6896                     }
6897                 };
6898             }
6899             return cache;
6900         }
6901 
6902         public final CacheHint getCacheHint() {
6903             return (cacheHint == null) ? DEFAULT_CACHE_HINT
6904                                        : cacheHint.get();
6905         }
6906 
6907         public final ObjectProperty<CacheHint> cacheHintProperty() {
6908             if (cacheHint == null) {
6909                 cacheHint = new ObjectPropertyBase<CacheHint>(DEFAULT_CACHE_HINT) {
6910                     @Override
6911                     protected void invalidated() {
6912                         NodeHelper.markDirty(Node.this, DirtyBits.NODE_CACHE);
6913                     }
6914 
6915                     @Override
6916                     public Object getBean() {
6917                         return Node.this;
6918                     }
6919 
6920                     @Override
6921                     public String getName() {
6922                         return "cacheHint";
6923                     }
6924                 };
6925             }
6926             return cacheHint;
6927         }
6928 
6929         public final Node getClip() {
6930             return (clip == null) ? DEFAULT_CLIP : clip.get();
6931         }
6932 
6933         public final ObjectProperty<Node> clipProperty() {
6934             if (clip == null) {
6935                 clip = new ObjectPropertyBase<Node>(DEFAULT_CLIP) {
6936 
6937                     //temp variables used when clip was invalid to rollback to
6938                     // last value
6939                     private Node oldClip;
6940 
6941                     @Override
6942                     protected void invalidated() {
6943                         final Node newClip = get();
6944                         if ((newClip != null)
6945                                 && ((newClip.isConnected()
6946                                            && newClip.clipParent != Node.this)
6947                                        || wouldCreateCycle(Node.this,
6948                                                            newClip))) {
6949                             // Assigning this node to clip is illegal.
6950                             // Roll back to the previous state and throw an
6951                             // exception.
6952                             final String cause =
6953                                     newClip.isConnected()
6954                                         && (newClip.clipParent != Node.this)
6955                                             ? "node already connected"
6956                                             : "cycle detected";
6957 
6958                             if (isBound()) {
6959                                 unbind();
6960                                 set(oldClip);
6961                                 throw new IllegalArgumentException(
6962                                         "Node's clip set to incorrect value "
6963                                             + " through binding"
6964                                             + " (" + cause + ", node  = "
6965                                                    + Node.this + ", clip = "
6966                                                    + clip + ")."
6967                                             + " Binding has been removed.");
6968                             } else {
6969                                 set(oldClip);
6970                                 throw new IllegalArgumentException(
6971                                         "Node's clip set to incorrect value"
6972                                             + " (" + cause + ", node  = "
6973                                                    + Node.this + ", clip = "
6974                                                    + clip + ").");
6975                             }
6976                         } else {
6977                             if (oldClip != null) {
6978                                 oldClip.clipParent = null;
6979                                 oldClip.setScenes(null, null, /* reapplyCSS */ false);
6980                                 oldClip.updateTreeVisible(false);
6981                             }
6982 
6983                             if (newClip != null) {
6984                                 newClip.clipParent = Node.this;
6985                                 newClip.setScenes(getScene(), getSubScene(), /* reapplyCSS */ false);
6986                                 newClip.updateTreeVisible(true);
6987                             }
6988 
6989                             NodeHelper.markDirty(Node.this, DirtyBits.NODE_CLIP);
6990 
6991                             // the local bounds have (probably) changed
6992                             localBoundsChanged();
6993 
6994                             oldClip = newClip;
6995                         }
6996                     }
6997 
6998                     @Override
6999                     public Object getBean() {
7000                         return Node.this;
7001                     }
7002 
7003                     @Override
7004                     public String getName() {
7005                         return "clip";
7006                     }
7007                 };
7008             }
7009             return clip;
7010         }
7011 
7012         public final Cursor getCursor() {
7013             return (cursor == null) ? DEFAULT_CURSOR : cursor.get();
7014         }
7015 
7016         public final ObjectProperty<Cursor> cursorProperty() {
7017             if (cursor == null) {
7018                 cursor = new StyleableObjectProperty<Cursor>(DEFAULT_CURSOR) {
7019 
7020                     @Override
7021                     protected void invalidated() {
7022                         final Scene sceneValue = getScene();
7023                         if (sceneValue != null) {
7024                             sceneValue.markCursorDirty();
7025                         }
7026                     }
7027 
7028                     @Override
7029                     public CssMetaData getCssMetaData() {
7030                         return StyleableProperties.CURSOR;
7031                     }
7032 
7033                     @Override
7034                     public Object getBean() {
7035                         return Node.this;
7036                     }
7037 
7038                     @Override
7039                     public String getName() {
7040                         return "cursor";
7041                     }
7042 
7043                 };
7044             }
7045             return cursor;
7046         }
7047 
7048         public final DepthTest getDepthTest() {
7049             return (depthTest == null) ? DEFAULT_DEPTH_TEST
7050                                        : depthTest.get();
7051         }
7052 
7053         public final ObjectProperty<DepthTest> depthTestProperty() {
7054             if (depthTest == null) {
7055                 depthTest = new ObjectPropertyBase<DepthTest>(DEFAULT_DEPTH_TEST) {
7056                     @Override protected void invalidated() {
7057                         computeDerivedDepthTest();
7058                     }
7059 
7060                     @Override
7061                     public Object getBean() {
7062                         return Node.this;
7063                     }
7064 
7065                     @Override
7066                     public String getName() {
7067                         return "depthTest";
7068                     }
7069                 };
7070             }
7071             return depthTest;
7072         }
7073 
7074         public final boolean isDisable() {
7075             return (disable == null) ? DEFAULT_DISABLE : disable.get();
7076         }
7077 
7078         public final BooleanProperty disableProperty() {
7079             if (disable == null) {
7080                 disable = new BooleanPropertyBase(DEFAULT_DISABLE) {
7081                     @Override
7082                     protected void invalidated() {
7083                         updateDisabled();
7084                     }
7085 
7086                     @Override
7087                     public Object getBean() {
7088                         return Node.this;
7089                     }
7090 
7091                     @Override
7092                     public String getName() {
7093                         return "disable";
7094                     }
7095                 };
7096             }
7097             return disable;
7098         }
7099 
7100         public final boolean isCssSensitive() {
7101             return (cssSensitive == null) ? true : cssSensitive.get();
7102         }
7103 
7104         public final BooleanProperty cssSensitiveProperty() {
7105             if (cssSensitive == null) {
7106                 cssSensitive = new BooleanPropertyBase(true) {
7107                     @Override
7108                     protected void invalidated() {
7109                     }
7110 
7111                     @Override
7112                     public Object getBean() {
7113                         return Node.this;
7114                     }
7115 
7116                     @Override
7117                     public String getName() {
7118                         return "cssSensitive";
7119                     }
7120                 };
7121             }
7122             return cssSensitive;
7123         }
7124 
7125         public final Effect getEffect() {
7126             return (effect == null) ? DEFAULT_EFFECT : effect.get();
7127         }
7128 
7129         public final ObjectProperty<Effect> effectProperty() {
7130             if (effect == null) {
7131                 effect = new StyleableObjectProperty<Effect>(DEFAULT_EFFECT) {
7132                     private Effect oldEffect = null;
7133                     private int oldBits;
7134 
7135                     private final AbstractNotifyListener effectChangeListener =
7136                             new AbstractNotifyListener() {
7137 
7138                         @Override
7139                         public void invalidated(Observable valueModel) {
7140                             int newBits = ((IntegerProperty) valueModel).get();
7141                             int changedBits = newBits ^ oldBits;
7142                             oldBits = newBits;
7143                             if (EffectDirtyBits.isSet(
7144                                     changedBits,
7145                                     EffectDirtyBits.EFFECT_DIRTY)
7146                                 && EffectDirtyBits.isSet(
7147                                        newBits,
7148                                        EffectDirtyBits.EFFECT_DIRTY)) {
7149                                 NodeHelper.markDirty(Node.this, DirtyBits.EFFECT_EFFECT);
7150                             }
7151                             if (EffectDirtyBits.isSet(
7152                                     changedBits,
7153                                     EffectDirtyBits.BOUNDS_CHANGED)) {
7154                                 localBoundsChanged();
7155                             }
7156                         }
7157                     };
7158 
7159                     @Override
7160                     protected void invalidated() {
7161                         Effect _effect = get();
7162                         if (oldEffect != null) {
7163                             EffectHelper.effectDirtyProperty(oldEffect).removeListener(
7164                                     effectChangeListener.getWeakListener());
7165                         }
7166                         oldEffect = _effect;
7167                         if (_effect != null) {
7168                             EffectHelper.effectDirtyProperty(_effect)
7169                                    .addListener(
7170                                        effectChangeListener.getWeakListener());
7171                             if (EffectHelper.isEffectDirty(_effect)) {
7172                                 NodeHelper.markDirty(Node.this, DirtyBits.EFFECT_EFFECT);
7173                             }
7174                             oldBits = EffectHelper.effectDirtyProperty(_effect).get();
7175                         }
7176 
7177                         NodeHelper.markDirty(Node.this, DirtyBits.NODE_EFFECT);
7178                         // bounds may have changed regardeless whether
7179                         // the dirty flag on efffect is set
7180                         localBoundsChanged();
7181                     }
7182 
7183                     @Override
7184                     public CssMetaData getCssMetaData() {
7185                         return StyleableProperties.EFFECT;
7186                     }
7187 
7188                     @Override
7189                     public Object getBean() {
7190                         return Node.this;
7191                     }
7192 
7193                     @Override
7194                     public String getName() {
7195                         return "effect";
7196                     }
7197                 };
7198             }
7199             return effect;
7200         }
7201 
7202         public final InputMethodRequests getInputMethodRequests() {
7203             return (inputMethodRequests == null) ? DEFAULT_INPUT_METHOD_REQUESTS
7204                                                  : inputMethodRequests.get();
7205         }
7206 
7207         public ObjectProperty<InputMethodRequests>
7208                 inputMethodRequestsProperty() {
7209             if (inputMethodRequests == null) {
7210                 inputMethodRequests =
7211                         new SimpleObjectProperty<InputMethodRequests>(
7212                                 Node.this,
7213                                 "inputMethodRequests",
7214                                 DEFAULT_INPUT_METHOD_REQUESTS);
7215             }
7216             return inputMethodRequests;
7217         }
7218 
7219         public final boolean isMouseTransparent() {
7220             return (mouseTransparent == null) ? DEFAULT_MOUSE_TRANSPARENT
7221                                               : mouseTransparent.get();
7222         }
7223 
7224         public final BooleanProperty mouseTransparentProperty() {
7225             if (mouseTransparent == null) {
7226                 mouseTransparent =
7227                         new SimpleBooleanProperty(
7228                                 Node.this,
7229                                 "mouseTransparent",
7230                                 DEFAULT_MOUSE_TRANSPARENT);
7231             }
7232             return mouseTransparent;
7233         }
7234 
7235         public boolean canSetCursor() {
7236             return (cursor == null) || !cursor.isBound();
7237         }
7238 
7239         public boolean canSetEffect() {
7240             return (effect == null) || !effect.isBound();
7241         }
7242     }
7243 
7244     /* *************************************************************************
7245      *                                                                         *
7246      *                             Mouse Handling                              *
7247      *                                                                         *
7248      **************************************************************************/
7249 
7250     public final void setMouseTransparent(boolean value) {
7251         mouseTransparentProperty().set(value);
7252     }
7253 
7254     public final boolean isMouseTransparent() {
7255         return (miscProperties == null) ? DEFAULT_MOUSE_TRANSPARENT
7256                                         : miscProperties.isMouseTransparent();
7257     }
7258 
7259     /**
7260      * If {@code true}, this node (together with all its children) is completely
7261      * transparent to mouse events. When choosing target for mouse event, nodes
7262      * with {@code mouseTransparent} set to {@code true} and their subtrees
7263      * won't be taken into account.
7264      * @return is this {@code Node} (together with all its children) is completely
7265      * transparent to mouse events.
7266      */
7267     public final BooleanProperty mouseTransparentProperty() {
7268         return getMiscProperties().mouseTransparentProperty();
7269     }
7270 
7271     /**
7272      * Whether or not this {@code Node} is being hovered over. Typically this is
7273      * due to the mouse being over the node, though it could be due to a pen
7274      * hovering on a graphics tablet or other form of input.
7275      *
7276      * <p>Note that current implementation of hover relies on mouse enter and
7277      * exit events to determine whether this Node is in the hover state; this
7278      * means that this feature is currently supported only on systems that
7279      * have a mouse. Future implementations may provide alternative means of
7280      * supporting hover.
7281      *
7282      * @defaultValue false
7283      */
7284     private ReadOnlyBooleanWrapper hover;
7285 
7286     protected final void setHover(boolean value) {
7287         hoverPropertyImpl().set(value);
7288     }
7289 
7290     public final boolean isHover() {
7291         return hover == null ? false : hover.get();
7292     }
7293 
7294     public final ReadOnlyBooleanProperty hoverProperty() {
7295         return hoverPropertyImpl().getReadOnlyProperty();
7296     }
7297 
7298     private ReadOnlyBooleanWrapper hoverPropertyImpl() {
7299         if (hover == null) {
7300             hover = new ReadOnlyBooleanWrapper() {
7301 
7302                 @Override
7303                 protected void invalidated() {
7304                     PlatformLogger logger = Logging.getInputLogger();
7305                     if (logger.isLoggable(Level.FINER)) {
7306                         logger.finer(this + " hover=" + get());
7307                     }
7308                     pseudoClassStateChanged(HOVER_PSEUDOCLASS_STATE, get());
7309                 }
7310 
7311                 @Override
7312                 public Object getBean() {
7313                     return Node.this;
7314                 }
7315 
7316                 @Override
7317                 public String getName() {
7318                     return "hover";
7319                 }
7320             };
7321         }
7322         return hover;
7323     }
7324 
7325     /**
7326      * Whether or not the {@code Node} is pressed. Typically this is true when
7327      * the primary mouse button is down, though subclasses may define other
7328      * mouse button state or key state to cause the node to be "pressed".
7329      *
7330      * @defaultValue false
7331      */
7332     private ReadOnlyBooleanWrapper pressed;
7333 
7334     protected final void setPressed(boolean value) {
7335         pressedPropertyImpl().set(value);
7336     }
7337 
7338     public final boolean isPressed() {
7339         return pressed == null ? false : pressed.get();
7340     }
7341 
7342     public final ReadOnlyBooleanProperty pressedProperty() {
7343         return pressedPropertyImpl().getReadOnlyProperty();
7344     }
7345 
7346     private ReadOnlyBooleanWrapper pressedPropertyImpl() {
7347         if (pressed == null) {
7348             pressed = new ReadOnlyBooleanWrapper() {
7349 
7350                 @Override
7351                 protected void invalidated() {
7352                     PlatformLogger logger = Logging.getInputLogger();
7353                     if (logger.isLoggable(Level.FINER)) {
7354                         logger.finer(this + " pressed=" + get());
7355                     }
7356                     pseudoClassStateChanged(PRESSED_PSEUDOCLASS_STATE, get());
7357                 }
7358 
7359                 @Override
7360                 public Object getBean() {
7361                     return Node.this;
7362                 }
7363 
7364                 @Override
7365                 public String getName() {
7366                     return "pressed";
7367                 }
7368             };
7369         }
7370         return pressed;
7371     }
7372 
7373     public final void setOnContextMenuRequested(
7374             EventHandler<? super ContextMenuEvent> value) {
7375         onContextMenuRequestedProperty().set(value);
7376     }
7377 
7378     public final EventHandler<? super ContextMenuEvent> getOnContextMenuRequested() {
7379         return (eventHandlerProperties == null)
7380                 ? null : eventHandlerProperties.onContextMenuRequested();
7381     }
7382 
7383     /**
7384      * Defines a function to be called when a context menu
7385      * has been requested on this {@code Node}.
7386      * @return the event handler that is called when a context menu has been
7387      * requested on this {@code Node}
7388      * @since JavaFX 2.1
7389      */
7390     public final ObjectProperty<EventHandler<? super ContextMenuEvent>>
7391             onContextMenuRequestedProperty() {
7392         return getEventHandlerProperties().onContextMenuRequestedProperty();
7393     }
7394 
7395     public final void setOnMouseClicked(
7396             EventHandler<? super MouseEvent> value) {
7397         onMouseClickedProperty().set(value);
7398     }
7399 
7400     public final EventHandler<? super MouseEvent> getOnMouseClicked() {
7401         return (eventHandlerProperties == null)
7402                 ? null : eventHandlerProperties.getOnMouseClicked();
7403     }
7404 
7405     /**
7406      * Defines a function to be called when a mouse button has been clicked
7407      * (pressed and released) on this {@code Node}.
7408      * @return the event handler that is called when a mouse button has been
7409      * clicked (pressed and released) on this {@code Node}
7410      */
7411     public final ObjectProperty<EventHandler<? super MouseEvent>>
7412             onMouseClickedProperty() {
7413         return getEventHandlerProperties().onMouseClickedProperty();
7414     }
7415 
7416     public final void setOnMouseDragged(
7417             EventHandler<? super MouseEvent> value) {
7418         onMouseDraggedProperty().set(value);
7419     }
7420 
7421     public final EventHandler<? super MouseEvent> getOnMouseDragged() {
7422         return (eventHandlerProperties == null)
7423                 ? null : eventHandlerProperties.getOnMouseDragged();
7424     }
7425 
7426     /**
7427      * Defines a function to be called when a mouse button is pressed
7428      * on this {@code Node} and then dragged.
7429      * @return the event handler that is called when a mouse button is pressed
7430      * on this {@code Node} and then dragged
7431      */
7432     public final ObjectProperty<EventHandler<? super MouseEvent>>
7433             onMouseDraggedProperty() {
7434         return getEventHandlerProperties().onMouseDraggedProperty();
7435     }
7436 
7437     public final void setOnMouseEntered(
7438             EventHandler<? super MouseEvent> value) {
7439         onMouseEnteredProperty().set(value);
7440     }
7441 
7442     public final EventHandler<? super MouseEvent> getOnMouseEntered() {
7443         return (eventHandlerProperties == null)
7444                 ? null : eventHandlerProperties.getOnMouseEntered();
7445     }
7446 
7447     /**
7448      * Defines a function to be called when the mouse enters this {@code Node}.
7449      * @return the event handler that is called when a mouse enters this
7450      * {@code Node}
7451      */
7452     public final ObjectProperty<EventHandler<? super MouseEvent>>
7453             onMouseEnteredProperty() {
7454         return getEventHandlerProperties().onMouseEnteredProperty();
7455     }
7456 
7457     public final void setOnMouseExited(
7458             EventHandler<? super MouseEvent> value) {
7459         onMouseExitedProperty().set(value);
7460     }
7461 
7462     public final EventHandler<? super MouseEvent> getOnMouseExited() {
7463         return (eventHandlerProperties == null)
7464                 ? null : eventHandlerProperties.getOnMouseExited();
7465     }
7466 
7467     /**
7468      * Defines a function to be called when the mouse exits this {@code Node}.
7469      * @return the event handler that is called when a mouse exits this
7470      * {@code Node}
7471      */
7472     public final ObjectProperty<EventHandler<? super MouseEvent>>
7473             onMouseExitedProperty() {
7474         return getEventHandlerProperties().onMouseExitedProperty();
7475     }
7476 
7477     public final void setOnMouseMoved(
7478             EventHandler<? super MouseEvent> value) {
7479         onMouseMovedProperty().set(value);
7480     }
7481 
7482     public final EventHandler<? super MouseEvent> getOnMouseMoved() {
7483         return (eventHandlerProperties == null)
7484                 ? null : eventHandlerProperties.getOnMouseMoved();
7485     }
7486 
7487     /**
7488      * Defines a function to be called when mouse cursor moves within
7489      * this {@code Node} but no buttons have been pushed.
7490      * @return the event handler that is called when a mouse cursor moves
7491      * within this {@code Node} but no buttons have been pushed
7492      */
7493     public final ObjectProperty<EventHandler<? super MouseEvent>>
7494             onMouseMovedProperty() {
7495         return getEventHandlerProperties().onMouseMovedProperty();
7496     }
7497 
7498     public final void setOnMousePressed(
7499             EventHandler<? super MouseEvent> value) {
7500         onMousePressedProperty().set(value);
7501     }
7502 
7503     public final EventHandler<? super MouseEvent> getOnMousePressed() {
7504         return (eventHandlerProperties == null)
7505                 ? null : eventHandlerProperties.getOnMousePressed();
7506     }
7507 
7508     /**
7509      * Defines a function to be called when a mouse button
7510      * has been pressed on this {@code Node}.
7511      * @return the event handler that is called when a mouse button has been
7512      * pressed on this {@code Node}
7513      */
7514     public final ObjectProperty<EventHandler<? super MouseEvent>>
7515             onMousePressedProperty() {
7516         return getEventHandlerProperties().onMousePressedProperty();
7517     }
7518 
7519     public final void setOnMouseReleased(
7520             EventHandler<? super MouseEvent> value) {
7521         onMouseReleasedProperty().set(value);
7522     }
7523 
7524     public final EventHandler<? super MouseEvent> getOnMouseReleased() {
7525         return (eventHandlerProperties == null)
7526                 ? null : eventHandlerProperties.getOnMouseReleased();
7527     }
7528 
7529     /**
7530      * Defines a function to be called when a mouse button
7531      * has been released on this {@code Node}.
7532      * @return the event handler that is called when a mouse button has been
7533      * released on this {@code Node}
7534      */
7535     public final ObjectProperty<EventHandler<? super MouseEvent>>
7536             onMouseReleasedProperty() {
7537         return getEventHandlerProperties().onMouseReleasedProperty();
7538     }
7539 
7540     public final void setOnDragDetected(
7541             EventHandler<? super MouseEvent> value) {
7542         onDragDetectedProperty().set(value);
7543     }
7544 
7545     public final EventHandler<? super MouseEvent> getOnDragDetected() {
7546         return (eventHandlerProperties == null)
7547                 ? null : eventHandlerProperties.getOnDragDetected();
7548     }
7549 
7550     /**
7551      * Defines a function to be called when drag gesture has been
7552      * detected. This is the right place to start drag and drop operation.
7553      * @return the event handler that is called when drag gesture has been
7554      * detected
7555      */
7556     public final ObjectProperty<EventHandler<? super MouseEvent>>
7557             onDragDetectedProperty() {
7558         return getEventHandlerProperties().onDragDetectedProperty();
7559     }
7560 
7561     public final void setOnMouseDragOver(
7562             EventHandler<? super MouseDragEvent> value) {
7563         onMouseDragOverProperty().set(value);
7564     }
7565 
7566     public final EventHandler<? super MouseDragEvent> getOnMouseDragOver() {
7567         return (eventHandlerProperties == null)
7568                 ? null : eventHandlerProperties.getOnMouseDragOver();
7569     }
7570 
7571     /**
7572      * Defines a function to be called when a full press-drag-release gesture
7573      * progresses within this {@code Node}.
7574      * @return the event handler that is called when a full press-drag-release
7575      * gesture progresses within this {@code Node}
7576      * @since JavaFX 2.1
7577      */
7578     public final ObjectProperty<EventHandler<? super MouseDragEvent>>
7579             onMouseDragOverProperty() {
7580         return getEventHandlerProperties().onMouseDragOverProperty();
7581     }
7582 
7583     public final void setOnMouseDragReleased(
7584             EventHandler<? super MouseDragEvent> value) {
7585         onMouseDragReleasedProperty().set(value);
7586     }
7587 
7588     public final EventHandler<? super MouseDragEvent> getOnMouseDragReleased() {
7589         return (eventHandlerProperties == null)
7590                 ? null : eventHandlerProperties.getOnMouseDragReleased();
7591     }
7592 
7593     /**
7594      * Defines a function to be called when a full press-drag-release gesture
7595      * ends (by releasing mouse button) within this {@code Node}.
7596      * @return the event handler that is called when a full press-drag-release
7597      * gesture ends (by releasing mouse button) within this {@code Node}
7598      * @since JavaFX 2.1
7599      */
7600     public final ObjectProperty<EventHandler<? super MouseDragEvent>>
7601             onMouseDragReleasedProperty() {
7602         return getEventHandlerProperties().onMouseDragReleasedProperty();
7603     }
7604 
7605     public final void setOnMouseDragEntered(
7606             EventHandler<? super MouseDragEvent> value) {
7607         onMouseDragEnteredProperty().set(value);
7608     }
7609 
7610     public final EventHandler<? super MouseDragEvent> getOnMouseDragEntered() {
7611         return (eventHandlerProperties == null)
7612                 ? null : eventHandlerProperties.getOnMouseDragEntered();
7613     }
7614 
7615     /**
7616      * Defines a function to be called when a full press-drag-release gesture
7617      * enters this {@code Node}.
7618      * @return the event handler that is called when a full press-drag-release
7619      * gesture enters this {@code Node}
7620      * @since JavaFX 2.1
7621      */
7622     public final ObjectProperty<EventHandler<? super MouseDragEvent>>
7623             onMouseDragEnteredProperty() {
7624         return getEventHandlerProperties().onMouseDragEnteredProperty();
7625     }
7626 
7627     public final void setOnMouseDragExited(
7628             EventHandler<? super MouseDragEvent> value) {
7629         onMouseDragExitedProperty().set(value);
7630     }
7631 
7632     public final EventHandler<? super MouseDragEvent> getOnMouseDragExited() {
7633         return (eventHandlerProperties == null)
7634                 ? null : eventHandlerProperties.getOnMouseDragExited();
7635     }
7636 
7637     /**
7638      * Defines a function to be called when a full press-drag-release gesture
7639      * leaves this {@code Node}.
7640      * @return the event handler that is called when a full press-drag-release
7641      * gesture leaves this {@code Node}
7642      * @since JavaFX 2.1
7643      */
7644     public final ObjectProperty<EventHandler<? super MouseDragEvent>>
7645             onMouseDragExitedProperty() {
7646         return getEventHandlerProperties().onMouseDragExitedProperty();
7647     }
7648 
7649 
7650     /* *************************************************************************
7651      *                                                                         *
7652      *                           Gestures Handling                             *
7653      *                                                                         *
7654      **************************************************************************/
7655 
7656     public final void setOnScrollStarted(
7657             EventHandler<? super ScrollEvent> value) {
7658         onScrollStartedProperty().set(value);
7659     }
7660 
7661     public final EventHandler<? super ScrollEvent> getOnScrollStarted() {
7662         return (eventHandlerProperties == null)
7663                 ? null : eventHandlerProperties.getOnScrollStarted();
7664     }
7665 
7666     /**
7667      * Defines a function to be called when a scrolling gesture is detected.
7668      * @return the event handler that is called when a scrolling gesture is
7669      * detected
7670      * @since JavaFX 2.2
7671      */
7672     public final ObjectProperty<EventHandler<? super ScrollEvent>>
7673             onScrollStartedProperty() {
7674         return getEventHandlerProperties().onScrollStartedProperty();
7675     }
7676 
7677     public final void setOnScroll(
7678             EventHandler<? super ScrollEvent> value) {
7679         onScrollProperty().set(value);
7680     }
7681 
7682     public final EventHandler<? super ScrollEvent> getOnScroll() {
7683         return (eventHandlerProperties == null)
7684                 ? null : eventHandlerProperties.getOnScroll();
7685     }
7686 
7687     /**
7688      * Defines a function to be called when user performs a scrolling action.
7689      * @return the event handler that is called when user performs a scrolling
7690      * action
7691      */
7692     public final ObjectProperty<EventHandler<? super ScrollEvent>>
7693             onScrollProperty() {
7694         return getEventHandlerProperties().onScrollProperty();
7695     }
7696 
7697     public final void setOnScrollFinished(
7698             EventHandler<? super ScrollEvent> value) {
7699         onScrollFinishedProperty().set(value);
7700     }
7701 
7702     public final EventHandler<? super ScrollEvent> getOnScrollFinished() {
7703         return (eventHandlerProperties == null)
7704                 ? null : eventHandlerProperties.getOnScrollFinished();
7705     }
7706 
7707     /**
7708      * Defines a function to be called when a scrolling gesture ends.
7709      * @return the event handler that is called when a scrolling gesture ends
7710      * @since JavaFX 2.2
7711      */
7712     public final ObjectProperty<EventHandler<? super ScrollEvent>>
7713             onScrollFinishedProperty() {
7714         return getEventHandlerProperties().onScrollFinishedProperty();
7715     }
7716 
7717     public final void setOnRotationStarted(
7718             EventHandler<? super RotateEvent> value) {
7719         onRotationStartedProperty().set(value);
7720     }
7721 
7722     public final EventHandler<? super RotateEvent> getOnRotationStarted() {
7723         return (eventHandlerProperties == null)
7724                 ? null : eventHandlerProperties.getOnRotationStarted();
7725     }
7726 
7727     /**
7728      * Defines a function to be called when a rotation gesture is detected.
7729      * @return the event handler that is called when a rotation gesture is
7730      * detected
7731      * @since JavaFX 2.2
7732      */
7733     public final ObjectProperty<EventHandler<? super RotateEvent>>
7734             onRotationStartedProperty() {
7735         return getEventHandlerProperties().onRotationStartedProperty();
7736     }
7737 
7738     public final void setOnRotate(
7739             EventHandler<? super RotateEvent> value) {
7740         onRotateProperty().set(value);
7741     }
7742 
7743     public final EventHandler<? super RotateEvent> getOnRotate() {
7744         return (eventHandlerProperties == null)
7745                 ? null : eventHandlerProperties.getOnRotate();
7746     }
7747 
7748     /**
7749      * Defines a function to be called when user performs a rotation action.
7750      * @return the event handler that is called when user performs a rotation
7751      * action
7752      * @since JavaFX 2.2
7753      */
7754     public final ObjectProperty<EventHandler<? super RotateEvent>>
7755             onRotateProperty() {
7756         return getEventHandlerProperties().onRotateProperty();
7757     }
7758 
7759     public final void setOnRotationFinished(
7760             EventHandler<? super RotateEvent> value) {
7761         onRotationFinishedProperty().set(value);
7762     }
7763 
7764     public final EventHandler<? super RotateEvent> getOnRotationFinished() {
7765         return (eventHandlerProperties == null)
7766                 ? null : eventHandlerProperties.getOnRotationFinished();
7767     }
7768 
7769     /**
7770      * Defines a function to be called when a rotation gesture ends.
7771      * @return the event handler that is called when a rotation gesture ends
7772      * @since JavaFX 2.2
7773      */
7774     public final ObjectProperty<EventHandler<? super RotateEvent>>
7775             onRotationFinishedProperty() {
7776         return getEventHandlerProperties().onRotationFinishedProperty();
7777     }
7778 
7779     public final void setOnZoomStarted(
7780             EventHandler<? super ZoomEvent> value) {
7781         onZoomStartedProperty().set(value);
7782     }
7783 
7784     public final EventHandler<? super ZoomEvent> getOnZoomStarted() {
7785         return (eventHandlerProperties == null)
7786                 ? null : eventHandlerProperties.getOnZoomStarted();
7787     }
7788 
7789     /**
7790      * Defines a function to be called when a zooming gesture is detected.
7791      * @return the event handler that is called when a zooming gesture is
7792      * detected
7793      * @since JavaFX 2.2
7794      */
7795     public final ObjectProperty<EventHandler<? super ZoomEvent>>
7796             onZoomStartedProperty() {
7797         return getEventHandlerProperties().onZoomStartedProperty();
7798     }
7799 
7800     public final void setOnZoom(
7801             EventHandler<? super ZoomEvent> value) {
7802         onZoomProperty().set(value);
7803     }
7804 
7805     public final EventHandler<? super ZoomEvent> getOnZoom() {
7806         return (eventHandlerProperties == null)
7807                 ? null : eventHandlerProperties.getOnZoom();
7808     }
7809 
7810     /**
7811      * Defines a function to be called when user performs a zooming action.
7812      * @return the event handler that is called when user performs a zooming
7813      * action
7814      * @since JavaFX 2.2
7815      */
7816     public final ObjectProperty<EventHandler<? super ZoomEvent>>
7817             onZoomProperty() {
7818         return getEventHandlerProperties().onZoomProperty();
7819     }
7820 
7821     public final void setOnZoomFinished(
7822             EventHandler<? super ZoomEvent> value) {
7823         onZoomFinishedProperty().set(value);
7824     }
7825 
7826     public final EventHandler<? super ZoomEvent> getOnZoomFinished() {
7827         return (eventHandlerProperties == null)
7828                 ? null : eventHandlerProperties.getOnZoomFinished();
7829     }
7830 
7831     /**
7832      * Defines a function to be called when a zooming gesture ends.
7833      * @return the event handler that is called when a zooming gesture ends
7834      * @since JavaFX 2.2
7835      */
7836     public final ObjectProperty<EventHandler<? super ZoomEvent>>
7837             onZoomFinishedProperty() {
7838         return getEventHandlerProperties().onZoomFinishedProperty();
7839     }
7840 
7841     public final void setOnSwipeUp(
7842             EventHandler<? super SwipeEvent> value) {
7843         onSwipeUpProperty().set(value);
7844     }
7845 
7846     public final EventHandler<? super SwipeEvent> getOnSwipeUp() {
7847         return (eventHandlerProperties == null)
7848                 ? null : eventHandlerProperties.getOnSwipeUp();
7849     }
7850 
7851     /**
7852      * Defines a function to be called when an upward swipe gesture
7853      * centered over this node happens.
7854      * @return the event handler that is called when an upward swipe gesture
7855      * centered over this node happens
7856      * @since JavaFX 2.2
7857      */
7858     public final ObjectProperty<EventHandler<? super SwipeEvent>>
7859             onSwipeUpProperty() {
7860         return getEventHandlerProperties().onSwipeUpProperty();
7861     }
7862 
7863     public final void setOnSwipeDown(
7864             EventHandler<? super SwipeEvent> value) {
7865         onSwipeDownProperty().set(value);
7866     }
7867 
7868     public final EventHandler<? super SwipeEvent> getOnSwipeDown() {
7869         return (eventHandlerProperties == null)
7870                 ? null : eventHandlerProperties.getOnSwipeDown();
7871     }
7872 
7873     /**
7874      * Defines a function to be called when a downward swipe gesture
7875      * centered over this node happens.
7876      * @return the event handler that is called when a downward swipe gesture
7877      * centered over this node happens
7878      * @since JavaFX 2.2
7879      */
7880     public final ObjectProperty<EventHandler<? super SwipeEvent>>
7881             onSwipeDownProperty() {
7882         return getEventHandlerProperties().onSwipeDownProperty();
7883     }
7884 
7885     public final void setOnSwipeLeft(
7886             EventHandler<? super SwipeEvent> value) {
7887         onSwipeLeftProperty().set(value);
7888     }
7889 
7890     public final EventHandler<? super SwipeEvent> getOnSwipeLeft() {
7891         return (eventHandlerProperties == null)
7892                 ? null : eventHandlerProperties.getOnSwipeLeft();
7893     }
7894 
7895     /**
7896      * Defines a function to be called when a leftward swipe gesture
7897      * centered over this node happens.
7898      * @return the event handler that is called when a leftward swipe gesture
7899      * centered over this node happens
7900      * @since JavaFX 2.2
7901      */
7902     public final ObjectProperty<EventHandler<? super SwipeEvent>>
7903             onSwipeLeftProperty() {
7904         return getEventHandlerProperties().onSwipeLeftProperty();
7905     }
7906 
7907     public final void setOnSwipeRight(
7908             EventHandler<? super SwipeEvent> value) {
7909         onSwipeRightProperty().set(value);
7910     }
7911 
7912     public final EventHandler<? super SwipeEvent> getOnSwipeRight() {
7913         return (eventHandlerProperties == null)
7914                 ? null : eventHandlerProperties.getOnSwipeRight();
7915     }
7916 
7917     /**
7918      * Defines a function to be called when an rightward swipe gesture
7919      * centered over this node happens.
7920      * @return the event handler that is called when an rightward swipe gesture
7921      * centered over this node happens
7922      * @since JavaFX 2.2
7923      */
7924     public final ObjectProperty<EventHandler<? super SwipeEvent>>
7925             onSwipeRightProperty() {
7926         return getEventHandlerProperties().onSwipeRightProperty();
7927     }
7928 
7929 
7930     /* *************************************************************************
7931      *                                                                         *
7932      *                             Touch Handling                              *
7933      *                                                                         *
7934      **************************************************************************/
7935 
7936     public final void setOnTouchPressed(
7937             EventHandler<? super TouchEvent> value) {
7938         onTouchPressedProperty().set(value);
7939     }
7940 
7941     public final EventHandler<? super TouchEvent> getOnTouchPressed() {
7942         return (eventHandlerProperties == null)
7943                 ? null : eventHandlerProperties.getOnTouchPressed();
7944     }
7945 
7946     /**
7947      * Defines a function to be called when a new touch point is pressed.
7948      * @return the event handler that is called when a new touch point is pressed
7949      * @since JavaFX 2.2
7950      */
7951     public final ObjectProperty<EventHandler<? super TouchEvent>>
7952             onTouchPressedProperty() {
7953         return getEventHandlerProperties().onTouchPressedProperty();
7954     }
7955 
7956     public final void setOnTouchMoved(
7957             EventHandler<? super TouchEvent> value) {
7958         onTouchMovedProperty().set(value);
7959     }
7960 
7961     public final EventHandler<? super TouchEvent> getOnTouchMoved() {
7962         return (eventHandlerProperties == null)
7963                 ? null : eventHandlerProperties.getOnTouchMoved();
7964     }
7965 
7966     /**
7967      * Defines a function to be called when a touch point is moved.
7968      * @return the event handler that is called when a touch point is moved
7969      * @since JavaFX 2.2
7970      */
7971     public final ObjectProperty<EventHandler<? super TouchEvent>>
7972             onTouchMovedProperty() {
7973         return getEventHandlerProperties().onTouchMovedProperty();
7974     }
7975 
7976     public final void setOnTouchReleased(
7977             EventHandler<? super TouchEvent> value) {
7978         onTouchReleasedProperty().set(value);
7979     }
7980 
7981     public final EventHandler<? super TouchEvent> getOnTouchReleased() {
7982         return (eventHandlerProperties == null)
7983                 ? null : eventHandlerProperties.getOnTouchReleased();
7984     }
7985 
7986     /**
7987      * Defines a function to be called when a touch point is released.
7988      * @return the event handler that is called when a touch point is released
7989      * @since JavaFX 2.2
7990      */
7991     public final ObjectProperty<EventHandler<? super TouchEvent>>
7992             onTouchReleasedProperty() {
7993         return getEventHandlerProperties().onTouchReleasedProperty();
7994     }
7995 
7996     public final void setOnTouchStationary(
7997             EventHandler<? super TouchEvent> value) {
7998         onTouchStationaryProperty().set(value);
7999     }
8000 
8001     public final EventHandler<? super TouchEvent> getOnTouchStationary() {
8002         return (eventHandlerProperties == null)
8003                 ? null : eventHandlerProperties.getOnTouchStationary();
8004     }
8005 
8006     /**
8007      * Defines a function to be called when a touch point stays pressed and
8008      * still.
8009      * @return the event handler that is called when a touch point stays pressed
8010      * and still
8011      * @since JavaFX 2.2
8012      */
8013     public final ObjectProperty<EventHandler<? super TouchEvent>>
8014             onTouchStationaryProperty() {
8015         return getEventHandlerProperties().onTouchStationaryProperty();
8016     }
8017 
8018     /* *************************************************************************
8019      *                                                                         *
8020      *                           Keyboard Handling                             *
8021      *                                                                         *
8022      **************************************************************************/
8023 
8024     public final void setOnKeyPressed(
8025             EventHandler<? super KeyEvent> value) {
8026         onKeyPressedProperty().set(value);
8027     }
8028 
8029     public final EventHandler<? super KeyEvent> getOnKeyPressed() {
8030         return (eventHandlerProperties == null)
8031                 ? null : eventHandlerProperties.getOnKeyPressed();
8032     }
8033 
8034     /**
8035      * Defines a function to be called when this {@code Node} or its child
8036      * {@code Node} has input focus and a key has been pressed. The function
8037      * is called only if the event hasn't been already consumed during its
8038      * capturing or bubbling phase.
8039      * @return the event handler that is called when this {@code Node} or its
8040      * child {@code Node} has input focus and a key has been pressed
8041      */
8042     public final ObjectProperty<EventHandler<? super KeyEvent>>
8043             onKeyPressedProperty() {
8044         return getEventHandlerProperties().onKeyPressedProperty();
8045     }
8046 
8047     public final void setOnKeyReleased(
8048             EventHandler<? super KeyEvent> value) {
8049         onKeyReleasedProperty().set(value);
8050     }
8051 
8052     public final EventHandler<? super KeyEvent> getOnKeyReleased() {
8053         return (eventHandlerProperties == null)
8054                 ? null : eventHandlerProperties.getOnKeyReleased();
8055     }
8056 
8057     /**
8058      * Defines a function to be called when this {@code Node} or its child
8059      * {@code Node} has input focus and a key has been released. The function
8060      * is called only if the event hasn't been already consumed during its
8061      * capturing or bubbling phase.
8062      * @return the event handler that is called when this {@code Node} or its
8063      * child {@code Node} has input focus and a key has been released
8064      */
8065     public final ObjectProperty<EventHandler<? super KeyEvent>>
8066             onKeyReleasedProperty() {
8067         return getEventHandlerProperties().onKeyReleasedProperty();
8068     }
8069 
8070     public final void setOnKeyTyped(
8071             EventHandler<? super KeyEvent> value) {
8072         onKeyTypedProperty().set(value);
8073     }
8074 
8075     public final EventHandler<? super KeyEvent> getOnKeyTyped() {
8076         return (eventHandlerProperties == null)
8077                 ? null : eventHandlerProperties.getOnKeyTyped();
8078     }
8079 
8080     /**
8081      * Defines a function to be called when this {@code Node} or its child
8082      * {@code Node} has input focus and a key has been typed. The function
8083      * is called only if the event hasn't been already consumed during its
8084      * capturing or bubbling phase.
8085      * @return the event handler that is called when this {@code Node} or its
8086      * child {@code Node} has input focus and a key has been typed
8087      */
8088     public final ObjectProperty<EventHandler<? super KeyEvent>>
8089             onKeyTypedProperty() {
8090         return getEventHandlerProperties().onKeyTypedProperty();
8091     }
8092 
8093     /* *************************************************************************
8094      *                                                                         *
8095      *                           Input Method Handling                         *
8096      *                                                                         *
8097      **************************************************************************/
8098 
8099     public final void setOnInputMethodTextChanged(
8100             EventHandler<? super InputMethodEvent> value) {
8101         onInputMethodTextChangedProperty().set(value);
8102     }
8103 
8104     public final EventHandler<? super InputMethodEvent>
8105             getOnInputMethodTextChanged() {
8106         return (eventHandlerProperties == null)
8107                 ? null : eventHandlerProperties.getOnInputMethodTextChanged();
8108     }
8109 
8110     /**
8111      * Defines a function to be called when this {@code Node}
8112      * has input focus and the input method text has changed.  If this
8113      * function is not defined in this {@code Node}, then it
8114      * receives the result string of the input method composition as a
8115      * series of {@code onKeyTyped} function calls.
8116      * <p>
8117      * When the {@code Node} loses the input focus, the JavaFX runtime
8118      * automatically commits the existing composed text if any.
8119      * </p>
8120      * @return the event handler that is called when this {@code Node} has input
8121      * focus and the input method text has changed
8122      */
8123     public final ObjectProperty<EventHandler<? super InputMethodEvent>>
8124             onInputMethodTextChangedProperty() {
8125         return getEventHandlerProperties().onInputMethodTextChangedProperty();
8126     }
8127 
8128     public final void setInputMethodRequests(InputMethodRequests value) {
8129         inputMethodRequestsProperty().set(value);
8130     }
8131 
8132     public final InputMethodRequests getInputMethodRequests() {
8133         return (miscProperties == null)
8134                        ? DEFAULT_INPUT_METHOD_REQUESTS
8135                        : miscProperties.getInputMethodRequests();
8136     }
8137 
8138     /**
8139      * Property holding InputMethodRequests.
8140      *
8141      * @return InputMethodRequestsProperty
8142      */
8143     public final ObjectProperty<InputMethodRequests> inputMethodRequestsProperty() {
8144         return getMiscProperties().inputMethodRequestsProperty();
8145     }
8146 
8147     /***************************************************************************
8148      *                                                                         *
8149      *                             Focus Traversal                             *
8150      *                                                                         *
8151      **************************************************************************/
8152 
8153     /**
8154      * Special boolean property which allows for atomic focus change.
8155      * Focus change means defocusing the old focus owner and focusing a new
8156      * one. With a usual property, defocusing the old node fires the value
8157      * changed event and user code can react with something that breaks
8158      * focusability of the new node, or even remove the new node from the scene.
8159      * This leads to various error states. This property allows for setting
8160      * the state without firing the event. The focus change first sets both
8161      * properties and then fires both events. This makes the focus change look
8162      * like an atomic operation - when the old node is notified to loose focus,
8163      * the new node is already focused.
8164      */
8165     final class FocusedProperty extends ReadOnlyBooleanPropertyBase {
8166         private boolean value;
8167         private boolean valid = true;
8168         private boolean needsChangeEvent = false;
8169 
8170         public void store(final boolean value) {
8171             if (value != this.value) {
8172                 this.value = value;
8173                 markInvalid();
8174             }
8175         }
8176 
8177         public void notifyListeners() {
8178             if (needsChangeEvent) {
8179                 fireValueChangedEvent();
8180                 needsChangeEvent = false;
8181             }
8182         }
8183 
8184         private void markInvalid() {
8185             if (valid) {
8186                 valid = false;
8187 
8188                 pseudoClassStateChanged(FOCUSED_PSEUDOCLASS_STATE, get());
8189                 PlatformLogger logger = Logging.getFocusLogger();
8190                 if (logger.isLoggable(Level.FINE)) {
8191                     logger.fine(this + " focused=" + get());
8192                 }
8193 
8194                 needsChangeEvent = true;
8195 
8196                 notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUSED);
8197             }
8198         }
8199 
8200         @Override
8201         public boolean get() {
8202             valid = true;
8203             return value;
8204         }
8205 
8206         @Override
8207         public Object getBean() {
8208             return Node.this;
8209         }
8210 
8211         @Override
8212         public String getName() {
8213             return "focused";
8214         }
8215     }
8216 
8217     /**
8218      * Indicates whether this {@code Node} currently has the input focus.
8219      * To have the input focus, a node must be the {@code Scene}'s focus
8220      * owner, and the scene must be in a {@code Stage} that is visible
8221      * and active. See {@link #requestFocus()} for more information.
8222      *
8223      * @see #requestFocus()
8224      * @defaultValue false
8225      */
8226     private FocusedProperty focused;
8227 
8228     protected final void setFocused(boolean value) {
8229         FocusedProperty fp = focusedPropertyImpl();
8230         if (fp.value != value) {
8231             fp.store(value);
8232             fp.notifyListeners();
8233         }
8234     }
8235 
8236     public final boolean isFocused() {
8237         return focused == null ? false : focused.get();
8238     }
8239 
8240     public final ReadOnlyBooleanProperty focusedProperty() {
8241         return focusedPropertyImpl();
8242     }
8243 
8244     private FocusedProperty focusedPropertyImpl() {
8245         if (focused == null) {
8246             focused = new FocusedProperty();
8247         }
8248         return focused;
8249     }
8250 
8251     /**
8252      * Specifies whether this {@code Node} should be a part of focus traversal
8253      * cycle. When this property is {@code true} focus can be moved to this
8254      * {@code Node} and from this {@code Node} using regular focus traversal
8255      * keys. On a desktop such keys are usually {@code TAB} for moving focus
8256      * forward and {@code SHIFT+TAB} for moving focus backward.
8257      *
8258      * When a {@code Scene} is created, the system gives focus to a
8259      * {@code Node} whose {@code focusTraversable} variable is true
8260      * and that is eligible to receive the focus,
8261      * unless the focus had been set explicitly via a call
8262      * to {@link #requestFocus()}.
8263      *
8264      * @see #requestFocus()
8265      * @defaultValue false
8266      */
8267     private BooleanProperty focusTraversable;
8268 
8269     public final void setFocusTraversable(boolean value) {
8270         focusTraversableProperty().set(value);
8271     }
8272     public final boolean isFocusTraversable() {
8273         return focusTraversable == null ? false : focusTraversable.get();
8274     }
8275 
8276     public final BooleanProperty focusTraversableProperty() {
8277         if (focusTraversable == null) {
8278             focusTraversable = new StyleableBooleanProperty(false) {
8279 
8280                 @Override
8281                 public void invalidated() {
8282                     Scene _scene = getScene();
8283                     if (_scene != null) {
8284                         if (get()) {
8285                             _scene.initializeInternalEventDispatcher();
8286                         }
8287                         focusSetDirty(_scene);
8288                     }
8289                 }
8290 
8291                 @Override
8292                 public CssMetaData getCssMetaData() {
8293                     return StyleableProperties.FOCUS_TRAVERSABLE;
8294                 }
8295 
8296                 @Override
8297                 public Object getBean() {
8298                     return Node.this;
8299                 }
8300 
8301                 @Override
8302                 public String getName() {
8303                     return "focusTraversable";
8304                 }
8305             };
8306         }
8307         return focusTraversable;
8308     }
8309 
8310     /**
8311      * Called when something has changed on this node that *may* have made the
8312      * scene's focus dirty. This covers the cases where this node is the focus
8313      * owner and it may have lost eligibility, or it's traversable and it may
8314      * have gained eligibility. Note that we do not want to use disabled
8315      * or treeVisible here, as this function is called from their
8316      * "on invalidate" triggers, and using them will cause them to be
8317      * revalidated. The pulse will revalidate everything and make the final
8318      * determination.
8319      */
8320     private void focusSetDirty(Scene s) {
8321         if (s != null &&
8322             (this == s.getFocusOwner() || isFocusTraversable())) {
8323                 s.setFocusDirty(true);
8324         }
8325     }
8326 
8327     /**
8328      * Requests that this {@code Node} get the input focus, and that this
8329      * {@code Node}'s top-level ancestor become the focused window. To be
8330      * eligible to receive the focus, the node must be part of a scene, it and
8331      * all of its ancestors must be visible, and it must not be disabled.
8332      * If this node is eligible, this function will cause it to become this
8333      * {@code Scene}'s "focus owner". Each scene has at most one focus owner
8334      * node. The focus owner will not actually have the input focus, however,
8335      * unless the scene belongs to a {@code Stage} that is both visible
8336      * and active.
8337      */
8338     public void requestFocus() {
8339         if (getScene() != null) {
8340             getScene().requestFocus(this);
8341         }
8342     }
8343 
8344     /**
8345      * Traverses from this node in the direction indicated. Note that this
8346      * node need not actually have the focus, nor need it be focusTraversable.
8347      * However, the node must be part of a scene, otherwise this request
8348      * is ignored.
8349      */
8350     final boolean traverse(Direction dir) {
8351         if (getScene() == null) {
8352             return false;
8353         }
8354         return getScene().traverse(this, dir);
8355     }
8356 
8357     ////////////////////////////
8358     //  Private Implementation
8359     ////////////////////////////
8360 
8361      /**
8362       * Returns a string representation for the object.
8363       * @return a string representation for the object.
8364       */
8365     @Override
8366     public String toString() {
8367         String klassName = getClass().getName();
8368         String simpleName = klassName.substring(klassName.lastIndexOf('.')+1);
8369         StringBuilder sbuf = new StringBuilder(simpleName);
8370         boolean hasId = id != null && !"".equals(getId());
8371         boolean hasStyleClass = !getStyleClass().isEmpty();
8372 
8373         if (!hasId) {
8374             sbuf.append('@');
8375             sbuf.append(Integer.toHexString(hashCode()));
8376         } else {
8377             sbuf.append("[id=");
8378             sbuf.append(getId());
8379             if (!hasStyleClass) sbuf.append("]");
8380         }
8381         if (hasStyleClass) {
8382             if (!hasId) sbuf.append('[');
8383             else sbuf.append(", ");
8384             sbuf.append("styleClass=");
8385             sbuf.append(getStyleClass());
8386             sbuf.append("]");
8387         }
8388         return sbuf.toString();
8389     }
8390 
8391     private void preprocessMouseEvent(MouseEvent e) {
8392         final EventType<?> eventType = e.getEventType();
8393         if (eventType == MouseEvent.MOUSE_PRESSED) {
8394             for (Node n = this; n != null; n = n.getParent()) {
8395                 n.setPressed(e.isPrimaryButtonDown());
8396             }
8397             return;
8398         }
8399         if (eventType == MouseEvent.MOUSE_RELEASED) {
8400             for (Node n = this; n != null; n = n.getParent()) {
8401                 n.setPressed(e.isPrimaryButtonDown());
8402             }
8403             return;
8404         }
8405 
8406         if (e.getTarget() == this) {
8407             // the mouse event types are translated only when the node uses
8408             // its internal event dispatcher, so both entered / exited variants
8409             // are possible here
8410 
8411             if ((eventType == MouseEvent.MOUSE_ENTERED)
8412                     || (eventType == MouseEvent.MOUSE_ENTERED_TARGET)) {
8413                 setHover(true);
8414                 return;
8415             }
8416 
8417             if ((eventType == MouseEvent.MOUSE_EXITED)
8418                     || (eventType == MouseEvent.MOUSE_EXITED_TARGET)) {
8419                 setHover(false);
8420                 return;
8421             }
8422         }
8423     }
8424 
8425     void markDirtyLayoutBranch() {
8426         Parent p = getParent();
8427         while (p != null && p.layoutFlag == LayoutFlags.CLEAN) {
8428             p.setLayoutFlag(LayoutFlags.DIRTY_BRANCH);
8429             if (p.isSceneRoot()) {
8430                 Toolkit.getToolkit().requestNextPulse();
8431                 if (getSubScene() != null) {
8432                     getSubScene().setDirtyLayout(p);
8433                 }
8434             }
8435             p = p.getParent();
8436         }
8437 
8438     }
8439 
8440     private boolean isWindowShowing() {
8441         Scene s = getScene();
8442         if (s == null) return false;
8443         Window w = s.getWindow();
8444         return w != null && w.isShowing();
8445     }
8446 
8447     private void updateTreeShowing() {
8448         setTreeShowing(isTreeVisible() && isWindowShowing());
8449     }
8450 
8451     private boolean treeShowing;
8452     private TreeShowingPropertyReadOnly treeShowingRO;
8453 
8454     final void setTreeShowing(boolean value) {
8455         if (treeShowing != value) {
8456             treeShowing = value;
8457             ((TreeShowingPropertyReadOnly) treeShowingProperty()).invalidate();
8458         }
8459     }
8460 
8461     final boolean isTreeShowing() {
8462         return treeShowingProperty().get();
8463     }
8464 
8465     final BooleanExpression treeShowingProperty() {
8466         if (treeShowingRO == null) {
8467             treeShowingRO = new TreeShowingPropertyReadOnly();
8468         }
8469         return treeShowingRO;
8470     }
8471 
8472     class TreeShowingPropertyReadOnly extends BooleanExpression {
8473 
8474         private ExpressionHelper<Boolean> helper;
8475         private boolean valid;
8476 
8477         @Override
8478         public void addListener(InvalidationListener listener) {
8479             helper = ExpressionHelper.addListener(helper, this, listener);
8480         }
8481 
8482         @Override
8483         public void removeListener(InvalidationListener listener) {
8484             helper = ExpressionHelper.removeListener(helper, listener);
8485         }
8486 
8487         @Override
8488         public void addListener(ChangeListener<? super Boolean> listener) {
8489             helper = ExpressionHelper.addListener(helper, this, listener);
8490         }
8491 
8492         @Override
8493         public void removeListener(ChangeListener<? super Boolean> listener) {
8494             helper = ExpressionHelper.removeListener(helper, listener);
8495         }
8496 
8497         protected void invalidate() {
8498             if (valid) {
8499                 valid = false;
8500                 ExpressionHelper.fireValueChangedEvent(helper);
8501             }
8502         }
8503 
8504         @Override
8505         public boolean get() {
8506             valid = true;
8507             return Node.this.treeShowing;
8508         }
8509 
8510     }
8511 
8512     private void updateTreeVisible(boolean parentChanged) {
8513         boolean isTreeVisible = isVisible();
8514         final Node parentNode = getParent() != null ? getParent() :
8515                     clipParent != null ? clipParent :
8516                     getSubScene() != null ? getSubScene() : null;
8517         if (isTreeVisible) {
8518             isTreeVisible = parentNode == null || parentNode.isTreeVisible();
8519         }
8520         // When the parent has changed to visible and we have unsynchornized visibility,
8521         // we have to synchronize, because the rendering will now pass throught the newly-visible parent
8522         // Otherwise an invisible Node might get rendered
8523         if (parentChanged && parentNode != null && parentNode.isTreeVisible()
8524                 && isDirty(DirtyBits.NODE_VISIBLE)) {
8525             addToSceneDirtyList();
8526         }
8527         setTreeVisible(isTreeVisible);
8528 
8529         updateTreeShowing();
8530     }
8531 
8532     private boolean treeVisible;
8533     private TreeVisiblePropertyReadOnly treeVisibleRO;
8534 
8535     final void setTreeVisible(boolean value) {
8536         if (treeVisible != value) {
8537             treeVisible = value;
8538             updateCanReceiveFocus();
8539             focusSetDirty(getScene());
8540             if (getClip() != null) {
8541                 getClip().updateTreeVisible(true);
8542             }
8543             if (treeVisible && !isDirtyEmpty()) {
8544                 addToSceneDirtyList();
8545             }
8546             ((TreeVisiblePropertyReadOnly) treeVisibleProperty()).invalidate();
8547             if (Node.this instanceof SubScene) {
8548                 Node subSceneRoot = ((SubScene)Node.this).getRoot();
8549                 if (subSceneRoot != null) {
8550                     // SubScene.getRoot() is only null if it's constructor
8551                     // has not finished.
8552                     subSceneRoot.setTreeVisible(value && subSceneRoot.isVisible());
8553                 }
8554             }
8555         }
8556     }
8557 
8558     final boolean isTreeVisible() {
8559         return treeVisibleProperty().get();
8560     }
8561 
8562     final BooleanExpression treeVisibleProperty() {
8563         if (treeVisibleRO == null) {
8564             treeVisibleRO = new TreeVisiblePropertyReadOnly();
8565         }
8566         return treeVisibleRO;
8567     }
8568 
8569     class TreeVisiblePropertyReadOnly extends BooleanExpression {
8570 
8571         private ExpressionHelper<Boolean> helper;
8572         private boolean valid;
8573 
8574         @Override
8575         public void addListener(InvalidationListener listener) {
8576             helper = ExpressionHelper.addListener(helper, this, listener);
8577         }
8578 
8579         @Override
8580         public void removeListener(InvalidationListener listener) {
8581             helper = ExpressionHelper.removeListener(helper, listener);
8582         }
8583 
8584         @Override
8585         public void addListener(ChangeListener<? super Boolean> listener) {
8586             helper = ExpressionHelper.addListener(helper, this, listener);
8587         }
8588 
8589         @Override
8590         public void removeListener(ChangeListener<? super Boolean> listener) {
8591             helper = ExpressionHelper.removeListener(helper, listener);
8592         }
8593 
8594         protected void invalidate() {
8595             if (valid) {
8596                 valid = false;
8597                 ExpressionHelper.fireValueChangedEvent(helper);
8598             }
8599         }
8600 
8601         @Override
8602         public boolean get() {
8603             valid = true;
8604             return Node.this.treeVisible;
8605         }
8606 
8607     }
8608 
8609     private boolean canReceiveFocus = false;
8610 
8611     private void setCanReceiveFocus(boolean value) {
8612         canReceiveFocus = value;
8613     }
8614 
8615     final boolean isCanReceiveFocus() {
8616         return canReceiveFocus;
8617     }
8618 
8619     private void updateCanReceiveFocus() {
8620         setCanReceiveFocus(getScene() != null
8621           && !isDisabled()
8622           && isTreeVisible());
8623     }
8624 
8625     // for indenting messages based on scene-graph depth
8626     String indent() {
8627         String indent = "";
8628         Parent p = this.getParent();
8629         while (p != null) {
8630             indent += "  ";
8631             p = p.getParent();
8632         }
8633         return indent;
8634     }
8635 
8636     /*
8637      * Should we underline the mnemonic character?
8638      */
8639     private BooleanProperty showMnemonics;
8640 
8641     final void setShowMnemonics(boolean value) {
8642         showMnemonicsProperty().set(value);
8643     }
8644 
8645     final boolean isShowMnemonics() {
8646         return showMnemonics == null ? false : showMnemonics.get();
8647     }
8648 
8649     final BooleanProperty showMnemonicsProperty() {
8650         if (showMnemonics == null) {
8651             showMnemonics = new BooleanPropertyBase(false) {
8652 
8653                 @Override
8654                 protected void invalidated() {
8655                     pseudoClassStateChanged(SHOW_MNEMONICS_PSEUDOCLASS_STATE, get());
8656                 }
8657 
8658                 @Override
8659                 public Object getBean() {
8660                     return Node.this;
8661                 }
8662 
8663                 @Override
8664                 public String getName() {
8665                     return "showMnemonics";
8666                 }
8667             };
8668         }
8669         return showMnemonics;
8670     }
8671 
8672 
8673     /**
8674      * References a node that is a labelFor this node.
8675      * Accessible via a NodeAccessor. See Label.labelFor for details.
8676      */
8677     private Node labeledBy = null;
8678 
8679 
8680     /***************************************************************************
8681      *                                                                         *
8682      *                         Event Dispatch                                  *
8683      *                                                                         *
8684      **************************************************************************/
8685 
8686     // PENDING_DOC_REVIEW
8687     /**
8688      * Specifies the event dispatcher for this node. The default event
8689      * dispatcher sends the received events to the registered event handlers and
8690      * filters. When replacing the value with a new {@code EventDispatcher},
8691      * the new dispatcher should forward events to the replaced dispatcher
8692      * to maintain the node's default event handling behavior.
8693      */
8694     private ObjectProperty<EventDispatcher> eventDispatcher;
8695 
8696     public final void setEventDispatcher(EventDispatcher value) {
8697         eventDispatcherProperty().set(value);
8698     }
8699 
8700     public final EventDispatcher getEventDispatcher() {
8701         return eventDispatcherProperty().get();
8702     }
8703 
8704     public final ObjectProperty<EventDispatcher> eventDispatcherProperty() {
8705         initializeInternalEventDispatcher();
8706         return eventDispatcher;
8707     }
8708 
8709     private NodeEventDispatcher internalEventDispatcher;
8710 
8711     // PENDING_DOC_REVIEW
8712     /**
8713      * Registers an event handler to this node. The handler is called when the
8714      * node receives an {@code Event} of the specified type during the bubbling
8715      * phase of event delivery.
8716      *
8717      * @param <T> the specific event class of the handler
8718      * @param eventType the type of the events to receive by the handler
8719      * @param eventHandler the handler to register
8720      * @throws NullPointerException if the event type or handler is null
8721      */
8722     public final <T extends Event> void addEventHandler(
8723             final EventType<T> eventType,
8724             final EventHandler<? super T> eventHandler) {
8725         getInternalEventDispatcher().getEventHandlerManager()
8726                                     .addEventHandler(eventType, eventHandler);
8727     }
8728 
8729     // PENDING_DOC_REVIEW
8730     /**
8731      * Unregisters a previously registered event handler from this node. One
8732      * handler might have been registered for different event types, so the
8733      * caller needs to specify the particular event type from which to
8734      * unregister the handler.
8735      *
8736      * @param <T> the specific event class of the handler
8737      * @param eventType the event type from which to unregister
8738      * @param eventHandler the handler to unregister
8739      * @throws NullPointerException if the event type or handler is null
8740      */
8741     public final <T extends Event> void removeEventHandler(
8742             final EventType<T> eventType,
8743             final EventHandler<? super T> eventHandler) {
8744         getInternalEventDispatcher()
8745                 .getEventHandlerManager()
8746                 .removeEventHandler(eventType, eventHandler);
8747     }
8748 
8749     // PENDING_DOC_REVIEW
8750     /**
8751      * Registers an event filter to this node. The filter is called when the
8752      * node receives an {@code Event} of the specified type during the capturing
8753      * phase of event delivery.
8754      *
8755      * @param <T> the specific event class of the filter
8756      * @param eventType the type of the events to receive by the filter
8757      * @param eventFilter the filter to register
8758      * @throws NullPointerException if the event type or filter is null
8759      */
8760     public final <T extends Event> void addEventFilter(
8761             final EventType<T> eventType,
8762             final EventHandler<? super T> eventFilter) {
8763         getInternalEventDispatcher().getEventHandlerManager()
8764                                     .addEventFilter(eventType, eventFilter);
8765     }
8766 
8767     // PENDING_DOC_REVIEW
8768     /**
8769      * Unregisters a previously registered event filter from this node. One
8770      * filter might have been registered for different event types, so the
8771      * caller needs to specify the particular event type from which to
8772      * unregister the filter.
8773      *
8774      * @param <T> the specific event class of the filter
8775      * @param eventType the event type from which to unregister
8776      * @param eventFilter the filter to unregister
8777      * @throws NullPointerException if the event type or filter is null
8778      */
8779     public final <T extends Event> void removeEventFilter(
8780             final EventType<T> eventType,
8781             final EventHandler<? super T> eventFilter) {
8782         getInternalEventDispatcher().getEventHandlerManager()
8783                                     .removeEventFilter(eventType, eventFilter);
8784     }
8785 
8786     /**
8787      * Sets the handler to use for this event type. There can only be one such handler
8788      * specified at a time. This handler is guaranteed to be called as the last, after
8789      * handlers added using {@link #addEventHandler(javafx.event.EventType, javafx.event.EventHandler)}.
8790      * This is used for registering the user-defined onFoo event handlers.
8791      *
8792      * @param <T> the specific event class of the handler
8793      * @param eventType the event type to associate with the given eventHandler
8794      * @param eventHandler the handler to register, or null to unregister
8795      * @throws NullPointerException if the event type is null
8796      */
8797     protected final <T extends Event> void setEventHandler(
8798             final EventType<T> eventType,
8799             final EventHandler<? super T> eventHandler) {
8800         getInternalEventDispatcher().getEventHandlerManager()
8801                                     .setEventHandler(eventType, eventHandler);
8802     }
8803 
8804     private NodeEventDispatcher getInternalEventDispatcher() {
8805         initializeInternalEventDispatcher();
8806         return internalEventDispatcher;
8807     }
8808 
8809     private void initializeInternalEventDispatcher() {
8810         if (internalEventDispatcher == null) {
8811             internalEventDispatcher = createInternalEventDispatcher();
8812             eventDispatcher = new SimpleObjectProperty<EventDispatcher>(
8813                                           Node.this,
8814                                           "eventDispatcher",
8815                                           internalEventDispatcher);
8816         }
8817     }
8818 
8819     private NodeEventDispatcher createInternalEventDispatcher() {
8820         return new NodeEventDispatcher(this);
8821     }
8822 
8823     /**
8824      * Event dispatcher for invoking preprocessing of mouse events
8825      */
8826     private EventDispatcher preprocessMouseEventDispatcher;
8827 
8828     // PENDING_DOC_REVIEW
8829     /**
8830      * Construct an event dispatch chain for this node. The event dispatch chain
8831      * contains all event dispatchers from the stage to this node.
8832      *
8833      * @param tail the initial chain to build from
8834      * @return the resulting event dispatch chain for this node
8835      */
8836     @Override
8837     public EventDispatchChain buildEventDispatchChain(
8838             EventDispatchChain tail) {
8839 
8840         if (preprocessMouseEventDispatcher == null) {
8841             preprocessMouseEventDispatcher = (event, tail1) -> {
8842                 event = tail1.dispatchEvent(event);
8843                 if (event instanceof MouseEvent) {
8844                     preprocessMouseEvent((MouseEvent) event);
8845                 }
8846 
8847                 return event;
8848             };
8849         }
8850 
8851         tail = tail.prepend(preprocessMouseEventDispatcher);
8852 
8853         // prepend all event dispatchers from this node to the root
8854         Node curNode = this;
8855         do {
8856             if (curNode.eventDispatcher != null) {
8857                 final EventDispatcher eventDispatcherValue =
8858                         curNode.eventDispatcher.get();
8859                 if (eventDispatcherValue != null) {
8860                     tail = tail.prepend(eventDispatcherValue);
8861                 }
8862             }
8863             final Node curParent = curNode.getParent();
8864             curNode = curParent != null ? curParent : curNode.getSubScene();
8865         } while (curNode != null);
8866 
8867         if (getScene() != null) {
8868             // prepend scene's dispatch chain
8869             tail = getScene().buildEventDispatchChain(tail);
8870         }
8871 
8872         return tail;
8873     }
8874 
8875     // PENDING_DOC_REVIEW
8876     /**
8877      * Fires the specified event. By default the event will travel through the
8878      * hierarchy from the stage to this node. Any event filter encountered will
8879      * be notified and can consume the event. If not consumed by the filters,
8880      * the event handlers on this node are notified. If these don't consume the
8881      * event either, the event will travel back the same path it arrived to
8882      * this node. All event handlers encountered are called and can consume the
8883      * event.
8884      * <p>
8885      * This method must be called on the FX user thread.
8886      *
8887      * @param event the event to fire
8888      */
8889     public final void fireEvent(Event event) {
8890 
8891         /* Log input events.  We do a coarse filter for at least the FINE
8892          * level and then granularize from there.
8893          */
8894         if (event instanceof InputEvent) {
8895             PlatformLogger logger = Logging.getInputLogger();
8896             if (logger.isLoggable(Level.FINE)) {
8897                 EventType eventType = event.getEventType();
8898                 if (eventType == MouseEvent.MOUSE_ENTERED ||
8899                     eventType == MouseEvent.MOUSE_EXITED) {
8900                     logger.finer(event.toString());
8901                 } else if (eventType == MouseEvent.MOUSE_MOVED ||
8902                            eventType == MouseEvent.MOUSE_DRAGGED) {
8903                     logger.finest(event.toString());
8904                 } else {
8905                     logger.fine(event.toString());
8906                 }
8907             }
8908         }
8909 
8910         Event.fireEvent(this, event);
8911     }
8912 
8913     /***************************************************************************
8914      *                                                                         *
8915      *                         Stylesheet Handling                             *
8916      *                                                                         *
8917      **************************************************************************/
8918 
8919 
8920     /**
8921      * {@inheritDoc}
8922      * @return {@code getClass().getName()} without the package name
8923      * @since JavaFX 8.0
8924      */
8925     @Override
8926     public String getTypeSelector() {
8927 
8928         final Class<?> clazz = getClass();
8929         final Package pkg = clazz.getPackage();
8930 
8931         // package could be null. not likely, but could be.
8932         int plen = 0;
8933         if (pkg != null) {
8934             plen = pkg.getName().length();
8935         }
8936 
8937         final int clen = clazz.getName().length();
8938         final int pos = (0 < plen && plen < clen) ? plen + 1 : 0;
8939 
8940         return clazz.getName().substring(pos);
8941     }
8942 
8943     /**
8944      * {@inheritDoc}
8945      * @return {@code getParent()}
8946      * @since JavaFX 8.0
8947      */
8948     @Override
8949     public Styleable getStyleableParent() {
8950         return getParent();
8951     }
8952 
8953 
8954     /**
8955      * Returns the initial focus traversable state of this node, for use
8956      * by the JavaFX CSS engine to correctly set its initial value. This method
8957      * can be overridden by subclasses in instances where focus traversable should
8958      * initially be true (as the default implementation of this method is to return
8959      * false).
8960      *
8961      * @return the initial focus traversable state for this {@code Node}.
8962      * @since 9
8963      */
8964     protected Boolean getInitialFocusTraversable() {
8965         return Boolean.FALSE;
8966     }
8967 
8968     /**
8969      * Returns the initial cursor state of this node, for use
8970      * by the JavaFX CSS engine to correctly set its initial value. This method
8971      * can be overridden by subclasses in instances where the cursor should
8972      * initially be non-null (as the default implementation of this method is to return
8973      * null).
8974      *
8975      * @return the initial cursor state for this {@code Node}.
8976      * @since 9
8977      */
8978     protected Cursor getInitialCursor() {
8979         return null;
8980     }
8981 
8982      /**
8983       * Super-lazy instantiation pattern from Bill Pugh.
8984       */
8985      private static class StyleableProperties {
8986 
8987         private static final CssMetaData<Node,Cursor> CURSOR =
8988             new CssMetaData<Node,Cursor>("-fx-cursor", CursorConverter.getInstance()) {
8989 
8990                 @Override
8991                 public boolean isSettable(Node node) {
8992                     return node.miscProperties == null || node.miscProperties.canSetCursor();
8993                 }
8994 
8995                 @Override
8996                 public StyleableProperty<Cursor> getStyleableProperty(Node node) {
8997                     return (StyleableProperty<Cursor>)node.cursorProperty();
8998                 }
8999 
9000                 @Override
9001                 public Cursor getInitialValue(Node node) {
9002                     // Most controls default focusTraversable to true.
9003                     // Give a way to have them return the correct default value.
9004                     return node.getInitialCursor();
9005                 }
9006 
9007             };
9008         private static final CssMetaData<Node,Effect> EFFECT =
9009             new CssMetaData<Node,Effect>("-fx-effect", EffectConverter.getInstance()) {
9010 
9011                 @Override
9012                 public boolean isSettable(Node node) {
9013                     return node.miscProperties == null || node.miscProperties.canSetEffect();
9014                 }
9015 
9016                 @Override
9017                 public StyleableProperty<Effect> getStyleableProperty(Node node) {
9018                     return (StyleableProperty<Effect>)node.effectProperty();
9019                 }
9020             };
9021         private static final CssMetaData<Node,Boolean> FOCUS_TRAVERSABLE =
9022             new CssMetaData<Node,Boolean>("-fx-focus-traversable",
9023                 BooleanConverter.getInstance(), Boolean.FALSE) {
9024 
9025                 @Override
9026                 public boolean isSettable(Node node) {
9027                     return node.focusTraversable == null || !node.focusTraversable.isBound();
9028                 }
9029 
9030                 @Override
9031                 public StyleableProperty<Boolean> getStyleableProperty(Node node) {
9032                     return (StyleableProperty<Boolean>)node.focusTraversableProperty();
9033                 }
9034 
9035                 @Override
9036                 public Boolean getInitialValue(Node node) {
9037                     // Most controls default focusTraversable to true.
9038                     // Give a way to have them return the correct default value.
9039                     return node.getInitialFocusTraversable();
9040                 }
9041 
9042             };
9043         private static final CssMetaData<Node,Number> OPACITY =
9044             new CssMetaData<Node,Number>("-fx-opacity",
9045                 SizeConverter.getInstance(), 1.0) {
9046 
9047                 @Override
9048                 public boolean isSettable(Node node) {
9049                     return node.opacity == null || !node.opacity.isBound();
9050                 }
9051 
9052                 @Override
9053                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9054                     return (StyleableProperty<Number>)node.opacityProperty();
9055                 }
9056             };
9057         private static final CssMetaData<Node,BlendMode> BLEND_MODE =
9058             new CssMetaData<Node,BlendMode>("-fx-blend-mode", new EnumConverter<BlendMode>(BlendMode.class)) {
9059 
9060                 @Override
9061                 public boolean isSettable(Node node) {
9062                     return node.blendMode == null || !node.blendMode.isBound();
9063                 }
9064 
9065                 @Override
9066                 public StyleableProperty<BlendMode> getStyleableProperty(Node node) {
9067                     return (StyleableProperty<BlendMode>)node.blendModeProperty();
9068                 }
9069             };
9070         private static final CssMetaData<Node,Number> ROTATE =
9071             new CssMetaData<Node,Number>("-fx-rotate",
9072                 SizeConverter.getInstance(), 0.0) {
9073 
9074                 @Override
9075                 public boolean isSettable(Node node) {
9076                     return node.nodeTransformation == null
9077                         || node.nodeTransformation.rotate == null
9078                         || node.nodeTransformation.canSetRotate();
9079                 }
9080 
9081                 @Override
9082                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9083                     return (StyleableProperty<Number>)node.rotateProperty();
9084                 }
9085             };
9086         private static final CssMetaData<Node,Number> SCALE_X =
9087             new CssMetaData<Node,Number>("-fx-scale-x",
9088                 SizeConverter.getInstance(), 1.0) {
9089 
9090                 @Override
9091                 public boolean isSettable(Node node) {
9092                     return node.nodeTransformation == null
9093                         || node.nodeTransformation.scaleX == null
9094                         || node.nodeTransformation.canSetScaleX();
9095                 }
9096 
9097                 @Override
9098                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9099                     return (StyleableProperty<Number>)node.scaleXProperty();
9100                 }
9101             };
9102         private static final CssMetaData<Node,Number> SCALE_Y =
9103             new CssMetaData<Node,Number>("-fx-scale-y",
9104                 SizeConverter.getInstance(), 1.0) {
9105 
9106                 @Override
9107                 public boolean isSettable(Node node) {
9108                     return node.nodeTransformation == null
9109                         || node.nodeTransformation.scaleY == null
9110                         || node.nodeTransformation.canSetScaleY();
9111                 }
9112 
9113                 @Override
9114                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9115                     return (StyleableProperty<Number>)node.scaleYProperty();
9116                 }
9117             };
9118         private static final CssMetaData<Node,Number> SCALE_Z =
9119             new CssMetaData<Node,Number>("-fx-scale-z",
9120                 SizeConverter.getInstance(), 1.0) {
9121 
9122                 @Override
9123                 public boolean isSettable(Node node) {
9124                     return node.nodeTransformation == null
9125                         || node.nodeTransformation.scaleZ == null
9126                         || node.nodeTransformation.canSetScaleZ();
9127                 }
9128 
9129                 @Override
9130                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9131                     return (StyleableProperty<Number>)node.scaleZProperty();
9132                 }
9133             };
9134         private static final CssMetaData<Node,Number> TRANSLATE_X =
9135             new CssMetaData<Node,Number>("-fx-translate-x",
9136                 SizeConverter.getInstance(), 0.0) {
9137 
9138                 @Override
9139                 public boolean isSettable(Node node) {
9140                     return node.nodeTransformation == null
9141                         || node.nodeTransformation.translateX == null
9142                         || node.nodeTransformation.canSetTranslateX();
9143                 }
9144 
9145                 @Override
9146                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9147                     return (StyleableProperty<Number>)node.translateXProperty();
9148                 }
9149             };
9150         private static final CssMetaData<Node,Number> TRANSLATE_Y =
9151             new CssMetaData<Node,Number>("-fx-translate-y",
9152                 SizeConverter.getInstance(), 0.0) {
9153 
9154                 @Override
9155                 public boolean isSettable(Node node) {
9156                     return node.nodeTransformation == null
9157                         || node.nodeTransformation.translateY == null
9158                         || node.nodeTransformation.canSetTranslateY();
9159                 }
9160 
9161                 @Override
9162                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9163                     return (StyleableProperty<Number>)node.translateYProperty();
9164                 }
9165             };
9166         private static final CssMetaData<Node,Number> TRANSLATE_Z =
9167             new CssMetaData<Node,Number>("-fx-translate-z",
9168                 SizeConverter.getInstance(), 0.0) {
9169 
9170                 @Override
9171                 public boolean isSettable(Node node) {
9172                     return node.nodeTransformation == null
9173                         || node.nodeTransformation.translateZ == null
9174                         || node.nodeTransformation.canSetTranslateZ();
9175                 }
9176 
9177                 @Override
9178                 public StyleableProperty<Number> getStyleableProperty(Node node) {
9179                     return (StyleableProperty<Number>)node.translateZProperty();
9180                 }
9181             };
9182          private static final CssMetaData<Node, Number> VIEW_ORDER
9183                  = new CssMetaData<Node, Number>("-fx-view-order",
9184                          SizeConverter.getInstance(), 0.0) {
9185 
9186                      @Override
9187                      public boolean isSettable(Node node) {
9188                          return node.miscProperties == null
9189                          || node.miscProperties.viewOrder == null
9190                          || !node.miscProperties.viewOrder.isBound();
9191                      }
9192 
9193                      @Override
9194                      public StyleableProperty<Number> getStyleableProperty(Node node) {
9195                          return (StyleableProperty<Number>) node.viewOrderProperty();
9196                      }
9197                  };
9198         private static final CssMetaData<Node,Boolean> VISIBILITY =
9199             new CssMetaData<Node,Boolean>("visibility",
9200                 new StyleConverter<String,Boolean>() {
9201 
9202                     @Override
9203                     // [ visible | hidden | collapse | inherit ]
9204                     public Boolean convert(ParsedValue<String, Boolean> value, Font font) {
9205                         final String sval = value != null ? value.getValue() : null;
9206                         return "visible".equalsIgnoreCase(sval);
9207                     }
9208 
9209                 },
9210                 Boolean.TRUE) {
9211 
9212                 @Override
9213                 public boolean isSettable(Node node) {
9214                     return node.visible == null || !node.visible.isBound();
9215                 }
9216 
9217                 @Override
9218                 public StyleableProperty<Boolean> getStyleableProperty(Node node) {
9219                     return (StyleableProperty<Boolean>)node.visibleProperty();
9220                 }
9221             };
9222 
9223          private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
9224 
9225          static {
9226 
9227              final List<CssMetaData<? extends Styleable, ?>> styleables =
9228                      new ArrayList<CssMetaData<? extends Styleable, ?>>();
9229              styleables.add(CURSOR);
9230              styleables.add(EFFECT);
9231              styleables.add(FOCUS_TRAVERSABLE);
9232              styleables.add(OPACITY);
9233              styleables.add(BLEND_MODE);
9234              styleables.add(ROTATE);
9235              styleables.add(SCALE_X);
9236              styleables.add(SCALE_Y);
9237              styleables.add(SCALE_Z);
9238              styleables.add(VIEW_ORDER);
9239              styleables.add(TRANSLATE_X);
9240              styleables.add(TRANSLATE_Y);
9241              styleables.add(TRANSLATE_Z);
9242              styleables.add(VISIBILITY);
9243              STYLEABLES = Collections.unmodifiableList(styleables);
9244 
9245          }
9246     }
9247 
9248     /**
9249      * @return The CssMetaData associated with this class, which may include the
9250      * CssMetaData of its superclasses.
9251      * @since JavaFX 8.0
9252      */
9253     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
9254         //
9255         // Super-lazy instantiation pattern from Bill Pugh. StyleableProperties
9256         // is referenced no earlier (and therefore loaded no earlier by the
9257         // class loader) than the moment that  getClassCssMetaData() is called.
9258         // This avoids loading the CssMetaData instances until the point at
9259         // which CSS needs the data.
9260         //
9261         return StyleableProperties.STYLEABLES;
9262     }
9263 
9264     /**
9265      * This method should delegate to {@link Node#getClassCssMetaData()} so that
9266      * a Node's CssMetaData can be accessed without the need for reflection.
9267      *
9268      * @return The CssMetaData associated with this node, which may include the
9269      * CssMetaData of its superclasses.
9270      * @since JavaFX 8.0
9271      */
9272 
9273     @Override
9274     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
9275         return getClassCssMetaData();
9276     }
9277 
9278     /*
9279      * @return  The Styles that match this CSS property for the given Node. The
9280      * list is sorted by descending specificity.
9281      */
9282     // SB-dependency: RT-21096 has been filed to track this
9283     static List<Style> getMatchingStyles(CssMetaData cssMetaData, Styleable styleable) {
9284          return CssStyleHelper.getMatchingStyles(styleable, cssMetaData);
9285     }
9286 
9287     final ObservableMap<StyleableProperty<?>, List<Style>> getStyleMap() {
9288          ObservableMap<StyleableProperty<?>, List<Style>> map =
9289                  (ObservableMap<StyleableProperty<?>, List<Style>>)getProperties().get("STYLEMAP");
9290          Map<StyleableProperty<?>, List<Style>> ret = CssStyleHelper.getMatchingStyles(map, this);
9291          if (ret != null) {
9292              if (ret instanceof ObservableMap) return (ObservableMap)ret;
9293              return FXCollections.observableMap(ret);
9294          }
9295          return FXCollections.<StyleableProperty<?>, List<Style>>emptyObservableMap();
9296      }
9297 
9298      /*
9299       * RT-17293
9300       */
9301      // SB-dependency: RT-21096 has been filed to track this
9302      final void setStyleMap(ObservableMap<StyleableProperty<?>, List<Style>> styleMap) {
9303          if (styleMap != null) getProperties().put("STYLEMAP", styleMap);
9304          else getProperties().remove("STYLEMAP");
9305      }
9306 
9307     /*
9308      * Find CSS styles that were used to style this Node in its current pseudo-class state. The map will contain the styles from this node and,
9309      * if the node is a Parent, its children. The node corresponding to an entry in the Map can be obtained by casting a StyleableProperty key to a
9310      * javafx.beans.property.Property and calling getBean(). The List contains only those styles used to style the property and will contain
9311      * styles used to resolve lookup values.
9312      *
9313      * @param styleMap A Map to be populated with the styles. If null, a new Map will be allocated.
9314      * @return The Map populated with matching styles.
9315      */
9316     // SB-dependency: RT-21096 has been filed to track this
9317     Map<StyleableProperty<?>,List<Style>> findStyles(Map<StyleableProperty<?>,List<Style>> styleMap) {
9318 
9319         Map<StyleableProperty<?>, List<Style>> ret = CssStyleHelper.getMatchingStyles(styleMap, this);
9320         return (ret != null) ? ret : Collections.<StyleableProperty<?>, List<Style>>emptyMap();
9321     }
9322 
9323     /**
9324      * Flags used to indicate in which way this node is dirty (or whether it
9325      * is clean) and what must happen during the next CSS cycle on the
9326      * scenegraph.
9327      */
9328     CssFlags cssFlag = CssFlags.CLEAN;
9329 
9330     /**
9331      * Needed for testing.
9332      */
9333     final CssFlags getCSSFlags() { return cssFlag; }
9334 
9335     /**
9336      * Called when a CSS pseudo-class change would cause styles to be reapplied.
9337      */
9338     private void requestCssStateTransition() {
9339         // If there is no scene, then we cannot make it dirty, so we'll leave
9340         // the flag alone
9341         if (getScene() == null) return;
9342         // Don't bother doing anything if the cssFlag is not CLEAN.
9343         // If the flag indicates a DIRTY_BRANCH, the flag needs to be changed
9344         // to UPDATE to ensure that NodeHelper.processCSS is called on the node.
9345         if (cssFlag == CssFlags.CLEAN || cssFlag == CssFlags.DIRTY_BRANCH) {
9346             cssFlag = CssFlags.UPDATE;
9347             notifyParentsOfInvalidatedCSS();
9348         }
9349     }
9350 
9351     /**
9352      * Used to specify that a pseudo-class of this Node has changed. If the
9353      * pseudo-class is used in a CSS selector that matches this Node, CSS will
9354      * be reapplied. Typically, this method is called from the {@code invalidated}
9355      * method of a property that is used as a pseudo-class. For example:
9356      * <pre><code>
9357      *
9358      *     private static final PseudoClass MY_PSEUDO_CLASS_STATE = PseudoClass.getPseudoClass("my-state");
9359      *
9360      *     BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) {
9361      *
9362      *           {@literal @}Override public void invalidated() {
9363      *                pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE, get());
9364      *           }
9365      *
9366      *           {@literal @}Override public Object getBean() {
9367      *               return MyControl.this;
9368      *           }
9369      *
9370      *           {@literal @}Override public String getName() {
9371      *               return "myPseudoClassState";
9372      *           }
9373      *       };
9374      * </code></pre>
9375      * @param pseudoClass the pseudo-class that has changed state
9376      * @param active whether or not the state is active
9377      * @since JavaFX 8.0
9378      */
9379     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
9380 
9381         final boolean modified = active
9382                 ? pseudoClassStates.add(pseudoClass)
9383                 : pseudoClassStates.remove(pseudoClass);
9384 
9385         if (modified && styleHelper != null) {
9386             final boolean isTransition = styleHelper.pseudoClassStateChanged(pseudoClass);
9387             if (isTransition) {
9388                 requestCssStateTransition();
9389             }
9390         }
9391    }
9392 
9393     // package so that StyleHelper can get at it
9394     final ObservableSet<PseudoClass> pseudoClassStates = new PseudoClassState();
9395     /**
9396      * @return The active pseudo-class states of this Node, wrapped in an unmodifiable ObservableSet
9397      * @since JavaFX 8.0
9398      */
9399     public final ObservableSet<PseudoClass> getPseudoClassStates() {
9400 
9401         return FXCollections.unmodifiableObservableSet(pseudoClassStates);
9402 
9403     }
9404 
9405     // Walks up the tree telling each parent that the pseudo class state of
9406     // this node has changed.
9407     final void notifyParentsOfInvalidatedCSS() {
9408         SubScene subScene = getSubScene();
9409         Parent root = (subScene != null) ?
9410                 subScene.getRoot() : getScene().getRoot();
9411 
9412         if (!root.isDirty(DirtyBits.NODE_CSS)) {
9413             // Ensure that Scene.root is marked as dirty. If the scene isn't
9414             // dirty, nothing will get repainted. This bit is cleared from
9415             // Scene in doCSSPass().
9416             NodeHelper.markDirty(root, DirtyBits.NODE_CSS);
9417             if (subScene != null) {
9418                 // If the node is part of a subscene, then we must ensure that
9419                 // the we not only mark subScene.root dirty, but continue and
9420                 // call subScene.notifyParentsOfInvalidatedCSS() until
9421                 // Scene.root gets marked dirty, via the recurisve call:
9422                 subScene.cssFlag = CssFlags.UPDATE;
9423                 subScene.notifyParentsOfInvalidatedCSS();
9424             }
9425         }
9426         Parent _parent = getParent();
9427         while (_parent != null) {
9428             if (_parent.cssFlag == CssFlags.CLEAN) {
9429                 _parent.cssFlag = CssFlags.DIRTY_BRANCH;
9430                 _parent = _parent.getParent();
9431             } else {
9432                 _parent = null;
9433             }
9434         }
9435     }
9436 
9437     final void reapplyCSS() {
9438 
9439         if (getScene() == null) return;
9440 
9441         if (cssFlag == CssFlags.REAPPLY) return;
9442 
9443         // RT-36838 - don't reapply CSS in the middle of an update
9444         if (cssFlag == CssFlags.UPDATE) {
9445             cssFlag = CssFlags.REAPPLY;
9446             notifyParentsOfInvalidatedCSS();
9447             return;
9448         }
9449 
9450         reapplyCss();
9451 
9452         //
9453         // One idiom employed by developers is to, during the layout pass,
9454         // add or remove nodes from the scene. For example, a ScrollPane
9455         // might add scroll bars to itself if it determines during layout
9456         // that it needs them, or a ListView might add cells to itself if
9457         // it determines that it needs to. In such situations we must
9458         // apply the CSS immediately and not add it to the scene's queue
9459         // for deferred action.
9460         //
9461         if (getParent() != null && getParent().isPerformingLayout()) {
9462             NodeHelper.processCSS(this);
9463         } else {
9464             notifyParentsOfInvalidatedCSS();
9465         }
9466 
9467     }
9468 
9469     //
9470     // This method "reapplies" CSS to this node and all of its children. Reapplying CSS
9471     // means that new style maps are calculated for the node. The process of reapplying
9472     // CSS may reset the CSS properties of a node to their initial state, but the _new_
9473     // styles are not applied as part of this process.
9474     //
9475     // There is no check of the CSS state of a child since reapply takes precedence
9476     // over other CSS states.
9477     //
9478     private void reapplyCss() {
9479                 
9480         if (isCssSensitive() == false) {
9481             return;
9482         }
9483 
9484         // Hang on to current styleHelper so we can know whether
9485         // createStyleHelper returned the same styleHelper
9486         final CssStyleHelper oldStyleHelper = styleHelper;
9487 
9488         // CSS state is "REAPPLY"
9489         cssFlag = CssFlags.REAPPLY;
9490 
9491         styleHelper = CssStyleHelper.createStyleHelper(this);
9492 
9493         // REAPPLY to my children, too.
9494         if (this instanceof Parent) {
9495 
9496             // minor optimization to avoid calling createStyleHelper on children
9497             // when we know there will not be any change in the style maps.
9498             final boolean visitChildren =
9499                     // If we don't have a styleHelper, then we should visit the children of this parent
9500                     // since there might be styles that depend on being a child of this parent.
9501                     // In other words, we have .a > .b { blah: blort; }, but no styles for ".a" itself.
9502                     styleHelper == null ||
9503                     // if the styleHelper changed, then we definitely need to visit the children
9504                     // since the new styles may have an effect on the children's styles calculated values.
9505                     (oldStyleHelper != styleHelper) ||
9506                     // If our parent is null, then we're the root of a scene or sub-scene, most likely,
9507                     // and we'll visit children because elsewhere the code depends on root.reapplyCSS()
9508                     // to force css to be reapplied (whether it needs to be or not).
9509                     (getParent() == null) ||
9510                     // If our parent's cssFlag is other than clean, then the parent may have just had
9511                     // CSS reapplied. If the parent just had CSS reapplied, then some of its styles
9512                     // may affect my children's styles.
9513                     (getParent().cssFlag != CssFlags.CLEAN);
9514 
9515             if (visitChildren) {
9516 
9517                 List<Node> children = ((Parent) this).getChildren();
9518                 for (int n = 0, nMax = children.size(); n < nMax; n++) {
9519                     Node child = children.get(n);
9520                     child.reapplyCss();
9521                 }
9522             }
9523 
9524         } else if (this instanceof SubScene) {
9525 
9526             // SubScene root is a Parent, but reapplyCss is a private method in Node
9527             final Node subSceneRoot = ((SubScene)this).getRoot();
9528             if (subSceneRoot != null) {
9529                 subSceneRoot.reapplyCss();
9530             }
9531 
9532         } else if (styleHelper == null) {
9533             //
9534             // If this is not a Parent and there is no styleHelper, then the CSS state is "CLEAN"
9535             // since there are no styles to apply or children to update.
9536             //
9537             cssFlag = CssFlags.CLEAN;
9538             return;
9539         }
9540 
9541         cssFlag = CssFlags.UPDATE;
9542 
9543     }
9544 
9545     void processCSS() {
9546         switch (cssFlag) {
9547             case CLEAN:
9548                 break;
9549             case DIRTY_BRANCH:
9550             {
9551                 Parent me = (Parent)this;
9552                 // clear the flag first in case the flag is set to something
9553                 // other than clean by downstream processing.
9554                 me.cssFlag = CssFlags.CLEAN;
9555 
9556                 if (isCssSensitive() == false) {
9557                     return;
9558                 }
9559                 List<Node> children = me.getChildren();
9560                 for (int i=0, max=children.size(); i<max; i++) {
9561                     children.get(i).processCSS();
9562                 }
9563                 break;
9564             }
9565             case REAPPLY:
9566             case UPDATE:
9567             default:
9568                 NodeHelper.processCSS(this);
9569         }
9570     }
9571 
9572     /**
9573      * If required, apply styles to this Node and its children, if any. This method does not normally need to
9574      * be invoked directly but may be used in conjunction with {@link Parent#layout()} to size a Node before the
9575      * next pulse, or if the {@link #getScene() Scene} is not in a {@link javafx.stage.Stage}.
9576      * <p>Provided that the Node's {@link #getScene() Scene} is not null, CSS is applied to this Node regardless
9577      * of whether this Node's CSS state is clean. CSS styles are applied from the top-most parent
9578      * of this Node whose CSS state is other than clean, which may affect the styling of other nodes.
9579      * This method is a no-op if the Node is not in a Scene. The Scene does not have to be in a Stage.</p>
9580      * <p>This method does not invoke the {@link Parent#layout()} method. Typically, the caller will use the
9581      * following sequence of operations.</p>
9582      * <pre>{@code
9583      *     parentNode.applyCss();
9584      *     parentNode.layout();
9585      * }</pre>
9586      * <p>As a more complete example, the following code uses {@code applyCss()} and {@code layout()} to find
9587      * the width and height of the Button before the Stage has been shown. If either the call to {@code applyCss()}
9588      * or the call to {@code layout()} is commented out, the calls to {@code getWidth()} and {@code getHeight()}
9589      * will return zero (until some time after the Stage is shown). </p>
9590      * <pre><code>
9591      * {@literal @}Override
9592      * public void start(Stage stage) throws Exception {
9593      *
9594      *    Group root = new Group();
9595      *    Scene scene = new Scene(root);
9596      *
9597      *    Button button = new Button("Hello World");
9598      *    root.getChildren().add(button);
9599      *
9600      *    root.applyCss();
9601      *    root.layout();
9602      *
9603      *    double width = button.getWidth();
9604      *    double height = button.getHeight();
9605      *
9606      *    System.out.println(width + ", " + height);
9607      *
9608      *    stage.setScene(scene);
9609      *    stage.show();
9610      * }
9611      * </code></pre>
9612      * @since JavaFX 8.0
9613      */
9614     public final void applyCss() {
9615 
9616         if (isCssSensitive() == false) {
9617             return;
9618         }
9619 
9620         if (getScene() == null) {
9621             return;
9622         }
9623 
9624         // update, unless reapply
9625         if (cssFlag != CssFlags.REAPPLY) cssFlag = CssFlags.UPDATE;
9626 
9627         //
9628         // RT-28394 - need to see if any ancestor has a flag UPDATE
9629         // If so, process css from the top-most CssFlags.UPDATE node
9630         // since my ancestor's styles may affect mine.
9631         //
9632         // If the scene-graph root isn't NODE_CSS dirty, then all my
9633         // ancestor flags should be CLEAN and I can skip this lookup.
9634         //
9635         Node topMost = this;
9636 
9637         final boolean dirtyRoot = getScene().getRoot().isDirty(com.sun.javafx.scene.DirtyBits.NODE_CSS);
9638         if (dirtyRoot) {
9639 
9640             Node _parent = getParent();
9641             while (_parent != null) {
9642                 if (_parent.cssFlag == CssFlags.UPDATE || _parent.cssFlag == CssFlags.REAPPLY) {
9643                     topMost = _parent;
9644                 }
9645                 _parent = _parent.getParent();
9646             }
9647 
9648             // Note: this code used to mark the parent nodes with DIRTY_BRANCH,
9649             // but that isn't necessary since UPDATE will apply css to all of
9650             // a Parent's children.
9651 
9652             // If we're at the root of the scene-graph, make sure the NODE_CSS
9653             // dirty bit is cleared (see Scene#doCSSPass())
9654             if (topMost == getScene().getRoot()) {
9655                 getScene().getRoot().clearDirty(DirtyBits.NODE_CSS);
9656             }
9657         }
9658 
9659         topMost.processCSS();
9660 
9661     }
9662 
9663     /*
9664      * If invoked, will update styles from here on down. This method should not be called directly. If
9665      * overridden, the overriding method must at some point call {@code super.processCSSImpl} to ensure that
9666      * this Node's CSS state is properly updated.
9667      *
9668      * Note that the difference between this method and {@link #applyCss()} is that this method
9669      * updates styles for this node on down; whereas, {@code applyCss()} looks for the top-most ancestor that needs
9670      * CSS update and apply styles from that node on down.
9671      *
9672      * Note: This method MUST only be called via its accessor method.
9673      */
9674     private void doProcessCSS() {
9675 
9676         // Nothing to do...
9677         if (cssFlag == CssFlags.CLEAN) return;
9678 
9679         // if REAPPLY was deferred, process it now...
9680         if (cssFlag == CssFlags.REAPPLY) {
9681             reapplyCss();
9682         }
9683 
9684         // Clear the flag first in case the flag is set to something
9685         // other than clean by downstream processing.
9686         cssFlag = CssFlags.CLEAN;
9687 
9688         if (isCssSensitive() == false) {
9689             return;
9690         }
9691 
9692         // Transition to the new state and apply styles
9693         if (styleHelper != null && getScene() != null) {
9694             styleHelper.transitionToState(this);
9695         }
9696     }
9697 
9698 
9699     /**
9700      * A StyleHelper for this node.
9701      * A StyleHelper contains all the css styles for this node
9702      * and knows how to apply them when our state changes.
9703      */
9704     CssStyleHelper styleHelper;
9705 
9706     private static final PseudoClass HOVER_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("hover");
9707     private static final PseudoClass PRESSED_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("pressed");
9708     private static final PseudoClass DISABLED_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("disabled");
9709     private static final PseudoClass FOCUSED_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("focused");
9710     private static final PseudoClass SHOW_MNEMONICS_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("show-mnemonics");
9711 
9712     private static abstract class LazyTransformProperty
9713             extends ReadOnlyObjectProperty<Transform> {
9714 
9715         protected static final int VALID = 0;
9716         protected static final int INVALID = 1;
9717         protected static final int VALIDITY_UNKNOWN = 2;
9718         protected int valid = INVALID;
9719 
9720         private ExpressionHelper<Transform> helper;
9721 
9722         private Transform transform;
9723         private boolean canReuse = false;
9724 
9725         @Override
9726         public void addListener(InvalidationListener listener) {
9727             helper = ExpressionHelper.addListener(helper, this, listener);
9728         }
9729 
9730         @Override
9731         public void removeListener(InvalidationListener listener) {
9732             helper = ExpressionHelper.removeListener(helper, listener);
9733         }
9734 
9735         @Override
9736         public void addListener(ChangeListener<? super Transform> listener) {
9737             helper = ExpressionHelper.addListener(helper, this, listener);
9738         }
9739 
9740         @Override
9741         public void removeListener(ChangeListener<? super Transform> listener) {
9742             helper = ExpressionHelper.removeListener(helper, listener);
9743         }
9744 
9745         protected Transform getInternalValue() {
9746             if (valid == INVALID ||
9747                     (valid == VALIDITY_UNKNOWN && computeValidity() == INVALID)) {
9748                 transform = computeTransform(canReuse ? transform : null);
9749                 canReuse = true;
9750                 valid = validityKnown() ? VALID : VALIDITY_UNKNOWN;
9751             }
9752 
9753             return transform;
9754         }
9755 
9756         @Override
9757         public Transform get() {
9758             transform = getInternalValue();
9759             canReuse = false;
9760             return transform;
9761         }
9762 
9763         public void validityUnknown() {
9764             if (valid == VALID) {
9765                 valid = VALIDITY_UNKNOWN;
9766             }
9767         }
9768 
9769         public void invalidate() {
9770             if (valid != INVALID) {
9771                 valid = INVALID;
9772                 ExpressionHelper.fireValueChangedEvent(helper);
9773             }
9774         }
9775 
9776         protected abstract boolean validityKnown();
9777         protected abstract int computeValidity();
9778         protected abstract Transform computeTransform(Transform reuse);
9779     }
9780 
9781     private static abstract class LazyBoundsProperty
9782             extends ReadOnlyObjectProperty<Bounds> {
9783         private ExpressionHelper<Bounds> helper;
9784         private boolean valid;
9785 
9786         private Bounds bounds;
9787 
9788         @Override
9789         public void addListener(InvalidationListener listener) {
9790             helper = ExpressionHelper.addListener(helper, this, listener);
9791         }
9792 
9793         @Override
9794         public void removeListener(InvalidationListener listener) {
9795             helper = ExpressionHelper.removeListener(helper, listener);
9796         }
9797 
9798         @Override
9799         public void addListener(ChangeListener<? super Bounds> listener) {
9800             helper = ExpressionHelper.addListener(helper, this, listener);
9801         }
9802 
9803         @Override
9804         public void removeListener(ChangeListener<? super Bounds> listener) {
9805             helper = ExpressionHelper.removeListener(helper, listener);
9806         }
9807 
9808         @Override
9809         public Bounds get() {
9810             if (!valid) {
9811                 bounds = computeBounds();
9812                 valid = true;
9813             }
9814 
9815             return bounds;
9816         }
9817 
9818         public void invalidate() {
9819             if (valid) {
9820                 valid = false;
9821                 ExpressionHelper.fireValueChangedEvent(helper);
9822             }
9823         }
9824 
9825         protected abstract Bounds computeBounds();
9826     }
9827 
9828     private static final BoundsAccessor boundsAccessor = (bounds, tx, node) -> node.getGeomBounds(bounds, tx);
9829 
9830     /**
9831      * The accessible role for this {@code Node}.
9832      * <p>
9833      * The screen reader uses the role of a node to determine the
9834      * attributes and actions that are supported.
9835      *
9836      * @defaultValue {@link AccessibleRole#NODE}
9837      * @see AccessibleRole
9838      *
9839      * @since JavaFX 8u40
9840      */
9841     private ObjectProperty<AccessibleRole> accessibleRole;
9842 
9843     public final void setAccessibleRole(AccessibleRole value) {
9844         if (value == null) value = AccessibleRole.NODE;
9845         accessibleRoleProperty().set(value);
9846     }
9847 
9848     public final AccessibleRole getAccessibleRole() {
9849         if (accessibleRole == null) return AccessibleRole.NODE;
9850         return accessibleRoleProperty().get();
9851     }
9852 
9853     public final ObjectProperty<AccessibleRole> accessibleRoleProperty() {
9854         if (accessibleRole == null) {
9855             accessibleRole = new SimpleObjectProperty<AccessibleRole>(this, "accessibleRole", AccessibleRole.NODE);
9856         }
9857         return accessibleRole;
9858     }
9859 
9860     public final void setAccessibleRoleDescription(String value) {
9861         accessibleRoleDescriptionProperty().set(value);
9862     }
9863 
9864     public final String getAccessibleRoleDescription() {
9865         if (accessibilityProperties == null) return null;
9866         if (accessibilityProperties.accessibleRoleDescription == null) return null;
9867         return accessibleRoleDescriptionProperty().get();
9868     }
9869 
9870     /**
9871      * The role description of this {@code Node}.
9872      * <p>
9873      * Noramlly, when a role is provided for a node, the screen reader
9874      * speaks the role as well as the contents of the node.  When this
9875      * value is set, it is possbile to override the default.  This is
9876      * useful because the set of roles is predefined.  For example,
9877      * it is possible to set the role of a node to be a button, but
9878      * have the role description be arbitrary text.
9879      *
9880      * @return the role description of this {@code Node}.
9881      * @defaultValue null
9882      *
9883      * @since JavaFX 8u40
9884      */
9885     public final ObjectProperty<String> accessibleRoleDescriptionProperty() {
9886         return getAccessibilityProperties().getAccessibleRoleDescription();
9887     }
9888 
9889     public final void setAccessibleText(String value) {
9890         accessibleTextProperty().set(value);
9891     }
9892 
9893     public final String getAccessibleText() {
9894         if (accessibilityProperties == null) return null;
9895         if (accessibilityProperties.accessibleText == null) return null;
9896         return accessibleTextProperty().get();
9897     }
9898 
9899     /**
9900      * The accessible text for this {@code Node}.
9901      * <p>
9902      * This property is used to set the text that the screen
9903      * reader will speak.  If a node normally speaks text,
9904      * that text is overriden.  For example, a button
9905      * usually speaks using the text in the control but will
9906      * no longer do this when this value is set.
9907      *
9908      * @return accessible text for this {@code Node}.
9909      * @defaultValue null
9910      *
9911      * @since JavaFX 8u40
9912      */
9913     public final ObjectProperty<String> accessibleTextProperty() {
9914         return getAccessibilityProperties().getAccessibleText();
9915     }
9916 
9917     public final void setAccessibleHelp(String value) {
9918         accessibleHelpProperty().set(value);
9919     }
9920 
9921     public final String getAccessibleHelp() {
9922         if (accessibilityProperties == null) return null;
9923         if (accessibilityProperties.accessibleHelp == null) return null;
9924         return accessibleHelpProperty().get();
9925     }
9926 
9927     /**
9928      * The accessible help text for this {@code Node}.
9929      * <p>
9930      * The help text provides a more detailed description of the
9931      * accessible text for a node.  By default, if the node has
9932      * a tool tip, this text is used.
9933      *
9934      * @return the accessible help text for this {@code Node}.
9935      * @defaultValue null
9936      *
9937      * @since JavaFX 8u40
9938      */
9939     public final ObjectProperty<String> accessibleHelpProperty() {
9940         return getAccessibilityProperties().getAccessibleHelp();
9941     }
9942 
9943     AccessibilityProperties accessibilityProperties;
9944     private AccessibilityProperties getAccessibilityProperties() {
9945         if (accessibilityProperties == null) {
9946             accessibilityProperties = new AccessibilityProperties();
9947         }
9948         return accessibilityProperties;
9949     }
9950 
9951     private class AccessibilityProperties {
9952         ObjectProperty<String> accessibleRoleDescription;
9953         ObjectProperty<String> getAccessibleRoleDescription() {
9954             if (accessibleRoleDescription == null) {
9955                 accessibleRoleDescription = new SimpleObjectProperty<String>(Node.this, "accessibleRoleDescription", null);
9956             }
9957             return accessibleRoleDescription;
9958         }
9959         ObjectProperty<String> accessibleText;
9960         ObjectProperty<String> getAccessibleText() {
9961             if (accessibleText == null) {
9962                 accessibleText = new SimpleObjectProperty<String>(Node.this, "accessibleText", null);
9963             }
9964             return accessibleText;
9965         }
9966         ObjectProperty<String> accessibleHelp;
9967         ObjectProperty<String> getAccessibleHelp() {
9968             if (accessibleHelp == null) {
9969                 accessibleHelp = new SimpleObjectProperty<String>(Node.this, "accessibleHelp", null);
9970             }
9971             return accessibleHelp;
9972         }
9973     }
9974 
9975     /**
9976      * This method is called by the assistive technology to request
9977      * the value for an attribute.
9978      * <p>
9979      * This method is commonly overridden by subclasses to implement
9980      * attributes that are required for a specific role.<br>
9981      * If a particular attribute is not handled, the superclass implementation
9982      * must be called.
9983      * </p>
9984      *
9985      * @param attribute the requested attribute
9986      * @param parameters optional list of parameters
9987      * @return the value for the requested attribute
9988      *
9989      * @see AccessibleAttribute
9990      *
9991      * @since JavaFX 8u40
9992      */
9993     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
9994         switch (attribute) {
9995             case ROLE: return getAccessibleRole();
9996             case ROLE_DESCRIPTION: return getAccessibleRoleDescription();
9997             case TEXT: return getAccessibleText();
9998             case HELP: return getAccessibleHelp();
9999             case PARENT: return getParent();
10000             case SCENE: return getScene();
10001             case BOUNDS: return localToScreen(getBoundsInLocal());
10002             case DISABLED: return isDisabled();
10003             case FOCUSED: return isFocused();
10004             case VISIBLE: return isVisible();
10005             case LABELED_BY: return labeledBy;
10006             default: return null;
10007         }
10008     }
10009 
10010     /**
10011      * This method is called by the assistive technology to request the action
10012      * indicated by the argument should be executed.
10013      * <p>
10014      * This method is commonly overridden by subclasses to implement
10015      * action that are required for a specific role.<br>
10016      * If a particular action is not handled, the superclass implementation
10017      * must be called.
10018      * </p>
10019      *
10020      * @param action the action to execute
10021      * @param parameters optional list of parameters
10022      *
10023      * @see AccessibleAction
10024      *
10025      * @since JavaFX 8u40
10026      */
10027     public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
10028         switch (action) {
10029             case REQUEST_FOCUS:
10030                 if (isFocusTraversable()) {
10031                     requestFocus();
10032                 }
10033                 break;
10034             case SHOW_MENU: {
10035                 Bounds b = getBoundsInLocal();
10036                 Point2D pt = localToScreen(b.getMaxX(), b.getMaxY());
10037                 ContextMenuEvent event =
10038                     new ContextMenuEvent(ContextMenuEvent.CONTEXT_MENU_REQUESTED,
10039                     b.getMaxX(), b.getMaxY(), pt.getX(), pt.getY(),
10040                     false, new PickResult(this, b.getMaxX(), b.getMaxY()));
10041                 Event.fireEvent(this, event);
10042                 break;
10043             }
10044             default:
10045         }
10046     }
10047 
10048     /**
10049      * This method is called by the application to notify the assistive
10050      * technology that the value for an attribute has changed.
10051      *
10052      * @param attributes the attribute whose value has changed
10053      *
10054      * @see AccessibleAttribute
10055      *
10056      * @since JavaFX 8u40
10057      */
10058     public final void notifyAccessibleAttributeChanged(AccessibleAttribute attributes) {
10059         if (accessible == null) {
10060             Scene scene = getScene();
10061             if (scene != null) {
10062                 accessible = scene.removeAccessible(this);
10063             }
10064         }
10065         if (accessible != null) {
10066             accessible.sendNotification(attributes);
10067         }
10068     }
10069 
10070     Accessible accessible;
10071     Accessible getAccessible() {
10072         if (accessible == null) {
10073             Scene scene = getScene();
10074             /* It is possible the node was reparented and getAccessible()
10075              * is called before the pulse. Try to recycle the accessible
10076              * before creating a new one.
10077              * Note: this code relies that an accessible can never be on
10078              * more than one Scene#accMap. Thus, the only way
10079              * scene#removeAccessible() returns non-null is if the node
10080              * old scene and new scene are the same object.
10081              */
10082             if (scene != null) {
10083                 accessible = scene.removeAccessible(this);
10084             }
10085         }
10086         if (accessible == null) {
10087             accessible = Application.GetApplication().createAccessible();
10088             accessible.setEventHandler(new Accessible.EventHandler() {
10089                 @SuppressWarnings("deprecation")
10090                 @Override public AccessControlContext getAccessControlContext() {
10091                     Scene scene = getScene();
10092                     if (scene == null) {
10093                         /* This can happen during the release process of an accessible object. */
10094                         throw new RuntimeException("Accessbility requested for node not on a scene");
10095                     }
10096                     if (scene.getPeer() != null) {
10097                         return scene.getPeer().getAccessControlContext();
10098                     } else {
10099                         /* In some rare cases the accessible for a Node is needed
10100                          * before its scene is made visible. For example, the screen reader
10101                          * might ask a Menu for its ContextMenu before the ContextMenu
10102                          * is made visible. That is a problem because the Window for the
10103                          * ContextMenu is only created immediately before the first time
10104                          * it is shown.
10105                          */
10106                         return scene.acc;
10107                     }
10108                 }
10109                 @Override public Object getAttribute(AccessibleAttribute attribute, Object... parameters) {
10110                     return queryAccessibleAttribute(attribute, parameters);
10111                 }
10112                 @Override public void executeAction(AccessibleAction action, Object... parameters) {
10113                     executeAccessibleAction(action, parameters);
10114                 }
10115                 @Override public String toString() {
10116                     String klassName = Node.this.getClass().getName();
10117                     return klassName.substring(klassName.lastIndexOf('.')+1);
10118                 }
10119             });
10120         }
10121         return accessible;
10122     }
10123 
10124     void releaseAccessible() {
10125         Accessible acc = this.accessible;
10126         if (acc != null) {
10127             accessible = null;
10128             acc.dispose();
10129         }
10130     }
10131 
10132 }
10133