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