From 43ee96da86bb1a5d1f308a8499dabbbd8750dd80 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Tue, 5 Nov 2019 10:38:34 -0600 Subject: [PATCH] editor cut/copy/paste --- src/jexer/TEditorWidget.java | 248 ++++++++++++++++++++++++++++----- src/jexer/TKeypress.java | 35 +++++ src/jexer/bits/ColorTheme.java | 5 + 3 files changed, 254 insertions(+), 34 deletions(-) diff --git a/src/jexer/TEditorWidget.java b/src/jexer/TEditorWidget.java index dcd2fea..893145b 100644 --- a/src/jexer/TEditorWidget.java +++ b/src/jexer/TEditorWidget.java @@ -31,6 +31,7 @@ package jexer; import java.io.IOException; import jexer.bits.CellAttributes; +import jexer.bits.StringUtils; import jexer.event.TCommandEvent; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; @@ -80,11 +81,6 @@ public class TEditorWidget extends TWidget implements EditMenuUser { */ private int leftColumn = 0; - /** - * If true, selection is a rectangle. - */ - private boolean selectionRectangle = false; - /** * If true, the mouse is dragging a selection. */ @@ -171,13 +167,12 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = leftColumn + mouse.getX(); selectionLine1 = topLine + mouse.getY(); - } else if (mouse.isShift() || mouse.isCtrl()) { + } else if (mouse.isShift()) { inSelection = true; selectionColumn0 = leftColumn + mouse.getX(); selectionLine0 = topLine + mouse.getY(); selectionColumn1 = selectionColumn0; selectionLine1 = selectionLine0; - selectionRectangle = mouse.isAlt() | mouse.isCtrl(); } // Set the row and column @@ -196,7 +191,6 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = document.getCursor(); selectionLine1 = document.getLineNumber(); - selectionRectangle = mouse.isCtrl(); } return; } @@ -213,7 +207,6 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = document.getCursor(); selectionLine1 = document.getLineNumber(); - selectionRectangle = mouse.isCtrl(); } return; } else { @@ -237,13 +230,12 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = leftColumn + mouse.getX(); selectionLine1 = topLine + mouse.getY(); - } else if (mouse.isShift() || mouse.isCtrl()) { + } else if (mouse.isShift()) { inSelection = true; selectionColumn0 = leftColumn + mouse.getX(); selectionLine0 = topLine + mouse.getY(); selectionColumn1 = selectionColumn0; selectionLine1 = selectionLine0; - selectionRectangle = mouse.isAlt() | mouse.isCtrl(); } // Set the row and column @@ -262,7 +254,6 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = document.getCursor(); selectionLine1 = document.getLineNumber(); - selectionRectangle = mouse.isCtrl(); } return; } @@ -279,7 +270,6 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = document.getCursor(); selectionLine1 = document.getLineNumber(); - selectionRectangle = mouse.isCtrl(); } return; } else { @@ -310,7 +300,7 @@ public class TEditorWidget extends TWidget implements EditMenuUser { */ @Override public void onKeypress(final TKeypressEvent keypress) { - if (keypress.getKey().isShift() || keypress.getKey().isCtrl()) { + if (keypress.getKey().isShift()) { // Selection. if (!inSelection) { inSelection = true; @@ -318,10 +308,20 @@ public class TEditorWidget extends TWidget implements EditMenuUser { selectionLine0 = document.getLineNumber(); selectionColumn1 = selectionColumn0; selectionLine1 = selectionLine0; - selectionRectangle = keypress.getKey().isCtrl(); } } else { - inSelection = false; + if (keypress.equals(kbLeft) + || keypress.equals(kbRight) + || keypress.equals(kbUp) + || keypress.equals(kbDown) + || keypress.equals(kbPgDn) + || keypress.equals(kbPgUp) + || keypress.equals(kbHome) + || keypress.equals(kbEnd) + ) { + // Non-shifted navigation keys disable selection. + inSelection = false; + } } if (keypress.equals(kbLeft) @@ -336,11 +336,15 @@ public class TEditorWidget extends TWidget implements EditMenuUser { alignTopLine(true); } else if (keypress.equals(kbAltLeft) || keypress.equals(kbCtrlLeft) + || keypress.equals(kbAltShiftLeft) + || keypress.equals(kbCtrlShiftLeft) ) { document.backwardsWord(); alignTopLine(false); } else if (keypress.equals(kbAltRight) || keypress.equals(kbCtrlRight) + || keypress.equals(kbAltShiftRight) + || keypress.equals(kbCtrlShiftRight) ) { document.forwardsWord(); alignTopLine(true); @@ -354,13 +358,19 @@ public class TEditorWidget extends TWidget implements EditMenuUser { ) { document.down(); alignTopLine(true); - } else if (keypress.equals(kbPgUp)) { + } else if (keypress.equals(kbPgUp) + || keypress.equals(kbShiftPgUp) + ) { document.up(getHeight() - 1); alignTopLine(false); - } else if (keypress.equals(kbPgDn)) { + } else if (keypress.equals(kbPgDn) + || keypress.equals(kbShiftPgDn) + ) { document.down(getHeight() - 1); alignTopLine(true); - } else if (keypress.equals(kbHome)) { + } else if (keypress.equals(kbHome) + || keypress.equals(kbShiftHome) + ) { if (document.home()) { leftColumn = 0; if (leftColumn < 0) { @@ -368,38 +378,54 @@ public class TEditorWidget extends TWidget implements EditMenuUser { } setCursorX(0); } - } else if (keypress.equals(kbEnd)) { + } else if (keypress.equals(kbEnd) + || keypress.equals(kbShiftEnd) + ) { if (document.end()) { alignCursor(); } - } else if (keypress.equals(kbCtrlHome)) { + } else if (keypress.equals(kbCtrlHome) + || keypress.equals(kbCtrlShiftHome) + ) { document.setLineNumber(0); document.home(); topLine = 0; leftColumn = 0; setCursorX(0); setCursorY(0); - } else if (keypress.equals(kbCtrlEnd)) { + } else if (keypress.equals(kbCtrlEnd) + || keypress.equals(kbCtrlShiftEnd) + ) { document.setLineNumber(document.getLineCount() - 1); document.end(); alignTopLine(false); } else if (keypress.equals(kbIns)) { document.setOverwrite(!document.getOverwrite()); } else if (keypress.equals(kbDel)) { - document.del(); + if (inSelection) { + deleteSelection(); + } else { + document.del(); + } alignCursor(); } else if (keypress.equals(kbBackspace) || keypress.equals(kbBackspaceDel) ) { - document.backspace(); + if (inSelection) { + deleteSelection(); + } else { + document.backspace(); + } alignTopLine(false); } else if (keypress.equals(kbTab)) { + deleteSelection(); // Add spaces until we hit modulo 8. for (int i = document.getCursor(); (i + 1) % 8 != 0; i++) { document.addChar(' '); } alignCursor(); } else if (keypress.equals(kbEnter)) { + deleteSelection(); document.enter(); alignTopLine(true); } else if (!keypress.getKey().isFnKey() @@ -407,6 +433,7 @@ public class TEditorWidget extends TWidget implements EditMenuUser { && !keypress.getKey().isCtrl() ) { // Plain old keystroke, process it + deleteSelection(); document.addChar(keypress.getKey().getChar()); alignCursor(); } else { @@ -417,7 +444,6 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection) { selectionColumn1 = document.getCursor(); selectionLine1 = document.getLineNumber(); - selectionRectangle = keypress.getKey().isCtrl(); } } @@ -458,18 +484,14 @@ public class TEditorWidget extends TWidget implements EditMenuUser { public void onCommand(final TCommandEvent command) { if (command.equals(cmCut)) { // Copy text to clipboard, and then remove it. - - // TODO - + copySelection(); deleteSelection(); return; } if (command.equals(cmCopy)) { // Copy text to clipboard. - - // TODO - + copySelection(); return; } @@ -506,6 +528,26 @@ public class TEditorWidget extends TWidget implements EditMenuUser { */ @Override public void draw() { + CellAttributes selectedColor = getTheme().getColor("teditor.selected"); + + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; + + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 <= selectionLine0)) + || ((selectionColumn1 <= selectionColumn0) + && (selectionLine1 < selectionLine0)) + ) { + // The user selected from bottom-right to top-left. Reverse the + // coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + for (int i = 0; i < getHeight(); i++) { // Background line getScreen().hLineXY(0, i, getWidth(), ' ', defaultColor); @@ -524,9 +566,35 @@ public class TEditorWidget extends TWidget implements EditMenuUser { break; } } - } - // TODO: highlight selected region + // Highlight selected region + if (inSelection) { + if (startRow == endRow) { + if (topLine + i == startRow) { + for (x = startCol; x <= endCol; x++) { + putAttrXY(x - leftColumn, i, selectedColor); + } + } + } else { + if (topLine + i == startRow) { + for (x = startCol; x < line.getDisplayLength(); x++) { + putAttrXY(x - leftColumn, i, selectedColor); + } + } else if (topLine + i == endRow) { + for (x = 0; x <= endCol; x++) { + putAttrXY(x - leftColumn, i, selectedColor); + } + } else if ((topLine + i >= startRow) + && (topLine + i <= endRow) + ) { + for (x = 0; x < getWidth(); x++) { + putAttrXY(x, i, selectedColor); + } + } + } + } + + } } } @@ -767,8 +835,120 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (inSelection == false) { return; } + inSelection = false; + + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; + + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 <= selectionLine0)) + || ((selectionColumn1 <= selectionColumn0) + && (selectionLine1 < selectionLine0)) + ) { + // The user selected from bottom-right to top-left. Reverse the + // coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + + // Place the cursor on the selection end, and "press backspace" until + // the cursor matches the selection start. + document.setLineNumber(endRow); + document.setCursor(endCol + 1); + while (!((document.getLineNumber() == startRow) + && (document.getCursor() == startCol)) + ) { + document.backspace(); + } + alignTopLine(true); + } + + /** + * Copy text within the selection bounds to clipboard. + */ + private void copySelection() { + if (inSelection == false) { + return; + } + + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; + + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 <= selectionLine0)) + || ((selectionColumn1 <= selectionColumn0) + && (selectionLine1 < selectionLine0)) + ) { + // The user selected from bottom-right to top-left. Reverse the + // coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + + StringBuilder sb = new StringBuilder(); + + if (endRow > startRow) { + // First line + String line = document.getLine(startRow).getRawString(); + int x = 0; + for (int i = 0; i < line.length(); ) { + int ch = line.codePointAt(i); + + if (x >= startCol) { + sb.append(Character.toChars(ch)); + } + x += StringUtils.width(ch); + i += Character.charCount(ch); + } + sb.append("\n"); + + // Middle lines + for (int y = startRow + 1; y < endRow; y++) { + sb.append(document.getLine(y).getRawString()); + sb.append("\n"); + } + + // Final line + line = document.getLine(endRow).getRawString(); + x = 0; + for (int i = 0; i < line.length(); ) { + int ch = line.codePointAt(i); + + if (x > endCol) { + break; + } + + sb.append(Character.toChars(ch)); + x += StringUtils.width(ch); + i += Character.charCount(ch); + } + } else { + assert (startRow == endRow); + + // Only one line + String line = document.getLine(startRow).getRawString(); + int x = 0; + for (int i = 0; i < line.length(); ) { + int ch = line.codePointAt(i); + + if ((x >= startCol) && (x <= endCol)) { + sb.append(Character.toChars(ch)); + } + + x += StringUtils.width(ch); + i += Character.charCount(ch); + } + } - // TODO + getClipboard().copyText(sb.toString()); } // ------------------------------------------------------------------------ diff --git a/src/jexer/TKeypress.java b/src/jexer/TKeypress.java index c965e7d..8b0703e 100644 --- a/src/jexer/TKeypress.java +++ b/src/jexer/TKeypress.java @@ -612,6 +612,41 @@ public class TKeypress { public static final TKeypress kbAltShiftZ = new TKeypress(false, 0, 'Z', true, false, true); + public static final TKeypress kbAltShiftHome = new TKeypress(true, + TKeypress.HOME, ' ', true, false, true); + public static final TKeypress kbAltShiftEnd = new TKeypress(true, + TKeypress.END, ' ', true, false, true); + public static final TKeypress kbAltShiftPgUp = new TKeypress(true, + TKeypress.PGUP, ' ', true, false, true); + public static final TKeypress kbAltShiftPgDn = new TKeypress(true, + TKeypress.PGDN, ' ', true, false, true); + public static final TKeypress kbAltShiftUp = new TKeypress(true, + TKeypress.UP, ' ', true, false, true); + public static final TKeypress kbAltShiftDown = new TKeypress(true, + TKeypress.DOWN, ' ', true, false, true); + public static final TKeypress kbAltShiftLeft = new TKeypress(true, + TKeypress.LEFT, ' ', true, false, true); + public static final TKeypress kbAltShiftRight = new TKeypress(true, + TKeypress.RIGHT, ' ', true, false, true); + + public static final TKeypress kbCtrlShiftHome = new TKeypress(true, + TKeypress.HOME, ' ', false, true, true); + public static final TKeypress kbCtrlShiftEnd = new TKeypress(true, + TKeypress.END, ' ', false, true, true); + public static final TKeypress kbCtrlShiftPgUp = new TKeypress(true, + TKeypress.PGUP, ' ', false, true, true); + public static final TKeypress kbCtrlShiftPgDn = new TKeypress(true, + TKeypress.PGDN, ' ', false, true, true); + public static final TKeypress kbCtrlShiftUp = new TKeypress(true, + TKeypress.UP, ' ', false, true, true); + public static final TKeypress kbCtrlShiftDown = new TKeypress(true, + TKeypress.DOWN, ' ', false, true, true); + public static final TKeypress kbCtrlShiftLeft = new TKeypress(true, + TKeypress.LEFT, ' ', false, true, true); + public static final TKeypress kbCtrlShiftRight = new TKeypress(true, + TKeypress.RIGHT, ' ', false, true, true); + + /** * Backspace as ^H. */ diff --git a/src/jexer/bits/ColorTheme.java b/src/jexer/bits/ColorTheme.java index ffba4d4..60eb9bd 100644 --- a/src/jexer/bits/ColorTheme.java +++ b/src/jexer/bits/ColorTheme.java @@ -633,6 +633,11 @@ public class ColorTheme { color.setBackColor(Color.BLUE); color.setBold(false); colors.put("teditor", color); + color = new CellAttributes(); + color.setForeColor(Color.BLACK); + color.setBackColor(Color.CYAN); + color.setBold(false); + colors.put("teditor.selected", color); // TTable color = new CellAttributes(); -- 2.27.0