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