1 /*
   2  * Copyright (c) 2011, 2014, 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 com.sun.javafx.css.converters.EnumConverter;
  49 import com.sun.javafx.css.converters.SizeConverter;
  50 import com.sun.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      */
 157     public CharSequence getCharacters() {
 158         return ((TextFieldContent)getContent()).characters;
 159     }
 160 
 161 
 162     /***************************************************************************
 163      *                                                                         *
 164      * Properties                                                              *
 165      *                                                                         *
 166      **************************************************************************/
 167 
 168     /**
 169      * The preferred number of text columns. This is used for
 170      * calculating the {@code TextField}'s preferred width.
 171      */
 172     private IntegerProperty prefColumnCount = new StyleableIntegerProperty(DEFAULT_PREF_COLUMN_COUNT) {
 173 
 174         private int oldValue = get();
 175         
 176         @Override
 177         protected void invalidated() {
 178             int value = get();
 179             if (value < 0) {
 180                 if (isBound()) {
 181                     unbind();
 182                 }
 183                 set(oldValue);
 184                 throw new IllegalArgumentException("value cannot be negative.");
 185             }
 186             oldValue = value;
 187         }
 188         
 189         @Override public CssMetaData<TextField,Number> getCssMetaData() {
 190             return StyleableProperties.PREF_COLUMN_COUNT;
 191         }
 192 
 193         @Override
 194         public Object getBean() {
 195             return TextField.this;
 196         }
 197 
 198         @Override
 199         public String getName() {
 200             return "prefColumnCount";
 201         }
 202     };
 203     public final IntegerProperty prefColumnCountProperty() { return prefColumnCount; }
 204     public final int getPrefColumnCount() { return prefColumnCount.getValue(); }
 205     public final void setPrefColumnCount(int value) { prefColumnCount.setValue(value); }
 206 
 207 
 208     /**
 209      * The action handler associated with this text field, or
 210      * <tt>null</tt> if no action handler is assigned.
 211      *
 212      * The action handler is normally called when the user types the ENTER key.
 213      */
 214     private ObjectProperty<EventHandler<ActionEvent>> onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
 215         @Override
 216         protected void invalidated() {
 217             setEventHandler(ActionEvent.ACTION, get());
 218         }
 219 
 220         @Override
 221         public Object getBean() {
 222             return TextField.this;
 223         }
 224 
 225         @Override
 226         public String getName() {
 227             return "onAction";
 228         }
 229     };
 230     public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { return onAction; }
 231     public final EventHandler<ActionEvent> getOnAction() { return onActionProperty().get(); }
 232     public final void setOnAction(EventHandler<ActionEvent> value) { onActionProperty().set(value); }
 233 
 234     /**
 235      * Specifies how the text should be aligned when there is empty
 236      * space within the TextField.
 237      * @since JavaFX 2.1
 238      */
 239     public final ObjectProperty<Pos> alignmentProperty() {
 240         if (alignment == null) {
 241             alignment = new StyleableObjectProperty<Pos>(Pos.CENTER_LEFT) {
 242 
 243                 @Override public CssMetaData<TextField,Pos> getCssMetaData() {
 244                     return StyleableProperties.ALIGNMENT;
 245                 }
 246 
 247                 @Override public Object getBean() {
 248                     return TextField.this;
 249                 }
 250 
 251                 @Override public String getName() {
 252                     return "alignment";
 253                 }
 254             };
 255         }
 256         return alignment;
 257     }
 258     private ObjectProperty<Pos> alignment;
 259     public final void setAlignment(Pos value) { alignmentProperty().set(value); }
 260     public final Pos getAlignment() { return alignment == null ? Pos.CENTER_LEFT : alignment.get(); }
 261 
 262     /***************************************************************************
 263      *                                                                         *
 264      * Methods                                                                 *
 265      *                                                                         *
 266      **************************************************************************/
 267 
 268     /** {@inheritDoc} */
 269     @Override protected Skin<?> createDefaultSkin() {
 270         return new TextFieldSkin(this);
 271     }
 272 
 273     /***************************************************************************
 274      *                                                                         *
 275      * Stylesheet Handling                                                     *
 276      *                                                                         *
 277      **************************************************************************/
 278 
 279      /**
 280       * @treatAsPrivate implementation detail
 281       */
 282     private static class StyleableProperties {
 283         private static final CssMetaData<TextField, Pos> ALIGNMENT =
 284             new CssMetaData<TextField, Pos>("-fx-alignment",
 285                 new EnumConverter<Pos>(Pos.class), Pos.CENTER_LEFT ) {
 286 
 287             @Override public boolean isSettable(TextField n) {
 288                 return (n.alignment == null || !n.alignment.isBound());
 289             }
 290 
 291             @Override public StyleableProperty<Pos> getStyleableProperty(TextField n) {
 292                 return (StyleableProperty<Pos>)(WritableValue<Pos>)n.alignmentProperty();
 293             }
 294         };
 295 
 296         private static final CssMetaData<TextField,Number> PREF_COLUMN_COUNT =
 297             new CssMetaData<TextField,Number>("-fx-pref-column-count",
 298                 SizeConverter.getInstance(), DEFAULT_PREF_COLUMN_COUNT) {
 299 
 300             @Override
 301             public boolean isSettable(TextField n) {
 302                 return n.prefColumnCount == null || !n.prefColumnCount.isBound();
 303             }
 304 
 305             @Override
 306             public StyleableProperty<Number> getStyleableProperty(TextField n) {
 307                 return (StyleableProperty<Number>)(WritableValue<Number>)n.prefColumnCountProperty();
 308             }
 309         };
 310 
 311         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 312         static {
 313             final List<CssMetaData<? extends Styleable, ?>> styleables =
 314                 new ArrayList<CssMetaData<? extends Styleable, ?>>(TextInputControl.getClassCssMetaData());
 315             styleables.add(ALIGNMENT);
 316             styleables.add(PREF_COLUMN_COUNT);
 317             STYLEABLES = Collections.unmodifiableList(styleables);
 318         }
 319     }
 320 
 321     /**
 322      * @return The CssMetaData associated with this class, which may include the
 323      * CssMetaData of its super classes.
 324      * @since JavaFX 8.0
 325      */
 326     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 327         return StyleableProperties.STYLEABLES;
 328     }
 329 
 330     /**
 331      * {@inheritDoc}
 332      * @since JavaFX 8.0
 333      */
 334     @Override
 335     public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 336         return getClassCssMetaData();
 337     }
 338 }