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