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