forked from openjdk/jfx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RichTextArea.java
1383 lines (1242 loc) · 49.9 KB
/
RichTextArea.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This code borrows heavily from the following project, with permission from the author:
// https://github.com/andy-goryachev/FxEditor
package javafx.scene.control.rich;
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.WritableValue;
import javafx.css.CssMetaData;
import javafx.css.FontCssMetaData;
import javafx.css.SimpleStyleableObjectProperty;
import javafx.css.StyleConverter;
import javafx.css.StyleOrigin;
import javafx.css.Styleable;
import javafx.css.StyleableBooleanProperty;
import javafx.css.StyleableObjectProperty;
import javafx.css.StyleableProperty;
import javafx.css.converter.InsetsConverter;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.AccessibleAttribute;
import javafx.scene.AccessibleRole;
import javafx.scene.control.Control;
import javafx.scene.control.behavior.FunctionTag;
import javafx.scene.control.rich.model.EditableRichTextModel;
import javafx.scene.control.rich.model.StyleAttrs;
import javafx.scene.control.rich.model.StyledTextModel;
import javafx.scene.control.rich.skin.RichTextAreaSkin;
import javafx.scene.control.util.Util;
import javafx.scene.input.DataFormat;
import javafx.scene.text.Font;
import javafx.util.Duration;
import com.sun.javafx.scene.NodeHelper;
import com.sun.javafx.scene.control.rich.Params;
import com.sun.javafx.scene.control.rich.RichTextAreaSkinHelper;
import com.sun.javafx.scene.control.rich.VFlow;
/**
* Text input component that allows a user to enter multiple lines of rich text.
*/
public class RichTextArea extends Control {
/** Deletes previous symbol */
public static final FunctionTag BACKSPACE = new FunctionTag();
/** Copies selected text to the clipboard */
public static final FunctionTag COPY = new FunctionTag();
/** Cuts selected text and places it to the clipboard */
public static final FunctionTag CUT = new FunctionTag();
/** Deletes symbol at the caret */
public static final FunctionTag DELETE = new FunctionTag();
/** Deletes paragraph at the caret, or selected paragraphs */
public static final FunctionTag DELETE_PARAGRAPH = new FunctionTag();
/** Inserts a single line break */
public static final FunctionTag INSERT_LINE_BREAK = new FunctionTag();
/** Inserts a TAB symbol */
public static final FunctionTag INSERT_TAB = new FunctionTag();
/** Moves the caret to end of the document */
public static final FunctionTag MOVE_DOCUMENT_END = new FunctionTag();
/** Moves the caret to beginning of the document */
public static final FunctionTag MOVE_DOCUMENT_START = new FunctionTag();
/** Moves the caret one visual text line down */
public static final FunctionTag MOVE_DOWN = new FunctionTag();
/** Moves the caret one symbol to the left */
public static final FunctionTag MOVE_LEFT = new FunctionTag();
/** Moves the caret to the end of the current paragraph */
public static final FunctionTag MOVE_PARAGRAPH_END = new FunctionTag();
/** Moves the caret to the beginning of the current paragraph */
public static final FunctionTag MOVE_PARAGRAPH_START = new FunctionTag();
/** Moves the caret one symbol to the right */
public static final FunctionTag MOVE_RIGHT = new FunctionTag();
/** Moves the caret one visual text line up */
public static final FunctionTag MOVE_UP = new FunctionTag();
/** Moves the caret one word left (previous word if LTR, next word if RTL) */
public static final FunctionTag MOVE_WORD_LEFT = new FunctionTag();
/** Moves the caret to the next word */
public static final FunctionTag MOVE_WORD_NEXT = new FunctionTag();
/** Moves the caret to the end of next word */
public static final FunctionTag MOVE_WORD_NEXT_END = new FunctionTag();
/** Moves the caret to the previous word */
public static final FunctionTag MOVE_WORD_PREVIOUS = new FunctionTag();
/** Moves the caret one word right (next word if LTR, previous word if RTL) */
public static final FunctionTag MOVE_WORD_RIGHT = new FunctionTag();
/** Moves the caret one page down */
public static final FunctionTag PAGE_DOWN = new FunctionTag();
/** Moves the caret one page up */
public static final FunctionTag PAGE_UP = new FunctionTag();
/** Inserts rich text from the clipboard */
public static final FunctionTag PASTE = new FunctionTag();
/** Inserts plain text from the clipboard */
public static final FunctionTag PASTE_PLAIN_TEXT = new FunctionTag();
/** Reverts the last undo operation */
public static final FunctionTag REDO = new FunctionTag();
/** Selects all text in the document */
public static final FunctionTag SELECT_ALL = new FunctionTag();
/** Selects text (or extends selection) from the current caret position to the end of document */
public static final FunctionTag SELECT_DOCUMENT_END = new FunctionTag();
/** Selects text (or extends selection) from the current caret position to the start of document */
public static final FunctionTag SELECT_DOCUMENT_START = new FunctionTag();
/** Selects text (or extends selection) from the current caret position one visual text line down */
public static final FunctionTag SELECT_DOWN = new FunctionTag();
/** Selects text (or extends selection) from the current position to one symbol to the left */
public static final FunctionTag SELECT_LEFT = new FunctionTag();
/** Selects text (or extends selection) from the current position to one page down */
public static final FunctionTag SELECT_PAGE_DOWN = new FunctionTag();
/** Selects text (or extends selection) from the current position to one page up */
public static final FunctionTag SELECT_PAGE_UP = new FunctionTag();
/** Selects text (or extends selection) of the current paragraph */
public static final FunctionTag SELECT_PARAGRAPH = new FunctionTag();
/** Selects text (or extends selection) from the current position to one symbol to the right */
public static final FunctionTag SELECT_RIGHT = new FunctionTag();
/** Selects text (or extends selection) from the current caret position one visual text line up */
public static final FunctionTag SELECT_UP = new FunctionTag();
/** Selects word at the caret position */
public static final FunctionTag SELECT_WORD = new FunctionTag();
/** Extends selection to the previous word (LTR) or next word (RTL) */
public static final FunctionTag SELECT_WORD_LEFT = new FunctionTag();
/** Extends selection to the next word */
public static final FunctionTag SELECT_WORD_NEXT = new FunctionTag();
/** Extends selection to the end of next word */
public static final FunctionTag SELECT_WORD_NEXT_END = new FunctionTag();
/** Extends selection to the previous word */
public static final FunctionTag SELECT_WORD_PREVIOUS = new FunctionTag();
/** Extends selection to the next word (LTR) or previous word (RTL) */
public static final FunctionTag SELECT_WORD_RIGHT = new FunctionTag();
/** Undoes the last edit operation */
public static final FunctionTag UNDO = new FunctionTag();
private final ConfigurationParameters config;
private final ObjectProperty<StyledTextModel> model = new SimpleObjectProperty<>(this, "model");
private final SimpleBooleanProperty displayCaretProperty = new SimpleBooleanProperty(this, "displayCaret", true);
private ObjectProperty<StyleAttrs> defaultParagraphAttributes;
private SimpleBooleanProperty editableProperty;
private StyleableObjectProperty<Font> font;
private final ReadOnlyObjectWrapper<Duration> caretBlinkPeriod;
private final SelectionModel selectionModel = new SingleSelectionModel();
private ReadOnlyIntegerWrapper tabSizeProperty;
private ObjectProperty<SideDecorator> leftDecorator;
private ObjectProperty<SideDecorator> rightDecorator;
private ObjectProperty<Insets> contentPadding;
private BooleanProperty highlightCurrentParagraph;
private BooleanProperty useContentWidth;
private BooleanProperty useContentHeight;
/**
* Creates an editable instance with default configuration parameters,
* using an in-memory model {@link EditableRichTextModel}.
*/
public RichTextArea() {
this(new EditableRichTextModel());
}
/**
* Creates an instance with default configuration parameters, using the specified model.
* @param model styled text model
*/
public RichTextArea(StyledTextModel model) {
this(ConfigurationParameters.defaultConfig(), model);
}
/**
* Creates an instance with the specified configuration parameters and model.
* @param c configuration parameters
* @param m styled text model
*/
public RichTextArea(ConfigurationParameters c, StyledTextModel m) {
this.config = c;
caretBlinkPeriod = new ReadOnlyObjectWrapper<>(this, "caretBlinkPeriod", Duration.millis(Params.DEFAULT_CARET_BLINK_PERIOD));
setFocusTraversable(true);
getStyleClass().add("rich-text-area");
setAccessibleRole(AccessibleRole.TEXT_AREA);
if (m != null) {
setModel(m);
}
}
@Override
protected RichTextAreaSkin createDefaultSkin() {
return new RichTextAreaSkin(this, config);
}
/**
* Determines the {@link StyledTextModel} to use with this RichTextArea.
* The model can be null.
* @return the model property
*/
public final ObjectProperty<StyledTextModel> modelProperty() {
return model;
}
public final void setModel(StyledTextModel m) {
modelProperty().set(m);
}
public final StyledTextModel getModel() {
return model.get();
}
/**
* The default font to use for text in the RichTextArea.
* If the RichTextArea's text is
* rich text then this font may or may not be used depending on the font
* information embedded in the rich text, but in any case where a default
* font is required, this font will be used.
* @return the font property
*/
public final ObjectProperty<Font> fontProperty() {
if (font == null) {
font = new StyleableObjectProperty<Font>(Font.getDefault()) {
private boolean fontSetByCss;
@Override
public void applyStyle(StyleOrigin newOrigin, Font value) {
// RT-20727 JDK-8127428
// if CSS is setting the font, then make sure invalidate doesn't call NodeHelper.reapplyCSS
try {
// super.applyStyle calls set which might throw if value is bound.
// Have to make sure fontSetByCss is reset.
fontSetByCss = true;
super.applyStyle(newOrigin, value);
} catch (Exception e) {
throw e;
} finally {
fontSetByCss = false;
}
}
@Override
public void set(Font value) {
Font old = get();
if (value == null ? old == null : value.equals(old)) {
return;
}
super.set(value);
}
@Override
protected void invalidated() {
// RT-20727 JDK-8127428
// if font is changed by calling setFont, then
// css might need to be reapplied since font size affects
// calculated values for styles with relative values
if (fontSetByCss == false) {
NodeHelper.reapplyCSS(RichTextArea.this);
}
}
@Override
public CssMetaData<RichTextArea, Font> getCssMetaData() {
return StyleableProperties.FONT;
}
@Override
public Object getBean() {
return RichTextArea.this;
}
@Override
public String getName() {
return "font";
}
};
}
return font;
}
public final void setFont(Font value) {
fontProperty().setValue(value);
}
public final Font getFont() {
return font == null ? Font.getDefault() : font.getValue();
}
/**
* Indicates whether text should be wrapped in this RichTextArea.
* If a run of text exceeds the width of the {@code RichTextArea},
* then this variable indicates whether the text should wrap onto
* another line.
* Setting this property to {@code true} has a side effect of hiding the horizontal scroll bar.
* @defaultValue false
*/
private StyleableBooleanProperty wrapText = new StyleableBooleanProperty(false) {
@Override
public Object getBean() {
return RichTextArea.this;
}
@Override
public String getName() {
return "wrapText";
}
@Override
public CssMetaData<RichTextArea, Boolean> getCssMetaData() {
return StyleableProperties.WRAP_TEXT;
}
};
public final BooleanProperty wrapTextProperty() {
return wrapText;
}
public final boolean isWrapText() {
return wrapText.getValue();
}
public final void setWrapText(boolean value) {
wrapText.setValue(value);
}
/**
* This property controls whether caret will be displayed or not.
* TODO StyleableProperty ?
* @return the display caret property
*/
public final BooleanProperty displayCaretProperty() {
return displayCaretProperty;
}
public final void setDisplayCaret(boolean on) {
displayCaretProperty.set(on);
}
public final boolean isDisplayCaret() {
return displayCaretProperty.get();
}
/**
* Indicates whether this RichTextArea can be edited by the user.
* @return the editable property
*/
public final BooleanProperty editableProperty() {
if (editableProperty == null) {
editableProperty = new SimpleBooleanProperty(this, "editable", true);
}
return editableProperty;
}
public final boolean isEditable() {
if (editableProperty == null) {
return true;
}
return editableProperty().get();
}
public final void setEditable(boolean on) {
editableProperty().set(on);
}
/**
* Indicates whether the current paragraph will be visually highlighted.
* TODO StyleableProperty ?
* @return the highlight current paragraph property
*/
public final BooleanProperty highlightCurrentParagraphProperty() {
if (highlightCurrentParagraph == null) {
highlightCurrentParagraph = new SimpleBooleanProperty(this, "highlightCurrentParagraph", true);
}
return highlightCurrentParagraph;
}
public final boolean isHighlightCurrentParagraph() {
if (highlightCurrentParagraph == null) {
return true;
}
return highlightCurrentParagraph.get();
}
public final void setHighlightCurrentParagraph(boolean on) {
highlightCurrentParagraphProperty().set(on);
}
@Override
public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
switch (attribute) {
// TODO possibly large text - could we send just what is displayed?
// case TEXT: {
// String accText = getAccessibleText();
// if (accText != null && !accText.isEmpty())
// return accText;
//
// String text = getText();
// if (text == null || text.isEmpty()) {
// text = getPromptText();
// }
// return text;
// }
case EDITABLE:
return isEditable();
// case SELECTION_START:
// return getSelection().getStart();
// case SELECTION_END:
// return getSelection().getEnd();
// case CARET_OFFSET:
// return getCaretPosition();
// case FONT:
// return getFont();
default:
return super.queryAccessibleAttribute(attribute, parameters);
}
}
private static class StyleableProperties {
private static final CssMetaData<RichTextArea, Insets> CONTENT_PADDING =
new CssMetaData<>("-fx-content-padding", InsetsConverter.getInstance()) {
@Override
public boolean isSettable(RichTextArea t) {
return t.contentPadding == null || !t.contentPadding.isBound();
}
@Override
public StyleableProperty<Insets> getStyleableProperty(RichTextArea t) {
return (StyleableProperty<Insets>)t.contentPaddingProperty();
}
};
// TODO remove if switched to paragraph attributes
private static final FontCssMetaData<RichTextArea> FONT =
new FontCssMetaData<>("-fx-font", Font.getDefault()) {
@Override
public boolean isSettable(RichTextArea n) {
return n.font == null || !n.font.isBound();
}
@Override
public StyleableProperty<Font> getStyleableProperty(RichTextArea n) {
return (StyleableProperty<Font>)(WritableValue<Font>)n.fontProperty();
}
};
private static final CssMetaData<RichTextArea,Boolean> WRAP_TEXT =
new CssMetaData<>("-fx-wrap-text", StyleConverter.getBooleanConverter(), false) {
@Override
public boolean isSettable(RichTextArea t) {
return !t.wrapText.isBound();
}
@Override
public StyleableProperty<Boolean> getStyleableProperty(RichTextArea t) {
return (StyleableProperty<Boolean>)t.wrapTextProperty();
}
};
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES = Util.initStyleables(
Control.getClassCssMetaData(),
CONTENT_PADDING,
FONT,
WRAP_TEXT
);
}
/**
* Gets the {@code CssMetaData} associated with this class, which may include the
* {@code CssMetaData} of its superclasses.
* @return the {@code CssMetaData}
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return StyleableProperties.STYLEABLES;
}
@Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return getClassCssMetaData();
}
private VFlow vflow() {
return RichTextAreaSkinHelper.getVFlow(this);
}
/**
* Finds a text position corresponding to the specified screen coordinates.
* @param screenX screen x coordinate
* @param screenY screen y coordinate
* @return the TextPosition
*/
public TextPos getTextPosition(double screenX, double screenY) {
Point2D local = vflow().getContentPane().screenToLocal(screenX, screenY);
return vflow().getTextPosLocal(local.getX(), local.getY());
}
/**
* Determines the caret blink rate.
* @return the caret blunk period property
*/
public final ReadOnlyObjectProperty<Duration> caretBlinkPeriodProperty() {
return caretBlinkPeriod.getReadOnlyProperty();
}
public final void setCaretBlinkPeriod(Duration period) {
if (period == null) {
throw new NullPointerException("caret blink period cannot be null");
}
caretBlinkPeriod.set(period);
}
public final Duration getCaretBlinkPeriod() {
return caretBlinkPeriod.get();
}
/**
* Moves the caret and anchor to the new position, unless {@code extendSelection} is true, in which case
* extend selection from the existing anchor to the newly set caret position.
* @param p text position
* @param extendSelection specifies whether to clear (false) or extend (true) any existing selection
*/
public void moveCaret(TextPos p, boolean extendSelection) {
if (extendSelection) {
extendSelection(p);
} else {
select(p, p);
}
}
/**
* Tracks the caret position within the document. The value can be null.
* <p>
* Important note: setting a {@link SelectionSegment} causes an update to both anchor and caret properties.
* Typically, they both should be either null (corresponding to a null selection segment) or non-null.
* However, it is possible to read one null value and one non-null value in a listener. To lessen the impact,
* the caretProperty is updated last, so any listener monitoring the caret property would read the right anchor
* value. A listener monitoring the anchorProperty might see erroneous value for the caret, so keep that in mind.
*
* @return the caret position property
*/
public final ReadOnlyProperty<TextPos> caretPositionProperty() {
return selectionModel.caretPositionProperty();
}
public final TextPos getCaretPosition() {
return caretPositionProperty().getValue();
}
public final TextPos getAnchorPosition() {
return anchorPositionProperty().getValue();
}
/**
* Tracks the selection anchor position within the document. The value can be null.
* <p>
* Important note: setting a {@link SelectionSegment} causes an update to both anchor and caret properties.
* Typically, they both should be either null (corresponding to a null selection segment) or non-null.
* However, it is possible to read one null value and one non-null value in a listener. To lessen the impact,
* the caretProperty is updated last, so any listener monitoring the caret property would read the right anchor
* value. A listener monitoring the anchorProperty might see erroneous value for the caret, so keep that in mind.
*
* @return the anchor position property
*/
public final ReadOnlyProperty<TextPos> anchorPositionProperty() {
return selectionModel.anchorPositionProperty();
}
/**
* Tracks the current selection segment position.
* The value can be null.
* @return the selection segment property
*/
public final ReadOnlyProperty<SelectionSegment> selectionSegmentProperty() {
return selectionModel.selectionSegmentProperty();
}
/**
* Clears existing selection, if any.
*/
public void clearSelection() {
selectionModel.clear();
}
/**
* Moves the caret to the specified position, clearing the selection.
* @param pos the text position
*/
public void setCaret(TextPos pos) {
StyledTextModel model = getModel();
if (model != null) {
Marker m = model.getMarker(pos);
selectionModel.setSelection(m, m);
}
}
/**
* Selects the specified range and places the caret at the new position.
* @param anchor the new selection anchor position
* @param caret the new caret position
*/
public void select(TextPos anchor, TextPos caret) {
StyledTextModel model = getModel();
if (model != null) {
Marker ma = model.getMarker(anchor);
Marker mc = model.getMarker(caret);
selectionModel.setSelection(ma, mc);
}
}
/**
* Extends selection from the existing anchor to the new position.
* @param pos the text position
*/
public void extendSelection(TextPos pos) {
StyledTextModel model = getModel();
if (model != null) {
Marker m = model.getMarker(pos);
selectionModel.extendSelection(m);
}
}
/**
* Returns the number of paragraphs in the model. If model is null, returns 0.
* @return the paragraph count
*/
public int getParagraphCount() {
StyledTextModel m = getModel();
return (m == null) ? 0 : m.size();
}
/**
* Returns the plain text at the specified paragraph index.
* @param modelIndex paragraph index
* @return plain text string, or null
* @throws IllegalArgumentException if the modelIndex is outside of the range supported by the model
*/
public String getPlainText(int modelIndex) {
if ((modelIndex < 0) || (modelIndex >= getParagraphCount())) {
throw new IllegalArgumentException("No paragraph at index=" + modelIndex);
}
return getModel().getPlainText(modelIndex);
}
private RichTextAreaSkin richTextAreaSkin() {
return (RichTextAreaSkin)getSkin();
}
/**
* The size of a tab stop in spaces.
*
* @return the {@code tabSize} property
* @defaultValue 8
*/
// FIX this makes sense only in the context of CodeArea
// public final ReadOnlyIntegerProperty tabSizeProperty() {
// return tabSizePropertyPrivate().getReadOnlyProperty();
// }
//
// public final void setTabSize(int n) {
// if ((n < 1) || (n > Params.MAX_TAB_SIZE)) {
// throw new IllegalArgumentException("tab size out of range (1-" + Params.MAX_TAB_SIZE + ") " + n);
// }
// tabSizePropertyPrivate().set(n);
// }
//
// public final int getTabSize() {
// if (tabSizeProperty == null) {
// return 8;
// }
// return tabSizeProperty.get();
// }
//
// private ReadOnlyIntegerWrapper tabSizePropertyPrivate() {
// if (tabSizeProperty == null) {
// tabSizeProperty = new ReadOnlyIntegerWrapper(8);
// }
// return tabSizeProperty;
// }
/**
* Determines whether the preferred width is the same as the content width.
* When set to true, the horizontal scroll bar is disabled.
*
* @defaultValue false
* @return the use content width property
*/
public final BooleanProperty useContentWidthProperty() {
if (useContentWidth == null) {
useContentWidth = new SimpleBooleanProperty();
}
return useContentWidth;
}
public final boolean isUseContentWidth() {
return useContentWidth == null ? false : useContentWidth.get();
}
public final void setUseContentWidth(boolean on) {
useContentWidthProperty().set(true);
}
/**
* Determines whether the preferred height is the same as the content height.
* When set to true, the vertical scroll bar is disabled.
*
* @defaultValue false
* @return the use content height property
*/
public final BooleanProperty useContentHeightProperty() {
if (useContentHeight == null) {
useContentHeight = new SimpleBooleanProperty();
}
return useContentHeight;
}
public final boolean isUseContentHeight() {
return useContentHeight == null ? false : useContentHeight.get();
}
public final void setUseContentHeight(boolean on) {
useContentHeightProperty().set(true);
}
/**
* Replaces the specified range with the new text.
* @param start start text position
* @param end end text position
* @param text text string to insert
* @param createUndo when true, creates an undo-redo entry
* @return new caret position at the end of inserted text, or null if the change cannot be made
*/
public TextPos replaceText(TextPos start, TextPos end, String text, boolean createUndo) {
if (canEdit()) {
StyledTextModel m = getModel();
return m.replace(vflow(), start, end, text, createUndo);
}
return null;
}
/**
* When selection exists, deletes selected text. Otherwise, deletes the symbol before the caret.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void backspace() {
execute(BACKSPACE);
}
/**
* When selection exists, copies the selected rich text to the clipboard in all formats supported
* by the model.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void copy() {
execute(COPY);
}
/**
* Copies the text in the specified format when selection exists and when the export in this format
* is supported by the model, and the skin must be installed; otherwise, this method is a no-op.
* @param format the data format to use
*/
public void copy(DataFormat format) {
RichTextAreaSkin skin = richTextAreaSkin();
if (skin != null) {
skin.copy(format);
}
}
/**
* When selection exists, removes the selected rich text and places it into the clipboard.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void cut() {
execute(CUT);
}
/**
* When selection exists, deletes selected text. Otherwise, deletes the symbol at the caret.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void delete() {
execute(DELETE);
}
/**
* Inserts a line break at the caret. If selection exists, first deletes the selected text.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void insertLineBreak() {
execute(INSERT_LINE_BREAK);
}
/**
* Inserts a tab symbol at the caret. If selection exists, first deletes the selected text.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void insertTab() {
execute(INSERT_TAB);
}
/**
* Moves the caret to after the last character of the text, also clearing the selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveDocumentEnd() {
execute(MOVE_DOCUMENT_END);
}
/**
* Moves the caret to before the first character of the text.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveDocumentStart() {
execute(MOVE_DOCUMENT_START);
}
/**
* Moves the caret down.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveDown() {
execute(MOVE_DOWN);
}
/**
* Moves the caret left.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveLeft() {
execute(MOVE_LEFT);
}
/**
* Moves the caret to the end of the current paragraph.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveParagraphEnd() {
execute(MOVE_PARAGRAPH_END);
}
/**
* Moves the caret to the start of the current paragraph.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveLineStart() {
execute(MOVE_PARAGRAPH_START);
}
/**
* Moves the caret to the next symbol.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveRight() {
execute(MOVE_RIGHT);
}
/**
* Moves the caret up.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveUp() {
execute(MOVE_UP);
}
/**
* Moves the caret to the beginning of previous word in a left-to-right setting,
* or the beginning of the next word in a right-to-left setting.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveLeftWord() {
execute(MOVE_WORD_PREVIOUS);
}
/**
* Moves the caret to the beginning of next word in a left-to-right setting,
* or the beginning of the previous word in a right-to-left setting.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveRightWord() {
execute(MOVE_WORD_NEXT);
}
/**
* Moves the caret to the beginning of previous word.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void movePreviousWord() {
execute(MOVE_WORD_PREVIOUS);
}
/**
* Moves the caret to the beginning of next word.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveNextWord() {
execute(MOVE_WORD_NEXT);
}
/**
* Moves the caret to the end of the next word.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void moveEndOfNextWord() {
execute(MOVE_WORD_NEXT_END);
}
/**
* Move caret one page down.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void pageDown() {
execute(PAGE_DOWN);
}
/**
* Move caret one page up.
* This method has a side effect of clearing an existing selection.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void pageUp() {
execute(PAGE_UP);
}
/**
* Pastes the clipboard content at the caret, or, if selection exists, replacing the selected text.
* The model decides which format to use.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void paste() {
execute(PASTE);
}
/**
* Pastes the clipboard content at the caret, or, if selection exists, replacing the selected text.
* The format must be supported by the model, and the skin must be installed,
* otherwise this method has no effect.
* @param format the data format to use
*/
public void paste(DataFormat format) {
RichTextAreaSkin skin = richTextAreaSkin();
if (skin != null) {
skin.paste(format);
}
}
/**
* Pastes the plain text clipboard content at the caret, or, if selection exists, replacing the selected text.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void pastePlainText() {
execute(PASTE_PLAIN_TEXT);
}
/**
* If possible, redoes the last undone modification. If {@link #isRedoable()} returns
* false, then calling this method has no effect.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/
public void redo() {
execute(REDO);
}
/**
* Selects all the text in the document.
* <p>This action can be changed by remapping the default behavior, @see {@link Control#getInputMap()}.
*/