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
028
029import com.sun.javafx.css.Selector;
030import com.sun.javafx.css.SimpleSelector;
031import com.sun.javafx.css.StyleManager;
032import com.sun.javafx.css.converters.BooleanConverter;
033import com.sun.javafx.css.converters.EnumConverter;
034import com.sun.javafx.css.converters.InsetsConverter;
035import com.sun.javafx.css.converters.PaintConverter;
036import com.sun.javafx.css.converters.SizeConverter;
037import com.sun.javafx.css.converters.StringConverter;
038import com.sun.javafx.css.parser.CSSParser;
039import java.net.MalformedURLException;
040import java.net.URL;
041import java.util.ArrayList;
042import java.util.Collections;
043import java.util.List;
044
045import javafx.beans.property.BooleanProperty;
046import javafx.beans.property.DoubleProperty;
047import javafx.beans.property.ObjectProperty;
048import javafx.beans.property.ReadOnlyObjectProperty;
049import javafx.beans.property.SimpleBooleanProperty;
050import javafx.beans.property.SimpleStringProperty;
051import javafx.beans.property.StringProperty;
052import javafx.geometry.Insets;
053import javafx.geometry.Orientation;
054import javafx.geometry.Pos;
055import javafx.scene.Node;
056import javafx.scene.image.Image;
057import javafx.scene.image.ImageView;
058import javafx.scene.layout.Region;
059import javafx.scene.paint.Color;
060import javafx.scene.paint.Paint;
061import javafx.scene.text.Font;
062import javafx.scene.text.TextAlignment;
063
064import javafx.beans.DefaultProperty;
065import javafx.css.CssMetaData;
066import javafx.css.FontCssMetaData;
067import javafx.css.StyleOrigin;
068import javafx.css.Styleable;
069import javafx.css.StyleableBooleanProperty;
070import javafx.css.StyleableDoubleProperty;
071import javafx.css.StyleableObjectProperty;
072import javafx.css.StyleableProperty;
073
074import javafx.css.StyleableStringProperty;
075
076
077/**
078 * A Labeled {@link Control} is one which has as part of its user interface
079 * a textual content associated with it. For example, a {@link Button} displays
080 * {@code text}, as does a {@link Label}, a {@link Tooltip}, and many
081 * other controls.
082 * <p>
083 * Labeled is also a convenient base class from which to extend when building
084 * new Controls which, as part of their UI, display read-only textual content.
085 * </p>
086 *  
087 * <p>Example of how to place a graphic above the text:
088 * <pre><code>
089 *  Image image = new Image(getClass().getResourceAsStream("image.png"));
090 *  ImageView imageView = new ImageView();
091 *  imageView.setImage(image);
092 *  Label label = new Label("text", imageView);
093 *  label.setContentDisplay(ContentDisplay.TOP);
094 * </code></pre>
095 *
096 * @see Button
097 * @see Label
098 * @see ToggleButton
099 */
100@DefaultProperty("text")
101public abstract class Labeled extends Control {
102
103    private final static String DEFAULT_ELLIPSIS_STRING = "...";
104
105
106    /***************************************************************************
107     *                                                                         *
108     * Constructors                                                            *
109     *                                                                         *
110     **************************************************************************/
111
112    /**
113     * Creates a Label with no text and graphic
114     */
115    public Labeled() { }
116
117    /**
118     * Creates a Label with text
119     * @param text The text for the label.
120     */
121    public Labeled(String text) {
122        setText(text);
123    }
124
125    /**
126     * Creates a Label with text and a graphic
127     * @param text The text for the label.
128     * @param graphic The graphic for the label.
129     */
130    public Labeled(String text, Node graphic) {
131        setText(text);
132        setGraphic(graphic);
133    }
134
135    /***************************************************************************
136     *                                                                         *
137     * Properties                                                              *
138     *                                                                         *
139     **************************************************************************/
140    /**
141     * The text to display in the label. The text may be null.
142     */
143    public final StringProperty textProperty() {
144        if (text == null) {
145            text = new SimpleStringProperty(this, "text", "");
146        }
147        return text;
148    }
149    private StringProperty text;
150    public final void setText(String value) { textProperty().setValue(value); }
151    public final String getText() { return text == null ? "" : text.getValue(); }
152
153    /**
154     * Specifies how the text and graphic within the Labeled should be
155     * aligned when there is empty space within the Labeled.
156     */
157    public final ObjectProperty<Pos> alignmentProperty() {
158        if (alignment == null) {
159            alignment = new StyleableObjectProperty<Pos>(Pos.CENTER_LEFT) {
160
161                @Override public CssMetaData getCssMetaData() {
162                    return StyleableProperties.ALIGNMENT;
163                }
164
165                @Override
166                public Object getBean() {
167                    return Labeled.this;
168                }
169
170                @Override
171                public String getName() {
172                    return "alignment";
173                }
174            };
175        }
176        return alignment;
177    }
178    private ObjectProperty<Pos> alignment;
179    public final void setAlignment(Pos value) { alignmentProperty().set(value); }
180    public final Pos getAlignment() { return alignment == null ? Pos.CENTER_LEFT : alignment.get(); }
181
182
183    /**
184     * Specifies the behavior for lines of text <em>when text is multiline</em>
185     * Unlike {@link #contentDisplayProperty} which affects the graphic and text, this setting
186     * only affects multiple lines of text relative to the text bounds.
187     */
188    public final ObjectProperty<TextAlignment> textAlignmentProperty() {
189        if (textAlignment == null) {
190            textAlignment = new StyleableObjectProperty<TextAlignment>(TextAlignment.LEFT) {
191                
192                @Override 
193                public CssMetaData getCssMetaData() {
194                    return StyleableProperties.TEXT_ALIGNMENT;
195                }
196
197                @Override
198                public Object getBean() {
199                    return Labeled.this;
200                }
201
202                @Override
203                public String getName() {
204                    return "textAlignment";
205                }
206            };
207        }
208        return textAlignment;
209    }
210    private ObjectProperty<TextAlignment> textAlignment;
211    public final void setTextAlignment(TextAlignment value) { textAlignmentProperty().setValue(value); }
212    public final TextAlignment getTextAlignment() { return textAlignment == null ? TextAlignment.LEFT : textAlignment.getValue(); }
213
214    /**
215     * Specifies the behavior to use if the text of the {@code Labeled}
216     * exceeds the available space for rendering the text.
217     */
218    public final ObjectProperty<OverrunStyle> textOverrunProperty() {
219        if (textOverrun == null) {
220            textOverrun = new StyleableObjectProperty<OverrunStyle>(OverrunStyle.ELLIPSIS) {
221                
222                @Override 
223                public CssMetaData getCssMetaData() {
224                    return StyleableProperties.TEXT_OVERRUN;
225                }
226
227                @Override
228                public Object getBean() {
229                    return Labeled.this;
230                }
231
232                @Override
233                public String getName() {
234                    return "textOverrun";
235                }
236            };
237        }
238        return textOverrun;
239    }
240    private ObjectProperty<OverrunStyle> textOverrun;
241    public final void setTextOverrun(OverrunStyle value) { textOverrunProperty().setValue(value); }
242    public final OverrunStyle getTextOverrun() { return textOverrun == null ? OverrunStyle.ELLIPSIS : textOverrun.getValue(); }
243
244    /**
245     * Specifies the string to display for the ellipsis when text is truncated.
246     *
247     * <table border="0" cellpadding="0" cellspacing="0"><tr><th>Examples</th></tr>
248     *   <tr class="altColor"><td align="right">"..."</td>        <td>- Default value for most locales</td>
249     *   <tr class="rowColor"><td align="right">" . . . "</td>    <td></td>
250     *   <tr class="altColor"><td align="right">" [...] "</td>    <td></td>
251     *   <tr class="rowColor"><td align="right">"&#92;u2026"</td> <td>- The Unicode ellipsis character '&hellip;'</td>
252     *   <tr class="altColor"><td align="right">""</td>           <td>- No ellipsis, just display the truncated string</td>
253     * </table>
254     *
255     * <p>Note that not all fonts support all Unicode characters.
256     *
257     * @see <a href="http://en.wikipedia.org/wiki/Ellipsis#Computer_representations">Wikipedia:ellipsis</a>
258     * @since 2.2
259     */
260    public final StringProperty ellipsisStringProperty() {
261        if (ellipsisString == null) {
262            ellipsisString = new StyleableStringProperty(DEFAULT_ELLIPSIS_STRING) {
263                @Override public Object getBean() {
264                    return Labeled.this;
265                }
266
267                @Override public String getName() {
268                    return "ellipsisString";
269                }
270
271                @Override public CssMetaData getCssMetaData() {
272                    return StyleableProperties.ELLIPSIS_STRING;
273                }                
274            };
275        }
276        return ellipsisString;
277    }
278    private StringProperty ellipsisString;
279    public final void setEllipsisString(String value) { ellipsisStringProperty().set((value == null) ? "" : value); }
280    public final String getEllipsisString() { return ellipsisString == null ? DEFAULT_ELLIPSIS_STRING : ellipsisString.get(); }
281
282
283    /**
284     * If a run of text exceeds the width of the Labeled, then this variable
285     * indicates whether the text should wrap onto another line.
286     */
287    public final BooleanProperty wrapTextProperty() {
288        if (wrapText == null) {
289            wrapText = new StyleableBooleanProperty() {
290                
291                @Override 
292                public CssMetaData getCssMetaData() {
293                    return StyleableProperties.WRAP_TEXT;
294                }
295
296                @Override
297                public Object getBean() {
298                    return Labeled.this;
299                }
300
301                @Override
302                public String getName() {
303                    return "wrapText";
304                }
305            };
306        }
307        return wrapText;
308    }
309    private BooleanProperty wrapText;
310    public final void setWrapText(boolean value) { wrapTextProperty().setValue(value); }
311    public final boolean isWrapText() { return wrapText == null ? false : wrapText.getValue(); }
312
313    /**
314     * If wrapText is true, then contentBias will be HORIZONTAL, otherwise it is null.
315     * @return orientation of width/height dependency or null if there is none
316     */
317    @Override public Orientation getContentBias() {
318        return isWrapText()? Orientation.HORIZONTAL : null;
319    }
320
321    /**
322     * The default font to use for text in the Labeled. If the Label's text is
323     * rich text then this font may or may not be used depending on the font
324     * information embedded in the rich text, but in any case where a default
325     * font is required, this font will be used.
326     */
327    public final ObjectProperty<Font> fontProperty() {
328
329        if (font == null) {
330            font = new StyleableObjectProperty<Font>(Font.getDefault()) {
331
332                private boolean fontSetByCss = false;
333
334                @Override
335                public void applyStyle(StyleOrigin newOrigin, Font value) {
336
337                    //
338                    // RT-20727 - if CSS is setting the font, then make sure invalidate doesn't call impl_reapplyCSS
339                    //
340                    try {
341                        // super.applyStyle calls set which might throw if value is bound.
342                        // Have to make sure fontSetByCss is reset.
343                        fontSetByCss = true;
344                        super.applyStyle(newOrigin, value);
345                    } catch(Exception e) {
346                        throw e;
347                    } finally {
348                        fontSetByCss = false;
349                    }
350                }
351
352                @Override
353                public void set(Font value) {
354
355                    final Font oldValue = get();
356                    if (value != null ? !value.equals(oldValue) : oldValue != null) {
357                        super.set(value);
358                    }
359
360                }
361                
362                @Override
363                protected void invalidated() {
364                    // RT-20727 - if font is changed by calling setFont, then
365                    // css might need to be reapplied since font size affects
366                    // calculated values for styles with relative values
367                    if(fontSetByCss == false) {
368                        Labeled.this.impl_reapplyCSS();
369                    }
370                }
371                
372                @Override 
373                public CssMetaData getCssMetaData() {
374                    return StyleableProperties.FONT;
375                }
376
377                @Override
378                public Object getBean() {
379                    return Labeled.this;
380                }
381
382                @Override
383                public String getName() {
384                    return "font";
385                }
386            };
387        }
388        return font;
389    }
390    private ObjectProperty<Font> font;
391    public final void setFont(Font value) { fontProperty().setValue(value); }
392    public final Font getFont() { return font == null ? Font.getDefault() : font.getValue(); }
393    
394
395    /**
396     * An optional icon for the Labeled. This can be positioned relative to the
397     * text by using {@link #setContentDisplay}.  The node specified for this
398     * variable cannot appear elsewhere in the scene graph, otherwise
399     * the {@code IllegalArgumentException} is thrown.  See the class
400     * description of {@link javafx.scene.Node Node} for more detail.
401     */
402    public final ObjectProperty<Node> graphicProperty() {
403        if (graphic == null) {
404            graphic = new StyleableObjectProperty<Node>() {
405
406                // The graphic is styleable by css, but it is the 
407                // imageUrlProperty that handles the style value. 
408                @Override
409                public CssMetaData getCssMetaData() {
410                    return StyleableProperties.GRAPHIC;
411                }
412                                
413                @Override
414                public Object getBean() {
415                    return Labeled.this;
416                }
417
418                @Override
419                public String getName() {
420                    return "graphic";
421                }
422            };
423        }
424        return graphic;
425    }
426    private ObjectProperty<Node> graphic;
427    public final void setGraphic(Node value) {
428        graphicProperty().setValue(value);
429    }
430    public final Node getGraphic() { return graphic == null ? null : graphic.getValue(); }
431
432    private StringProperty imageUrl = null;
433    /**
434     * The imageUrl property is set from CSS and then the graphic property is
435     * set from the invalidated method. This ensures that the same image isn't
436     * reloaded. 
437     */
438    private StringProperty imageUrlProperty() {
439        if (imageUrl == null) {
440            imageUrl = new StyleableStringProperty() {
441
442                @Override
443                public void applyStyle(StyleOrigin origin, String v) {
444                    super.applyStyle(origin, v);
445                    if (v == null) {
446                        ((StyleableProperty)graphicProperty()).applyStyle(origin, null);
447                    } else if (v.startsWith(CSSParser.SPECIAL_REGION_URL_PREFIX)) {
448                        final Region region = new Region();
449                        final String styleClassOrId = v.substring(CSSParser.SPECIAL_REGION_URL_PREFIX.length());
450                        if (styleClassOrId.length() > 0) {
451                            Selector s = Selector.createSelector(styleClassOrId);
452                            if (s instanceof SimpleSelector) {
453                                SimpleSelector ss = (SimpleSelector)s;
454                                region.setId(ss.getId());
455                                region.getStyleClass().addAll(ss.getStyleClasses());
456                            }
457                        }
458                        ((StyleableProperty)graphicProperty()).applyStyle(origin, region);
459                    } else {
460                        URL url = null;
461                        try {
462                            url = new URL(v);
463                        } catch (MalformedURLException malf) {
464                            // This may be a relative URL, so try resolving it using the application classloader
465                            final ClassLoader cl = Thread.currentThread().getContextClassLoader();
466                            url = cl.getResource(v);
467                        }
468                        if (url != null) {
469                            final Image img = StyleManager.getInstance().getCachedImage(url.toExternalForm());
470                            ((StyleableProperty)graphicProperty()).applyStyle(origin, new ImageView(img));
471                        }
472                    }
473                }
474
475                @Override
476                public Object getBean() {
477                    return Labeled.this;
478                }
479
480                @Override
481                public String getName() {
482                    return "imageUrl";
483                }
484
485                @Override
486                public CssMetaData getCssMetaData() {
487                    return StyleableProperties.GRAPHIC;
488                }
489                
490            };
491        }
492        return imageUrl;
493    }
494    
495    /**
496     * Whether all text should be underlined.
497     */
498    public final BooleanProperty underlineProperty() {
499        if (underline == null) {
500            underline = new StyleableBooleanProperty(false) {
501
502                @Override
503                public CssMetaData getCssMetaData() {
504                    return StyleableProperties.UNDERLINE;
505                }
506
507                @Override
508                public Object getBean() {
509                    return Labeled.this;
510                }
511
512                @Override
513                public String getName() {
514                    return "underline";
515                }
516            };
517        }
518        return underline;
519    }
520    private BooleanProperty underline;
521    public final void setUnderline(boolean value) { underlineProperty().setValue(value); }
522    public final boolean isUnderline() { return underline == null ? false : underline.getValue(); }
523
524    /**
525     * Specifies the space in pixel between lines.
526     */
527    public final DoubleProperty lineSpacingProperty() {
528        if (lineSpacing == null) {
529            lineSpacing = new StyleableDoubleProperty(0) {
530
531                @Override
532                public CssMetaData getCssMetaData() {
533                    return StyleableProperties.LINE_SPACING;
534                }
535
536                @Override
537                public Object getBean() {
538                    return Labeled.this;
539                }
540
541                @Override
542                public String getName() {
543                    return "lineSpacing";
544                }
545            };
546        }
547        return lineSpacing;
548    }
549    private DoubleProperty lineSpacing;
550    public final void setLineSpacing(double value) { lineSpacingProperty().setValue(value); }
551    public final double getLineSpacing() { return lineSpacing == null ? 0 : lineSpacing.getValue(); }
552
553    /**
554     * Specifies the positioning of the graphic relative to the text.
555     */
556    public final ObjectProperty<ContentDisplay> contentDisplayProperty() {
557        if (contentDisplay == null) {
558            contentDisplay = new StyleableObjectProperty<ContentDisplay>(ContentDisplay.LEFT) {
559                
560                @Override 
561                public CssMetaData getCssMetaData() {
562                    return StyleableProperties.CONTENT_DISPLAY;
563                }
564
565                @Override
566                public Object getBean() {
567                    return Labeled.this;
568                }
569
570                @Override
571                public String getName() {
572                    return "contentDisplay";
573                }
574            };
575        }
576        return contentDisplay;
577    }
578    private ObjectProperty<ContentDisplay> contentDisplay;
579    public final void setContentDisplay(ContentDisplay value) { contentDisplayProperty().setValue(value); }
580    public final ContentDisplay getContentDisplay() { return contentDisplay == null ? ContentDisplay.LEFT : contentDisplay.getValue(); }
581
582    /**
583     * The padding around the Labeled's text and graphic content.
584     * By default labelPadding is Insets.EMPTY and cannot be set to null.
585     * Subclasses may add nodes outside this padding and inside the Labeled's padding.
586     *
587     * This property can only be set from CSS.
588     */
589    public final ReadOnlyObjectProperty<Insets> labelPaddingProperty() {
590        return labelPaddingPropertyImpl();
591    }
592    private ObjectProperty<Insets> labelPaddingPropertyImpl() {
593        if (labelPadding == null) {
594            labelPadding = new StyleableObjectProperty<Insets>(Insets.EMPTY) {
595                private Insets lastValidValue = Insets.EMPTY;
596
597                @Override
598                public void invalidated() {
599                    final Insets newValue = get();
600                    if (newValue == null) {
601                        set(lastValidValue);
602                        throw new NullPointerException("cannot set labelPadding to null");
603                    }
604                    lastValidValue = newValue;
605                    requestLayout();
606                }
607                
608                @Override
609                public CssMetaData getCssMetaData() {
610                    return StyleableProperties.LABEL_PADDING;
611                }
612
613                @Override
614                public Object getBean() {
615                    return Labeled.this;
616                }
617
618                @Override
619                public String getName() {
620                    return "labelPadding";
621                }
622            };
623        }
624        return labelPadding;
625    }
626    private ObjectProperty<Insets> labelPadding;
627    private void setLabelPadding(Insets value) { labelPaddingPropertyImpl().set(value); }
628    public final Insets getLabelPadding() { return labelPadding == null ? Insets.EMPTY : labelPadding.get(); }
629
630    /**
631     * The amount of space between the graphic and text
632     */
633    public final DoubleProperty graphicTextGapProperty() {
634        if (graphicTextGap == null) {
635            graphicTextGap = new StyleableDoubleProperty(4) {
636                
637                @Override
638                public CssMetaData getCssMetaData() {
639                    return StyleableProperties.GRAPHIC_TEXT_GAP;
640                }
641
642                @Override
643                public Object getBean() {
644                    return Labeled.this;
645                }
646
647                @Override
648                public String getName() {
649                    return "graphicTextGap";
650                }
651            };
652        }
653        return graphicTextGap;
654    }
655    private DoubleProperty graphicTextGap;
656    public final void setGraphicTextGap(double value) { graphicTextGapProperty().setValue(value); }
657    public final double getGraphicTextGap() { return graphicTextGap == null ? 4 : graphicTextGap.getValue(); }
658
659
660    /**
661     * The {@link Paint} used to fill the text.
662     */
663    private ObjectProperty<Paint> textFill; // TODO for now change this
664
665    public final void setTextFill(Paint value) {
666        textFillProperty().set(value);
667    }
668
669    public final Paint getTextFill() {
670        return textFill == null ? Color.BLACK : textFill.get();
671    }
672
673    public final ObjectProperty<Paint> textFillProperty() {
674        if (textFill == null) {
675            textFill = new StyleableObjectProperty<Paint>(Color.BLACK) {
676                
677                @Override 
678                public CssMetaData getCssMetaData() {
679                    return StyleableProperties.TEXT_FILL;
680                }
681
682                @Override
683                public Object getBean() {
684                    return Labeled.this;
685                }
686
687                @Override
688                public String getName() {
689                    return "textFill";
690                }
691            };
692        }
693        return textFill;
694    }
695
696
697    /**
698     * MnemonicParsing property to enable/disable text parsing.
699     * If this is set to true, then the Label text will be
700     * parsed to see if it contains the mnemonic parsing character '_'.
701     * When a mnemonic is detected the key combination will
702     * be determined based on the succeeding character, and the mnemonic
703     * added.
704     * 
705     * <p>
706     * The default value for Labeled is false, but it
707     * is enabled by default on some Controls.
708     * </p>
709     */
710    private BooleanProperty mnemonicParsing;
711    public final void setMnemonicParsing(boolean value) {
712        mnemonicParsingProperty().set(value);
713    }
714    public final boolean isMnemonicParsing() {
715        return mnemonicParsing == null ? false : mnemonicParsing.get();
716    }
717    public final BooleanProperty mnemonicParsingProperty() {
718        if (mnemonicParsing == null) {
719            mnemonicParsing = new SimpleBooleanProperty(this, "mnemonicParsing");
720        }
721        return mnemonicParsing;
722    }
723
724    //    /**
725    //     * This is the symbol that is searched for in the text and used as
726    //     * a mnemonic. You can change what symbol is used. Using the symbol
727    //     * more than once will cause the symbol to be escaped. Thus, if "_"
728    //     * (the default) is used, then the string "H_ello World" will use
729    //     * "e" as the mnemonic. If "H__ello World" is used, then no mnemonic
730    //     * will be used and the text will be rendered as "H_ello World".
731    //     * TODO: Have i18n review this part of the API to confirm proper
732    //     * externalization will work as expected
733    //     */
734
735    /***************************************************************************
736     *                                                                         *
737     * Stylesheet Handling                                                     *
738     *                                                                         *
739     **************************************************************************/
740
741     /**
742      * Not everything uses the default value of false for alignment. 
743      * This method provides a way to have them return the correct initial value.
744      * @treatAsPrivate implementation detail
745      */
746    @Deprecated
747    protected Pos impl_cssGetAlignmentInitialValue() {
748        return Pos.CENTER_LEFT;
749    }
750    
751     /**
752      * @treatAsPrivate implementation detail
753      */
754    private static class StyleableProperties {
755        private static final FontCssMetaData<Labeled> FONT = 
756            new FontCssMetaData<Labeled>("-fx-font", Font.getDefault()) {
757
758            @Override
759            public boolean isSettable(Labeled n) {
760                return n.font == null || !n.font.isBound();
761            }
762
763            @Override
764            public StyleableProperty<Font> getStyleableProperty(Labeled n) {
765                return (StyleableProperty)n.fontProperty();
766            }
767        };
768        
769        private static final CssMetaData<Labeled,Pos> ALIGNMENT = 
770                new CssMetaData<Labeled,Pos>("-fx-alignment",
771                new EnumConverter<Pos>(Pos.class), Pos.CENTER_LEFT ) {
772
773            @Override
774            public boolean isSettable(Labeled n) {
775                return n.alignment == null || !n.alignment.isBound();
776            }
777
778            @Override
779            public StyleableProperty<Pos> getStyleableProperty(Labeled n) {
780                return (StyleableProperty)n.alignmentProperty();
781            }
782            
783            @Override
784            public Pos getInitialValue(Labeled n) {
785                return n.impl_cssGetAlignmentInitialValue();
786            }
787        };
788        
789        private static final CssMetaData<Labeled,TextAlignment> TEXT_ALIGNMENT = 
790                new CssMetaData<Labeled,TextAlignment>("-fx-text-alignment",
791                new EnumConverter<TextAlignment>(TextAlignment.class),
792                TextAlignment.LEFT) {
793
794            @Override
795            public boolean isSettable(Labeled n) {
796                return n.textAlignment == null || !n.textAlignment.isBound();
797            }
798
799            @Override
800            public StyleableProperty<TextAlignment> getStyleableProperty(Labeled n) {
801                return (StyleableProperty)n.textAlignmentProperty();
802            }
803        };
804        
805        private static final CssMetaData<Labeled,Paint> TEXT_FILL = 
806                new CssMetaData<Labeled,Paint>("-fx-text-fill",
807                PaintConverter.getInstance(), Color.BLACK) {
808
809            @Override
810            public boolean isSettable(Labeled n) {
811                return n.textFill == null || !n.textFill.isBound();
812            }
813
814            @Override
815            public StyleableProperty<Paint> getStyleableProperty(Labeled n) {
816                return (StyleableProperty)n.textFillProperty();
817            }
818        };
819        
820        private static final CssMetaData<Labeled,OverrunStyle> TEXT_OVERRUN = 
821                new CssMetaData<Labeled,OverrunStyle>("-fx-text-overrun",
822                new EnumConverter<OverrunStyle>(OverrunStyle.class), 
823                OverrunStyle.ELLIPSIS) {
824
825            @Override
826            public boolean isSettable(Labeled n) {
827                return n.textOverrun == null || !n.textOverrun.isBound();
828            }
829
830            @Override
831            public StyleableProperty<OverrunStyle> getStyleableProperty(Labeled n) {
832                return (StyleableProperty)n.textOverrunProperty();
833            }
834        };
835
836        private static final CssMetaData<Labeled,String> ELLIPSIS_STRING =
837                new CssMetaData<Labeled,String>("-fx-ellipsis-string",
838                StringConverter.getInstance(), DEFAULT_ELLIPSIS_STRING) {
839
840            @Override public boolean isSettable(Labeled n) {
841                return n.ellipsisString == null || !n.ellipsisString.isBound();
842            }
843
844            @Override public StyleableProperty<String> getStyleableProperty(Labeled n) {
845                return (StyleableProperty)n.ellipsisStringProperty();
846            }
847        };
848
849        private static final CssMetaData<Labeled,Boolean> WRAP_TEXT = 
850                new CssMetaData<Labeled,Boolean>("-fx-wrap-text",
851                BooleanConverter.getInstance(), false) {
852
853            @Override
854            public boolean isSettable(Labeled n) {
855                return n.wrapText == null || !n.wrapText.isBound();
856            }
857
858            @Override
859            public StyleableProperty<Boolean> getStyleableProperty(Labeled n) {
860                return (StyleableProperty)n.wrapTextProperty();
861            }
862        };
863        
864        private static final CssMetaData<Labeled,String> GRAPHIC = 
865            new CssMetaData<Labeled,String>("-fx-graphic",
866                StringConverter.getInstance()) {
867
868            @Override
869            public boolean isSettable(Labeled n) {
870                // Note that we care about the graphic, not imageUrl
871                return n.graphic == null || !n.graphic.isBound();
872            }
873
874            @Override
875            public StyleableProperty<String> getStyleableProperty(Labeled n) {
876                return (StyleableProperty)n.imageUrlProperty();
877            }
878        };
879        
880        private static final CssMetaData<Labeled,Boolean> UNDERLINE = 
881            new CssMetaData<Labeled,Boolean>("-fx-underline",
882                BooleanConverter.getInstance(), Boolean.FALSE) {
883
884            @Override
885            public boolean isSettable(Labeled n) {
886                return n.underline == null || !n.underline.isBound();
887            }
888
889            @Override
890            public StyleableProperty<Boolean> getStyleableProperty(Labeled n) {
891                return (StyleableProperty)n.underlineProperty();
892            }
893        };
894        
895        private static final CssMetaData<Labeled,Number> LINE_SPACING =
896            new CssMetaData<Labeled,Number>("-fx-line-spacing",
897                SizeConverter.getInstance(), 0) {
898
899            @Override
900            public boolean isSettable(Labeled n) {
901                return n.lineSpacing == null || !n.lineSpacing.isBound();
902            }
903
904            @Override
905            public StyleableProperty<Number> getStyleableProperty(Labeled n) {
906                return (StyleableProperty)n.lineSpacingProperty();
907            }
908        };
909
910        private static final CssMetaData<Labeled,ContentDisplay> CONTENT_DISPLAY = 
911            new CssMetaData<Labeled,ContentDisplay>("-fx-content-display",
912                new EnumConverter<ContentDisplay>(ContentDisplay.class), 
913                ContentDisplay.LEFT) {
914
915            @Override
916            public boolean isSettable(Labeled n) {
917                return n.contentDisplay == null || !n.contentDisplay.isBound();
918            }
919
920            @Override
921            public StyleableProperty<ContentDisplay> getStyleableProperty(Labeled n) {
922                return (StyleableProperty)n.contentDisplayProperty();
923            }
924        };
925        
926        private static final CssMetaData<Labeled,Insets> LABEL_PADDING = 
927            new CssMetaData<Labeled,Insets>("-fx-label-padding",
928                InsetsConverter.getInstance(), Insets.EMPTY) {
929
930            @Override
931            public boolean isSettable(Labeled n) {
932                return n.labelPadding == null || !n.labelPadding.isBound();
933            }
934
935            @Override
936            public StyleableProperty<Insets> getStyleableProperty(Labeled n) {
937                return (StyleableProperty)n.labelPaddingPropertyImpl();
938            }
939        };
940        
941        private static final CssMetaData<Labeled,Number> GRAPHIC_TEXT_GAP = 
942            new CssMetaData<Labeled,Number>("-fx-graphic-text-gap",
943                SizeConverter.getInstance(), 4.0) {
944
945            @Override
946            public boolean isSettable(Labeled n) {
947                return n.graphicTextGap == null || !n.graphicTextGap.isBound();
948            }
949
950            @Override
951            public StyleableProperty<Number> getStyleableProperty(Labeled n) {
952                return (StyleableProperty)n.graphicTextGapProperty();
953            }
954        };
955
956        private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
957        static {
958            final List<CssMetaData<? extends Styleable, ?>> styleables =
959                new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData());
960            Collections.addAll(styleables,
961                FONT,
962                ALIGNMENT,
963                TEXT_ALIGNMENT,
964                TEXT_FILL,
965                TEXT_OVERRUN,
966                ELLIPSIS_STRING,
967                WRAP_TEXT,
968                GRAPHIC,
969                UNDERLINE,
970                LINE_SPACING,
971                CONTENT_DISPLAY,
972                LABEL_PADDING,
973                GRAPHIC_TEXT_GAP
974            );
975            STYLEABLES = Collections.unmodifiableList(styleables);
976        }
977    }
978
979    /**
980     * @return The CssMetaData associated with this class, which may include the
981     * CssMetaData of its super classes.
982     */
983    public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
984        return StyleableProperties.STYLEABLES;
985    }
986
987    /**
988     * {@inheritDoc}
989     */
990    @Override
991    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
992        return getClassCssMetaData();
993    }
994
995 }