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