1 /*
   2  * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javafx.beans.InvalidationListener;
  33 import javafx.beans.property.IntegerProperty;
  34 import javafx.beans.property.ObjectProperty;
  35 import javafx.beans.property.ObjectPropertyBase;
  36 import javafx.beans.value.ChangeListener;
  37 import javafx.beans.value.WritableValue;
  38 import javafx.css.CssMetaData;
  39 import javafx.css.StyleableIntegerProperty;
  40 import javafx.css.StyleableObjectProperty;
  41 import javafx.css.StyleableProperty;
  42 import javafx.event.ActionEvent;
  43 import javafx.event.EventHandler;
  44 import javafx.geometry.Pos;
  45 import javafx.scene.AccessibleRole;
  46 
  47 import com.sun.javafx.binding.ExpressionHelper;
  48 import javafx.css.converter.EnumConverter;
  49 import javafx.css.converter.SizeConverter;
  50 import javafx.scene.control.skin.TextFieldSkin;
  51 
  52 import javafx.css.Styleable;
  53 
  54 
  55 /**
  56  * Text input component that allows a user to enter a single line of
  57  * unformatted text. Unlike in previous releases of JavaFX, support for multi-line
  58  * input is not available as part of the TextField control, however this is
  59  * the sole-purpose of the {@link TextArea} control. Additionally, if you want
  60  * a form of rich-text editing, there is also the
  61  * {@link javafx.scene.web.HTMLEditor HTMLEditor} control.
  62  *
  63  * <p>TextField supports the notion of showing {@link #promptTextProperty() prompt text}
  64  * to the user when there is no {@link #textProperty() text} already in the
  65  * TextField (either via the user, or set programmatically). This is a useful
  66  * way of informing the user as to what is expected in the text field, without
  67  * having to resort to {@link Tooltip tooltips} or on-screen {@link Label labels}.
  68  *
  69  * @see TextArea
  70  * @since JavaFX 2.0
  71  */
  72 public class TextField extends TextInputControl {
  73     // Text field content
  74     private static final class TextFieldContent implements Content {
  75         private ExpressionHelper<String> helper = null;
  76         private StringBuilder characters = new StringBuilder();
  77 
  78         @Override public String get(int start, int end) {
  79             return characters.substring(start, end);
  80         }
  81 
  82         @Override public void insert(int index, String text, boolean notifyListeners) {
  83             text = TextInputControl.filterInput(text, true, true);
  84             if (!text.isEmpty()) {
  85                 characters.insert(index, text);
  86                 if (notifyListeners) {
  87                     ExpressionHelper.fireValueChangedEvent(helper);
  88                 }
  89             }
  90         }
  91 
  92         @Override public void delete(int start, int end, boolean notifyListeners) {
  93             if (end > start) {
  94                 characters.delete(start, end);
  95                 if (notifyListeners) {
  96                     ExpressionHelper.fireValueChangedEvent(helper);
  97                 }
  98             }
  99         }
 100 
 101         @Override public int length() {
 102             return characters.length();
 103         }
 104 
 105         @Override public String get() {
 106             return characters.toString();
 107         }
 108 
 109         @Override public void addListener(ChangeListener<? super String> changeListener) {
 110             helper = ExpressionHelper.addListener(helper, this, changeListener);
 111         }
 112 
 113         @Override public void removeListener(ChangeListener<? super String> changeListener) {
 114             helper = ExpressionHelper.removeListener(helper, changeListener);
 115         }
 116 
 117         @Override public String getValue() {
 118             return get();
 119         }
 120 
 121         @Override public void addListener(InvalidationListener listener) {
 122             helper = ExpressionHelper.addListener(helper, this, listener);
 123         }
 124 
 125         @Override public void removeListener(InvalidationListener listener) {
 126             helper = ExpressionHelper.removeListener(helper, listener);
 127         }
 128     }
 129 
 130     /**
 131      * The default value for {@link #prefColumnCount}.
 132      */
 133     public static final int DEFAULT_PREF_COLUMN_COUNT = 12;
 134 
 135     /**
 136      * Creates a {@code TextField} with empty text content.
 137      */
 138     public TextField() {
 139         this("");
 140     }
 141 
 142     /**
 143      * Creates a {@code TextField} with initial text content.
 144      *
 145      * @param text A string for text content.
 146      */
 147     public TextField(String text) {
 148         super(new TextFieldContent());
 149         getStyleClass().add("text-field");
 150         setAccessibleRole(AccessibleRole.TEXT_FIELD);
 151         setText(text);
 152     }
 153 
 154     /**
 155      * Returns the character sequence backing the text field's content.
 156      * @return the character sequence backing the text field's content
 157      */
 158     public CharSequence getCharacters() {
 159         return ((TextFieldContent)getContent()).characters;
 160     }
 161 
 162 
 163     /***************************************************************************
 164      *                                                                         *
 165      * Properties                                                              *
 166      *                                                                         *
 167      **************************************************************************/
 168 
 169     /**
 170      * The preferred number of text columns. This is used for
 171      * calculating the {@code TextField}'s preferred width.
 172      */
 173     private IntegerProperty prefColumnCount = new StyleableIntegerProperty(DEFAULT_PREF_COLUMN_COUNT) {
 174 
 175         private int oldValue = get();
 176 
 177         @Override
 178         protected void invalidated() {
 179             int value = get();
 180             if (value < 0) {
 181                 if (isBound()) {
 182                     unbind();
 183                 }
 184                 set(oldValue);
 185                 throw new IllegalArgumentException("value cannot be negative.");
 186             }
 187             oldValue = value;
 188         }
 189 
 190         @Override public CssMetaData<TextField,Number> getCssMetaData() {
 191             return StyleableProperties.PREF_COLUMN_COUNT;
 192         }
 193 
 194         @Override
 195         public Object getBean() {
 196             return TextField.this;
 197         }
 198 
 199         @Override
 200         public String getName() {
 201             return "prefColumnCount";
 202         }
 203     };
 204     public final IntegerProperty prefColumnCountProperty() { return prefColumnCount; }
 205     public final int getPrefColumnCount() { return prefColumnCount.getValue(); }
 206     public final void setPrefColumnCount(int value) { prefColumnCount.setValue(value); }
 207 
 208 
 209     /**
 210      * The action handler associated with this text field, or
 211      * <tt>null</tt> if no action handler is assigned.
 212      *
 213      * The action handler is normally called when the user types the ENTER key.
 214      */
 215     private ObjectProperty<EventHandler<ActionEvent>> onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
 216         @Override
 217         protected void invalidated() {
 218             setEventHandler(ActionEvent.ACTION, get());
 219         }
 220 
 221         @Override
 222         public Object getBean() {
 223             return TextField.this;
 224         }
 225 
 226         @Override
 227         public String getName() {
 228             return "onAction";
 229         }
 230     };
 231     public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { return onAction; }
 232     public final EventHandler<ActionEvent> getOnAction() { return onActionProperty().get(); }
 233     public final void setOnAction(EventHandler<ActionEvent> value) { onActionProperty().set(value); }
 234 
 235     /**
 236      * Specifies how the text should be aligned when there is empty
 237      * space within the TextField.
 238      * @return the alignment property
 239      * @since JavaFX 2.1
 240      */
 241     public final ObjectProperty<Pos> alignmentProperty() {
 242         if (alignment == null) {
 243             alignment = new StyleableObjectProperty<Pos>(Pos.CENTER_LEFT) {
 244 
 245                 @Override public CssMetaData<TextField,Pos> getCssMetaData() {
 246                     return StyleableProperties.ALIGNMENT;
 247                 }
 248 
 249                 @Override public Object getBean() {
 250                     return TextField.this;
 251                 }
 252 
 253                 @Override public String getName() {
 254                     return "alignment";
 255                 }
 256             };
 257         }
 258         return alignment;
 259     }
 260     private ObjectProperty<Pos> alignment;
 261     public final void setAlignment(Pos value) { alignmentProperty().set(value); }
 262     public final Pos getAlignment() { return alignment == null ? Pos.CENTER_LEFT : alignment.get(); }
 263 
 264     /***************************************************************************
 265      *                                                                         *
 266      * Methods                                                                 *
 267      *                                                                         *
 268      **************************************************************************/
 269 
 270     /** {@inheritDoc} */
 271     @Override protected Skin<?> createDefaultSkin() {
 272         return new TextFieldSkin(this);
 273     }
 274 
 275     /***************************************************************************
 276      *                                                                         *
 277      * Stylesheet Handling                                                     *
 278      *                                                                         *
 279      **************************************************************************/
 280 
 281     private static class StyleableProperties {
 282         private static final CssMetaData<TextField, Pos> ALIGNMENT =
 283             new CssMetaData<TextField, Pos>("-fx-alignment",
 284                 new EnumConverter<Pos>(Pos.class), Pos.CENTER_LEFT ) {
 285 
 286             @Override public boolean isSettable(TextField n) {
 287                 return (n.alignment == null || !n.alignment.isBound());
 288             }
 289 
 290             @Override public StyleableProperty<Pos> getStyleableProperty(TextField n) {
 291                 return (StyleableProperty<Pos>)(WritableValue<Pos>)n.alignmentProperty();
 292             }
 293         };
 294 
 295         private static final CssMetaData<TextField,Number> PREF_COLUMN_COUNT =
 296             new CssMetaData<TextField,Number>("-fx-pref-column-count",
 297                 SizeConverter.getInstance(), DEFAULT_PREF_COLUMN_COUNT) {
 298 
 299             @Override
 300             public boolean isSettable(TextField n) {
 301                 return n.prefColumnCount == null || !n.prefColumnCount.isBound();
 302             }
 303 
 304             @Override
 305             public StyleableProperty<Number> getStyleableProperty(TextField n) {
 306                 return (StyleableProperty<Number>)(WritableValue<Number>)n.prefColumnCountProperty();
 307             }
 308         };
 309 
 310         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 311         static {
 312             final List<CssMetaData<? extends Styleable, ?>> styleables =
 313                 new ArrayList<CssMetaData<? extends Styleable, ?>>(TextInputControl.getClassCssMetaData());
 314             styleables.add(ALIGNMENT);
 315             styleables.add(PREF_COLUMN_COUNT);
 316             STYLEABLES = Collections.unmodifiableList(styleables);
 317         }
 318     }
 319 
 320     /**
 321      * @return The CssMetaData associated with this class, which may include the
 322      * CssMetaData of its superclasses.
 323      * @since JavaFX 8.0
 324      */
 325     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 326         return StyleableProperties.STYLEABLES;
 327     }
 328 
 329     /**
 330      * {@inheritDoc}
 331      * @since JavaFX 8.0
 332      */
 333     @Override
 334     public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 335         return getClassCssMetaData();
 336     }
 337 }