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