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