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