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()) {
168 selectionLine0
= topLine
+ mouse
.getY();
169 selectionColumn0
= leftColumn
+ mouse
.getX();
170 selectionColumn0
= Math
.max(0, Math
.min(selectionColumn0
,
171 document
.getLine(selectionLine0
).getDisplayLength() - 1));
172 selectionColumn1
= selectionColumn0
;
173 selectionLine1
= selectionLine0
;
175 // Set the row and column
176 int newLine
= topLine
+ mouse
.getY();
177 int newX
= leftColumn
+ mouse
.getX();
178 if (newLine
> document
.getLineCount() - 1) {
180 document
.setLineNumber(document
.getLineCount() - 1);
182 if (newLine
> document
.getLineCount() - 1) {
183 setCursorY(document
.getLineCount() - 1 - topLine
);
185 setCursorY(mouse
.getY());
189 selectionColumn1
= document
.getCursor();
190 selectionLine1
= document
.getLineNumber();
195 document
.setLineNumber(newLine
);
196 setCursorY(mouse
.getY());
197 if (newX
>= document
.getCurrentLine().getDisplayLength()) {
201 document
.setCursor(newX
);
202 setCursorX(mouse
.getX());
205 selectionColumn1
= document
.getCursor();
206 selectionLine1
= document
.getLineNumber();
214 super.onMouseDown(mouse
);
218 * Handle mouse motion events.
220 * @param mouse mouse motion event
223 public void onMouseMotion(final TMouseEvent mouse
) {
225 if (mouse
.isMouse1()) {
226 // Set the row and column
227 int newLine
= topLine
+ mouse
.getY();
228 int newX
= leftColumn
+ mouse
.getX();
229 if ((newLine
< 0) || (newX
< 0)) {
235 selectionColumn1
= newX
;
236 selectionLine1
= newLine
;
239 selectionColumn0
= newX
;
240 selectionLine0
= newLine
;
241 selectionColumn1
= selectionColumn0
;
242 selectionLine1
= selectionLine0
;
245 if (newLine
> document
.getLineCount() - 1) {
247 document
.setLineNumber(document
.getLineCount() - 1);
249 if (newLine
> document
.getLineCount() - 1) {
250 setCursorY(document
.getLineCount() - 1 - topLine
);
252 setCursorY(mouse
.getY());
256 selectionColumn1
= document
.getCursor();
257 selectionLine1
= document
.getLineNumber();
261 document
.setLineNumber(newLine
);
262 setCursorY(mouse
.getY());
263 if (newX
>= document
.getCurrentLine().getDisplayLength()) {
267 document
.setCursor(newX
);
268 setCursorX(mouse
.getX());
271 selectionColumn1
= document
.getCursor();
272 selectionLine1
= document
.getLineNumber();
278 super.onMouseDown(mouse
);
284 * @param keypress keystroke event
287 public void onKeypress(final TKeypressEvent keypress
) {
288 if (keypress
.getKey().isShift()) {
292 selectionColumn0
= document
.getCursor();
293 selectionLine0
= document
.getLineNumber();
294 selectionColumn1
= selectionColumn0
;
295 selectionLine1
= selectionLine0
;
298 if (keypress
.equals(kbLeft
)
299 || keypress
.equals(kbRight
)
300 || keypress
.equals(kbUp
)
301 || keypress
.equals(kbDown
)
302 || keypress
.equals(kbPgDn
)
303 || keypress
.equals(kbPgUp
)
304 || keypress
.equals(kbHome
)
305 || keypress
.equals(kbEnd
)
307 // Non-shifted navigation keys disable selection.
312 if (keypress
.equals(kbLeft
)
313 || keypress
.equals(kbShiftLeft
)
317 } else if (keypress
.equals(kbRight
)
318 || keypress
.equals(kbShiftRight
)
322 } else if (keypress
.equals(kbAltLeft
)
323 || keypress
.equals(kbCtrlLeft
)
324 || keypress
.equals(kbAltShiftLeft
)
325 || keypress
.equals(kbCtrlShiftLeft
)
327 document
.backwardsWord();
329 } else if (keypress
.equals(kbAltRight
)
330 || keypress
.equals(kbCtrlRight
)
331 || keypress
.equals(kbAltShiftRight
)
332 || keypress
.equals(kbCtrlShiftRight
)
334 document
.forwardsWord();
336 } else if (keypress
.equals(kbUp
)
337 || keypress
.equals(kbShiftUp
)
341 } else if (keypress
.equals(kbDown
)
342 || keypress
.equals(kbShiftDown
)
346 } else if (keypress
.equals(kbPgUp
)
347 || keypress
.equals(kbShiftPgUp
)
349 document
.up(getHeight() - 1);
351 } else if (keypress
.equals(kbPgDn
)
352 || keypress
.equals(kbShiftPgDn
)
354 document
.down(getHeight() - 1);
356 } else if (keypress
.equals(kbHome
)
357 || keypress
.equals(kbShiftHome
)
359 if (document
.home()) {
361 if (leftColumn
< 0) {
366 } else if (keypress
.equals(kbEnd
)
367 || keypress
.equals(kbShiftEnd
)
369 if (document
.end()) {
372 } else if (keypress
.equals(kbCtrlHome
)
373 || keypress
.equals(kbCtrlShiftHome
)
375 document
.setLineNumber(0);
381 } else if (keypress
.equals(kbCtrlEnd
)
382 || keypress
.equals(kbCtrlShiftEnd
)
384 document
.setLineNumber(document
.getLineCount() - 1);
387 } else if (keypress
.equals(kbIns
)) {
388 document
.setOverwrite(!document
.getOverwrite());
389 } else if (keypress
.equals(kbDel
)) {
396 } else if (keypress
.equals(kbBackspace
)
397 || keypress
.equals(kbBackspaceDel
)
402 document
.backspace();
405 } else if (keypress
.equals(kbTab
)) {
407 // Add spaces until we hit modulo 8.
408 for (int i
= document
.getCursor(); (i
+ 1) % 8 != 0; i
++) {
409 document
.addChar(' ');
412 } else if (keypress
.equals(kbEnter
)) {
416 } else if (!keypress
.getKey().isFnKey()
417 && !keypress
.getKey().isAlt()
418 && !keypress
.getKey().isCtrl()
420 // Plain old keystroke, process it
422 document
.addChar(keypress
.getKey().getChar());
425 // Pass other keys (tab etc.) on to TWidget
426 super.onKeypress(keypress
);
430 selectionColumn1
= document
.getCursor();
431 selectionLine1
= document
.getLineNumber();
436 * Method that subclasses can override to handle window/screen resize
439 * @param resize resize event
442 public void onResize(final TResizeEvent resize
) {
443 // Change my width/height, and pull the cursor in as needed.
444 if (resize
.getType() == TResizeEvent
.Type
.WIDGET
) {
445 setWidth(resize
.getWidth());
446 setHeight(resize
.getHeight());
447 // See if the cursor is now outside the window, and if so move
449 if (getCursorX() >= getWidth()) {
450 leftColumn
+= getCursorX() - (getWidth() - 1);
451 setCursorX(getWidth() - 1);
453 if (getCursorY() >= getHeight()) {
454 topLine
+= getCursorY() - (getHeight() - 1);
455 setCursorY(getHeight() - 1);
458 // Let superclass handle it
459 super.onResize(resize
);
464 * Handle posted command events.
466 * @param command command event
469 public void onCommand(final TCommandEvent command
) {
470 if (command
.equals(cmCut
)) {
471 // Copy text to clipboard, and then remove it.
477 if (command
.equals(cmCopy
)) {
478 // Copy text to clipboard.
483 if (command
.equals(cmPaste
)) {
484 // Delete selected text, then paste text from clipboard.
487 String text
= getClipboard().pasteText();
489 for (int i
= 0; i
< text
.length(); ) {
490 int ch
= text
.codePointAt(i
);
491 onKeypress(new TKeypressEvent(false, 0, ch
, false, false,
493 i
+= Character
.charCount(ch
);
499 if (command
.equals(cmClear
)) {
507 // ------------------------------------------------------------------------
508 // TWidget ----------------------------------------------------------------
509 // ------------------------------------------------------------------------
516 CellAttributes selectedColor
= getTheme().getColor("teditor.selected");
518 boolean drawSelection
= true;
520 int startCol
= selectionColumn0
;
521 int startRow
= selectionLine0
;
522 int endCol
= selectionColumn1
;
523 int endRow
= selectionLine1
;
525 if (((selectionColumn1
< selectionColumn0
)
526 && (selectionLine1
== selectionLine0
))
527 || (selectionLine1
< selectionLine0
)
529 // The user selected from bottom-to-top and/or right-to-left.
530 // Reverse the coordinates for the inverted section.
531 startCol
= selectionColumn1
;
532 startRow
= selectionLine1
;
533 endCol
= selectionColumn0
;
534 endRow
= selectionLine0
;
536 if ((startCol
== endCol
) && (startRow
== endRow
)) {
537 drawSelection
= false;
540 for (int i
= 0; i
< getHeight(); i
++) {
542 getScreen().hLineXY(0, i
, getWidth(), ' ', defaultColor
);
544 // Now draw document's line
545 if (topLine
+ i
< document
.getLineCount()) {
546 Line line
= document
.getLine(topLine
+ i
);
548 for (Word word
: line
.getWords()) {
549 // For now, we are cheating: draw outside the left region
550 // if needed and let screen do the clipping.
551 getScreen().putStringXY(x
- leftColumn
, i
, word
.getText(),
553 x
+= word
.getDisplayLength();
554 if (x
- leftColumn
> getWidth()) {
559 // Highlight selected region
560 if (inSelection
&& drawSelection
) {
561 if (startRow
== endRow
) {
562 if (topLine
+ i
== startRow
) {
563 for (x
= startCol
; x
<= endCol
; x
++) {
564 putAttrXY(x
- leftColumn
, i
, selectedColor
);
568 if (topLine
+ i
== startRow
) {
569 for (x
= startCol
; x
< line
.getDisplayLength(); x
++) {
570 putAttrXY(x
- leftColumn
, i
, selectedColor
);
572 } else if (topLine
+ i
== endRow
) {
573 for (x
= 0; x
<= endCol
; x
++) {
574 putAttrXY(x
- leftColumn
, i
, selectedColor
);
576 } else if ((topLine
+ i
>= startRow
)
577 && (topLine
+ i
<= endRow
)
579 for (x
= 0; x
< getWidth(); x
++) {
580 putAttrXY(x
, i
, selectedColor
);
590 // ------------------------------------------------------------------------
591 // TEditorWidget ----------------------------------------------------------
592 // ------------------------------------------------------------------------
595 * Align visible area with document current line.
597 * @param topLineIsTop if true, make the top visible line the document
598 * current line if it was off-screen. If false, make the bottom visible
599 * line the document current line.
601 private void alignTopLine(final boolean topLineIsTop
) {
602 int line
= document
.getLineNumber();
604 if ((line
< topLine
) || (line
> topLine
+ getHeight() - 1)) {
605 // Need to move topLine to bring document back into view.
607 topLine
= line
- (getHeight() - 1);
611 assert (topLine
>= 0);
614 assert (topLine
>= 0);
619 System.err.println("line " + line + " topLine " + topLine);
622 // Document is in view, let's set cursorY
623 assert (line
>= topLine
);
624 setCursorY(line
- topLine
);
629 * Align document current line with visible area.
631 * @param topLineIsTop if true, make the top visible line the document
632 * current line if it was off-screen. If false, make the bottom visible
633 * line the document current line.
635 private void alignDocument(final boolean topLineIsTop
) {
636 int line
= document
.getLineNumber();
637 int cursor
= document
.getCursor();
639 if ((line
< topLine
) || (line
> topLine
+ getHeight() - 1)) {
640 // Need to move document to ensure it fits view.
642 document
.setLineNumber(topLine
);
644 document
.setLineNumber(topLine
+ (getHeight() - 1));
646 if (cursor
< document
.getCurrentLine().getDisplayLength()) {
647 document
.setCursor(cursor
);
652 System.err.println("getLineNumber() " + document.getLineNumber() +
653 " topLine " + topLine);
656 // Document is in view, let's set cursorY
657 setCursorY(document
.getLineNumber() - topLine
);
662 * Align visible cursor with document cursor.
664 private void alignCursor() {
665 int width
= getWidth();
667 int desiredX
= document
.getCursor() - leftColumn
;
669 // We need to push the screen to the left.
670 leftColumn
= document
.getCursor();
671 } else if (desiredX
> width
- 1) {
672 // We need to push the screen to the right.
673 leftColumn
= document
.getCursor() - (width
- 1);
677 System.err.println("document cursor " + document.getCursor() +
678 " leftColumn " + leftColumn);
682 setCursorX(document
.getCursor() - leftColumn
);
686 * Get the number of lines in the underlying Document.
688 * @return the number of lines
690 public int getLineCount() {
691 return document
.getLineCount();
695 * Get the current visible top row number. 1-based.
697 * @return the visible top row number. Row 1 is the first row.
699 public int getVisibleRowNumber() {
704 * Set the current visible row number. 1-based.
706 * @param row the new visible row number. Row 1 is the first row.
708 public void setVisibleRowNumber(final int row
) {
710 if ((row
> 0) && (row
< document
.getLineCount())) {
717 * Get the current editing row number. 1-based.
719 * @return the editing row number. Row 1 is the first row.
721 public int getEditingRowNumber() {
722 return document
.getLineNumber() + 1;
726 * Set the current editing row number. 1-based.
728 * @param row the new editing row number. Row 1 is the first row.
730 public void setEditingRowNumber(final int row
) {
732 if ((row
> 0) && (row
< document
.getLineCount())) {
733 document
.setLineNumber(row
- 1);
739 * Set the current visible column number. 1-based.
741 * @return the visible column number. Column 1 is the first column.
743 public int getVisibleColumnNumber() {
744 return leftColumn
+ 1;
748 * Set the current visible column number. 1-based.
750 * @param column the new visible column number. Column 1 is the first
753 public void setVisibleColumnNumber(final int column
) {
755 if ((column
> 0) && (column
< document
.getLineLengthMax())) {
756 leftColumn
= column
- 1;
762 * Get the current editing column number. 1-based.
764 * @return the editing column number. Column 1 is the first column.
766 public int getEditingColumnNumber() {
767 return document
.getCursor() + 1;
771 * Set the current editing column number. 1-based.
773 * @param column the new editing column number. Column 1 is the first
776 public void setEditingColumnNumber(final int column
) {
777 if ((column
> 0) && (column
< document
.getLineLength())) {
778 document
.setCursor(column
- 1);
784 * Get the maximum possible row number. 1-based.
786 * @return the maximum row number. Row 1 is the first row.
788 public int getMaximumRowNumber() {
789 return document
.getLineCount() + 1;
793 * Get the maximum possible column number. 1-based.
795 * @return the maximum column number. Column 1 is the first column.
797 public int getMaximumColumnNumber() {
798 return document
.getLineLengthMax() + 1;
802 * Get the current editing row plain text. 1-based.
804 * @param row the editing row number. Row 1 is the first row.
805 * @return the plain text of the row
807 public String
getEditingRawLine(final int row
) {
808 Line line
= document
.getLine(row
- 1);
809 return line
.getRawString();
813 * Get the dirty value.
815 * @return true if the buffer is dirty
817 public boolean isDirty() {
818 return document
.isDirty();
822 * Unset the dirty flag.
824 public void setNotDirty() {
825 document
.setNotDirty();
829 * Save contents to file.
831 * @param filename file to save to
832 * @throws IOException if a java.io operation throws
834 public void saveToFilename(final String filename
) throws IOException
{
835 document
.saveToFilename(filename
);
839 * Delete text within the selection bounds.
841 private void deleteSelection() {
847 int startCol
= selectionColumn0
;
848 int startRow
= selectionLine0
;
849 int endCol
= selectionColumn1
;
850 int endRow
= selectionLine1
;
853 System.err.println("INITIAL: " + startRow + " " + startCol + " " +
854 endRow + " " + endCol + " " +
855 document.getLineNumber() + " " + document.getCursor());
858 if (((selectionColumn1
< selectionColumn0
)
859 && (selectionLine1
== selectionLine0
))
860 || (selectionLine1
< selectionLine0
)
862 // The user selected from bottom-to-top and/or right-to-left.
863 // Reverse the coordinates for the inverted section.
864 startCol
= selectionColumn1
;
865 startRow
= selectionLine1
;
866 endCol
= selectionColumn0
;
867 endRow
= selectionLine0
;
869 if (endRow
>= document
.getLineCount()) {
870 // The selection started beyond EOF, trim it to EOF.
871 endRow
= document
.getLineCount() - 1;
872 endCol
= document
.getLine(endRow
).getDisplayLength();
873 } else if (endRow
== document
.getLineCount() - 1) {
874 // The selection started beyond EOF, trim it to EOF.
875 if (endCol
>= document
.getLine(endRow
).getDisplayLength()) {
876 endCol
= document
.getLine(endRow
).getDisplayLength() - 1;
881 System.err.println("FLIP: " + startRow + " " + startCol + " " +
882 endRow + " " + endCol + " " +
883 document.getLineNumber() + " " + document.getCursor());
884 System.err.println(" --END: " + endRow + " " + document.getLineCount() +
885 " " + document.getLine(endRow).getDisplayLength());
888 assert (endRow
< document
.getLineCount());
889 if (endCol
>= document
.getLine(endRow
).getDisplayLength()) {
890 endCol
= document
.getLine(endRow
).getDisplayLength() - 1;
895 if (startCol
>= document
.getLine(startRow
).getDisplayLength()) {
896 startCol
= document
.getLine(startRow
).getDisplayLength() - 1;
902 // Place the cursor on the selection end, and "press backspace" until
903 // the cursor matches the selection start.
905 System.err.println("BEFORE: " + startRow + " " + startCol + " " +
906 endRow + " " + endCol + " " +
907 document.getLineNumber() + " " + document.getCursor());
909 document
.setLineNumber(endRow
);
910 document
.setCursor(endCol
+ 1);
911 while (!((document
.getLineNumber() == startRow
)
912 && (document
.getCursor() == startCol
))
915 System.err.println("DURING: " + startRow + " " + startCol + " " +
916 endRow + " " + endCol + " " +
917 document.getLineNumber() + " " + document.getCursor());
920 document
.backspace();
926 * Copy text within the selection bounds to clipboard.
928 private void copySelection() {
932 getClipboard().copyText(getSelection());
938 * @param startRow the starting row number. 0-based: row 0 is the first
940 * @param startColumn the starting column number. 0-based: column 0 is
942 * @param endRow the ending row number. 0-based: row 0 is the first row.
943 * @param endColumn the ending column number. 0-based: column 0 is the
946 public void setSelection(final int startRow
, final int startColumn
,
947 final int endRow
, final int endColumn
) {
950 selectionLine0
= startRow
;
951 selectionColumn0
= startColumn
;
952 selectionLine1
= endRow
;
953 selectionColumn1
= endColumn
;
957 * Copy text within the selection bounds to a string.
959 * @return the selection as a string, or null if there is no selection
961 public String
getSelection() {
966 int startCol
= selectionColumn0
;
967 int startRow
= selectionLine0
;
968 int endCol
= selectionColumn1
;
969 int endRow
= selectionLine1
;
971 if (((selectionColumn1
< selectionColumn0
)
972 && (selectionLine1
== selectionLine0
))
973 || (selectionLine1
< selectionLine0
)
975 // The user selected from bottom-to-top and/or right-to-left.
976 // Reverse the coordinates for the inverted section.
977 startCol
= selectionColumn1
;
978 startRow
= selectionLine1
;
979 endCol
= selectionColumn0
;
980 endRow
= selectionLine0
;
983 StringBuilder sb
= new StringBuilder();
985 if (endRow
> startRow
) {
987 String line
= document
.getLine(startRow
).getRawString();
989 for (int i
= 0; i
< line
.length(); ) {
990 int ch
= line
.codePointAt(i
);
993 sb
.append(Character
.toChars(ch
));
995 x
+= StringUtils
.width(ch
);
996 i
+= Character
.charCount(ch
);
1001 for (int y
= startRow
+ 1; y
< endRow
; y
++) {
1002 sb
.append(document
.getLine(y
).getRawString());
1007 line
= document
.getLine(endRow
).getRawString();
1009 for (int i
= 0; i
< line
.length(); ) {
1010 int ch
= line
.codePointAt(i
);
1016 sb
.append(Character
.toChars(ch
));
1017 x
+= StringUtils
.width(ch
);
1018 i
+= Character
.charCount(ch
);
1021 assert (startRow
== endRow
);
1024 String line
= document
.getLine(startRow
).getRawString();
1026 for (int i
= 0; i
< line
.length(); ) {
1027 int ch
= line
.codePointAt(i
);
1029 if ((x
>= startCol
) && (x
<= endCol
)) {
1030 sb
.append(Character
.toChars(ch
));
1033 x
+= StringUtils
.width(ch
);
1034 i
+= Character
.charCount(ch
);
1037 return sb
.toString();
1041 * Get the selection starting row number.
1043 * @return the starting row number, or -1 if there is no selection.
1044 * 0-based: row 0 is the first row.
1046 public int getSelectionStartRow() {
1051 int startCol
= selectionColumn0
;
1052 int startRow
= selectionLine0
;
1053 int endCol
= selectionColumn1
;
1054 int endRow
= selectionLine1
;
1056 if (((selectionColumn1
< selectionColumn0
)
1057 && (selectionLine1
== selectionLine0
))
1058 || (selectionLine1
< selectionLine0
)
1060 // The user selected from bottom-to-top and/or right-to-left.
1061 // Reverse the coordinates for the inverted section.
1062 startCol
= selectionColumn1
;
1063 startRow
= selectionLine1
;
1064 endCol
= selectionColumn0
;
1065 endRow
= selectionLine0
;
1071 * Get the selection starting column number.
1073 * @return the starting column number, or -1 if there is no selection.
1074 * 0-based: column 0 is the first column.
1076 public int getSelectionStartColumn() {
1081 int startCol
= selectionColumn0
;
1082 int startRow
= selectionLine0
;
1083 int endCol
= selectionColumn1
;
1084 int endRow
= selectionLine1
;
1086 if (((selectionColumn1
< selectionColumn0
)
1087 && (selectionLine1
== selectionLine0
))
1088 || (selectionLine1
< selectionLine0
)
1090 // The user selected from bottom-to-top and/or right-to-left.
1091 // Reverse the coordinates for the inverted section.
1092 startCol
= selectionColumn1
;
1093 startRow
= selectionLine1
;
1094 endCol
= selectionColumn0
;
1095 endRow
= selectionLine0
;
1101 * Get the selection ending row number.
1103 * @return the ending row number, or -1 if there is no selection.
1104 * 0-based: row 0 is the first row.
1106 public int getSelectionEndRow() {
1111 int startCol
= selectionColumn0
;
1112 int startRow
= selectionLine0
;
1113 int endCol
= selectionColumn1
;
1114 int endRow
= selectionLine1
;
1116 if (((selectionColumn1
< selectionColumn0
)
1117 && (selectionLine1
== selectionLine0
))
1118 || (selectionLine1
< selectionLine0
)
1120 // The user selected from bottom-to-top and/or right-to-left.
1121 // Reverse the coordinates for the inverted section.
1122 startCol
= selectionColumn1
;
1123 startRow
= selectionLine1
;
1124 endCol
= selectionColumn0
;
1125 endRow
= selectionLine0
;
1131 * Get the selection ending column number.
1133 * @return the ending column number, or -1 if there is no selection.
1134 * 0-based: column 0 is the first column.
1136 public int getSelectionEndColumn() {
1141 int startCol
= selectionColumn0
;
1142 int startRow
= selectionLine0
;
1143 int endCol
= selectionColumn1
;
1144 int endRow
= selectionLine1
;
1146 if (((selectionColumn1
< selectionColumn0
)
1147 && (selectionLine1
== selectionLine0
))
1148 || (selectionLine1
< selectionLine0
)
1150 // The user selected from bottom-to-top and/or right-to-left.
1151 // Reverse the coordinates for the inverted section.
1152 startCol
= selectionColumn1
;
1153 startRow
= selectionLine1
;
1154 endCol
= selectionColumn0
;
1155 endRow
= selectionLine0
;
1161 * Unset the selection.
1163 public void unsetSelection() {
1164 inSelection
= false;
1168 * Replace whatever is being selected with new text. If not in
1169 * selection, nothing is replaced.
1171 * @param text the new replacement text
1173 public void replaceSelection(final String text
) {
1178 // Delete selected text, then paste text from clipboard.
1181 for (int i
= 0; i
< text
.length(); ) {
1182 int ch
= text
.codePointAt(i
);
1183 onKeypress(new TKeypressEvent(false, 0, ch
, false, false,
1185 i
+= Character
.charCount(ch
);
1190 * Check if selection is available.
1192 * @return true if a selection has been made
1194 public boolean hasSelection() {
1199 * Get the entire contents of the editor as one string.
1201 * @return the editor contents
1203 public String
getText() {
1204 return document
.getText();
1208 * Set the entire contents of the editor from one string.
1210 * @param text the new contents
1212 public void setText(final String text
) {
1213 document
= new Document(text
, defaultColor
);
1219 // ------------------------------------------------------------------------
1220 // EditMenuUser -----------------------------------------------------------
1221 // ------------------------------------------------------------------------
1224 * Check if the cut menu item should be enabled.
1226 * @return true if the cut menu item should be enabled
1228 public boolean isEditMenuCut() {
1233 * Check if the copy menu item should be enabled.
1235 * @return true if the copy menu item should be enabled
1237 public boolean isEditMenuCopy() {
1242 * Check if the paste menu item should be enabled.
1244 * @return true if the paste menu item should be enabled
1246 public boolean isEditMenuPaste() {
1251 * Check if the clear menu item should be enabled.
1253 * @return true if the clear menu item should be enabled
1255 public boolean isEditMenuClear() {