custom command line terminal window, blinking in color theme, scrolling region resize
authorKevin Lamonte <kevin.lamonte@gmail.com>
Wed, 23 Aug 2017 19:44:11 +0000 (15:44 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Wed, 23 Aug 2017 19:44:11 +0000 (15:44 -0400)
src/jexer/TApplication.java
src/jexer/TTerminalWindow.java
src/jexer/bits/ColorTheme.java
src/jexer/tterminal/ECMA48.java

index b4c53ec3a5ea8899db40c6dbdda283abe85153f7..78a81ef85f7d59b261cb123e60774f3b93d46779 100644 (file)
@@ -2720,6 +2720,37 @@ public class TApplication implements Runnable {
         return new TTerminalWindow(this, x, y, flags);
     }
 
+    /**
+     * Convenience function to open a terminal window and execute a custom
+     * command line inside it.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param commandLine the command line to execute
+     * @return the terminal new window
+     */
+    public final TTerminalWindow openTerminal(final int x, final int y,
+        final String commandLine) {
+
+        return openTerminal(x, y, TWindow.RESIZABLE, commandLine);
+    }
+
+    /**
+     * Convenience function to open a terminal window and execute a custom
+     * command line inside it.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param flags mask of CENTERED, MODAL, or RESIZABLE
+     * @param commandLine the command line to execute
+     * @return the terminal new window
+     */
+    public final TTerminalWindow openTerminal(final int x, final int y,
+        final int flags, final String commandLine) {
+
+        return new TTerminalWindow(this, x, y, flags, commandLine);
+    }
+
     /**
      * Convenience function to spawn an file open box.
      *
index 3dc6a4c19e11322f22d4d20126721cb01f4b7209..31834eebc2324bc02f9e7261277d3994a108ca1b 100644 (file)
@@ -146,18 +146,11 @@ public class TTerminalWindow extends TScrollableWindow
     }
 
     /**
-     * Public constructor spawns a shell.
+     * Spawn the shell.
      *
-     * @param application TApplication that manages this window
-     * @param x column relative to parent
-     * @param y row relative to parent
-     * @param flags mask of CENTERED, MODAL, or RESIZABLE
+     * @param commandLine the command line to execute
      */
-    public TTerminalWindow(final TApplication application, final int x,
-        final int y, final int flags) {
-
-        super(application, i18n.getString("windowTitle"), x, y,
-            80 + 2, 24 + 2, flags);
+    private void spawnShell(final String commandLine) {
 
         vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
         setBottomValue(0);
@@ -166,43 +159,8 @@ public class TTerminalWindow extends TScrollableWindow
         ECMA48.DeviceType deviceType = ECMA48.DeviceType.XTERM;
 
         try {
-            String [] cmdShellWindows = {
-                "cmd.exe"
-            };
-
-            // You cannot run a login shell in a bare Process interactively,
-            // due to libc's behavior of buffering when stdin/stdout aren't a
-            // tty.  Use 'script' instead to run a shell in a pty.  And
-            // because BSD and GNU differ on the '-f' vs '-F' flags, we need
-            // two different commands.  Lovely.
-            String [] cmdShellGNU = {
-                "script", "-fqe", "/dev/null"
-            };
-            String [] cmdShellBSD = {
-                "script", "-q", "-F", "/dev/null"
-            };
-            String [] cmdShellPtypipe = {
-                "ptypipe", "/bin/bash", "--login"
-            };
-            // Spawn a shell and pass its I/O to the other constructor.
-
-            ProcessBuilder pb;
-            if ((System.getProperty("jexer.TTerminal.ptypipe") != null)
-                && (System.getProperty("jexer.TTerminal.ptypipe").
-                    equals("true"))
-            ) {
-                pb = new ProcessBuilder(cmdShellPtypipe);
-                ptypipe = true;
-            } else if (System.getProperty("os.name").startsWith("Windows")) {
-                pb = new ProcessBuilder(cmdShellWindows);
-            } else if (System.getProperty("os.name").startsWith("Mac")) {
-                pb = new ProcessBuilder(cmdShellBSD);
-            } else if (System.getProperty("os.name").startsWith("Linux")) {
-                pb = new ProcessBuilder(cmdShellGNU);
-            } else {
-                // When all else fails, assume GNU.
-                pb = new ProcessBuilder(cmdShellGNU);
-            }
+            String [] command = commandLine.split("\\s");
+            ProcessBuilder pb = new ProcessBuilder(command);
             Map<String, String> env = pb.environment();
             env.put("TERM", ECMA48.deviceTypeTerm(deviceType));
             env.put("LANG", ECMA48.deviceTypeLang(deviceType, "en"));
@@ -229,6 +187,91 @@ public class TTerminalWindow extends TScrollableWindow
         newStatusBar(i18n.getString("statusBarRunning"));
     }
 
+    /**
+     * Public constructor spawns a custom command line.
+     *
+     * @param application TApplication that manages this window
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param flags mask of CENTERED, MODAL, or RESIZABLE
+     * @param commandLine the command line to execute
+     */
+    public TTerminalWindow(final TApplication application, final int x,
+        final int y, final int flags, final String commandLine) {
+
+        super(application, i18n.getString("windowTitle"), x, y,
+            80 + 2, 24 + 2, flags);
+
+        String cmdShellWindows = "cmd.exe /c" + commandLine;
+        String cmdShellGNU = "script -fqe /dev/null -c " + commandLine;
+        String cmdShellBSD = "script -q -F /dev/null -c " + commandLine;
+        String cmdShellPtypipe = "ptypipe " + commandLine;
+
+        // Spawn a shell and pass its I/O to the other constructor.
+        if ((System.getProperty("jexer.TTerminal.ptypipe") != null)
+            && (System.getProperty("jexer.TTerminal.ptypipe").
+                equals("true"))
+        ) {
+            ptypipe = true;
+            spawnShell(cmdShellPtypipe);
+        } else if (System.getProperty("os.name").startsWith("Windows")) {
+            spawnShell(cmdShellWindows);
+        } else if (System.getProperty("os.name").startsWith("Mac")) {
+            spawnShell(cmdShellBSD);
+        } else if (System.getProperty("os.name").startsWith("Linux")) {
+            spawnShell(cmdShellGNU);
+        } else {
+            // When all else fails, assume GNU.
+            spawnShell(cmdShellGNU);
+        }
+    }
+
+    /**
+     * Public constructor spawns a shell.
+     *
+     * @param application TApplication that manages this window
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param flags mask of CENTERED, MODAL, or RESIZABLE
+     */
+    public TTerminalWindow(final TApplication application, final int x,
+        final int y, final int flags) {
+
+        super(application, i18n.getString("windowTitle"), x, y,
+            80 + 2, 24 + 2, flags);
+
+        String cmdShellWindows = "cmd.exe";
+
+        // You cannot run a login shell in a bare Process interactively, due
+        // to libc's behavior of buffering when stdin/stdout aren't a tty.
+        // Use 'script' instead to run a shell in a pty.  And because BSD and
+        // GNU differ on the '-f' vs '-F' flags, we need two different
+        // commands.  Lovely.
+        String cmdShellGNU = "script -fqe /dev/null";
+        String cmdShellBSD = "script -q -F /dev/null";
+
+        // ptypipe is another solution that permits dynamic window resizing.
+        String cmdShellPtypipe = "ptypipe /bin/bash --login";
+
+        // Spawn a shell and pass its I/O to the other constructor.
+        if ((System.getProperty("jexer.TTerminal.ptypipe") != null)
+            && (System.getProperty("jexer.TTerminal.ptypipe").
+                equals("true"))
+        ) {
+            ptypipe = true;
+            spawnShell(cmdShellPtypipe);
+        } else if (System.getProperty("os.name").startsWith("Windows")) {
+            spawnShell(cmdShellWindows);
+        } else if (System.getProperty("os.name").startsWith("Mac")) {
+            spawnShell(cmdShellBSD);
+        } else if (System.getProperty("os.name").startsWith("Linux")) {
+            spawnShell(cmdShellGNU);
+        } else {
+            // When all else fails, assume GNU.
+            spawnShell(cmdShellGNU);
+        }
+    }
+
     /**
      * Terminate the child of the 'script' process used on POSIX.  This may
      * or may not work.
index 1c7c62092c4f7ab7d20048fbad027e380066c4d4..e55398a8235f38b4677f2dc9cd81edf514cdf735 100644 (file)
@@ -130,27 +130,39 @@ public final class ColorTheme {
         String line = bufferedReader.readLine();
         for (; line != null; line = bufferedReader.readLine()) {
             String key;
-            String bold;
+            boolean bold = false;
+            boolean blink = false;
             String foreColor;
             String backColor;
+            String token;
 
             // Look for lines that resemble:
             //     "key = blah on blah"
             //     "key = bold blah on blah"
+            //     "key = blink bold blah on blah"
+            //     "key = bold blink blah on blah"
+            //     "key = blink blah on blah"
             StringTokenizer tokenizer = new StringTokenizer(line);
             key = tokenizer.nextToken();
             if (!tokenizer.nextToken().equals("=")) {
                 // Skip this line
                 continue;
             }
-            bold = tokenizer.nextToken();
-            if (!bold.toLowerCase().equals("bold")) {
-                // "key = blah on blah"
-                foreColor = bold;
-            } else {
-                // "key = bold blah on blah"
-                foreColor = tokenizer.nextToken().toLowerCase();
+            token = tokenizer.nextToken();
+            while (token.equals("bold") || token.equals("blink")) {
+                if (token.equals("bold")) {
+                    bold = true;
+                    token = tokenizer.nextToken();
+                }
+                if (token.equals("blink")) {
+                    blink = true;
+                    token = tokenizer.nextToken();
+                }
             }
+
+            // What's left is "blah on blah" or "blah"
+            foreColor = token.toLowerCase();
+
             if (!tokenizer.nextToken().toLowerCase().equals("on")) {
                 // Skip this line
                 continue;
@@ -158,9 +170,12 @@ public final class ColorTheme {
             backColor = tokenizer.nextToken().toLowerCase();
 
             CellAttributes color = new CellAttributes();
-            if (bold.equals("bold")) {
+            if (bold) {
                 color.setBold(true);
             }
+            if (blink) {
+                color.setBlink(true);
+            }
             color.setForeColor(Color.getColor(foreColor));
             color.setBackColor(Color.getColor(backColor));
             colors.put(key, color);
index 96f4e4083cc0bba8085d2df1311492214142017d..31bb5b1573cda8d66922036a40027e117b52e83f 100644 (file)
@@ -567,9 +567,11 @@ public class ECMA48 implements Runnable {
      * @param height the new height
      */
     public final void setHeight(final int height) {
+        int delta = height - this.height;
         this.height = height;
-        if (scrollRegionBottom >= height) {
-            scrollRegionBottom = height - 1;
+        scrollRegionBottom += delta;
+        if (scrollRegionBottom < 0) {
+            scrollRegionBottom = height;
         }
         if (scrollRegionTop >= scrollRegionBottom) {
             scrollRegionTop = 0;