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