1 /*
   2  * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.lang.ref.WeakReference;
  29 import java.lang.reflect.Constructor;
  30 import java.lang.reflect.InvocationTargetException;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.List;
  34 
  35 import com.sun.javafx.scene.control.ControlAcceleratorSupport;
  36 import javafx.application.Application;
  37 import javafx.beans.property.ObjectProperty;
  38 import javafx.beans.property.ObjectPropertyBase;
  39 import javafx.beans.property.SimpleObjectProperty;
  40 import javafx.beans.property.StringProperty;
  41 import javafx.beans.value.WritableValue;
  42 import javafx.collections.ObservableList;
  43 import javafx.css.CssParser;
  44 import javafx.event.EventHandler;
  45 import javafx.scene.AccessibleAction;
  46 import javafx.scene.AccessibleAttribute;
  47 import javafx.scene.Node;
  48 import javafx.scene.input.ContextMenuEvent;
  49 import javafx.scene.layout.Region;
  50 import com.sun.javafx.application.PlatformImpl;
  51 import javafx.css.CssMetaData;
  52 import com.sun.javafx.css.StyleManager;
  53 import javafx.css.StyleableObjectProperty;
  54 import javafx.css.StyleableStringProperty;
  55 import javafx.css.converter.StringConverter;
  56 import com.sun.javafx.scene.control.Logging;
  57 import com.sun.javafx.scene.control.ModuleHelper;
  58 import javafx.css.Styleable;
  59 import javafx.css.StyleableProperty;
  60 import sun.util.logging.PlatformLogger;
  61 import sun.util.logging.PlatformLogger.Level;
  62 
  63 
  64 /**
  65  * Base class for all user interface controls. A "Control" is a node in the
  66  * scene graph which can be manipulated by the user. Controls provide
  67  * additional variables and behaviors beyond those of Node to support
  68  * common user interactions in a manner which is consistent and predictable
  69  * for the user.
  70  * <p>
  71  * Additionally, controls support explicit skinning to make it easy to
  72  * leverage the functionality of a control while customizing its appearance.
  73  * <p>
  74  * See specific Control subclasses for information on how to use individual
  75  * types of controls.
  76  * <p> Most controls have their focusTraversable property set to true by default, however
  77  * read-only controls such as {@link Label} and {@link ProgressIndicator}, and some
  78  * controls that are containers {@link ScrollPane} and {@link ToolBar} do not.
  79  * Consult individual control documentation for details.
  80  * @since JavaFX 2.0
  81  */
  82 public abstract class Control extends Region implements Skinnable {
  83 
  84     static {
  85         // Ensures that the default application user agent stylesheet is loaded
  86         if (Application.getUserAgentStylesheet() == null) {
  87             PlatformImpl.setDefaultPlatformUserAgentStylesheet();
  88         }
  89     }
  90 
  91     /**
  92      * Utility for loading a class in a manner that will work with multiple
  93      * class loaders, as is typically found in OSGI modular applications.
  94      * In particular, this method will attempt to just load the class
  95      * identified by className. If that fails, it attempts to load the
  96      * class using the current thread's context class loader. If that fails,
  97      * it attempts to use the class loader of the supplied "instance", and
  98      * if it still fails it walks up the class hierarchy of the instance
  99      * and attempts to use the class loader of each class in the super-type
 100      * hierarchy.
 101      *
 102      * @param className The name of the class we want to load
 103      * @param instance An optional instance used to help find the class to load
 104      * @return The class. Cannot return null
 105      * @throws ClassNotFoundException If the class cannot be found using any technique.
 106      */
 107     static Class<?> loadClass(final String className, final Object instance)
 108             throws ClassNotFoundException
 109     {
 110         try {
 111             // Try just loading the class
 112             return Class.forName(className);
 113         } catch (ClassNotFoundException ex) {
 114             // RT-17525 : Use context class loader only if Class.forName fails.
 115             if (Thread.currentThread().getContextClassLoader() != null) {
 116                 try {
 117                     return Thread.currentThread().getContextClassLoader().loadClass(className);
 118                 } catch (ClassNotFoundException ex2) {
 119                     // Do nothing, just fall through
 120                 }
 121             }
 122 
 123             // RT-14177: Try looking up the class using the class loader of the
 124             //           current class, walking up the list of superclasses
 125             //           and checking each of them, before bailing and using
 126             //           the context class loader.
 127             if (instance != null) {
 128                 Class<?> currentType = instance.getClass();
 129                 while (currentType != null) {
 130                     try {
 131                         return currentType.getClassLoader().loadClass(className);
 132                     } catch (ClassNotFoundException ex2) {
 133                         currentType = currentType.getSuperclass();
 134                     }
 135                 }
 136             }
 137 
 138             // We failed to find the class using any of the above means, so we're going
 139             // to just throw the ClassNotFoundException that we caught earlier
 140             throw ex;
 141         }
 142     }
 143 
 144     /***************************************************************************
 145      *                                                                         *
 146      * Private fields                                                          *
 147      *                                                                         *
 148      **************************************************************************/
 149 
 150     private List<CssMetaData<? extends Styleable, ?>> styleableProperties;
 151 
 152     /**
 153      * A private reference directly to the SkinBase instance that is used as the
 154      * Skin for this Control. A Control's Skin doesn't have to be of type
 155      * SkinBase, although 98% of the time or greater it probably will be.
 156      * Because instanceof checks and reading a value from a property are
 157      * not cheap (on interpreters on slower hardware or mobile devices)
 158      * it pays to have a direct reference here to the skinBase. We simply
 159      * need to check this variable -- if it is not null then we know the
 160      * Skin is a SkinBase and this is a direct reference to it. If it is null
 161      * then we know the skin is not a SkinBase and we need to call getSkin().
 162      */
 163     private SkinBase<?> skinBase;
 164 
 165     /***************************************************************************
 166     *                                                                         *
 167     * Event Handlers / Listeners                                              *
 168     *                                                                         *
 169     **************************************************************************/
 170 
 171     /**
 172      * Handles context menu requests by popping up the menu.
 173      * Note that we use this pattern to remove some of the anonymous inner
 174      * classes which we'd otherwise have to create. When lambda expressions
 175      * are supported, we could do it that way instead (or use MethodHandles).
 176      */
 177     private final static EventHandler<ContextMenuEvent> contextMenuHandler = event -> {
 178         if (event.isConsumed()) return;
 179 
 180         // If a context menu was shown, consume the event to prevent multiple context menus
 181         Object source = event.getSource();
 182         if (source instanceof Control) {
 183             Control c = (Control) source;
 184             if (c.getContextMenu() != null) {
 185                 c.getContextMenu().show(c, event.getScreenX(), event.getScreenY());
 186                 event.consume();
 187             }
 188         }
 189     };
 190 
 191 
 192 
 193     /***************************************************************************
 194      *                                                                         *
 195      * Properties                                                              *
 196      *                                                                         *
 197      **************************************************************************/
 198 
 199 
 200 
 201     // --- skin
 202     /**
 203      * Skin is responsible for rendering this {@code Control}. From the
 204      * perspective of the {@code Control}, the {@code Skin} is a black box.
 205      * It listens and responds to changes in state in a {@code Control}.
 206      * <p>
 207      * There is a one-to-one relationship between a {@code Control} and its
 208      * {@code Skin}. Every {@code Skin} maintains a back reference to the
 209      * {@code Control} via the {@link Skin#getSkinnable()} method.
 210      * <p>
 211      * A skin may be null.
 212      */
 213     @Override public final ObjectProperty<Skin<?>> skinProperty() { return skin; }
 214     @Override public final void setSkin(Skin<?> value) {
 215         skinProperty().set(value);
 216     }
 217     @Override public final Skin<?> getSkin() { return skinProperty().getValue(); }
 218     private ObjectProperty<Skin<?>> skin = new StyleableObjectProperty<Skin<?>>() {
 219         // We store a reference to the oldValue so that we can handle
 220         // changes in the skin properly in the case of binding. This is
 221         // only needed because invalidated() does not currently take
 222         // a reference to the old value.
 223         private Skin<?> oldValue;
 224 
 225         @Override
 226         //This code is basically a kind of optimization that prevents a Skin that is equal but not instance equal.
 227         //Although it's not kosher from the property perspective (bindings won't pass through set), it should not do any harm.
 228         //But it should be evaluated in the future.
 229         public void set(Skin<?> v) {
 230             if (v == null
 231                 ? oldValue == null
 232                 : oldValue != null && v.getClass().equals(oldValue.getClass()))
 233                 return;
 234 
 235             super.set(v);
 236         }
 237 
 238         @Override protected void invalidated() {
 239             Skin<?> skin = get();
 240             // Collect the name of the currently installed skin class. We do this
 241             // so that subsequent updates from CSS to the same skin class will not
 242             // result in reinstalling the skin
 243             currentSkinClassName = skin == null ? null : skin.getClass().getName();
 244 
 245             // if someone calls setSkin, we need to make it look like they
 246             // called set on skinClassName in order to keep CSS from overwriting
 247             // the skin.
 248             skinClassNameProperty().set(currentSkinClassName);
 249 
 250 
 251             // Dispose of the old skin
 252             if (oldValue != null) oldValue.dispose();
 253 
 254             // Get the new value, and save it off as the new oldValue
 255             oldValue = skin;
 256 
 257             // Reset skinBase to null - it will be set to the new Skin if it
 258             // is a SkinBase, otherwise it will remain null, as expected
 259             skinBase = null;
 260 
 261             // We have two paths, one for "legacy" Skins, and one for
 262             // any Skin which extends from SkinBase. Legacy Skins will
 263             // produce a single node which will be the only child of
 264             // the Control via the getNode() method on the Skin. A
 265             // SkinBase will manipulate the children of the Control
 266             // directly. Further, we maintain a direct reference to
 267             // the skinBase for more optimal updates later.
 268             if (skin instanceof SkinBase) {
 269                 // record a reference of the skin, if it is a SkinBase, for
 270                 // performance reasons
 271                 skinBase = (SkinBase<?>) skin;
 272                 // Note I do not remove any children here, because the
 273                 // skin will have already configured all the children
 274                 // by the time setSkin has been called. This is because
 275                 // our Skin interface was lacking an initialize method (doh!)
 276                 // and so the Skin constructor is where it adds listeners
 277                 // and so forth. For SkinBase implementations, the
 278                 // constructor is also where it will take ownership of
 279                 // the children.
 280             } else {
 281                 final Node n = getSkinNode();
 282                 if (n != null) {
 283                     getChildren().setAll(n);
 284                 } else {
 285                     getChildren().clear();
 286                 }
 287             }
 288 
 289             // clear out the styleable properties so that the list is rebuilt
 290             // next time they are requested.
 291             styleableProperties = null;
 292 
 293             // calling impl_reapplyCSS() as the styleable properties may now
 294             // be different, as we will now be able to return styleable properties
 295             // belonging to the skin. If impl_reapplyCSS() is not called, the
 296             // getCssMetaData() method is never called, so the
 297             // skin properties are never exposed.
 298             impl_reapplyCSS();
 299 
 300             // DEBUG: Log that we've changed the skin
 301             final PlatformLogger logger = Logging.getControlsLogger();
 302             if (logger.isLoggable(Level.FINEST)) {
 303                 logger.finest("Stored skin[" + getValue() + "] on " + this);
 304             }
 305         }
 306 
 307         // This method should be CssMetaData<Control,Skin> getCssMetaData(),
 308         // but SKIN is CssMetaData<Control,String>. This does not matter to
 309         // the CSS code which doesn't care about the actual type. Hence,
 310         // we'll suppress the warnings
 311         @Override @SuppressWarnings({"unchecked", "rawtype"})
 312         public CssMetaData getCssMetaData() {
 313             return StyleableProperties.SKIN;
 314         }
 315 
 316         @Override
 317         public Object getBean() {
 318             return Control.this;
 319         }
 320 
 321         @Override
 322         public String getName() {
 323             return "skin";
 324         }
 325     };
 326 
 327 
 328     // --- tooltip
 329     /**
 330      * The ToolTip for this control.
 331      */
 332     public final ObjectProperty<Tooltip> tooltipProperty() {
 333         if (tooltip == null) {
 334             tooltip = new ObjectPropertyBase<Tooltip>() {
 335                 private Tooltip old = null;
 336                 @Override protected void invalidated() {
 337                     Tooltip t = get();
 338                     // install / uninstall
 339                     if (t != old) {
 340                         if (old != null) {
 341                             Tooltip.uninstall(Control.this, old);
 342                         }
 343                         if (t != null) {
 344                             Tooltip.install(Control.this, t);
 345                         }
 346                         old = t;
 347                     }
 348                 }
 349 
 350                 @Override
 351                 public Object getBean() {
 352                     return Control.this;
 353                 }
 354 
 355                 @Override
 356                 public String getName() {
 357                     return "tooltip";
 358                 }
 359             };
 360         }
 361         return tooltip;
 362     }
 363     private ObjectProperty<Tooltip> tooltip;
 364     public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); }
 365     public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); }
 366 
 367 
 368     // --- context menu
 369     /**
 370      * The ContextMenu to show for this control.
 371      */
 372     private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu") {
 373         private WeakReference<ContextMenu> contextMenuRef;
 374 
 375         @Override protected void invalidated() {
 376             ContextMenu oldMenu = contextMenuRef == null ? null : contextMenuRef.get();
 377             if (oldMenu != null) {
 378                 ControlAcceleratorSupport.removeAcceleratorsFromScene(oldMenu.getItems(), Control.this);
 379             }
 380 
 381             ContextMenu ctx = get();
 382             contextMenuRef = new WeakReference<>(ctx);
 383 
 384             if (ctx != null) {
 385                 // set this flag so contextmenu show will be relative to parent window not anchor
 386                 ctx.setShowRelativeToWindow(true); //RT-15160
 387 
 388                 // if a context menu is set, we need to install any accelerators
 389                 // belonging to its menu items ASAP into the scene that this
 390                 // Control is in (if the control is not in a Scene, we will need
 391                 // to wait until it is and then do it).
 392                 ControlAcceleratorSupport.addAcceleratorsIntoScene(ctx.getItems(), Control.this);
 393             }
 394         }
 395     };
 396     public final ObjectProperty<ContextMenu> contextMenuProperty() { return contextMenu; }
 397     public final void setContextMenu(ContextMenu value) { contextMenu.setValue(value); }
 398     public final ContextMenu getContextMenu() { return contextMenu == null ? null : contextMenu.getValue(); }
 399 
 400 
 401 
 402     /***************************************************************************
 403      *                                                                         *
 404      * Constructors                                                            *
 405      *                                                                         *
 406      **************************************************************************/
 407 
 408     /**
 409      *  Create a new Control.
 410      */
 411     protected Control() {
 412         // focusTraversable is styleable through css. Calling setFocusTraversable
 413         // makes it look to css like the user set the value and css will not
 414         // override. Initializing focusTraversable by calling applyStyle
 415         // with null for StyleOrigin ensures that css will be able to override
 416         // the value.
 417         final StyleableProperty<Boolean> prop = (StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty();
 418         prop.applyStyle(null, Boolean.TRUE);
 419 
 420         // we add a listener for menu request events to show the context menu
 421         // that may be set on the Control
 422         this.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
 423 
 424         // TODO re-enable when InputMap moves back to Node / Control
 425 //        // Most controls need an input map, so we set this to be non-null in
 426 //        // Control to save people from running into NPEs.
 427 //        setInputMap(new InputMap(this));
 428     }
 429 
 430 
 431 
 432     /***************************************************************************
 433      *                                                                         *
 434      * Public API                                                              *
 435      *                                                                         *
 436      **************************************************************************/
 437 
 438     // Proposed dispose() API.
 439     // Note that there is impl code for a dispose method in TableRowSkinBase
 440     // and TableCell (just search for dispose())
 441 //    public void dispose() {
 442 //        Skin skin = getSkin();
 443 //        if (skin != null) {
 444 //            skin.dispose();
 445 //        }
 446 //    }
 447 
 448     /**
 449      * Returns <code>true</code> since all Controls are resizable.
 450      * @return whether this node can be resized by its parent during layout
 451      */
 452     @Override public boolean isResizable() {
 453         return true;
 454     }
 455 
 456     // Implementation of the Resizable interface.
 457     // Because only the skin can know the min, pref, and max sizes, these
 458     // functions are implemented to delegate to skin. If there is no skin then
 459     // we simply return 0 for all the values since a Control without a Skin
 460     // doesn't render
 461     /**
 462      * Computes the minimum allowable width of the Control, based on the provided
 463      * height. The minimum width is not calculated within the Control, instead
 464      * the calculation is delegated to the {@link Node#minWidth(double)} method
 465      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 466      *
 467      * @param height The height of the Control, in case this value might dictate
 468      *      the minimum width.
 469      * @return A double representing the minimum width of this control.
 470      */
 471     @Override protected double computeMinWidth(final double height) {
 472         if (skinBase != null) {
 473             return skinBase.computeMinWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 474         } else {
 475             final Node skinNode = getSkinNode();
 476             return skinNode == null ? 0 : skinNode.minWidth(height);
 477         }
 478     }
 479 
 480     /**
 481      * Computes the minimum allowable height of the Control, based on the provided
 482      * width. The minimum height is not calculated within the Control, instead
 483      * the calculation is delegated to the {@link Node#minHeight(double)} method
 484      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 485      *
 486      * @param width The width of the Control, in case this value might dictate
 487      *      the minimum height.
 488      * @return A double representing the minimum height of this control.
 489      */
 490     @Override protected double computeMinHeight(final double width) {
 491         if (skinBase != null) {
 492             return skinBase.computeMinHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 493         } else {
 494             final Node skinNode = getSkinNode();
 495             return skinNode == null ? 0 : skinNode.minHeight(width);
 496         }
 497     }
 498 
 499     /**
 500      * Computes the maximum allowable width of the Control, based on the provided
 501      * height. The maximum width is not calculated within the Control, instead
 502      * the calculation is delegated to the {@link Node#maxWidth(double)} method
 503      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 504      *
 505      * @param height The height of the Control, in case this value might dictate
 506      *      the maximum width.
 507      * @return A double representing the maximum width of this control.
 508      */
 509     @Override protected double computeMaxWidth(double height) {
 510         if (skinBase != null) {
 511             return skinBase.computeMaxWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 512         } else {
 513             final Node skinNode = getSkinNode();
 514             return skinNode == null ? 0 : skinNode.maxWidth(height);
 515         }
 516     }
 517 
 518     /**
 519      * Computes the maximum allowable height of the Control, based on the provided
 520      * width. The maximum height is not calculated within the Control, instead
 521      * the calculation is delegated to the {@link Node#maxHeight(double)} method
 522      * of the {@link Skin}. If the Skin is null, the returned value is 0.
 523      *
 524      * @param width The width of the Control, in case this value might dictate
 525      *      the maximum height.
 526      * @return A double representing the maximum height of this control.
 527      */
 528     @Override protected double computeMaxHeight(double width) {
 529         if (skinBase != null) {
 530             return skinBase.computeMaxHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 531         } else {
 532             final Node skinNode = getSkinNode();
 533             return skinNode == null ? 0 : skinNode.maxHeight(width);
 534         }
 535     }
 536 
 537     /** {@inheritDoc} */
 538     @Override protected double computePrefWidth(double height) {
 539         if (skinBase != null) {
 540             return skinBase.computePrefWidth(height, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 541         } else {
 542             final Node skinNode = getSkinNode();
 543             return skinNode == null ? 0 : skinNode.prefWidth(height);
 544         }
 545     }
 546 
 547     /** {@inheritDoc} */
 548     @Override protected double computePrefHeight(double width) {
 549         if (skinBase != null) {
 550             return skinBase.computePrefHeight(width, snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 551         } else {
 552             final Node skinNode = getSkinNode();
 553             return skinNode == null ? 0 : skinNode.prefHeight(width);
 554         }
 555     }
 556 
 557     /** {@inheritDoc} */
 558     @Override public double getBaselineOffset() {
 559         if (skinBase != null) {
 560             return skinBase.computeBaselineOffset(snappedTopInset(), snappedRightInset(), snappedBottomInset(), snappedLeftInset());
 561         } else {
 562             final Node skinNode = getSkinNode();
 563             return skinNode == null ? 0 : skinNode.getBaselineOffset();
 564         }
 565     }
 566 
 567     /***************************************************************************
 568      * Implementation of layout bounds for the Control. We want to preserve    *
 569      * the lazy semantics of layout bounds. So whenever the width/height       *
 570      * changes on the node, we end up invalidating layout bounds. We then      *
 571      * recompute it on demand.                                                 *
 572      **************************************************************************/
 573 
 574     /** {@inheritDoc} */
 575     @Override protected void layoutChildren() {
 576         if (skinBase != null) {
 577             final double x = snappedLeftInset();
 578             final double y = snappedTopInset();
 579             final double w = snapSize(getWidth()) - x - snappedRightInset();
 580             final double h = snapSize(getHeight()) - y - snappedBottomInset();
 581             skinBase.layoutChildren(x, y, w, h);
 582         } else {
 583             Node n = getSkinNode();
 584             if (n != null) {
 585                 n.resizeRelocate(0, 0, getWidth(), getHeight());
 586             }
 587         }
 588     }
 589 
 590     /***************************************************************************
 591      * Forward the following to the skin                                       *
 592      **************************************************************************/
 593 
 594     /**
 595      * Create a new instance of the default skin for this control. This is called to create a skin for the control if
 596      * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
 597      *
 598      * @return  new instance of default skin for this control. If null then the control will have no skin unless one
 599      *          is provided by css.
 600      * @since JavaFX 8.0
 601      */
 602     protected Skin<?> createDefaultSkin() {
 603         return null;
 604     }
 605 
 606     /***************************************************************************
 607      *                                                                         *
 608      * Package API for SkinBase                                                *
 609      *                                                                         *
 610      **************************************************************************/
 611 
 612     // package private for SkinBase
 613     ObservableList<Node> getControlChildren() {
 614         return getChildren();
 615     }
 616 
 617 
 618     /***************************************************************************
 619      *                                                                         *
 620      * Private implementation                                                  *
 621      *                                                                         *
 622      **************************************************************************/
 623 
 624     /**
 625      * Gets the Skin's node, or returns null if there is no Skin.
 626      * Convenience method for getting the node of the skin. This is null-safe,
 627      * meaning if skin is null then it will return null instead of throwing
 628      * a NullPointerException.
 629      *
 630      * @return The Skin's node, or null.
 631      */
 632     private Node getSkinNode() {
 633         assert skinBase == null;
 634         Skin<?> skin = getSkin();
 635         return skin == null ? null : skin.getNode();
 636     }
 637 
 638     /**
 639      * Keeps a reference to the name of the class currently acting as the skin.
 640      */
 641     private String currentSkinClassName = null;
 642     private StringProperty skinClassName;
 643 
 644     /**
 645      * @treatAsPrivate
 646      * @since JavaFX 2.1
 647      */
 648     @Deprecated protected StringProperty skinClassNameProperty() {
 649         if (skinClassName == null) {
 650             skinClassName = new StyleableStringProperty() {
 651 
 652                 @Override
 653                 public void set(String v) {
 654                     // do not allow the skin to be set to null through CSS
 655                     if (v == null || v.isEmpty() || v.equals(get())) return;
 656                     super.set(v);
 657                 }
 658 
 659                 @Override
 660                 public void invalidated() {
 661 
 662                     if (get() != null) {
 663                         if (!get().equals(currentSkinClassName)) {
 664                             loadSkinClass(Control.this, skinClassName.get());
 665                         }
 666                         // Note: CSS should not set skin to null
 667                     }
 668                 }
 669 
 670                 @Override
 671                 public Object getBean() {
 672                     return Control.this;
 673                 }
 674 
 675                 @Override
 676                 public String getName() {
 677                     return "skinClassName";
 678                 }
 679 
 680                 @Override
 681                 public CssMetaData<Control,String> getCssMetaData() {
 682                     return StyleableProperties.SKIN;
 683                 }
 684 
 685             };
 686         }
 687         return skinClassName;
 688     }
 689 
 690     static void loadSkinClass(final Skinnable control, final String skinClassName) {
 691         if (skinClassName == null || skinClassName.isEmpty()) {
 692             final String msg =
 693                 "Empty -fx-skin property specified for control " + control;
 694             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 695             if (errors != null) {
 696                 CssParser.ParseError error = new CssParser.ParseError(msg);
 697                 errors.add(error); // RT-19884
 698             }
 699             Logging.getControlsLogger().severe(msg);
 700             return;
 701         }
 702 
 703         try {
 704             final Class<?> skinClass = Control.loadClass(skinClassName, control);
 705             Object skinModule = ModuleHelper.getModule(skinClass);
 706             Object thisModule = ModuleHelper.getModule(Control.class);
 707             if (skinModule != thisModule) {
 708                 ModuleHelper.addReads(thisModule, skinModule);
 709             }
 710             Constructor<?>[] constructors = skinClass.getConstructors();
 711             Constructor<?> skinConstructor = null;
 712             for (Constructor<?> c : constructors) {
 713                 Class<?>[] parameterTypes = c.getParameterTypes();
 714                 if (parameterTypes.length == 1 && Skinnable.class.isAssignableFrom(parameterTypes[0])) {
 715                     skinConstructor = c;
 716                     break;
 717                 }
 718             }
 719 
 720             if (skinConstructor == null) {
 721                 final String msg =
 722                     "No valid constructor defined in '" + skinClassName + "' for control " + control +
 723                         ".\r\nYou must provide a constructor that accepts a single "
 724                         + "Skinnable (e.g. Control or PopupControl) parameter in " + skinClassName + ".";
 725                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 726                 if (errors != null) {
 727                     CssParser.ParseError error = new CssParser.ParseError(msg);
 728                     errors.add(error); // RT-19884
 729                 }
 730                 Logging.getControlsLogger().severe(msg);
 731             } else {
 732                 Skin<?> skinInstance = (Skin<?>) skinConstructor.newInstance(control);
 733                 // Do not call setSkin here since it has the side effect of
 734                 // also setting the skinClassName!
 735                 control.skinProperty().set(skinInstance);
 736             }
 737         } catch (InvocationTargetException e) {
 738             final String msg =
 739                 "Failed to load skin '" + skinClassName + "' for control " + control;
 740             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 741             if (errors != null) {
 742                 CssParser.ParseError error = new CssParser.ParseError(msg + " :" + e.getLocalizedMessage());
 743                 errors.add(error); // RT-19884
 744             }
 745             Logging.getControlsLogger().severe(msg, e.getCause());
 746         } catch (Exception e) {
 747             final String msg =
 748                 "Failed to load skin '" + skinClassName + "' for control " + control;
 749             final List<CssParser.ParseError> errors = StyleManager.getErrors();
 750             if (errors != null) {
 751                 CssParser.ParseError error = new CssParser.ParseError(msg + " :" + e.getLocalizedMessage());
 752                 errors.add(error); // RT-19884
 753             }
 754             Logging.getControlsLogger().severe(msg, e);
 755         }
 756     }
 757 
 758     /***************************************************************************
 759      *                                                                         *
 760      * StyleSheet Handling                                                     *
 761      *                                                                         *
 762      **************************************************************************/
 763 
 764     private static class StyleableProperties {
 765         private static final CssMetaData<Control,String> SKIN =
 766             new CssMetaData<Control,String>("-fx-skin",
 767                 StringConverter.getInstance()) {
 768 
 769             @Override
 770             public boolean isSettable(Control n) {
 771                 return (n.skin == null || !n.skin.isBound());
 772             }
 773 
 774             @Override
 775             public StyleableProperty<String> getStyleableProperty(Control n) {
 776                 return (StyleableProperty<String>)(WritableValue<String>)n.skinClassNameProperty();
 777             }
 778         };
 779 
 780         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 781         static {
 782             final List<CssMetaData<? extends Styleable, ?>> styleables =
 783                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData());
 784             styleables.add(SKIN);
 785             STYLEABLES = Collections.unmodifiableList(styleables);
 786         }
 787     }
 788 
 789     /**
 790      * @return The CssMetaData associated with this class, which may include the
 791      * CssMetaData of its super classes.
 792      * @since JavaFX 8.0
 793      */
 794     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 795         return StyleableProperties.STYLEABLES;
 796     }
 797 
 798     /**
 799      * This method returns a {@link List} containing all {@link CssMetaData} for
 800      * both this Control (returned from {@link #getControlCssMetaData()} and its
 801      * {@link Skin}, assuming the {@link #skinProperty() skin property} is a
 802      * {@link SkinBase}.
 803      *
 804      * <p>Developers who wish to provide custom CssMetaData are therefore
 805      * encouraged to override {@link Control#getControlCssMetaData()} or
 806      * {@link SkinBase#getCssMetaData()}, depending on where the CssMetaData
 807      * resides.
 808      * @since JavaFX 8.0
 809      */
 810     @Override
 811     public final List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 812         if (styleableProperties == null) {
 813 
 814             // RT-29162: make sure properties only show up once in the list
 815             java.util.Map<String, CssMetaData<? extends Styleable, ?>> map =
 816                 new java.util.HashMap<String, CssMetaData<? extends Styleable, ?>>();
 817 
 818             List<CssMetaData<? extends Styleable, ?>> list =  getControlCssMetaData();
 819 
 820             for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) {
 821 
 822                 CssMetaData<? extends Styleable, ?> metaData = list.get(n);
 823                 if (metaData == null) continue;
 824 
 825                 map.put(metaData.getProperty(), metaData);
 826             }
 827 
 828             //
 829             // if both control and skin base have the same property, use the
 830             // one from skin base since it may be a specialization of the
 831             // property in the control. For instance, Label has -fx-font and
 832             // so does LabeledText which is Label's skin.
 833             //
 834             list =  skinBase != null ? skinBase.getCssMetaData() : null;
 835 
 836             for (int n=0, nMax = list != null ? list.size() : 0; n<nMax; n++) {
 837 
 838                 CssMetaData<? extends Styleable, ?> metaData = list.get(n);
 839                 if (metaData == null) continue;
 840 
 841                 map.put(metaData.getProperty(), metaData);
 842             }
 843 
 844             styleableProperties = new ArrayList<CssMetaData<? extends Styleable, ?>>();
 845             styleableProperties.addAll(map.values());
 846         }
 847         return styleableProperties;
 848     }
 849 
 850     /**
 851      * @return unmodifiable list of the controls css styleable properties
 852      * @since JavaFX 8.0
 853      */
 854     protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 855         return getClassCssMetaData();
 856     }
 857 
 858     /**
 859      * @treatAsPrivate implementation detail
 860      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 861      */
 862     @Deprecated
 863     @Override protected void impl_processCSS(WritableValue<Boolean> unused) {
 864 
 865         super.impl_processCSS(unused);
 866 
 867         if (getSkin() == null) {
 868             // try to create default skin
 869             final Skin<?> defaultSkin = createDefaultSkin();
 870             if (defaultSkin != null) {
 871                 skinProperty().set(defaultSkin);
 872                 super.impl_processCSS(unused);
 873             } else {
 874                 final String msg = "The -fx-skin property has not been defined in CSS for " + this +
 875                                    " and createDefaultSkin() returned null.";
 876                 final List<CssParser.ParseError> errors = StyleManager.getErrors();
 877                 if (errors != null) {
 878                     CssParser.ParseError error = new CssParser.ParseError(msg);
 879                     errors.add(error); // RT-19884
 880                 }
 881                 Logging.getControlsLogger().severe(msg);
 882             }
 883         }
 884     }
 885 
 886     /**
 887      * Returns the initial focus traversable state of this control, for use
 888      * by the JavaFX CSS engine to correctly set its initial value. By default all
 889      * UI controls are focus traversable, so this method is overridden in Control
 890      * to set the initial traversable state to true.
 891      *
 892      * @since 9
 893      */
 894     @Override protected Boolean getInitialFocusTraversable() {
 895         return Boolean.TRUE;
 896     }
 897 
 898 
 899     /***************************************************************************
 900      *                                                                         *
 901      * Accessibility handling                                                  *
 902      *                                                                         *
 903      **************************************************************************/
 904 
 905     @Override
 906     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 907         switch (attribute) {
 908             case HELP:
 909                 String help = getAccessibleHelp();
 910                 if (help != null && !help.isEmpty()) return help;
 911                 Tooltip tooltip = getTooltip();
 912                 return tooltip == null ? "" : tooltip.getText();
 913             default:
 914         }
 915         if (skinBase != null) {
 916             Object result = skinBase.queryAccessibleAttribute(attribute, parameters);
 917             if (result != null) return result;
 918         }
 919         return super.queryAccessibleAttribute(attribute, parameters);
 920     }
 921 
 922     @Override
 923     public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 924         if (skinBase != null) {
 925             skinBase.executeAccessibleAction(action, parameters);
 926         }
 927         super.executeAccessibleAction(action, parameters);
 928     }
 929 }