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.
315 if ((selectionColumn0
== selectionColumn1
)
316 && (selectionLine0
== selectionLine1
)
318 // The user clicked a spot and started typing.
323 if (keypress
.equals(kbLeft
)
324 || keypress
.equals(kbShiftLeft
)
328 } else if (keypress
.equals(kbRight
)
329 || keypress
.equals(kbShiftRight
)
333 } else if (keypress
.equals(kbAltLeft
)
334 || keypress
.equals(kbCtrlLeft
)
335 || keypress
.equals(kbAltShiftLeft
)
336 || keypress
.equals(kbCtrlShiftLeft
)
338 document
.backwardsWord();
340 } else if (keypress
.equals(kbAltRight
)
341 || keypress
.equals(kbCtrlRight
)
342 || keypress
.equals(kbAltShiftRight
)
343 || keypress
.equals(kbCtrlShiftRight
)
345 document
.forwardsWord();
347 } else if (keypress
.equals(kbUp
)
348 || keypress
.equals(kbShiftUp
)
352 } else if (keypress
.equals(kbDown
)
353 || keypress
.equals(kbShiftDown
)
357 } else if (keypress
.equals(kbPgUp
)
358 || keypress
.equals(kbShiftPgUp
)
360 document
.up(getHeight() - 1);
362 } else if (keypress
.equals(kbPgDn
)
363 || keypress
.equals(kbShiftPgDn
)
365 document
.down(getHeight() - 1);
367 } else if (keypress
.equals(kbHome
)
368 || keypress
.equals(kbShiftHome
)
370 if (document
.home()) {
372 if (leftColumn
< 0) {
377 } else if (keypress
.equals(kbEnd
)
378 || keypress
.equals(kbShiftEnd
)
380 if (document
.end()) {
383 } else if (keypress
.equals(kbCtrlHome
)
384 || keypress
.equals(kbCtrlShiftHome
)
386 document
.setLineNumber(0);
392 } else if (keypress
.equals(kbCtrlEnd
)
393 || keypress
.equals(kbCtrlShiftEnd
)
395 document
.setLineNumber(document
.getLineCount() - 1);
398 } else if (keypress
.equals(kbIns
)) {
399 document
.setOverwrite(!document
.getOverwrite());
400 } else if (keypress
.equals(kbDel
)) {
407 } else if (keypress
.equals(kbBackspace
)
408 || keypress
.equals(kbBackspaceDel
)
413 document
.backspace();
416 } else if (keypress
.equals(kbTab
)) {
418 // Add spaces until we hit modulo 8.
419 for (int i
= document
.getCursor(); (i
+ 1) % 8 != 0; i
++) {
420 document
.addChar(' ');
423 } else if (keypress
.equals(kbEnter
)) {
427 } else if (!keypress
.getKey().isFnKey()
428 && !keypress
.getKey().isAlt()
429 && !keypress
.getKey().isCtrl()
431 // Plain old keystroke, process it
433 document
.addChar(keypress
.getKey().getChar());
436 // Pass other keys (tab etc.) on to TWidget
437 super.onKeypress(keypress
);
441 selectionColumn1
= document
.getCursor();
442 selectionLine1
= document
.getLineNumber();
447 * Method that subclasses can override to handle window/screen resize
450 * @param resize resize event
453 public void onResize(final TResizeEvent resize
) {
454 // Change my width/height, and pull the cursor in as needed.
455 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
456 setWidth(resize
.getWidth());
457 setHeight(resize
.getHeight());
458 // See if the cursor is now outside the window, and if so move
460 if (getCursorX() >= getWidth()) {
461 leftColumn
+= getCursorX() - (getWidth() - 1);
462 setCursorX(getWidth() - 1);
464 if (getCursorY() >= getHeight()) {
465 topLine
+= getCursorY() - (getHeight() - 1);
466 setCursorY(getHeight() - 1);
469 // Let superclass handle it
470 super.onResize(resize
);
475 * Handle posted command events.
477 * @param command command event
480 public void onCommand(final TCommandEvent command
) {
481 if (command
.equals(cmCut
)) {
482 // Copy text to clipboard, and then remove it.
488 if (command
.equals(cmCopy
)) {
489 // Copy text to clipboard.
494 if (command
.equals(cmPaste
)) {
495 // Delete selected text, then paste text from clipboard.
498 String text
= getClipboard().pasteText();
500 for (int i
= 0; i
< text
.length(); ) {
501 int ch
= text
.codePointAt(i
);
502 onKeypress(new TKeypressEvent(false, 0, ch
, false, false,
504 i
+= Character
.charCount(ch
);
510 if (command
.equals(cmClear
)) {
518 // ------------------------------------------------------------------------
519 // TWidget ----------------------------------------------------------------
520 // ------------------------------------------------------------------------
527 CellAttributes selectedColor
= getTheme().getColor("teditor.selected");
529 boolean drawSelection
= true;
531 int startCol
= selectionColumn0
;
532 int startRow
= selectionLine0
;
533 int endCol
= selectionColumn1
;
534 int endRow
= selectionLine1
;
536 if (((selectionColumn1
< selectionColumn0
)
537 && (selectionLine1
== selectionLine0
))
538 || (selectionLine1
< selectionLine0
)
540 // The user selected from bottom-to-top and/or right-to-left.
541 // Reverse the coordinates for the inverted section.
542 startCol
= selectionColumn1
;
543 startRow
= selectionLine1
;
544 endCol
= selectionColumn0
;
545 endRow
= selectionLine0
;
547 if ((startCol
== endCol
) && (startRow
== endRow
)) {
548 drawSelection
= false;
551 for (int i
= 0; i
< getHeight(); i
++) {
553 getScreen().hLineXY(0, i
, getWidth(), ' ', defaultColor
);
555 // Now draw document's line
556 if (topLine
+ i
< document
.getLineCount()) {
557 Line line
= document
.getLine(topLine
+ i
);
559 for (Word word
: line
.getWords()) {
560 // For now, we are cheating: draw outside the left region
561 // if needed and let screen do the clipping.
562 getScreen().putStringXY(x
- leftColumn
, i
, word
.getText(),
564 x
+= word
.getDisplayLength();
565 if (x
- leftColumn
> getWidth()) {
570 // Highlight selected region
571 if (inSelection
&& drawSelection
) {
572 if (startRow
== endRow
) {
573 if (topLine
+ i
== startRow
) {
574 for (x
= startCol
; x
<= endCol
; x
++) {
575 putAttrXY(x
- leftColumn
, i
, selectedColor
);
579 if (topLine
+ i
== startRow
) {
580 for (x
= startCol
; x
< line
.getDisplayLength(); x
++) {
581 putAttrXY(x
- leftColumn
, i
, selectedColor
);
583 } else if (topLine
+ i
== endRow
) {
584 for (x
= 0; x
<= endCol
; x
++) {
585 putAttrXY(x
- leftColumn
, i
, selectedColor
);
587 } else if ((topLine
+ i
>= startRow
)
588 && (topLine
+ i
<= endRow
)
590 for (x
= 0; x
< getWidth(); x
++) {
591 putAttrXY(x
, i
, selectedColor
);
601 // ------------------------------------------------------------------------
602 // TEditorWidget ----------------------------------------------------------
603 // ------------------------------------------------------------------------
606 * Align visible area with document current line.
608 * @param topLineIsTop if true, make the top visible line the document
609 * current line if it was off-screen. If false, make the bottom visible
610 * line the document current line.
612 private void alignTopLine(final boolean topLineIsTop
) {
613 int line
= document
.getLineNumber();
615 if ((line
< topLine
) || (line
> topLine
+ getHeight() - 1)) {
616 // Need to move topLine to bring document back into view.
618 topLine
= line
- (getHeight() - 1);
622 assert (topLine
>= 0);
625 assert (topLine
>= 0);
630 System.err.println("line " + line + " topLine " + topLine);
633 // Document is in view, let's set cursorY
634 assert (line
>= topLine
);
635 setCursorY(line
- topLine
);
640 * Align document current line with visible area.
642 * @param topLineIsTop if true, make the top visible line the document
643 * current line if it was off-screen. If false, make the bottom visible
644 * line the document current line.
646 private void alignDocument(final boolean topLineIsTop
) {
647 int line
= document
.getLineNumber();
648 int cursor
= document
.getCursor();
650 if ((line
< topLine
) || (line
> topLine
+ getHeight() - 1)) {
651 // Need to move document to ensure it fits view.
653 document
.setLineNumber(topLine
);
655 document
.setLineNumber(topLine
+ (getHeight() - 1));
657 if (cursor
< document
.getCurrentLine().getDisplayLength()) {
658 document
.setCursor(cursor
);
663 System.err.println("getLineNumber() " + document.getLineNumber() +
664 " topLine " + topLine);
667 // Document is in view, let's set cursorY
668 setCursorY(document
.getLineNumber() - topLine
);
673 * Align visible cursor with document cursor.
675 private void alignCursor() {
676 int width
= getWidth();
678 int desiredX
= document
.getCursor() - leftColumn
;
680 // We need to push the screen to the left.
681 leftColumn
= document
.getCursor();
682 } else if (desiredX
> width
- 1) {
683 // We need to push the screen to the right.
684 leftColumn
= document
.getCursor() - (width
- 1);
688 System.err.println("document cursor " + document.getCursor() +
689 " leftColumn " + leftColumn);
693 setCursorX(document
.getCursor() - leftColumn
);
697 * Get the number of lines in the underlying Document.
699 * @return the number of lines
701 public int getLineCount() {
702 return document
.getLineCount();
706 * Get the current visible top row number. 1-based.
708 * @return the visible top row number. Row 1 is the first row.
710 public int getVisibleRowNumber() {
715 * Set the current visible row number. 1-based.
717 * @param row the new visible row number. Row 1 is the first row.
719 public void setVisibleRowNumber(final int row
) {
721 if ((row
> 0) && (row
< document
.getLineCount())) {
728 * Get the current editing row number. 1-based.
730 * @return the editing row number. Row 1 is the first row.
732 public int getEditingRowNumber() {
733 return document
.getLineNumber() + 1;
737 * Set the current editing row number. 1-based.
739 * @param row the new editing row number. Row 1 is the first row.
741 public void setEditingRowNumber(final int row
) {
743 if ((row
> 0) && (row
< document
.getLineCount())) {
744 document
.setLineNumber(row
- 1);
750 * Set the current visible column number. 1-based.
752 * @return the visible column number. Column 1 is the first column.
754 public int getVisibleColumnNumber() {
755 return leftColumn
+ 1;
759 * Set the current visible column number. 1-based.
761 * @param column the new visible column number. Column 1 is the first
764 public void setVisibleColumnNumber(final int column
) {
766 if ((column
> 0) && (column
< document
.getLineLengthMax())) {
767 leftColumn
= column
- 1;
773 * Get the current editing column number. 1-based.
775 * @return the editing column number. Column 1 is the first column.
777 public int getEditingColumnNumber() {
778 return document
.getCursor() + 1;
782 * Set the current editing column number. 1-based.
784 * @param column the new editing column number. Column 1 is the first
787 public void setEditingColumnNumber(final int column
) {
788 if ((column
> 0) && (column
< document
.getLineLength())) {
789 document
.setCursor(column
- 1);
795 * Get the maximum possible row number. 1-based.
797 * @return the maximum row number. Row 1 is the first row.
799 public int getMaximumRowNumber() {
800 return document
.getLineCount() + 1;
804 * Get the maximum possible column number. 1-based.
806 * @return the maximum column number. Column 1 is the first column.
808 public int getMaximumColumnNumber() {
809 return document
.getLineLengthMax() + 1;
813 * Get the current editing row plain text. 1-based.
815 * @param row the editing row number. Row 1 is the first row.
816 * @return the plain text of the row
818 public String
getEditingRawLine(final int row
) {
819 Line line
= document
.getLine(row
- 1);
820 return line
.getRawString();
824 * Get the dirty value.
826 * @return true if the buffer is dirty
828 public boolean isDirty() {
829 return document
.isDirty();
833 * Unset the dirty flag.
835 public void setNotDirty() {
836 document
.setNotDirty();
840 * Save contents to file.
842 * @param filename file to save to
843 * @throws IOException if a java.io operation throws
845 public void saveToFilename(final String filename
) throws IOException
{
846 document
.saveToFilename(filename
);
850 * Delete text within the selection bounds.
852 private void deleteSelection() {
858 int startCol
= selectionColumn0
;
859 int startRow
= selectionLine0
;
860 int endCol
= selectionColumn1
;
861 int endRow
= selectionLine1
;
864 System.err.println("INITIAL: " + startRow + " " + startCol + " " +
865 endRow + " " + endCol + " " +
866 document.getLineNumber() + " " + document.getCursor());
869 if (((selectionColumn1
< selectionColumn0
)
870 && (selectionLine1
== selectionLine0
))
871 || (selectionLine1
< selectionLine0
)
873 // The user selected from bottom-to-top and/or right-to-left.
874 // Reverse the coordinates for the inverted section.
875 startCol
= selectionColumn1
;
876 startRow
= selectionLine1
;
877 endCol
= selectionColumn0
;
878 endRow
= selectionLine0
;
880 if (endRow
>= document
.getLineCount()) {
881 // The selection started beyond EOF, trim it to EOF.
882 endRow
= document
.getLineCount() - 1;
883 endCol
= document
.getLine(endRow
).getDisplayLength();
884 } else if (endRow
== document
.getLineCount() - 1) {
885 // The selection started beyond EOF, trim it to EOF.
886 if (endCol
>= document
.getLine(endRow
).getDisplayLength()) {
887 endCol
= document
.getLine(endRow
).getDisplayLength() - 1;
892 System.err.println("FLIP: " + startRow + " " + startCol + " " +
893 endRow + " " + endCol + " " +
894 document.getLineNumber() + " " + document.getCursor());
895 System.err.println(" --END: " + endRow + " " + document.getLineCount() +
896 " " + document.getLine(endRow).getDisplayLength());
899 assert (endRow
< document
.getLineCount());
900 if (endCol
>= document
.getLine(endRow
).getDisplayLength()) {
901 endCol
= document
.getLine(endRow
).getDisplayLength() - 1;
906 if (startCol
>= document
.getLine(startRow
).getDisplayLength()) {
907 startCol
= document
.getLine(startRow
).getDisplayLength() - 1;
913 // Place the cursor on the selection end, and "press backspace" until
914 // the cursor matches the selection start.
916 System.err.println("BEFORE: " + startRow + " " + startCol + " " +
917 endRow + " " + endCol + " " +
918 document.getLineNumber() + " " + document.getCursor());
920 document
.setLineNumber(endRow
);
921 document
.setCursor(endCol
+ 1);
922 while (!((document
.getLineNumber() == startRow
)
923 && (document
.getCursor() == startCol
))
926 System.err.println("DURING: " + startRow + " " + startCol + " " +
927 endRow + " " + endCol + " " +
928 document.getLineNumber() + " " + document.getCursor());
931 document
.backspace();
937 * Copy text within the selection bounds to clipboard.
939 private void copySelection() {
943 getClipboard().copyText(getSelection());
949 * @param startRow the starting row number. 0-based: row 0 is the first
951 * @param startColumn the starting column number. 0-based: column 0 is
953 * @param endRow the ending row number. 0-based: row 0 is the first row.
954 * @param endColumn the ending column number. 0-based: column 0 is the
957 public void setSelection(final int startRow
, final int startColumn
,
958 final int endRow
, final int endColumn
) {
961 selectionLine0
= startRow
;
962 selectionColumn0
= startColumn
;
963 selectionLine1
= endRow
;
964 selectionColumn1
= endColumn
;
968 * Copy text within the selection bounds to a string.
970 * @return the selection as a string, or null if there is no selection
972 public String
getSelection() {
977 int startCol
= selectionColumn0
;
978 int startRow
= selectionLine0
;
979 int endCol
= selectionColumn1
;
980 int endRow
= selectionLine1
;
982 if (((selectionColumn1
< selectionColumn0
)
983 && (selectionLine1
== selectionLine0
))
984 || (selectionLine1
< selectionLine0
)
986 // The user selected from bottom-to-top and/or right-to-left.
987 // Reverse the coordinates for the inverted section.
988 startCol
= selectionColumn1
;
989 startRow
= selectionLine1
;
990 endCol
= selectionColumn0
;
991 endRow
= selectionLine0
;
994 StringBuilder sb
= new StringBuilder();
996 if (endRow
> startRow
) {
998 String line
= document
.getLine(startRow
).getRawString();
1000 for (int i
= 0; i
< line
.length(); ) {
1001 int ch
= line
.codePointAt(i
);
1003 if (x
>= startCol
) {
1004 sb
.append(Character
.toChars(ch
));
1006 x
+= StringUtils
.width(ch
);
1007 i
+= Character
.charCount(ch
);
1012 for (int y
= startRow
+ 1; y
< endRow
; y
++) {
1013 sb
.append(document
.getLine(y
).getRawString());
1018 line
= document
.getLine(endRow
).getRawString();
1020 for (int i
= 0; i
< line
.length(); ) {
1021 int ch
= line
.codePointAt(i
);
1027 sb
.append(Character
.toChars(ch
));
1028 x
+= StringUtils
.width(ch
);
1029 i
+= Character
.charCount(ch
);
1032 assert (startRow
== endRow
);
1035 String line
= document
.getLine(startRow
).getRawString();
1037 for (int i
= 0; i
< line
.length(); ) {
1038 int ch
= line
.codePointAt(i
);
1040 if ((x
>= startCol
) && (x
<= endCol
)) {
1041 sb
.append(Character
.toChars(ch
));
1044 x
+= StringUtils
.width(ch
);
1045 i
+= Character
.charCount(ch
);
1048 return sb
.toString();
1052 * Get the selection starting row number.
1054 * @return the starting row number, or -1 if there is no selection.
1055 * 0-based: row 0 is the first row.
1057 public int getSelectionStartRow() {
1062 int startCol
= selectionColumn0
;
1063 int startRow
= selectionLine0
;
1064 int endCol
= selectionColumn1
;
1065 int endRow
= selectionLine1
;
1067 if (((selectionColumn1
< selectionColumn0
)
1068 && (selectionLine1
== selectionLine0
))
1069 || (selectionLine1
< selectionLine0
)
1071 // The user selected from bottom-to-top and/or right-to-left.
1072 // Reverse the coordinates for the inverted section.
1073 startCol
= selectionColumn1
;
1074 startRow
= selectionLine1
;
1075 endCol
= selectionColumn0
;
1076 endRow
= selectionLine0
;
1082 * Get the selection starting column number.
1084 * @return the starting column number, or -1 if there is no selection.
1085 * 0-based: column 0 is the first column.
1087 public int getSelectionStartColumn() {
1092 int startCol
= selectionColumn0
;
1093 int startRow
= selectionLine0
;
1094 int endCol
= selectionColumn1
;
1095 int endRow
= selectionLine1
;
1097 if (((selectionColumn1
< selectionColumn0
)
1098 && (selectionLine1
== selectionLine0
))
1099 || (selectionLine1
< selectionLine0
)
1101 // The user selected from bottom-to-top and/or right-to-left.
1102 // Reverse the coordinates for the inverted section.
1103 startCol
= selectionColumn1
;
1104 startRow
= selectionLine1
;
1105 endCol
= selectionColumn0
;
1106 endRow
= selectionLine0
;
1112 * Get the selection ending row number.
1114 * @return the ending row number, or -1 if there is no selection.
1115 * 0-based: row 0 is the first row.
1117 public int getSelectionEndRow() {
1122 int startCol
= selectionColumn0
;
1123 int startRow
= selectionLine0
;
1124 int endCol
= selectionColumn1
;
1125 int endRow
= selectionLine1
;
1127 if (((selectionColumn1
< selectionColumn0
)
1128 && (selectionLine1
== selectionLine0
))
1129 || (selectionLine1
< selectionLine0
)
1131 // The user selected from bottom-to-top and/or right-to-left.
1132 // Reverse the coordinates for the inverted section.
1133 startCol
= selectionColumn1
;
1134 startRow
= selectionLine1
;
1135 endCol
= selectionColumn0
;
1136 endRow
= selectionLine0
;
1142 * Get the selection ending column number.
1144 * @return the ending column number, or -1 if there is no selection.
1145 * 0-based: column 0 is the first column.
1147 public int getSelectionEndColumn() {
1152 int startCol
= selectionColumn0
;
1153 int startRow
= selectionLine0
;
1154 int endCol
= selectionColumn1
;
1155 int endRow
= selectionLine1
;
1157 if (((selectionColumn1
< selectionColumn0
)
1158 && (selectionLine1
== selectionLine0
))
1159 || (selectionLine1
< selectionLine0
)
1161 // The user selected from bottom-to-top and/or right-to-left.
1162 // Reverse the coordinates for the inverted section.
1163 startCol
= selectionColumn1
;
1164 startRow
= selectionLine1
;
1165 endCol
= selectionColumn0
;
1166 endRow
= selectionLine0
;
1172 * Unset the selection.
1174 public void unsetSelection() {
1175 inSelection
= false;
1179 * Replace whatever is being selected with new text. If not in
1180 * selection, nothing is replaced.
1182 * @param text the new replacement text
1184 public void replaceSelection(final String text
) {
1189 // Delete selected text, then paste text from clipboard.
1192 for (int i
= 0; i
< text
.length(); ) {
1193 int ch
= text
.codePointAt(i
);
1194 onKeypress(new TKeypressEvent(false, 0, ch
, false, false,
1196 i
+= Character
.charCount(ch
);
1201 * Check if selection is available.
1203 * @return true if a selection has been made
1205 public boolean hasSelection() {
1210 * Get the entire contents of the editor as one string.
1212 * @return the editor contents
1214 public String
getText() {
1215 return document
.getText();
1219 * Set the entire contents of the editor from one string.
1221 * @param text the new contents
1223 public void setText(final String text
) {
1224 document
= new Document(text
, defaultColor
);
1230 // ------------------------------------------------------------------------
1231 // EditMenuUser -----------------------------------------------------------
1232 // ------------------------------------------------------------------------
1235 * Check if the cut menu item should be enabled.
1237 * @return true if the cut menu item should be enabled
1239 public boolean isEditMenuCut() {
1244 * Check if the copy menu item should be enabled.
1246 * @return true if the copy menu item should be enabled
1248 public boolean isEditMenuCopy() {
1253 * Check if the paste menu item should be enabled.
1255 * @return true if the paste menu item should be enabled
1257 public boolean isEditMenuPaste() {
1262 * Check if the clear menu item should be enabled.
1264 * @return true if the clear menu item should be enabled
1266 public boolean isEditMenuClear() {