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