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