70 import com.sun.javafx.util.Utils;
71 import com.sun.javafx.binding.ExpressionHelper;
72 import com.sun.javafx.scene.NodeHelper;
73 import javafx.util.StringConverter;
74
75 /**
76 * Abstract base class for text input controls.
77 * @since JavaFX 2.0
78 */
79 @DefaultProperty("text")
80 public abstract class TextInputControl extends Control {
81 /**
82 * Interface representing a text input's content. Since it is an ObservableStringValue,
83 * you can also bind to, or observe the content.
84 * @since JavaFX 2.0
85 */
86 protected interface Content extends ObservableStringValue {
87 /**
88 * Retrieves a subset of the content.
89 *
90 * @param start
91 * @param end
92 */
93 public String get(int start, int end);
94
95 /**
96 * Inserts a sequence of characters into the content.
97 *
98 * @param index
99 * @param text
100 * @since JavaFX 2.1
101 */
102 public void insert(int index, String text, boolean notifyListeners);
103
104 /**
105 * Removes a sequence of characters from the content.
106 *
107 * @param start
108 * @param end
109 * @since JavaFX 2.1
110 */
111 public void delete(int start, int end, boolean notifyListeners);
112
113 /**
114 * Returns the number of characters represented by the content.
115 */
116 public int length();
117 }
118
119 /***************************************************************************
120 * *
121 * Constructors *
122 * *
123 **************************************************************************/
124
125 /**
126 * Creates a new TextInputControl. The content is an immutable property and
127 * must be specified (as non-null) at the time of construction.
128 *
129 * @param content a non-null implementation of Content.
130 */
131 protected TextInputControl(final Content content) {
132 this.content = content;
133
134 // Add a listener so that whenever the Content is changed, we notify
174 } else {
175 commitValue();
176 }
177 });
178
179 // Specify the default style class
180 getStyleClass().add("text-input");
181 }
182
183 /***************************************************************************
184 * *
185 * Properties *
186 * *
187 **************************************************************************/
188
189 /**
190 * The default font to use for text in the TextInputControl. If the TextInputControl's text is
191 * rich text then this font may or may not be used depending on the font
192 * information embedded in the rich text, but in any case where a default
193 * font is required, this font will be used.
194 * @since JavaFX 8.0
195 */
196 public final ObjectProperty<Font> fontProperty() {
197 if (font == null) {
198 font = new StyleableObjectProperty<Font>(Font.getDefault()) {
199
200
201 private boolean fontSetByCss = false;
202
203 @Override
204 public void applyStyle(StyleOrigin newOrigin, Font value) {
205
206 //
207 // RT-20727 - if CSS is setting the font, then make sure invalidate doesn't call NodeHelper.reapplyCSS
208 //
209 try {
210 // super.applyStyle calls set which might throw if value is bound.
211 // Have to make sure fontSetByCss is reset.
212 fontSetByCss = true;
213 super.applyStyle(newOrigin, value);
322 if (!isFocused()) {
323 updateText(get());
324 }
325 }
326
327 if (oldFormatter != null) {
328 oldFormatter.unbindFromControl();
329 }
330 } finally {
331 oldFormatter = formatter;
332 }
333 }
334 };
335 public final ObjectProperty<TextFormatter<?>> textFormatterProperty() { return textFormatter; }
336 public final TextFormatter<?> getTextFormatter() { return textFormatter.get(); }
337 public final void setTextFormatter(TextFormatter<?> value) { textFormatter.set(value); }
338
339 private final Content content;
340 /**
341 * Returns the text input's content model.
342 */
343 protected final Content getContent() {
344 return content;
345 }
346
347 /**
348 * The textual content of this TextInputControl.
349 */
350 private TextProperty text = new TextProperty();
351 public final String getText() { return text.get(); }
352 public final void setText(String value) { text.set(value); }
353 public final StringProperty textProperty() { return text; }
354
355 /**
356 * The number of characters in the text input.
357 */
358 private ReadOnlyIntegerWrapper length = new ReadOnlyIntegerWrapper(this, "length");
359 public final int getLength() { return length.get(); }
360 public final ReadOnlyIntegerProperty lengthProperty() { return length.getReadOnlyProperty(); }
361
426 /**
427 * The property describes if it's currently possible to redo the latest change of the content that was undone.
428 * @defaultValue false
429 * @since JavaFX 8u40
430 */
431 private final ReadOnlyBooleanWrapper redoable = new ReadOnlyBooleanWrapper(this, "redoable", false);
432 public final boolean isRedoable() { return redoable.get(); }
433 public final ReadOnlyBooleanProperty redoableProperty() { return redoable.getReadOnlyProperty(); }
434
435 /***************************************************************************
436 * *
437 * Methods *
438 * *
439 **************************************************************************/
440
441 /**
442 * Returns a subset of the text input's content.
443 *
444 * @param start must be a value between 0 and end - 1.
445 * @param end must be less than or equal to the length
446 */
447 public String getText(int start, int end) {
448 if (start > end) {
449 throw new IllegalArgumentException("The start must be <= the end");
450 }
451
452 if (start < 0
453 || end > getLength()) {
454 throw new IndexOutOfBoundsException();
455 }
456
457 return getContent().get(start, end);
458 }
459
460 /**
461 * Appends a sequence of characters to the content.
462 *
463 * @param text a non null String
464 */
465 public void appendText(String text) {
863 * caretPosition is moved to before the first char.
864 */
865 public void selectHome() {
866 selectRange(getAnchor(), 0);
867 }
868
869 /**
870 * Moves the caret to after the last char of text. This does not cause
871 * the selection to be cleared. Rather, the anchor stays put and the
872 * caretPosition is moved to after the last char.
873 */
874 public void selectEnd() {
875 final int textLength = getLength();
876 if (textLength > 0) selectRange(getAnchor(), textLength);
877 }
878
879 /**
880 * Deletes the character that precedes the current caret position from the
881 * text if there is no selection, or deletes the selection if there is one.
882 * This function returns true if the deletion succeeded, false otherwise.
883 */
884 public boolean deletePreviousChar() {
885 boolean failed = true;
886 if (isEditable() && !isDisabled()) {
887 final String text = getText();
888 final int dot = getCaretPosition();
889 final int mark = getAnchor();
890 if (dot != mark) {
891 // there is a selection of text to remove
892 replaceSelection("");
893 failed = false;
894 } else if (dot > 0) {
895 // The caret is not at the beginning, so remove some characters.
896 // Typically you'd only be removing a single character, but
897 // in some cases you must remove two depending on the unicode
898 // characters
899 // Note: Do not use charIterator here, because we do want to
900 // break up clusters when deleting backwards.
901 int p = Character.offsetByCodePoints(text, dot, -1);
902 deleteText(p, dot);
903 failed = false;
904 }
905 }
906 return !failed;
907 }
908
909 /**
910 * Deletes the character that follows the current caret position from the
911 * text if there is no selection, or deletes the selection if there is one.
912 * This function returns true if the deletion succeeded, false otherwise.
913 */
914 public boolean deleteNextChar() {
915 boolean failed = true;
916 if (isEditable() && !isDisabled()) {
917 final int textLength = getLength();
918 final String text = getText();
919 final int dot = getCaretPosition();
920 final int mark = getAnchor();
921 if (dot != mark) {
922 // there is a selection of text to remove
923 replaceSelection("");
924 failed = false;
925 } else if (textLength > 0 && dot < textLength) {
926 // The caret is not at the end, so remove some characters.
927 // Typically you'd only be removing a single character, but
928 // in some cases you must remove two depending on the unicode
929 // characters
930 if (charIterator == null) {
931 charIterator = BreakIterator.getCharacterInstance();
932 }
953 if (dot != mark) {
954 int pos = Math.max(dot, mark);
955 selectRange(pos, pos);
956 } else if (dot < textLength && textLength > 0) {
957 if (charIterator == null) {
958 charIterator = BreakIterator.getCharacterInstance();
959 }
960 charIterator.setText(getText());
961 int pos = charIterator.following(dot);
962 selectRange(pos, pos);
963 }
964 deselect();
965 }
966
967 /**
968 * Moves the caret position backward. If there is no selection, then the
969 * caret position is moved one character backward. If there is a selection,
970 * then the caret position is moved to the beginning of the selection and
971 * the selection cleared.
972 *
973 * @expert This function is intended to be used by experts, primarily
974 * by those implementing new Skins or Behaviors. It is not common
975 * for developers or designers to access this function directly.
976 */
977 public void backward() {
978 // user has moved caret to the left
979 final int textLength = getLength();
980 final int dot = getCaretPosition();
981 final int mark = getAnchor();
982 if (dot != mark) {
983 int pos = Math.min(dot, mark);
984 selectRange(pos, pos);
985 } else if (dot > 0 && textLength > 0) {
986 if (charIterator == null) {
987 charIterator = BreakIterator.getCharacterInstance();
988 }
989 charIterator.setText(getText());
990 int pos = charIterator.preceding(dot);
991 selectRange(pos, pos);
992 }
993 deselect();
994 }
995
996 /**
997 * Positions the caret to the position indicated by {@code pos}. This
998 * function will also clear the selection.
999 */
1000 public void positionCaret(int pos) {
1001 final int p = Utils.clamp(0, pos, getLength());
1002 selectRange(p, p);
1003 }
1004
1005 /**
1006 * Positions the caret to the position indicated by {@code pos} and extends
1007 * the selection, if there is one. If there is no selection, then a
1008 * selection is formed where the anchor is at the current caret position
1009 * and the caretPosition is moved to pos.
1010 */
1011 public void selectPositionCaret(int pos) {
1012 selectRange(getAnchor(), Utils.clamp(0, pos, getLength()));
1013 }
1014
1015 /**
1016 * Positions the anchor and caretPosition explicitly.
1017 */
1018 public void selectRange(int anchor, int caretPosition) {
1019 caretPosition = Utils.clamp(0, caretPosition, getLength());
1020 anchor = Utils.clamp(0, anchor, getLength());
1021
1022 TextFormatter.Change change = new TextFormatter.Change(this, getFormatterAccessor(), anchor, caretPosition);
1023 TextFormatter<?> formatter = getTextFormatter();
1024 if (formatter != null && formatter.getFilter() != null) {
1025 change = formatter.getFilter().apply(change);
1026 if (change == null) {
1027 return;
1028 }
1029 }
1030
1031 updateContent(change, false);
1032 }
1033
1034 private void doSelectRange(int anchor, int caretPosition) {
1035 this.caretPosition.set(Utils.clamp(0, caretPosition, getLength()));
1036 this.anchor.set(Utils.clamp(0, anchor, getLength()));
1037 this.selection.set(IndexRange.normalize(getAnchor(), getCaretPosition()));
1038 notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTION_START);
1039 }
1040
1041 /**
1042 * This function will extend the selection to include the specified pos.
1043 * This is different from selectPositionCaret in that it does not simply
1044 * move the caret. Rather, it will reposition the caret and anchor as necessary
1045 * to ensure that pos becomes the new caret and the far other end of the
1046 * selection becomes the anchor.
1047 */
1048 public void extendSelection(int pos) {
1049 final int p = Utils.clamp(0, pos, getLength());
1050 final int dot = getCaretPosition();
1051 final int mark = getAnchor();
1052 int start = Math.min(dot, mark);
1053 int end = Math.max(dot, mark);
1054 if (p < start) {
1055 selectRange(end, p);
1056 } else {
1057 selectRange(start, p);
1058 }
1059 }
1060
1061 /**
1062 * Clears the text.
1063 */
1064 public void clear() {
1065 deselect();
1066 if (!text.isBound()) {
1067 setText("");
1068 }
1069 }
1070
1071 /**
1072 * Clears the selection.
1073 */
1074 public void deselect() {
1075 // set the anchor equal to the caret position, which clears the selection
1076 // while also preserving the caret position
1077 selectRange(getCaretPosition(), getCaretPosition());
1078 }
1079
1080 /**
1081 * Replaces the selection with the given replacement String. If there is
1082 * no selection, then the replacement text is simply inserted at the current
1083 * caret position. If there was a selection, then the selection is cleared
1084 * and the given replacement text inserted.
1085 */
1086 public void replaceSelection(String replacement) {
1087 replaceText(getSelection(), replacement);
1088 }
1089
1090 /**
1091 * If possible, undoes the last modification. If {@link #isUndoable()} returns
1092 * false, then calling this method has no effect.
1093 * @since JavaFX 8u40
1094 */
1095 public final void undo() {
1096 if (isUndoable()) {
1097 // Apply reverse change here
1098 final int start = undoChange.start;
1099 final String newText = undoChange.newText;
1100 final String oldText = undoChange.oldText;
1101
1102 if (newText != null) {
1103 getContent().delete(start, start + newText.length(), oldText.isEmpty());
1104 }
|
70 import com.sun.javafx.util.Utils;
71 import com.sun.javafx.binding.ExpressionHelper;
72 import com.sun.javafx.scene.NodeHelper;
73 import javafx.util.StringConverter;
74
75 /**
76 * Abstract base class for text input controls.
77 * @since JavaFX 2.0
78 */
79 @DefaultProperty("text")
80 public abstract class TextInputControl extends Control {
81 /**
82 * Interface representing a text input's content. Since it is an ObservableStringValue,
83 * you can also bind to, or observe the content.
84 * @since JavaFX 2.0
85 */
86 protected interface Content extends ObservableStringValue {
87 /**
88 * Retrieves a subset of the content.
89 *
90 * @param start the start
91 * @param end the end
92 * @return a subset of the content
93 */
94 public String get(int start, int end);
95
96 /**
97 * Inserts a sequence of characters into the content.
98 *
99 * @param index the index
100 * @param text the text string
101 * @param notifyListeners the notify listener flag
102 * @since JavaFX 2.1
103 */
104 public void insert(int index, String text, boolean notifyListeners);
105
106 /**
107 * Removes a sequence of characters from the content.
108 *
109 * @param start the start
110 * @param end the end
111 * @param notifyListeners the notify listener flag
112 * @since JavaFX 2.1
113 */
114 public void delete(int start, int end, boolean notifyListeners);
115
116 /**
117 * Returns the number of characters represented by the content.
118 * @return the number of characters
119 */
120 public int length();
121 }
122
123 /***************************************************************************
124 * *
125 * Constructors *
126 * *
127 **************************************************************************/
128
129 /**
130 * Creates a new TextInputControl. The content is an immutable property and
131 * must be specified (as non-null) at the time of construction.
132 *
133 * @param content a non-null implementation of Content.
134 */
135 protected TextInputControl(final Content content) {
136 this.content = content;
137
138 // Add a listener so that whenever the Content is changed, we notify
178 } else {
179 commitValue();
180 }
181 });
182
183 // Specify the default style class
184 getStyleClass().add("text-input");
185 }
186
187 /***************************************************************************
188 * *
189 * Properties *
190 * *
191 **************************************************************************/
192
193 /**
194 * The default font to use for text in the TextInputControl. If the TextInputControl's text is
195 * rich text then this font may or may not be used depending on the font
196 * information embedded in the rich text, but in any case where a default
197 * font is required, this font will be used.
198 * @return the font property
199 * @since JavaFX 8.0
200 */
201 public final ObjectProperty<Font> fontProperty() {
202 if (font == null) {
203 font = new StyleableObjectProperty<Font>(Font.getDefault()) {
204
205
206 private boolean fontSetByCss = false;
207
208 @Override
209 public void applyStyle(StyleOrigin newOrigin, Font value) {
210
211 //
212 // RT-20727 - if CSS is setting the font, then make sure invalidate doesn't call NodeHelper.reapplyCSS
213 //
214 try {
215 // super.applyStyle calls set which might throw if value is bound.
216 // Have to make sure fontSetByCss is reset.
217 fontSetByCss = true;
218 super.applyStyle(newOrigin, value);
327 if (!isFocused()) {
328 updateText(get());
329 }
330 }
331
332 if (oldFormatter != null) {
333 oldFormatter.unbindFromControl();
334 }
335 } finally {
336 oldFormatter = formatter;
337 }
338 }
339 };
340 public final ObjectProperty<TextFormatter<?>> textFormatterProperty() { return textFormatter; }
341 public final TextFormatter<?> getTextFormatter() { return textFormatter.get(); }
342 public final void setTextFormatter(TextFormatter<?> value) { textFormatter.set(value); }
343
344 private final Content content;
345 /**
346 * Returns the text input's content model.
347 * @return the text input's content model
348 */
349 protected final Content getContent() {
350 return content;
351 }
352
353 /**
354 * The textual content of this TextInputControl.
355 */
356 private TextProperty text = new TextProperty();
357 public final String getText() { return text.get(); }
358 public final void setText(String value) { text.set(value); }
359 public final StringProperty textProperty() { return text; }
360
361 /**
362 * The number of characters in the text input.
363 */
364 private ReadOnlyIntegerWrapper length = new ReadOnlyIntegerWrapper(this, "length");
365 public final int getLength() { return length.get(); }
366 public final ReadOnlyIntegerProperty lengthProperty() { return length.getReadOnlyProperty(); }
367
432 /**
433 * The property describes if it's currently possible to redo the latest change of the content that was undone.
434 * @defaultValue false
435 * @since JavaFX 8u40
436 */
437 private final ReadOnlyBooleanWrapper redoable = new ReadOnlyBooleanWrapper(this, "redoable", false);
438 public final boolean isRedoable() { return redoable.get(); }
439 public final ReadOnlyBooleanProperty redoableProperty() { return redoable.getReadOnlyProperty(); }
440
441 /***************************************************************************
442 * *
443 * Methods *
444 * *
445 **************************************************************************/
446
447 /**
448 * Returns a subset of the text input's content.
449 *
450 * @param start must be a value between 0 and end - 1.
451 * @param end must be less than or equal to the length
452 * @return the subset of the text input's content
453 */
454 public String getText(int start, int end) {
455 if (start > end) {
456 throw new IllegalArgumentException("The start must be <= the end");
457 }
458
459 if (start < 0
460 || end > getLength()) {
461 throw new IndexOutOfBoundsException();
462 }
463
464 return getContent().get(start, end);
465 }
466
467 /**
468 * Appends a sequence of characters to the content.
469 *
470 * @param text a non null String
471 */
472 public void appendText(String text) {
870 * caretPosition is moved to before the first char.
871 */
872 public void selectHome() {
873 selectRange(getAnchor(), 0);
874 }
875
876 /**
877 * Moves the caret to after the last char of text. This does not cause
878 * the selection to be cleared. Rather, the anchor stays put and the
879 * caretPosition is moved to after the last char.
880 */
881 public void selectEnd() {
882 final int textLength = getLength();
883 if (textLength > 0) selectRange(getAnchor(), textLength);
884 }
885
886 /**
887 * Deletes the character that precedes the current caret position from the
888 * text if there is no selection, or deletes the selection if there is one.
889 * This function returns true if the deletion succeeded, false otherwise.
890 * @return true if the deletion succeeded, false otherwise
891 */
892 public boolean deletePreviousChar() {
893 boolean failed = true;
894 if (isEditable() && !isDisabled()) {
895 final String text = getText();
896 final int dot = getCaretPosition();
897 final int mark = getAnchor();
898 if (dot != mark) {
899 // there is a selection of text to remove
900 replaceSelection("");
901 failed = false;
902 } else if (dot > 0) {
903 // The caret is not at the beginning, so remove some characters.
904 // Typically you'd only be removing a single character, but
905 // in some cases you must remove two depending on the unicode
906 // characters
907 // Note: Do not use charIterator here, because we do want to
908 // break up clusters when deleting backwards.
909 int p = Character.offsetByCodePoints(text, dot, -1);
910 deleteText(p, dot);
911 failed = false;
912 }
913 }
914 return !failed;
915 }
916
917 /**
918 * Deletes the character that follows the current caret position from the
919 * text if there is no selection, or deletes the selection if there is one.
920 * This function returns true if the deletion succeeded, false otherwise.
921 * @return true if the deletion succeeded, false otherwise
922 */
923 public boolean deleteNextChar() {
924 boolean failed = true;
925 if (isEditable() && !isDisabled()) {
926 final int textLength = getLength();
927 final String text = getText();
928 final int dot = getCaretPosition();
929 final int mark = getAnchor();
930 if (dot != mark) {
931 // there is a selection of text to remove
932 replaceSelection("");
933 failed = false;
934 } else if (textLength > 0 && dot < textLength) {
935 // The caret is not at the end, so remove some characters.
936 // Typically you'd only be removing a single character, but
937 // in some cases you must remove two depending on the unicode
938 // characters
939 if (charIterator == null) {
940 charIterator = BreakIterator.getCharacterInstance();
941 }
962 if (dot != mark) {
963 int pos = Math.max(dot, mark);
964 selectRange(pos, pos);
965 } else if (dot < textLength && textLength > 0) {
966 if (charIterator == null) {
967 charIterator = BreakIterator.getCharacterInstance();
968 }
969 charIterator.setText(getText());
970 int pos = charIterator.following(dot);
971 selectRange(pos, pos);
972 }
973 deselect();
974 }
975
976 /**
977 * Moves the caret position backward. If there is no selection, then the
978 * caret position is moved one character backward. If there is a selection,
979 * then the caret position is moved to the beginning of the selection and
980 * the selection cleared.
981 *
982 * Note: This function is intended to be used by experts, primarily
983 * by those implementing new Skins or Behaviors. It is not common
984 * for developers or designers to access this function directly.
985 */
986 public void backward() {
987 // user has moved caret to the left
988 final int textLength = getLength();
989 final int dot = getCaretPosition();
990 final int mark = getAnchor();
991 if (dot != mark) {
992 int pos = Math.min(dot, mark);
993 selectRange(pos, pos);
994 } else if (dot > 0 && textLength > 0) {
995 if (charIterator == null) {
996 charIterator = BreakIterator.getCharacterInstance();
997 }
998 charIterator.setText(getText());
999 int pos = charIterator.preceding(dot);
1000 selectRange(pos, pos);
1001 }
1002 deselect();
1003 }
1004
1005 /**
1006 * Positions the caret to the position indicated by {@code pos}. This
1007 * function will also clear the selection.
1008 * @param pos the position
1009 */
1010 public void positionCaret(int pos) {
1011 final int p = Utils.clamp(0, pos, getLength());
1012 selectRange(p, p);
1013 }
1014
1015 /**
1016 * Positions the caret to the position indicated by {@code pos} and extends
1017 * the selection, if there is one. If there is no selection, then a
1018 * selection is formed where the anchor is at the current caret position
1019 * and the caretPosition is moved to pos.
1020 * @param pos the position
1021 */
1022 public void selectPositionCaret(int pos) {
1023 selectRange(getAnchor(), Utils.clamp(0, pos, getLength()));
1024 }
1025
1026 /**
1027 * Positions the anchor and caretPosition explicitly.
1028 * @param anchor the anchor
1029 * @param caretPosition the caretPosition
1030 */
1031 public void selectRange(int anchor, int caretPosition) {
1032 caretPosition = Utils.clamp(0, caretPosition, getLength());
1033 anchor = Utils.clamp(0, anchor, getLength());
1034
1035 TextFormatter.Change change = new TextFormatter.Change(this, getFormatterAccessor(), anchor, caretPosition);
1036 TextFormatter<?> formatter = getTextFormatter();
1037 if (formatter != null && formatter.getFilter() != null) {
1038 change = formatter.getFilter().apply(change);
1039 if (change == null) {
1040 return;
1041 }
1042 }
1043
1044 updateContent(change, false);
1045 }
1046
1047 private void doSelectRange(int anchor, int caretPosition) {
1048 this.caretPosition.set(Utils.clamp(0, caretPosition, getLength()));
1049 this.anchor.set(Utils.clamp(0, anchor, getLength()));
1050 this.selection.set(IndexRange.normalize(getAnchor(), getCaretPosition()));
1051 notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTION_START);
1052 }
1053
1054 /**
1055 * This function will extend the selection to include the specified pos.
1056 * This is different from selectPositionCaret in that it does not simply
1057 * move the caret. Rather, it will reposition the caret and anchor as necessary
1058 * to ensure that pos becomes the new caret and the far other end of the
1059 * selection becomes the anchor.
1060 * @param pos the position
1061 */
1062 public void extendSelection(int pos) {
1063 final int p = Utils.clamp(0, pos, getLength());
1064 final int dot = getCaretPosition();
1065 final int mark = getAnchor();
1066 int start = Math.min(dot, mark);
1067 int end = Math.max(dot, mark);
1068 if (p < start) {
1069 selectRange(end, p);
1070 } else {
1071 selectRange(start, p);
1072 }
1073 }
1074
1075 /**
1076 * Clears the text.
1077 */
1078 public void clear() {
1079 deselect();
1080 if (!text.isBound()) {
1081 setText("");
1082 }
1083 }
1084
1085 /**
1086 * Clears the selection.
1087 */
1088 public void deselect() {
1089 // set the anchor equal to the caret position, which clears the selection
1090 // while also preserving the caret position
1091 selectRange(getCaretPosition(), getCaretPosition());
1092 }
1093
1094 /**
1095 * Replaces the selection with the given replacement String. If there is
1096 * no selection, then the replacement text is simply inserted at the current
1097 * caret position. If there was a selection, then the selection is cleared
1098 * and the given replacement text inserted.
1099 * @param replacement the replacement string
1100 */
1101 public void replaceSelection(String replacement) {
1102 replaceText(getSelection(), replacement);
1103 }
1104
1105 /**
1106 * If possible, undoes the last modification. If {@link #isUndoable()} returns
1107 * false, then calling this method has no effect.
1108 * @since JavaFX 8u40
1109 */
1110 public final void undo() {
1111 if (isUndoable()) {
1112 // Apply reverse change here
1113 final int start = undoChange.start;
1114 final String newText = undoChange.newText;
1115 final String oldText = undoChange.oldText;
1116
1117 if (newText != null) {
1118 getContent().delete(start, start + newText.length(), oldText.isEmpty());
1119 }
|