2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.io
.IOException
;
33 import jexer
.bits
.CellAttributes
;
34 import jexer
.bits
.StringUtils
;
35 import jexer
.event
.TCommandEvent
;
36 import jexer
.event
.TKeypressEvent
;
37 import jexer
.event
.TMouseEvent
;
38 import jexer
.event
.TResizeEvent
;
39 import jexer
.teditor
.Document
;
40 import jexer
.teditor
.Line
;
41 import jexer
.teditor
.Word
;
42 import static jexer
.TCommand
.*;
43 import static jexer
.TKeypress
.*;
46 * TEditorWidget displays an editable text document. It is unaware of
47 * scrolling behavior, but can respond to mouse and keyboard events.
49 public class TEditorWidget
extends TWidget
implements EditMenuUser
{
51 // ------------------------------------------------------------------------
52 // Constants --------------------------------------------------------------
53 // ------------------------------------------------------------------------
56 * The number of lines to scroll on mouse wheel up/down.
58 private static final int wheelScrollSize
= 3;
60 // ------------------------------------------------------------------------
61 // Variables --------------------------------------------------------------
62 // ------------------------------------------------------------------------
65 * The document being edited.
67 private Document document
;
70 * The default color for the TEditor class.
72 private CellAttributes defaultColor
= null;
75 * The topmost line number in the visible area. 0-based.
77 private int topLine
= 0;
80 * The leftmost column number in the visible area. 0-based.
82 private int leftColumn
= 0;
85 * If true, the mouse is dragging a selection.
87 private boolean inSelection
= false;
90 * Selection starting column.
92 private int selectionColumn0
;
95 * Selection starting line.
97 private int selectionLine0
;
100 * Selection ending column.
102 private int selectionColumn1
;
105 * Selection ending line.
107 private int selectionLine1
;
109 // ------------------------------------------------------------------------
110 // Constructors -----------------------------------------------------------
111 // ------------------------------------------------------------------------
114 * Public constructor.
116 * @param parent parent widget
117 * @param text text on the screen
118 * @param x column relative to parent
119 * @param y row relative to parent
120 * @param width width of text area
121 * @param height height of text area
123 public TEditorWidget(final TWidget parent
, final String text
, final int x
,
124 final int y
, final int width
, final int height
) {
126 // Set parent and window
127 super(parent
, x
, y
, width
, height
);
129 setCursorVisible(true);
131 defaultColor
= getTheme().getColor("teditor");
132 document
= new Document(text
, defaultColor
);
135 // ------------------------------------------------------------------------
136 // Event handlers ---------------------------------------------------------
137 // ------------------------------------------------------------------------
140 * Handle mouse press events.
142 * @param mouse mouse button press event
145 public void onMouseDown(final TMouseEvent mouse
) {
146 if (mouse
.isMouseWheelUp()) {
147 for (int i
= 0; i
< wheelScrollSize
; i
++) {
150 alignDocument(false);
155 if (mouse
.isMouseWheelDown()) {
156 for (int i
= 0; i
< wheelScrollSize
; i
++) {
157 if (topLine
< document
.getLineCount() - 1) {
165 if (mouse
.isMouse1()) {
167 int newLine
= topLine
+ mouse
.getY();
168 int newX
= leftColumn
+ mouse
.getX();
171 if (newLine
> document
.getLineCount() - 1) {
172 selectionLine0
= document
.getLineCount() - 1;
174 selectionLine0
= topLine
+ mouse
.getY();
176 selectionColumn0
= leftColumn
+ mouse
.getX();
177 selectionColumn0
= Math
.max(0, Math
.min(selectionColumn0
,
178 document
.getLine(selectionLine0
).getDisplayLength() - 1));
179 selectionColumn1
= selectionColumn0
;
180 selectionLine1
= selectionLine0
;
182 // Set the row and column
183 if (newLine
> document
.getLineCount() - 1) {
185 document
.setLineNumber(document
.getLineCount() - 1);
187 if (newLine
> document
.getLineCount() - 1) {
188 setCursorY(document
.getLineCount() - 1 - topLine
);
190 setCursorY(mouse
.getY());
194 selectionColumn1
= document
.getCursor();
195 selectionLine1
= document
.getLineNumber();
200 document
.setLineNumber(newLine
);
201 setCursorY(mouse
.getY());
202 if (newX
>= document
.getCurrentLine().getDisplayLength()) {
206 document
.setCursor(newX
);
207 setCursorX(mouse
.getX());
210 selectionColumn1
= document
.getCursor();
211 selectionLine1
= document
.getLineNumber();
219 super.onMouseDown(mouse
);
223 * Handle mouse motion events.
225 * @param mouse mouse motion event
228 public void onMouseMotion(final TMouseEvent mouse
) {
230 if (mouse
.isMouse1()) {
231 // Set the row and column
232 int newLine
= topLine
+ mouse
.getY();
233 int newX
= leftColumn
+ mouse
.getX();
234 if ((newLine
< 0) || (newX
< 0)) {
240 selectionColumn1
= newX
;
241 selectionLine1
= newLine
;
244 selectionColumn0
= newX
;
245 selectionLine0
= newLine
;
246 selectionColumn1
= selectionColumn0
;
247 selectionLine1
= selectionLine0
;
250 if (newLine
> document
.getLineCount() - 1) {
252 document
.setLineNumber(document
.getLineCount() - 1);
254 if (newLine
> document
.getLineCount() - 1) {
255 setCursorY(document
.getLineCount() - 1 - topLine
);
257 setCursorY(mouse
.getY());
261 selectionColumn1
= document
.getCursor();
262 selectionLine1
= document
.getLineNumber();
266 document
.setLineNumber(newLine
);
267 setCursorY(mouse
.getY());
268 if (newX
>= document
.getCurrentLine().getDisplayLength()) {
272 document
.setCursor(newX
);
273 setCursorX(mouse
.getX());
276 selectionColumn1
= document
.getCursor();
277 selectionLine1
= document
.getLineNumber();
283 super.onMouseDown(mouse
);
289 * @param keypress keystroke event
292 public void onKeypress(final TKeypressEvent keypress
) {
293 if (keypress
.getKey().isShift()) {
297 selectionColumn0
= document
.getCursor();
298 selectionLine0
= document
.getLineNumber();
299 selectionColumn1
= selectionColumn0
;
300 selectionLine1
= selectionLine0
;
303 if (keypress
.equals(kbLeft
)
304 || keypress
.equals(kbRight
)
305 || keypress
.equals(kbUp
)
306 || keypress
.equals(kbDown
)
307 || keypress
.equals(kbPgDn
)
308 || keypress
.equals(kbPgUp
)
309 || keypress
.equals(kbHome
)
310 || keypress
.equals(kbEnd
)
312 // Non-shifted navigation keys disable selection.
317 if (keypress
.equals(kbLeft
)
318 || keypress
.equals(kbShiftLeft
)
322 } else if (keypress
.equals(kbRight
)
323 || keypress
.equals(kbShiftRight
)
327 } else if (keypress
.equals(kbAltLeft
)
328 || keypress
.equals(kbCtrlLeft
)
329 || keypress
.equals(kbAltShiftLeft
)
330 || keypress
.equals(kbCtrlShiftLeft
)
332 document
.backwardsWord();
334 } else if (keypress
.equals(kbAltRight
)
335 || keypress
.equals(kbCtrlRight
)
336 || keypress
.equals(kbAltShiftRight
)
337 || keypress
.equals(kbCtrlShiftRight
)
339 document
.forwardsWord();
341 } else if (keypress
.equals(kbUp
)
342 || keypress
.equals(kbShiftUp
)
346 } else if (keypress
.equals(kbDown
)
347 || keypress
.equals(kbShiftDown
)
351 } else if (keypress
.equals(kbPgUp
)
352 || keypress
.equals(kbShiftPgUp
)
354 document
.up(getHeight() - 1);
356 } else if (keypress
.equals(kbPgDn
)
357 || keypress
.equals(kbShiftPgDn
)
359 document
.down(getHeight() - 1);
361 } else if (keypress
.equals(kbHome
)
362 || keypress
.equals(kbShiftHome
)
364 if (document
.home()) {
366 if (leftColumn
< 0) {
371 } else if (keypress
.equals(kbEnd
)
372 || keypress
.equals(kbShiftEnd
)
374 if (document
.end()) {
377 } else if (keypress
.equals(kbCtrlHome
)
378 || keypress
.equals(kbCtrlShiftHome
)
380 document
.setLineNumber(0);
386 } else if (keypress
.equals(kbCtrlEnd
)
387 || keypress
.equals(kbCtrlShiftEnd
)
389 document
.setLineNumber(document
.getLineCount() - 1);
392 } else if (keypress
.equals(kbIns
)) {
393 document
.setOverwrite(!document
.getOverwrite());
394 } else if (keypress
.equals(kbDel
)) {
401 } else if (keypress
.equals(kbBackspace
)
402 || keypress
.equals(kbBackspaceDel
)
407 document
.backspace();
410 } else if (keypress
.equals(kbTab
)) {
412 // Add spaces until we hit modulo 8.
413 for (int i
= document
.getCursor(); (i
+ 1) % 8 != 0; i
++) {
414 document
.addChar(' ');
417 } else if (keypress
.equals(kbEnter
)) {
421 } else if (!keypress
.getKey().isFnKey()
422 && !keypress
.getKey().isAlt()
423 && !keypress
.getKey().isCtrl()
425 // Plain old keystroke, process it
427 document
.addChar(keypress
.getKey().getChar());
430 // Pass other keys (tab etc.) on to TWidget
431 super.onKeypress(keypress
);
435 selectionColumn1
= document
.getCursor();
436 selectionLine1
= document
.getLineNumber();
441 * Method that subclasses can override to handle window/screen resize
444 * @param resize resize event
447 public void onResize(final TResizeEvent resize
) {
448 // Change my width/height, and pull the cursor in as needed.
449 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
450 setWidth(resize
.getWidth());
451 setHeight(resize
.getHeight());
452 // See if the cursor is now outside the window, and if so move
454 if (getCursorX() >= getWidth()) {
455 leftColumn
+= getCursorX() - (getWidth() - 1);
456 setCursorX(getWidth() - 1);
458 if (getCursorY() >= getHeight()) {
459 topLine
+= getCursorY() - (getHeight() - 1);
460 setCursorY(getHeight() - 1);
463 // Let superclass handle it
464 super.onResize(resize
);
469 * Handle posted command events.
471 * @param command command event
474 public void onCommand(final TCommandEvent command
) {
475 if (command
.equals(cmCut
)) {
476 // Copy text to clipboard, and then remove it.
482 if (command
.equals(cmCopy
)) {
483 // Copy text to clipboard.
488 if (command
.equals(cmPaste
)) {
489 // Delete selected text, then paste text from clipboard.
492 String text
= getClipboard().pasteText();
494 for (int i
= 0; i
< text
.length(); ) {
495 int ch
= text
.codePointAt(i
);
496 onKeypress(new TKeypressEvent(false, 0, ch
, false, false,
498 i
+= Character
.charCount(ch
);
504 if (command
.equals(cmClear
)) {
512 // ------------------------------------------------------------------------
513 // TWidget ----------------------------------------------------------------
514 // ------------------------------------------------------------------------
521 CellAttributes selectedColor
= getTheme().getColor("teditor.selected");
523 boolean drawSelection
= true;
525 int startCol
= selectionColumn0
;
526 int startRow
= selectionLine0
;
527 int endCol
= selectionColumn1
;
528 int endRow
= selectionLine1
;
530 if (((selectionColumn1
< selectionColumn0
)
531 && (selectionLine1
== selectionLine0
))
532 || (selectionLine1
< selectionLine0
)
534 // The user selected from bottom-to-top and/or right-to-left.
535 // Reverse the coordinates for the inverted section.
536 startCol
= selectionColumn1
;
537 startRow
= selectionLine1
;
538 endCol
= selectionColumn0
;
539 endRow
= selectionLine0
;
541 if ((startCol
== endCol
) && (startRow
== endRow
)) {
542 drawSelection
= false;
545 for (int i
= 0; i
< getHeight(); i
++) {
547 getScreen().hLineXY(0, i
, getWidth(), ' ', defaultColor
);
549 // Now draw document's line
550 if (topLine
+ i
< document
.getLineCount()) {
551 Line line
= document
.getLine(topLine
+ i
);
553 for (Word word
: line
.getWords()) {
554 // For now, we are cheating: draw outside the left region
555 // if needed and let screen do the clipping.
556 getScreen().putStringXY(x
- leftColumn
, i
, word
.getText(),
558 x
+= word
.getDisplayLength();
559 if (x
- leftColumn
> getWidth()) {
564 // Highlight selected region
565 if (inSelection
&& drawSelection
) {
566 if (startRow
== endRow
) {
567 if (topLine
+ i
== startRow
) {
568 for (x
= startCol
; x
<= endCol
; x
++) {
569 putAttrXY(x
- leftColumn
, i
, selectedColor
);
573 if (topLine
+ i
== startRow
) {
574 for (x
= startCol
; x
< line
.getDisplayLength(); x
++) {
575 putAttrXY(x
- leftColumn
, i
, selectedColor
);
577 } else if (topLine
+ i
== endRow
) {
578 for (x
= 0; x
<= endCol
; x
++) {
579 putAttrXY(x
- leftColumn
, i
, selectedColor
);
581 } else if ((topLine
+ i
>= startRow
)
582 && (topLine
+ i
<= endRow
)
584 for (x
= 0; x
< getWidth(); x
++) {
585 putAttrXY(x
, i
, selectedColor
);
595 // ------------------------------------------------------------------------
596 // TEditorWidget ----------------------------------------------------------
597 // ------------------------------------------------------------------------
600 * Align visible area with document current line.
602 * @param topLineIsTop if true, make the top visible line the document
603 * current line if it was off-screen. If false, make the bottom visible
604 * line the document current line.
606 private void alignTopLine(final boolean topLineIsTop
) {
607 int line
= document
.getLineNumber();
609 if ((line
< topLine
) || (line
> topLine
+ getHeight() - 1)) {
610 // Need to move topLine to bring document back into view.
612 topLine
= line
- (getHeight() - 1);
616 assert (topLine
>= 0);
619 assert (topLine
>= 0);
624 System.err.println("line " + line + " topLine " + topLine);
627 // Document is in view, let's set cursorY
628 assert (line
>= topLine
);
629 setCursorY(line
- topLine
);
634 * Align document current line with visible area.
636 * @param topLineIsTop if true, make the top visible line the document
637 * current line if it was off-screen. If false, make the bottom visible
638 * line the document current line.
640 private void alignDocument(final boolean topLineIsTop
) {
641 int line
= document
.getLineNumber();
642 int cursor
= document
.getCursor();
644 if ((line
< topLine
) || (line
> topLine
+ getHeight() - 1)) {
645 // Need to move document to ensure it fits view.
647 document
.setLineNumber(topLine
);
649 document
.setLineNumber(topLine
+ (getHeight() - 1));
651 if (cursor
< document
.getCurrentLine().getDisplayLength()) {
652 document
.setCursor(cursor
);
657 System.err.println("getLineNumber() " + document.getLineNumber() +
658 " topLine " + topLine);
661 // Document is in view, let's set cursorY
662 setCursorY(document
.getLineNumber() - topLine
);
667 * Align visible cursor with document cursor.
669 private void alignCursor() {
670 int width
= getWidth();
672 int desiredX
= document
.getCursor() - leftColumn
;
674 // We need to push the screen to the left.
675 leftColumn
= document
.getCursor();
676 } else if (desiredX
> width
- 1) {
677 // We need to push the screen to the right.
678 leftColumn
= document
.getCursor() - (width
- 1);
682 System.err.println("document cursor " + document.getCursor() +
683 " leftColumn " + leftColumn);
687 setCursorX(document
.getCursor() - leftColumn
);
691 * Get the number of lines in the underlying Document.
693 * @return the number of lines
695 public int getLineCount() {
696 return document
.getLineCount();
700 * Get the current visible top row number. 1-based.
702 * @return the visible top row number. Row 1 is the first row.
704 public int getVisibleRowNumber() {
709 * Set the current visible row number. 1-based.
711 * @param row the new visible row number. Row 1 is the first row.
713 public void setVisibleRowNumber(final int row
) {
715 if ((row
> 0) && (row
< document
.getLineCount())) {
722 * Get the current editing row number. 1-based.
724 * @return the editing row number. Row 1 is the first row.
726 public int getEditingRowNumber() {
727 return document
.getLineNumber() + 1;
731 * Set the current editing row number. 1-based.
733 * @param row the new editing row number. Row 1 is the first row.
735 public void setEditingRowNumber(final int row
) {
737 if ((row
> 0) && (row
< document
.getLineCount())) {
738 document
.setLineNumber(row
- 1);
744 * Set the current visible column number. 1-based.
746 * @return the visible column number. Column 1 is the first column.
748 public int getVisibleColumnNumber() {
749 return leftColumn
+ 1;
753 * Set the current visible column number. 1-based.
755 * @param column the new visible column number. Column 1 is the first
758 public void setVisibleColumnNumber(final int column
) {
760 if ((column
> 0) && (column
< document
.getLineLengthMax())) {
761 leftColumn
= column
- 1;
767 * Get the current editing column number. 1-based.
769 * @return the editing column number. Column 1 is the first column.
771 public int getEditingColumnNumber() {
772 return document
.getCursor() + 1;
776 * Set the current editing column number. 1-based.
778 * @param column the new editing column number. Column 1 is the first
781 public void setEditingColumnNumber(final int column
) {
782 if ((column
> 0) && (column
< document
.getLineLength())) {
783 document
.setCursor(column
- 1);
789 * Get the maximum possible row number. 1-based.
791 * @return the maximum row number. Row 1 is the first row.
793 public int getMaximumRowNumber() {
794 return document
.getLineCount() + 1;
798 * Get the maximum possible column number. 1-based.
800 * @return the maximum column number. Column 1 is the first column.
802 public int getMaximumColumnNumber() {
803 return document
.getLineLengthMax() + 1;
807 * Get the current editing row plain text. 1-based.
809 * @param row the editing row number. Row 1 is the first row.
810 * @return the plain text of the row
812 public String
getEditingRawLine(final int row
) {
813 Line line
= document
.getLine(row
- 1);
814 return line
.getRawString();
818 * Get the dirty value.
820 * @return true if the buffer is dirty
822 public boolean isDirty() {
823 return document
.isDirty();
827 * Unset the dirty flag.
829 public void setNotDirty() {
830 document
.setNotDirty();
834 * Save contents to file.
836 * @param filename file to save to
837 * @throws IOException if a java.io operation throws
839 public void saveToFilename(final String filename
) throws IOException
{
840 document
.saveToFilename(filename
);
844 * Delete text within the selection bounds.
846 private void deleteSelection() {
852 int startCol
= selectionColumn0
;
853 int startRow
= selectionLine0
;
854 int endCol
= selectionColumn1
;
855 int endRow
= selectionLine1
;
858 System.err.println("INITIAL: " + startRow + " " + startCol + " " +
859 endRow + " " + endCol + " " +
860 document.getLineNumber() + " " + document.getCursor());
863 if (((selectionColumn1
< selectionColumn0
)
864 && (selectionLine1
== selectionLine0
))
865 || (selectionLine1
< selectionLine0
)
867 // The user selected from bottom-to-top and/or right-to-left.
868 // Reverse the coordinates for the inverted section.
869 startCol
= selectionColumn1
;
870 startRow
= selectionLine1
;
871 endCol
= selectionColumn0
;
872 endRow
= selectionLine0
;
874 if (endRow
>= document
.getLineCount()) {
875 // The selection started beyond EOF, trim it to EOF.
876 endRow
= document
.getLineCount() - 1;
877 endCol
= document
.getLine(endRow
).getDisplayLength();
878 } else if (endRow
== document
.getLineCount() - 1) {
879 // The selection started beyond EOF, trim it to EOF.
880 if (endCol
>= document
.getLine(endRow
).getDisplayLength()) {
881 endCol
= document
.getLine(endRow
).getDisplayLength() - 1;
886 System.err.println("FLIP: " + startRow + " " + startCol + " " +
887 endRow + " " + endCol + " " +
888 document.getLineNumber() + " " + document.getCursor());
889 System.err.println(" --END: " + endRow + " " + document.getLineCount() +
890 " " + document.getLine(endRow).getDisplayLength());
893 assert (endRow
< document
.getLineCount());
894 if (endCol
>= document
.getLine(endRow
).getDisplayLength()) {
895 endCol
= document
.getLine(endRow
).getDisplayLength() - 1;
900 if (startCol
>= document
.getLine(startRow
).getDisplayLength()) {
901 startCol
= document
.getLine(startRow
).getDisplayLength() - 1;
907 // Place the cursor on the selection end, and "press backspace" until
908 // the cursor matches the selection start.
910 System.err.println("BEFORE: " + startRow + " " + startCol + " " +
911 endRow + " " + endCol + " " +
912 document.getLineNumber() + " " + document.getCursor());
914 document
.setLineNumber(endRow
);
915 document
.setCursor(endCol
+ 1);
916 while (!((document
.getLineNumber() == startRow
)
917 && (document
.getCursor() == startCol
))
920 System.err.println("DURING: " + startRow + " " + startCol + " " +
921 endRow + " " + endCol + " " +
922 document.getLineNumber() + " " + document.getCursor());
925 document
.backspace();
931 * Copy text within the selection bounds to clipboard.
933 private void copySelection() {
937 getClipboard().copyText(getSelection());
943 * @param startRow the starting row number. 0-based: row 0 is the first
945 * @param startColumn the starting column number. 0-based: column 0 is
947 * @param endRow the ending row number. 0-based: row 0 is the first row.
948 * @param endColumn the ending column number. 0-based: column 0 is the
951 public void setSelection(final int startRow
, final int startColumn
,
952 final int endRow
, final int endColumn
) {
955 selectionLine0
= startRow
;
956 selectionColumn0
= startColumn
;
957 selectionLine1
= endRow
;
958 selectionColumn1
= endColumn
;
962 * Copy text within the selection bounds to a string.
964 * @return the selection as a string, or null if there is no selection
966 public String
getSelection() {
971 int startCol
= selectionColumn0
;
972 int startRow
= selectionLine0
;
973 int endCol
= selectionColumn1
;
974 int endRow
= selectionLine1
;
976 if (((selectionColumn1
< selectionColumn0
)
977 && (selectionLine1
== selectionLine0
))
978 || (selectionLine1
< selectionLine0
)
980 // The user selected from bottom-to-top and/or right-to-left.
981 // Reverse the coordinates for the inverted section.
982 startCol
= selectionColumn1
;
983 startRow
= selectionLine1
;
984 endCol
= selectionColumn0
;
985 endRow
= selectionLine0
;
988 StringBuilder sb
= new StringBuilder();
990 if (endRow
> startRow
) {
992 String line
= document
.getLine(startRow
).getRawString();
994 for (int i
= 0; i
< line
.length(); ) {
995 int ch
= line
.codePointAt(i
);
998 sb
.append(Character
.toChars(ch
));
1000 x
+= StringUtils
.width(ch
);
1001 i
+= Character
.charCount(ch
);
1006 for (int y
= startRow
+ 1; y
< endRow
; y
++) {
1007 sb
.append(document
.getLine(y
).getRawString());
1012 line
= document
.getLine(endRow
).getRawString();
1014 for (int i
= 0; i
< line
.length(); ) {
1015 int ch
= line
.codePointAt(i
);
1021 sb
.append(Character
.toChars(ch
));
1022 x
+= StringUtils
.width(ch
);
1023 i
+= Character
.charCount(ch
);
1026 assert (startRow
== endRow
);
1029 String line
= document
.getLine(startRow
).getRawString();
1031 for (int i
= 0; i
< line
.length(); ) {
1032 int ch
= line
.codePointAt(i
);
1034 if ((x
>= startCol
) && (x
<= endCol
)) {
1035 sb
.append(Character
.toChars(ch
));
1038 x
+= StringUtils
.width(ch
);
1039 i
+= Character
.charCount(ch
);
1042 return sb
.toString();
1046 * Get the selection starting row number.
1048 * @return the starting row number, or -1 if there is no selection.
1049 * 0-based: row 0 is the first row.
1051 public int getSelectionStartRow() {
1056 int startCol
= selectionColumn0
;
1057 int startRow
= selectionLine0
;
1058 int endCol
= selectionColumn1
;
1059 int endRow
= selectionLine1
;
1061 if (((selectionColumn1
< selectionColumn0
)
1062 && (selectionLine1
== selectionLine0
))
1063 || (selectionLine1
< selectionLine0
)
1065 // The user selected from bottom-to-top and/or right-to-left.
1066 // Reverse the coordinates for the inverted section.
1067 startCol
= selectionColumn1
;
1068 startRow
= selectionLine1
;
1069 endCol
= selectionColumn0
;
1070 endRow
= selectionLine0
;
1076 * Get the selection starting column number.
1078 * @return the starting column number, or -1 if there is no selection.
1079 * 0-based: column 0 is the first column.
1081 public int getSelectionStartColumn() {
1086 int startCol
= selectionColumn0
;
1087 int startRow
= selectionLine0
;
1088 int endCol
= selectionColumn1
;
1089 int endRow
= selectionLine1
;
1091 if (((selectionColumn1
< selectionColumn0
)
1092 && (selectionLine1
== selectionLine0
))
1093 || (selectionLine1
< selectionLine0
)
1095 // The user selected from bottom-to-top and/or right-to-left.
1096 // Reverse the coordinates for the inverted section.
1097 startCol
= selectionColumn1
;
1098 startRow
= selectionLine1
;
1099 endCol
= selectionColumn0
;
1100 endRow
= selectionLine0
;
1106 * Get the selection ending row number.
1108 * @return the ending row number, or -1 if there is no selection.
1109 * 0-based: row 0 is the first row.
1111 public int getSelectionEndRow() {
1116 int startCol
= selectionColumn0
;
1117 int startRow
= selectionLine0
;
1118 int endCol
= selectionColumn1
;
1119 int endRow
= selectionLine1
;
1121 if (((selectionColumn1
< selectionColumn0
)
1122 && (selectionLine1
== selectionLine0
))
1123 || (selectionLine1
< selectionLine0
)
1125 // The user selected from bottom-to-top and/or right-to-left.
1126 // Reverse the coordinates for the inverted section.
1127 startCol
= selectionColumn1
;
1128 startRow
= selectionLine1
;
1129 endCol
= selectionColumn0
;
1130 endRow
= selectionLine0
;
1136 * Get the selection ending column number.
1138 * @return the ending column number, or -1 if there is no selection.
1139 * 0-based: column 0 is the first column.
1141 public int getSelectionEndColumn() {
1146 int startCol
= selectionColumn0
;
1147 int startRow
= selectionLine0
;
1148 int endCol
= selectionColumn1
;
1149 int endRow
= selectionLine1
;
1151 if (((selectionColumn1
< selectionColumn0
)
1152 && (selectionLine1
== selectionLine0
))
1153 || (selectionLine1
< selectionLine0
)
1155 // The user selected from bottom-to-top and/or right-to-left.
1156 // Reverse the coordinates for the inverted section.
1157 startCol
= selectionColumn1
;
1158 startRow
= selectionLine1
;
1159 endCol
= selectionColumn0
;
1160 endRow
= selectionLine0
;
1166 * Unset the selection.
1168 public void unsetSelection() {
1169 inSelection
= false;
1173 * Replace whatever is being selected with new text. If not in
1174 * selection, nothing is replaced.
1176 * @param text the new replacement text
1178 public void replaceSelection(final String text
) {
1183 // Delete selected text, then paste text from clipboard.
1186 for (int i
= 0; i
< text
.length(); ) {
1187 int ch
= text
.codePointAt(i
);
1188 onKeypress(new TKeypressEvent(false, 0, ch
, false, false,
1190 i
+= Character
.charCount(ch
);
1195 * Check if selection is available.
1197 * @return true if a selection has been made
1199 public boolean hasSelection() {
1204 * Get the entire contents of the editor as one string.
1206 * @return the editor contents
1208 public String
getText() {
1209 return document
.getText();
1213 * Set the entire contents of the editor from one string.
1215 * @param text the new contents
1217 public void setText(final String text
) {
1218 document
= new Document(text
, defaultColor
);
1224 // ------------------------------------------------------------------------
1225 // EditMenuUser -----------------------------------------------------------
1226 // ------------------------------------------------------------------------
1229 * Check if the cut menu item should be enabled.
1231 * @return true if the cut menu item should be enabled
1233 public boolean isEditMenuCut() {
1238 * Check if the copy menu item should be enabled.
1240 * @return true if the copy menu item should be enabled
1242 public boolean isEditMenuCopy() {
1247 * Check if the paste menu item should be enabled.
1249 * @return true if the paste menu item should be enabled
1251 public boolean isEditMenuPaste() {
1256 * Check if the clear menu item should be enabled.
1258 * @return true if the clear menu item should be enabled
1260 public boolean isEditMenuClear() {