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