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