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