From 3c5921e6905d8ef8c6f4e0644a4144274cc6bedc Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sun, 17 Nov 2019 20:42:38 -0600 Subject: [PATCH] retrofit --- src/jexer/TApplication.java | 33 ++--- src/jexer/TEditorWidget.java | 187 +++++++++++++++++++++++--- src/jexer/TImage.java | 3 - src/jexer/TKeypress.java | 5 + src/jexer/TPasswordField.java | 1 - src/jexer/TRadioGroup.java | 18 +++ src/jexer/TSplitPane.java | 2 - src/jexer/TTerminalWidget.java | 81 +++++++++-- src/jexer/TTerminalWindow.java | 24 +--- src/jexer/TWidget.java | 15 +++ src/jexer/backend/ECMA48Terminal.java | 1 - src/jexer/backend/GlyphMaker.java | 5 +- src/jexer/bits/ColorTheme.java | 7 +- src/jexer/menu/TSubMenu.java | 29 ++++ src/jexer/tterminal/ECMA48.java | 4 +- src/jexer/tterminal/Sixel.java | 1 - 16 files changed, 329 insertions(+), 87 deletions(-) diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index dba59d7..8a33807 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -164,16 +164,6 @@ public class TApplication implements Runnable { */ private int mouseY; - /** - * Old version of mouse coordinate X. - */ - private int oldMouseX; - - /** - * Old version mouse coordinate Y. - */ - private int oldMouseY; - /** * Old drawn version of mouse coordinate X. */ @@ -1196,8 +1186,6 @@ public class TApplication implements Runnable { } mouseX = 0; mouseY = 0; - oldMouseX = 0; - oldMouseY = 0; } if (desktop != null) { desktop.setDimensions(0, desktopTop, resize.getWidth(), @@ -1275,8 +1263,6 @@ public class TApplication implements Runnable { } if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { - oldMouseX = mouseX; - oldMouseY = mouseY; mouseX = mouse.getX(); mouseY = mouse.getY(); } else { @@ -1465,8 +1451,6 @@ public class TApplication implements Runnable { TMouseEvent mouse = (TMouseEvent) event; if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { - oldMouseX = mouseX; - oldMouseY = mouseY; mouseX = mouse.getX(); mouseY = mouse.getY(); } else { @@ -1593,14 +1577,17 @@ public class TApplication implements Runnable { desktop.onIdle(); } - // Run any invokeLaters + // Run any invokeLaters. We make a copy, and run that, because one + // of these Runnables might add call TApplication.invokeLater(). + List invokes = new ArrayList(); synchronized (invokeLaters) { - for (Runnable invoke: invokeLaters) { - invoke.run(); - } + invokes.addAll(invokeLaters); invokeLaters.clear(); - doRepaint(); } + for (Runnable invoke: invokes) { + invoke.run(); + } + doRepaint(); } @@ -2058,7 +2045,9 @@ public class TApplication implements Runnable { // Draw the status bar of the top-level window TStatusBar statusBar = null; if (topLevel != null) { - statusBar = topLevel.getStatusBar(); + if (topLevel.isShown()) { + statusBar = topLevel.getStatusBar(); + } } if (statusBar != null) { getScreen().resetClipping(); diff --git a/src/jexer/TEditorWidget.java b/src/jexer/TEditorWidget.java index 7798ad8..9236da2 100644 --- a/src/jexer/TEditorWidget.java +++ b/src/jexer/TEditorWidget.java @@ -165,8 +165,10 @@ public class TEditorWidget extends TWidget implements EditMenuUser { if (mouse.isMouse1()) { // Selection. inSelection = true; - selectionColumn0 = leftColumn + mouse.getX(); selectionLine0 = topLine + mouse.getY(); + selectionColumn0 = leftColumn + mouse.getX(); + selectionColumn0 = Math.max(0, Math.min(selectionColumn0, + document.getLine(selectionLine0).getDisplayLength() - 1)); selectionColumn1 = selectionColumn0; selectionLine1 = selectionLine0; @@ -513,6 +515,8 @@ public class TEditorWidget extends TWidget implements EditMenuUser { public void draw() { CellAttributes selectedColor = getTheme().getColor("teditor.selected"); + boolean drawSelection = true; + int startCol = selectionColumn0; int startRow = selectionLine0; int endCol = selectionColumn1; @@ -529,6 +533,9 @@ public class TEditorWidget extends TWidget implements EditMenuUser { endCol = selectionColumn0; endRow = selectionLine0; } + if ((startCol == endCol) && (startRow == endRow)) { + drawSelection = false; + } for (int i = 0; i < getHeight(); i++) { // Background line @@ -550,7 +557,7 @@ public class TEditorWidget extends TWidget implements EditMenuUser { } // Highlight selected region - if (inSelection) { + if (inSelection && drawSelection) { if (startRow == endRow) { if (topLine + i == startRow) { for (x = startCol; x <= endCol; x++) { @@ -832,7 +839,7 @@ public class TEditorWidget extends TWidget implements EditMenuUser { * Delete text within the selection bounds. */ private void deleteSelection() { - if (inSelection == false) { + if (!inSelection) { return; } inSelection = false; @@ -919,9 +926,42 @@ public class TEditorWidget extends TWidget implements EditMenuUser { * Copy text within the selection bounds to clipboard. */ private void copySelection() { - if (inSelection == false) { + if (!inSelection) { return; } + getClipboard().copyText(getSelection()); + } + + /** + * Set the selection. + * + * @param startRow the starting row number. 0-based: row 0 is the first + * row. + * @param startColumn the starting column number. 0-based: column 0 is + * the first column. + * @param endRow the ending row number. 0-based: row 0 is the first row. + * @param endColumn the ending column number. 0-based: column 0 is the + * first column. + */ + public void setSelection(final int startRow, final int startColumn, + final int endRow, final int endColumn) { + + inSelection = true; + selectionLine0 = startRow; + selectionColumn0 = startColumn; + selectionLine1 = endRow; + selectionColumn1 = endColumn; + } + + /** + * Copy text within the selection bounds to a string. + * + * @return the selection as a string, or null if there is no selection + */ + public String getSelection() { + if (!inSelection) { + return null; + } int startCol = selectionColumn0; int startRow = selectionLine0; @@ -994,29 +1034,127 @@ public class TEditorWidget extends TWidget implements EditMenuUser { i += Character.charCount(ch); } } + return sb.toString(); + } + + /** + * Get the selection starting row number. + * + * @return the starting row number, or -1 if there is no selection. + * 0-based: row 0 is the first row. + */ + public int getSelectionStartRow() { + if (!inSelection) { + return -1; + } + + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; - getClipboard().copyText(sb.toString()); + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 == selectionLine0)) + || (selectionLine1 < selectionLine0) + ) { + // The user selected from bottom-to-top and/or right-to-left. + // Reverse the coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + return startRow; } /** - * Set the selection. + * Get the selection starting column number. * - * @param startRow the starting row number. 0-based: row 0 is the first - * row. - * @param startColumn the starting column number. 0-based: column 0 is - * the first column. - * @param endRow the ending row number. 0-based: row 0 is the first row. - * @param endColumn the ending column number. 0-based: column 0 is the - * first column. + * @return the starting column number, or -1 if there is no selection. + * 0-based: column 0 is the first column. */ - public void setSelection(final int startRow, final int startColumn, - final int endRow, final int endColumn) { + public int getSelectionStartColumn() { + if (!inSelection) { + return -1; + } - inSelection = true; - selectionLine0 = startRow; - selectionColumn0 = startColumn; - selectionLine1 = endRow; - selectionColumn1 = endColumn; + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; + + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 == selectionLine0)) + || (selectionLine1 < selectionLine0) + ) { + // The user selected from bottom-to-top and/or right-to-left. + // Reverse the coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + return startCol; + } + + /** + * Get the selection ending row number. + * + * @return the ending row number, or -1 if there is no selection. + * 0-based: row 0 is the first row. + */ + public int getSelectionEndRow() { + if (!inSelection) { + return -1; + } + + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; + + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 == selectionLine0)) + || (selectionLine1 < selectionLine0) + ) { + // The user selected from bottom-to-top and/or right-to-left. + // Reverse the coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + return endRow; + } + + /** + * Get the selection ending column number. + * + * @return the ending column number, or -1 if there is no selection. + * 0-based: column 0 is the first column. + */ + public int getSelectionEndColumn() { + if (!inSelection) { + return -1; + } + + int startCol = selectionColumn0; + int startRow = selectionLine0; + int endCol = selectionColumn1; + int endRow = selectionLine1; + + if (((selectionColumn1 < selectionColumn0) + && (selectionLine1 == selectionLine0)) + || (selectionLine1 < selectionLine0) + ) { + // The user selected from bottom-to-top and/or right-to-left. + // Reverse the coordinates for the inverted section. + startCol = selectionColumn1; + startRow = selectionLine1; + endCol = selectionColumn0; + endRow = selectionLine0; + } + return endCol; } /** @@ -1048,6 +1186,15 @@ public class TEditorWidget extends TWidget implements EditMenuUser { } } + /** + * Check if selection is available. + * + * @return true if a selection has been made + */ + public boolean hasSelection() { + return inSelection; + } + /** * Get the entire contents of the editor as one string. * diff --git a/src/jexer/TImage.java b/src/jexer/TImage.java index 2dfd6a3..b7bfbd0 100644 --- a/src/jexer/TImage.java +++ b/src/jexer/TImage.java @@ -30,9 +30,6 @@ package jexer; import java.awt.image.BufferedImage; -import jexer.backend.ECMA48Terminal; -import jexer.backend.MultiScreen; -import jexer.backend.SwingTerminal; import jexer.bits.Cell; import jexer.event.TCommandEvent; import jexer.event.TKeypressEvent; diff --git a/src/jexer/TKeypress.java b/src/jexer/TKeypress.java index 8b0703e..20db8bb 100644 --- a/src/jexer/TKeypress.java +++ b/src/jexer/TKeypress.java @@ -856,6 +856,11 @@ public class TKeypress { return "\u25C0\u2500\u2518"; } + // Special case: Space is "Space" + if (equals(kbSpace)) { + return "Space"; + } + if (equals(kbShiftLeft)) { return "Shift+\u2190"; } diff --git a/src/jexer/TPasswordField.java b/src/jexer/TPasswordField.java index 9c200d7..0be2b98 100644 --- a/src/jexer/TPasswordField.java +++ b/src/jexer/TPasswordField.java @@ -29,7 +29,6 @@ package jexer; import jexer.bits.CellAttributes; -import jexer.bits.GraphicsChars; import jexer.bits.StringUtils; /** diff --git a/src/jexer/TRadioGroup.java b/src/jexer/TRadioGroup.java index bfea5d6..d6bd7ff 100644 --- a/src/jexer/TRadioGroup.java +++ b/src/jexer/TRadioGroup.java @@ -60,6 +60,24 @@ public class TRadioGroup extends TWidget { // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Public constructor. + * + * @param parent parent widget + * @param x column relative to parent + * @param y row relative to parent + * @param width width of group + * @param label label to display on the group box + */ + public TRadioGroup(final TWidget parent, final int x, final int y, + final int width, final String label) { + + // Set parent and window + super(parent, x, y, width, 2); + + this.label = label; + } + /** * Public constructor. * diff --git a/src/jexer/TSplitPane.java b/src/jexer/TSplitPane.java index b771dd1..b308e9b 100644 --- a/src/jexer/TSplitPane.java +++ b/src/jexer/TSplitPane.java @@ -30,10 +30,8 @@ package jexer; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; -import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; -import jexer.menu.TMenu; /** * TSplitPane contains two widgets with a draggable horizontal or vertical diff --git a/src/jexer/TTerminalWidget.java b/src/jexer/TTerminalWidget.java index f488320..bf51e6b 100644 --- a/src/jexer/TTerminalWidget.java +++ b/src/jexer/TTerminalWidget.java @@ -28,27 +28,20 @@ */ package jexer; -import java.awt.Font; -import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; - -import java.io.InputStream; +import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import jexer.backend.ECMA48Terminal; import jexer.backend.GlyphMaker; -import jexer.backend.MultiScreen; import jexer.backend.SwingTerminal; import jexer.bits.Cell; -import jexer.bits.CellAttributes; import jexer.event.TCommandEvent; import jexer.event.TKeypressEvent; import jexer.event.TMenuEvent; @@ -86,6 +79,11 @@ public class TTerminalWidget extends TScrollableWidget */ private Process shell; + /** + * If true, something called 'ptypipe' is on the PATH and executable. + */ + private static boolean ptypipeOnPath = false; + /** * If true, we are using the ptypipe utility to support dynamic window * resizing. ptypipe is available at @@ -165,6 +163,13 @@ public class TTerminalWidget extends TScrollableWidget // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Static constructor. + */ + static { + checkForPtypipe(); + } + /** * Public constructor spawns a custom command line. * @@ -238,6 +243,14 @@ public class TTerminalWidget extends TScrollableWidget fullCommand = new String[command.length + 1]; fullCommand[0] = "ptypipe"; System.arraycopy(command, 0, fullCommand, 1, command.length); + } else if (System.getProperty("jexer.TTerminal.ptypipe", + "auto").equals("auto") + && (ptypipeOnPath == true) + ) { + ptypipe = true; + fullCommand = new String[command.length + 1]; + fullCommand[0] = "ptypipe"; + System.arraycopy(command, 0, fullCommand, 1, command.length); } else if (System.getProperty("os.name").startsWith("Windows")) { fullCommand = new String[3]; fullCommand[0] = "cmd"; @@ -347,6 +360,12 @@ public class TTerminalWidget extends TScrollableWidget ) { ptypipe = true; spawnShell(cmdShellPtypipe.split("\\s+")); + } else if (System.getProperty("jexer.TTerminal.ptypipe", + "auto").equals("auto") + && (ptypipeOnPath == true) + ) { + ptypipe = true; + spawnShell(cmdShellPtypipe.split("\\s+")); } else if (System.getProperty("os.name").startsWith("Windows")) { spawnShell(cmdShellWindows.split("\\s+")); } else if (System.getProperty("os.name").startsWith("Mac")) { @@ -776,6 +795,43 @@ public class TTerminalWidget extends TScrollableWidget // TTerminalWidget -------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Check for 'ptypipe' on the path. If available, set ptypipeOnPath. + */ + private static void checkForPtypipe() { + String systemPath = System.getenv("PATH"); + if (systemPath == null) { + return; + } + + String [] paths = systemPath.split(File.pathSeparator); + if (paths == null) { + return; + } + if (paths.length == 0) { + return; + } + for (int i = 0; i < paths.length; i++) { + File path = new File(paths[i]); + if (path.exists() && path.isDirectory()) { + File [] files = path.listFiles(); + if (files == null) { + continue; + } + if (files.length == 0) { + continue; + } + for (int j = 0; j < files.length; j++) { + File file = files[j]; + if (file.canExecute() && file.getName().equals("ptypipe")) { + ptypipeOnPath = true; + return; + } + } + } + } + } + /** * Get the desired window title. * @@ -1218,6 +1274,15 @@ public class TTerminalWidget extends TScrollableWidget return 24; } + /** + * Get the exit value for the emulator. + * + * @return exit value + */ + public int getExitValue() { + return exitValue; + } + // ------------------------------------------------------------------------ // EditMenuUser ----------------------------------------------------------- // ------------------------------------------------------------------------ diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index bb3e14a..aa555b0 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -28,35 +28,13 @@ */ package jexer; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - -import java.io.InputStream; -import java.io.IOException; -import java.lang.reflect.Field; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.ResourceBundle; -import jexer.backend.ECMA48Terminal; -import jexer.backend.GlyphMaker; -import jexer.backend.MultiScreen; -import jexer.backend.SwingTerminal; -import jexer.bits.Cell; -import jexer.bits.CellAttributes; +import jexer.menu.TMenu; import jexer.event.TKeypressEvent; import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; -import jexer.menu.TMenu; -import jexer.tterminal.DisplayLine; -import jexer.tterminal.DisplayListener; -import jexer.tterminal.ECMA48; import static jexer.TKeypress.*; /** diff --git a/src/jexer/TWidget.java b/src/jexer/TWidget.java index 012ed75..0d7d5bb 100644 --- a/src/jexer/TWidget.java +++ b/src/jexer/TWidget.java @@ -2163,6 +2163,21 @@ public abstract class TWidget implements Comparable { return new TRadioGroup(this, x, y, label); } + /** + * Convenience function to add a radio button group to this + * container/window. + * + * @param x column relative to parent + * @param y row relative to parent + * @param width width of group + * @param label label to display on the group box + */ + public final TRadioGroup addRadioGroup(final int x, final int y, + final int width, final String label) { + + return new TRadioGroup(this, x, y, width, label); + } + /** * Convenience function to add a text field to this container/window. * diff --git a/src/jexer/backend/ECMA48Terminal.java b/src/jexer/backend/ECMA48Terminal.java index 29c0e61..429e698 100644 --- a/src/jexer/backend/ECMA48Terminal.java +++ b/src/jexer/backend/ECMA48Terminal.java @@ -50,7 +50,6 @@ import java.util.HashMap; import java.util.List; import javax.imageio.ImageIO; -import jexer.TImage; import jexer.bits.Cell; import jexer.bits.CellAttributes; import jexer.bits.Color; diff --git a/src/jexer/backend/GlyphMaker.java b/src/jexer/backend/GlyphMaker.java index 84316b7..e5fcc52 100644 --- a/src/jexer/backend/GlyphMaker.java +++ b/src/jexer/backend/GlyphMaker.java @@ -29,6 +29,7 @@ package jexer.backend; import java.awt.Font; +import java.awt.FontFormatException; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; @@ -149,13 +150,13 @@ class GlyphMakerFont { InputStream in = loader.getResourceAsStream(filename); fontRoot = Font.createFont(Font.TRUETYPE_FONT, in); font = fontRoot.deriveFont(Font.PLAIN, fontSize - 2); - } catch (java.awt.FontFormatException e) { + } catch (FontFormatException e) { // Ideally we would report an error here, either via System.err // or TExceptionDialog. However, I do not want GlyphMaker to // know about available backends, so we quietly fallback to // whatever is available as MONO. font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); - } catch (java.io.IOException e) { + } catch (IOException e) { // See comment above. font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); } diff --git a/src/jexer/bits/ColorTheme.java b/src/jexer/bits/ColorTheme.java index 60eb9bd..640ea25 100644 --- a/src/jexer/bits/ColorTheme.java +++ b/src/jexer/bits/ColorTheme.java @@ -178,8 +178,11 @@ public class ColorTheme { return; } - while (token.equals("bold") || token.equals("blink")) { - if (token.equals("bold")) { + while (token.equals("bold") + || token.equals("bright") + || token.equals("blink") + ) { + if (token.equals("bold") || token.equals("bright")) { bold = true; token = tokenizer.nextToken(); } diff --git a/src/jexer/menu/TSubMenu.java b/src/jexer/menu/TSubMenu.java index e285c5a..be281b5 100644 --- a/src/jexer/menu/TSubMenu.java +++ b/src/jexer/menu/TSubMenu.java @@ -212,6 +212,21 @@ public class TSubMenu extends TMenuItem { return menu.addItem(id, label, key); } + /** + * Convenience function to add a custom menu item. + * + * @param id menu item ID. Must be greater than 1024. + * @param label menu item label + * @param key global keyboard accelerator + * @param enabled default state for enabled + * @return the new menu item + */ + public TMenuItem addItem(final int id, final String label, + final TKeypress key, final boolean enabled) { + + return menu.addItem(id, label, key, enabled); + } + /** * Convenience function to add a menu item. * @@ -223,6 +238,20 @@ public class TSubMenu extends TMenuItem { return menu.addItem(id, label); } + /** + * Convenience function to add a menu item. + * + * @param id menu item ID. Must be greater than 1024. + * @param label menu item label + * @param enabled default state for enabled + * @return the new menu item + */ + public TMenuItem addItem(final int id, final String label, + final boolean enabled) { + + return menu.addItem(id, label, enabled); + } + /** * Convenience function to add one of the default menu items. * diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index 5d7eaaa..33a35ca 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -28,7 +28,7 @@ */ package jexer.tterminal; -import java.awt.Graphics2D; +import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -7374,7 +7374,7 @@ public class ECMA48 implements Runnable { newImage = new BufferedImage(textWidth, textHeight, BufferedImage.TYPE_INT_ARGB); - java.awt.Graphics gr = newImage.getGraphics(); + Graphics gr = newImage.getGraphics(); gr.drawImage(image.getSubimage(x * textWidth, y * textHeight, width, height), 0, 0, null, null); diff --git a/src/jexer/tterminal/Sixel.java b/src/jexer/tterminal/Sixel.java index 5768a0f..b91e77a 100644 --- a/src/jexer/tterminal/Sixel.java +++ b/src/jexer/tterminal/Sixel.java @@ -31,7 +31,6 @@ package jexer.tterminal; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import java.util.ArrayList; import java.util.HashMap; /** -- 2.27.0