TStatusBar
authorKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 18 Mar 2017 20:46:35 +0000 (16:46 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 18 Mar 2017 20:46:35 +0000 (16:46 -0400)
20 files changed:
README.md
docs/TODO.md
src/jexer/TApplication.java
src/jexer/TCommand.java
src/jexer/TEditColorThemeWindow.java
src/jexer/TLabel.java
src/jexer/TStatusBar.java [new file with mode: 0644]
src/jexer/TTerminalWindow.java
src/jexer/TWidget.java
src/jexer/TWindow.java
src/jexer/bits/ColorTheme.java
src/jexer/bits/GraphicsChars.java
src/jexer/demos/DemoCheckboxWindow.java
src/jexer/demos/DemoMainWindow.java
src/jexer/demos/DemoMsgBoxWindow.java
src/jexer/demos/DemoTextFieldWindow.java [new file with mode: 0644]
src/jexer/demos/DemoTextWindow.java
src/jexer/demos/DemoTreeViewWindow.java
src/jexer/io/package-info.java
src/jexer/tterminal/ECMA48.java

index 2f146cc4f3b585d4678eeb3bd2c350109099ed32..22436558fb3f653491423ec7a26bb600fb3855aa 100644 (file)
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Jexer currently supports three backends:
   sequences generated by the library itself: ncurses is not required
   or linked to.  xterm mouse tracking using UTF8 and SGR coordinates
   are supported.  For the demo application, this is the default
   sequences generated by the library itself: ncurses is not required
   or linked to.  xterm mouse tracking using UTF8 and SGR coordinates
   are supported.  For the demo application, this is the default
-  backend on non-Windows platforms.
+  backend on non-Windows/non-Mac platforms.
 
 * The same command-line ECMA-48 / ANSI X3.64 type terminal as above,
   but to any general InputStream/OutputStream or Reader/Writer.  See
 
 * The same command-line ECMA-48 / ANSI X3.64 type terminal as above,
   but to any general InputStream/OutputStream or Reader/Writer.  See
@@ -23,9 +23,9 @@ Jexer currently supports three backends:
   character encoding than the default UTF-8.
 
 * Java Swing UI.  This backend can be selected by setting
   character encoding than the default UTF-8.
 
 * Java Swing UI.  This backend can be selected by setting
-  jexer.Swing=true.  The default window size for Swing is 132x40,
-  which is set in jexer.session.SwingSession.  For the demo
-  application, this is the default backend on Windows platforms.
+  jexer.Swing=true.  The default window size for Swing is 80x25, which
+  is set in jexer.session.SwingSession.  For the demo application,
+  this is the default backend on Windows and Mac platforms.
 
 Additional backends can be created by subclassing
 jexer.backend.Backend and passing it into the TApplication
 
 Additional backends can be created by subclassing
 jexer.backend.Backend and passing it into the TApplication
@@ -180,10 +180,11 @@ ambiguous.  This section describes such issues.
     input (see the ENABLE_LINE_INPUT flag for GetConsoleMode() and
     SetConsoleMode()).
 
     input (see the ENABLE_LINE_INPUT flag for GetConsoleMode() and
     SetConsoleMode()).
 
-  - TTerminalWindow launches 'script -fqe /dev/null' on non-Windows
-    platforms.  This is a workaround for the C library behavior of
-    checking for a tty: script launches $SHELL in a pseudo-tty.  This
-    works on Linux but might not on other Posix-y platforms.
+  - TTerminalWindow launches 'script -fqe /dev/null' or 'script -q -F
+    /dev/null' on non-Windows platforms.  This is a workaround for the
+    C library behavior of checking for a tty: script launches $SHELL
+    in a pseudo-tty.  This works on Linux and Mac but might not on
+    other Posix-y platforms.
 
   - Closing a TTerminalWindow without exiting the process inside it
     may result in a zombie 'script' process.
 
   - Closing a TTerminalWindow without exiting the process inside it
     may result in a zombie 'script' process.
index 47f07d0575cb97f0538ff90f9005689663ec2bcd..f3e1233621569b4a9f783f8f03331ef80d65a8cc 100644 (file)
@@ -7,11 +7,6 @@ Roadmap
 
 0.0.4
 
 
 0.0.4
 
-- TStatusBar
-  - TMenu version
-  - TWindow version
-  - Click mouse
-
 - TWindow
   - "Smart placement" for new windows
 
 - TWindow
   - "Smart placement" for new windows
 
index 91d2e98d7c039da4aeb9d01c84bb33385f762772..ff9c19a0b00c9b7d18fa554fc80787a4edb9dabc 100644 (file)
@@ -58,12 +58,17 @@ import jexer.io.Screen;
 import jexer.menu.TMenu;
 import jexer.menu.TMenuItem;
 import static jexer.TCommand.*;
 import jexer.menu.TMenu;
 import jexer.menu.TMenuItem;
 import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * TApplication sets up a full Text User Interface application.
  */
 public class TApplication implements Runnable {
 
 
 /**
  * TApplication sets up a full Text User Interface application.
  */
 public class TApplication implements Runnable {
 
+    // ------------------------------------------------------------------------
+    // Public constants -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * If true, emit thread stuff to System.err.
      */
     /**
      * If true, emit thread stuff to System.err.
      */
@@ -94,6 +99,10 @@ public class TApplication implements Runnable {
         XTERM
     }
 
         XTERM
     }
 
+    // ------------------------------------------------------------------------
+    // Primary/secondary event handlers ---------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * WidgetEventHandler is the main event consumer loop.  There are at most
      * two such threads in existence: the primary for normal case and a
     /**
      * WidgetEventHandler is the main event consumer loop.  There are at most
      * two such threads in existence: the primary for normal case and a
@@ -365,6 +374,10 @@ public class TApplication implements Runnable {
         lockoutHandleEvent = false;
     }
 
         lockoutHandleEvent = false;
     }
 
+    // ------------------------------------------------------------------------
+    // TApplication attributes ------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Access to the physical screen, keyboard, and mouse.
      */
     /**
      * Access to the physical screen, keyboard, and mouse.
      */
@@ -508,6 +521,23 @@ public class TApplication implements Runnable {
         return desktopBottom;
     }
 
         return desktopBottom;
     }
 
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Display the about dialog.
+     */
+    protected void showAboutDialog() {
+        messageBox("About", "Jexer Version " +
+            this.getClass().getPackage().getImplementationVersion(),
+            TMessageBox.Type.OK);
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Public constructor.
      *
     /**
      * Public constructor.
      *
@@ -620,6 +650,10 @@ public class TApplication implements Runnable {
         (new Thread(primaryEventHandler)).start();
     }
 
         (new Thread(primaryEventHandler)).start();
     }
 
+    // ------------------------------------------------------------------------
+    // Screen refresh loop ----------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Invert the cell color at a position.  This is used to track the mouse.
      *
     /**
      * Invert the cell color at a position.  This is used to track the mouse.
      *
@@ -681,6 +715,7 @@ public class TApplication implements Runnable {
         // Draw each window in reverse Z order
         List<TWindow> sorted = new LinkedList<TWindow>(windows);
         Collections.sort(sorted);
         // Draw each window in reverse Z order
         List<TWindow> sorted = new LinkedList<TWindow>(windows);
         Collections.sort(sorted);
+        TWindow topLevel = sorted.get(0);
         Collections.reverse(sorted);
         for (TWindow window: sorted) {
             window.drawChildren();
         Collections.reverse(sorted);
         for (TWindow window: sorted) {
             window.drawChildren();
@@ -699,6 +734,7 @@ public class TApplication implements Runnable {
             if (menu.isActive()) {
                 menuColor = theme.getColor("tmenu.highlighted");
                 menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted");
             if (menu.isActive()) {
                 menuColor = theme.getColor("tmenu.highlighted");
                 menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted");
+                topLevel = menu;
             } else {
                 menuColor = theme.getColor("tmenu");
                 menuMnemonicColor = theme.getColor("tmenu.mnemonic");
             } else {
                 menuColor = theme.getColor("tmenu");
                 menuMnemonicColor = theme.getColor("tmenu.mnemonic");
@@ -725,6 +761,20 @@ public class TApplication implements Runnable {
             menu.drawChildren();
         }
 
             menu.drawChildren();
         }
 
+        // Draw the status bar of the top-level window
+        TStatusBar statusBar = topLevel.getStatusBar();
+        if (statusBar != null) {
+            getScreen().resetClipping();
+            statusBar.setWidth(getScreen().getWidth());
+            statusBar.setY(getScreen().getHeight() - topLevel.getY());
+            statusBar.draw();
+        } else {
+            CellAttributes barColor = new CellAttributes();
+            barColor.setTo(getTheme().getColor("tstatusbar.text"));
+            getScreen().hLineXY(0, desktopBottom, getScreen().getWidth(), ' ',
+                barColor);
+        }
+
         // Draw the mouse pointer
         invertCell(mouseX, mouseY);
         oldMouseX = mouseX;
         // Draw the mouse pointer
         invertCell(mouseX, mouseY);
         oldMouseX = mouseX;
@@ -754,6 +804,10 @@ public class TApplication implements Runnable {
         repaint = false;
     }
 
         repaint = false;
     }
 
+    // ------------------------------------------------------------------------
+    // Main loop --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Run this application until it exits.
      */
     /**
      * Run this application until it exits.
      */
@@ -988,7 +1042,7 @@ public class TApplication implements Runnable {
                 }
             }
 
                 }
             }
 
-            if (!windowWillShortcut) {
+            if (!windowWillShortcut && !modalWindowActive()) {
                 TKeypress keypressLowercase = keypress.getKey().toLowerCase();
                 TMenuItem item = null;
                 synchronized (accelerators) {
                 TKeypress keypressLowercase = keypress.getKey().toLowerCase();
                 TMenuItem item = null;
                 synchronized (accelerators) {
@@ -1001,11 +1055,11 @@ public class TApplication implements Runnable {
                         return;
                     }
                 }
                         return;
                     }
                 }
-            }
 
 
-            // Handle the keypress
-            if (onKeypress(keypress)) {
-                return;
+                // Handle the keypress
+                if (onKeypress(keypress)) {
+                    return;
+                }
             }
         }
 
             }
         }
 
@@ -1120,31 +1174,9 @@ public class TApplication implements Runnable {
         }
     }
 
         }
     }
 
-    /**
-     * Get the amount of time I can sleep before missing a Timer tick.
-     *
-     * @param timeout = initial (maximum) timeout in millis
-     * @return number of milliseconds between now and the next timer event
-     */
-    private long getSleepTime(final long timeout) {
-        Date now = new Date();
-        long nowTime = now.getTime();
-        long sleepTime = timeout;
-        for (TTimer timer: timers) {
-            long nextTickTime = timer.getNextTick().getTime();
-            if (nextTickTime < nowTime) {
-                return 0;
-            }
-
-            long timeDifference = nextTickTime - nowTime;
-            if (timeDifference < sleepTime) {
-                sleepTime = timeDifference;
-            }
-        }
-        assert (sleepTime >= 0);
-        assert (sleepTime <= timeout);
-        return sleepTime;
-    }
+    // ------------------------------------------------------------------------
+    // TWindow management -----------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Close window.  Note that the window's destructor is NOT called by this
 
     /**
      * Close window.  Note that the window's destructor is NOT called by this
@@ -1254,9 +1286,11 @@ public class TApplication implements Runnable {
      */
     public final void addWindow(final TWindow window) {
         synchronized (windows) {
      */
     public final void addWindow(final TWindow window) {
         synchronized (windows) {
-            // Do not allow a modal window to spawn a non-modal window
-            if ((windows.size() > 0) && (windows.get(0).isModal())) {
-                assert (window.isModal());
+            // Do not allow a modal window to spawn a non-modal window.  If a
+            // modal window is active, then this window will become modal
+            // too.
+            if (modalWindowActive()) {
+                window.flags |= TWindow.MODAL;
             }
             for (TWindow w: windows) {
                 if (w.isActive()) {
             }
             for (TWindow w: windows) {
                 if (w.isActive()) {
@@ -1281,9 +1315,119 @@ public class TApplication implements Runnable {
         if (windows.size() == 0) {
             return false;
         }
         if (windows.size() == 0) {
             return false;
         }
-        return windows.get(windows.size() - 1).isModal();
+
+        for (TWindow w: windows) {
+            if (w.isModal()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Close all open windows.
+     */
+    private void closeAllWindows() {
+        // Don't do anything if we are in the menu
+        if (activeMenu != null) {
+            return;
+        }
+        while (windows.size() > 0) {
+            closeWindow(windows.get(0));
+        }
     }
 
     }
 
+    /**
+     * Re-layout the open windows as non-overlapping tiles.  This produces
+     * almost the same results as Turbo Pascal 7.0's IDE.
+     */
+    private void tileWindows() {
+        synchronized (windows) {
+            // Don't do anything if we are in the menu
+            if (activeMenu != null) {
+                return;
+            }
+            int z = windows.size();
+            if (z == 0) {
+                return;
+            }
+            int a = 0;
+            int b = 0;
+            a = (int)(Math.sqrt(z));
+            int c = 0;
+            while (c < a) {
+                b = (z - c) / a;
+                if (((a * b) + c) == z) {
+                    break;
+                }
+                c++;
+            }
+            assert (a > 0);
+            assert (b > 0);
+            assert (c < a);
+            int newWidth = (getScreen().getWidth() / a);
+            int newHeight1 = ((getScreen().getHeight() - 1) / b);
+            int newHeight2 = ((getScreen().getHeight() - 1) / (b + c));
+
+            List<TWindow> sorted = new LinkedList<TWindow>(windows);
+            Collections.sort(sorted);
+            Collections.reverse(sorted);
+            for (int i = 0; i < sorted.size(); i++) {
+                int logicalX = i / b;
+                int logicalY = i % b;
+                if (i >= ((a - 1) * b)) {
+                    logicalX = a - 1;
+                    logicalY = i - ((a - 1) * b);
+                }
+
+                TWindow w = sorted.get(i);
+                w.setX(logicalX * newWidth);
+                w.setWidth(newWidth);
+                if (i >= ((a - 1) * b)) {
+                    w.setY((logicalY * newHeight2) + 1);
+                    w.setHeight(newHeight2);
+                } else {
+                    w.setY((logicalY * newHeight1) + 1);
+                    w.setHeight(newHeight1);
+                }
+            }
+        }
+    }
+
+    /**
+     * Re-layout the open windows as overlapping cascaded windows.
+     */
+    private void cascadeWindows() {
+        synchronized (windows) {
+            // Don't do anything if we are in the menu
+            if (activeMenu != null) {
+                return;
+            }
+            int x = 0;
+            int y = 1;
+            List<TWindow> sorted = new LinkedList<TWindow>(windows);
+            Collections.sort(sorted);
+            Collections.reverse(sorted);
+            for (TWindow window: sorted) {
+                window.setX(x);
+                window.setY(y);
+                x++;
+                y++;
+                if (x > getScreen().getWidth()) {
+                    x = 0;
+                }
+                if (y >= getScreen().getHeight()) {
+                    y = 1;
+                }
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // TMenu management -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Check if a mouse event would hit either the active menu or any open
      * sub-menus.
     /**
      * Check if a mouse event would hit either the active menu or any open
      * sub-menus.
@@ -1485,127 +1629,6 @@ public class TApplication implements Runnable {
         }
     }
 
         }
     }
 
-    /**
-     * Method that TApplication subclasses can override to handle menu or
-     * posted command events.
-     *
-     * @param command command event
-     * @return if true, this event was consumed
-     */
-    protected boolean onCommand(final TCommandEvent command) {
-        // Default: handle cmExit
-        if (command.equals(cmExit)) {
-            if (messageBox("Confirmation", "Exit application?",
-                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
-                quit = true;
-            }
-            return true;
-        }
-
-        if (command.equals(cmShell)) {
-            openTerminal(0, 0, TWindow.RESIZABLE);
-            return true;
-        }
-
-        if (command.equals(cmTile)) {
-            tileWindows();
-            return true;
-        }
-        if (command.equals(cmCascade)) {
-            cascadeWindows();
-            return true;
-        }
-        if (command.equals(cmCloseAll)) {
-            closeAllWindows();
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Display the about dialog.
-     */
-    protected void showAboutDialog() {
-        messageBox("About", "Jexer Version " +
-            this.getClass().getPackage().getImplementationVersion(),
-            TMessageBox.Type.OK);
-    }
-
-    /**
-     * Method that TApplication subclasses can override to handle menu
-     * events.
-     *
-     * @param menu menu event
-     * @return if true, this event was consumed
-     */
-    protected boolean onMenu(final TMenuEvent menu) {
-
-        // Default: handle MID_EXIT
-        if (menu.getId() == TMenu.MID_EXIT) {
-            if (messageBox("Confirmation", "Exit application?",
-                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
-                quit = true;
-            }
-            return true;
-        }
-
-        if (menu.getId() == TMenu.MID_SHELL) {
-            openTerminal(0, 0, TWindow.RESIZABLE);
-            return true;
-        }
-
-        if (menu.getId() == TMenu.MID_TILE) {
-            tileWindows();
-            return true;
-        }
-        if (menu.getId() == TMenu.MID_CASCADE) {
-            cascadeWindows();
-            return true;
-        }
-        if (menu.getId() == TMenu.MID_CLOSE_ALL) {
-            closeAllWindows();
-            return true;
-        }
-        if (menu.getId() == TMenu.MID_ABOUT) {
-            showAboutDialog();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Method that TApplication subclasses can override to handle keystrokes.
-     *
-     * @param keypress keystroke event
-     * @return if true, this event was consumed
-     */
-    protected boolean onKeypress(final TKeypressEvent keypress) {
-        // Default: only menu shortcuts
-
-        // Process Alt-F, Alt-E, etc. menu shortcut keys
-        if (!keypress.getKey().isFnKey()
-            && keypress.getKey().isAlt()
-            && !keypress.getKey().isCtrl()
-            && (activeMenu == null)
-        ) {
-
-            assert (subMenus.size() == 0);
-
-            for (TMenu menu: menus) {
-                if (Character.toLowerCase(menu.getMnemonic().getShortcut())
-                    == Character.toLowerCase(keypress.getKey().getChar())
-                ) {
-                    activeMenu = menu;
-                    menu.setActive(true);
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
     /**
      * Add a menu item to the global list.  If it has a keyboard accelerator,
      * that will be added the global hash.
     /**
      * Add a menu item to the global list.  If it has a keyboard accelerator,
      * that will be added the global hash.
@@ -1738,6 +1761,9 @@ public class TApplication implements Runnable {
         fileMenu.addSeparator();
         fileMenu.addDefaultItem(TMenu.MID_SHELL);
         fileMenu.addDefaultItem(TMenu.MID_EXIT);
         fileMenu.addSeparator();
         fileMenu.addDefaultItem(TMenu.MID_SHELL);
         fileMenu.addDefaultItem(TMenu.MID_EXIT);
+        TStatusBar statusBar = fileMenu.newStatusBar("File-management " +
+            "commands (Open, Save, Print, etc.)");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return fileMenu;
     }
 
         return fileMenu;
     }
 
@@ -1752,6 +1778,9 @@ public class TApplication implements Runnable {
         editMenu.addDefaultItem(TMenu.MID_COPY);
         editMenu.addDefaultItem(TMenu.MID_PASTE);
         editMenu.addDefaultItem(TMenu.MID_CLEAR);
         editMenu.addDefaultItem(TMenu.MID_COPY);
         editMenu.addDefaultItem(TMenu.MID_PASTE);
         editMenu.addDefaultItem(TMenu.MID_CLEAR);
+        TStatusBar statusBar = editMenu.newStatusBar("Editor operations, " +
+            "undo, and Clipboard access");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return editMenu;
     }
 
         return editMenu;
     }
 
@@ -1771,6 +1800,9 @@ public class TApplication implements Runnable {
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_NEXT);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_PREVIOUS);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_CLOSE);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_NEXT);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_PREVIOUS);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_CLOSE);
+        TStatusBar statusBar = windowMenu.newStatusBar("Open, arrange, and " +
+            "list windows");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return windowMenu;
     }
 
         return windowMenu;
     }
 
@@ -1789,106 +1821,156 @@ public class TApplication implements Runnable {
         helpMenu.addDefaultItem(TMenu.MID_HELP_ACTIVE_FILE);
         helpMenu.addSeparator();
         helpMenu.addDefaultItem(TMenu.MID_ABOUT);
         helpMenu.addDefaultItem(TMenu.MID_HELP_ACTIVE_FILE);
         helpMenu.addSeparator();
         helpMenu.addDefaultItem(TMenu.MID_ABOUT);
+        TStatusBar statusBar = helpMenu.newStatusBar("Access online help");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return helpMenu;
     }
 
         return helpMenu;
     }
 
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
     /**
-     * Close all open windows.
+     * Method that TApplication subclasses can override to handle menu or
+     * posted command events.
+     *
+     * @param command command event
+     * @return if true, this event was consumed
      */
      */
-    private void closeAllWindows() {
-        // Don't do anything if we are in the menu
-        if (activeMenu != null) {
-            return;
+    protected boolean onCommand(final TCommandEvent command) {
+        // Default: handle cmExit
+        if (command.equals(cmExit)) {
+            if (messageBox("Confirmation", "Exit application?",
+                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
+                quit = true;
+            }
+            return true;
         }
         }
-        while (windows.size() > 0) {
-            closeWindow(windows.get(0));
+
+        if (command.equals(cmShell)) {
+            openTerminal(0, 0, TWindow.RESIZABLE);
+            return true;
+        }
+
+        if (command.equals(cmTile)) {
+            tileWindows();
+            return true;
+        }
+        if (command.equals(cmCascade)) {
+            cascadeWindows();
+            return true;
+        }
+        if (command.equals(cmCloseAll)) {
+            closeAllWindows();
+            return true;
         }
         }
+
+        return false;
     }
 
     /**
     }
 
     /**
-     * Re-layout the open windows as non-overlapping tiles.  This produces
-     * almost the same results as Turbo Pascal 7.0's IDE.
+     * Method that TApplication subclasses can override to handle menu
+     * events.
+     *
+     * @param menu menu event
+     * @return if true, this event was consumed
      */
      */
-    private void tileWindows() {
-        synchronized (windows) {
-            // Don't do anything if we are in the menu
-            if (activeMenu != null) {
-                return;
-            }
-            int z = windows.size();
-            if (z == 0) {
-                return;
-            }
-            int a = 0;
-            int b = 0;
-            a = (int)(Math.sqrt(z));
-            int c = 0;
-            while (c < a) {
-                b = (z - c) / a;
-                if (((a * b) + c) == z) {
-                    break;
-                }
-                c++;
+    protected boolean onMenu(final TMenuEvent menu) {
+
+        // Default: handle MID_EXIT
+        if (menu.getId() == TMenu.MID_EXIT) {
+            if (messageBox("Confirmation", "Exit application?",
+                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
+                quit = true;
             }
             }
-            assert (a > 0);
-            assert (b > 0);
-            assert (c < a);
-            int newWidth = (getScreen().getWidth() / a);
-            int newHeight1 = ((getScreen().getHeight() - 1) / b);
-            int newHeight2 = ((getScreen().getHeight() - 1) / (b + c));
+            return true;
+        }
 
 
-            List<TWindow> sorted = new LinkedList<TWindow>(windows);
-            Collections.sort(sorted);
-            Collections.reverse(sorted);
-            for (int i = 0; i < sorted.size(); i++) {
-                int logicalX = i / b;
-                int logicalY = i % b;
-                if (i >= ((a - 1) * b)) {
-                    logicalX = a - 1;
-                    logicalY = i - ((a - 1) * b);
-                }
+        if (menu.getId() == TMenu.MID_SHELL) {
+            openTerminal(0, 0, TWindow.RESIZABLE);
+            return true;
+        }
 
 
-                TWindow w = sorted.get(i);
-                w.setX(logicalX * newWidth);
-                w.setWidth(newWidth);
-                if (i >= ((a - 1) * b)) {
-                    w.setY((logicalY * newHeight2) + 1);
-                    w.setHeight(newHeight2);
-                } else {
-                    w.setY((logicalY * newHeight1) + 1);
-                    w.setHeight(newHeight1);
+        if (menu.getId() == TMenu.MID_TILE) {
+            tileWindows();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_CASCADE) {
+            cascadeWindows();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_CLOSE_ALL) {
+            closeAllWindows();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_ABOUT) {
+            showAboutDialog();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Method that TApplication subclasses can override to handle keystrokes.
+     *
+     * @param keypress keystroke event
+     * @return if true, this event was consumed
+     */
+    protected boolean onKeypress(final TKeypressEvent keypress) {
+        // Default: only menu shortcuts
+
+        // Process Alt-F, Alt-E, etc. menu shortcut keys
+        if (!keypress.getKey().isFnKey()
+            && keypress.getKey().isAlt()
+            && !keypress.getKey().isCtrl()
+            && (activeMenu == null)
+            && !modalWindowActive()
+        ) {
+
+            assert (subMenus.size() == 0);
+
+            for (TMenu menu: menus) {
+                if (Character.toLowerCase(menu.getMnemonic().getShortcut())
+                    == Character.toLowerCase(keypress.getKey().getChar())
+                ) {
+                    activeMenu = menu;
+                    menu.setActive(true);
+                    return true;
                 }
             }
         }
                 }
             }
         }
+
+        return false;
     }
 
     }
 
+    // ------------------------------------------------------------------------
+    // TTimer management ------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
     /**
-     * Re-layout the open windows as overlapping cascaded windows.
+     * Get the amount of time I can sleep before missing a Timer tick.
+     *
+     * @param timeout = initial (maximum) timeout in millis
+     * @return number of milliseconds between now and the next timer event
      */
      */
-    private void cascadeWindows() {
-        synchronized (windows) {
-            // Don't do anything if we are in the menu
-            if (activeMenu != null) {
-                return;
+    private long getSleepTime(final long timeout) {
+        Date now = new Date();
+        long nowTime = now.getTime();
+        long sleepTime = timeout;
+        for (TTimer timer: timers) {
+            long nextTickTime = timer.getNextTick().getTime();
+            if (nextTickTime < nowTime) {
+                return 0;
             }
             }
-            int x = 0;
-            int y = 1;
-            List<TWindow> sorted = new LinkedList<TWindow>(windows);
-            Collections.sort(sorted);
-            Collections.reverse(sorted);
-            for (TWindow window: sorted) {
-                window.setX(x);
-                window.setY(y);
-                x++;
-                y++;
-                if (x > getScreen().getWidth()) {
-                    x = 0;
-                }
-                if (y >= getScreen().getHeight()) {
-                    y = 1;
-                }
+
+            long timeDifference = nextTickTime - nowTime;
+            if (timeDifference < sleepTime) {
+                sleepTime = timeDifference;
             }
         }
             }
         }
+        assert (sleepTime >= 0);
+        assert (sleepTime <= timeout);
+        return sleepTime;
     }
 
     /**
     }
 
     /**
@@ -1920,6 +2002,10 @@ public class TApplication implements Runnable {
         }
     }
 
         }
     }
 
+    // ------------------------------------------------------------------------
+    // Other TWindow constructors ---------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Convenience function to spawn a message box.
      *
     /**
      * Convenience function to spawn a message box.
      *
index 0bb3f95bfc45c132d5dc5f248735d441e4a08c34..584994f948cdfd5dd2abb3329eb791981bd50958 100644 (file)
@@ -39,82 +39,87 @@ public class TCommand {
      * Immediately abort the application (e.g. remote side closed
      * connection).
      */
      * Immediately abort the application (e.g. remote side closed
      * connection).
      */
-    public static final int ABORT       = 1;
+    public static final int ABORT               = 1;
 
     /**
      * File open dialog.
      */
 
     /**
      * File open dialog.
      */
-    public static final int OPEN        = 2;
+    public static final int OPEN                = 2;
 
     /**
      * Exit application.
      */
 
     /**
      * Exit application.
      */
-    public static final int EXIT        = 3;
+    public static final int EXIT                = 3;
 
     /**
      * Spawn OS shell window.
      */
 
     /**
      * Spawn OS shell window.
      */
-    public static final int SHELL       = 4;
+    public static final int SHELL               = 4;
 
     /**
      * Cut selected text and copy to the clipboard.
      */
 
     /**
      * Cut selected text and copy to the clipboard.
      */
-    public static final int CUT         = 5;
+    public static final int CUT                 = 5;
 
     /**
      * Copy selected text to clipboard.
      */
 
     /**
      * Copy selected text to clipboard.
      */
-    public static final int COPY        = 6;
+    public static final int COPY                = 6;
 
     /**
      * Paste from clipboard.
      */
 
     /**
      * Paste from clipboard.
      */
-    public static final int PASTE       = 7;
+    public static final int PASTE               = 7;
 
     /**
      * Clear selected text without copying it to the clipboard.
      */
 
     /**
      * Clear selected text without copying it to the clipboard.
      */
-    public static final int CLEAR       = 8;
+    public static final int CLEAR               = 8;
 
     /**
      * Tile windows.
      */
 
     /**
      * Tile windows.
      */
-    public static final int TILE        = 9;
+    public static final int TILE                = 9;
 
     /**
      * Cascade windows.
      */
 
     /**
      * Cascade windows.
      */
-    public static final int CASCADE     = 10;
+    public static final int CASCADE             = 10;
 
     /**
      * Close all windows.
      */
 
     /**
      * Close all windows.
      */
-    public static final int CLOSE_ALL   = 11;
+    public static final int CLOSE_ALL           = 11;
 
     /**
      * Move (move/resize) window.
      */
 
     /**
      * Move (move/resize) window.
      */
-    public static final int WINDOW_MOVE = 12;
+    public static final int WINDOW_MOVE         = 12;
 
     /**
      * Zoom (maximize/restore) window.
      */
 
     /**
      * Zoom (maximize/restore) window.
      */
-    public static final int WINDOW_ZOOM = 13;
+    public static final int WINDOW_ZOOM         = 13;
 
     /**
      * Next window (like Alt-TAB).
      */
 
     /**
      * Next window (like Alt-TAB).
      */
-    public static final int WINDOW_NEXT = 14;
+    public static final int WINDOW_NEXT         = 14;
 
     /**
      * Previous window (like Shift-Alt-TAB).
      */
 
     /**
      * Previous window (like Shift-Alt-TAB).
      */
-    public static final int WINDOW_PREVIOUS = 15;
+    public static final int WINDOW_PREVIOUS     = 15;
 
     /**
      * Close window.
      */
 
     /**
      * Close window.
      */
-    public static final int WINDOW_CLOSE = 16;
+    public static final int WINDOW_CLOSE        = 16;
+
+    /**
+     * Enter help system.
+     */
+    public static final int HELP                = 20;
 
     /**
      * Type of command, one of EXIT, CASCADE, etc.
 
     /**
      * Type of command, one of EXIT, CASCADE, etc.
@@ -166,22 +171,23 @@ public class TCommand {
         return type;
     }
 
         return type;
     }
 
-    public static final TCommand cmAbort      = new TCommand(ABORT);
-    public static final TCommand cmExit       = new TCommand(EXIT);
-    public static final TCommand cmQuit       = new TCommand(EXIT);
-    public static final TCommand cmOpen       = new TCommand(OPEN);
-    public static final TCommand cmShell      = new TCommand(SHELL);
-    public static final TCommand cmCut        = new TCommand(CUT);
-    public static final TCommand cmCopy       = new TCommand(COPY);
-    public static final TCommand cmPaste      = new TCommand(PASTE);
-    public static final TCommand cmClear      = new TCommand(CLEAR);
-    public static final TCommand cmTile       = new TCommand(TILE);
-    public static final TCommand cmCascade    = new TCommand(CASCADE);
-    public static final TCommand cmCloseAll   = new TCommand(CLOSE_ALL);
-    public static final TCommand cmWindowMove = new TCommand(WINDOW_MOVE);
-    public static final TCommand cmWindowZoom = new TCommand(WINDOW_ZOOM);
-    public static final TCommand cmWindowNext = new TCommand(WINDOW_NEXT);
+    public static final TCommand cmAbort        = new TCommand(ABORT);
+    public static final TCommand cmExit         = new TCommand(EXIT);
+    public static final TCommand cmQuit         = new TCommand(EXIT);
+    public static final TCommand cmOpen         = new TCommand(OPEN);
+    public static final TCommand cmShell        = new TCommand(SHELL);
+    public static final TCommand cmCut          = new TCommand(CUT);
+    public static final TCommand cmCopy         = new TCommand(COPY);
+    public static final TCommand cmPaste        = new TCommand(PASTE);
+    public static final TCommand cmClear        = new TCommand(CLEAR);
+    public static final TCommand cmTile         = new TCommand(TILE);
+    public static final TCommand cmCascade      = new TCommand(CASCADE);
+    public static final TCommand cmCloseAll     = new TCommand(CLOSE_ALL);
+    public static final TCommand cmWindowMove   = new TCommand(WINDOW_MOVE);
+    public static final TCommand cmWindowZoom   = new TCommand(WINDOW_ZOOM);
+    public static final TCommand cmWindowNext   = new TCommand(WINDOW_NEXT);
     public static final TCommand cmWindowPrevious = new TCommand(WINDOW_PREVIOUS);
     public static final TCommand cmWindowPrevious = new TCommand(WINDOW_PREVIOUS);
-    public static final TCommand cmWindowClose = new TCommand(WINDOW_CLOSE);
+    public static final TCommand cmWindowClose  = new TCommand(WINDOW_CLOSE);
+    public static final TCommand cmHelp         = new TCommand(HELP);
 
 }
 
 }
index 976c8957b22f74db54f286903c997982c1a01916..47197d4216f82fd81a5b4801c3645bb42f72ef14 100644 (file)
@@ -707,6 +707,8 @@ public final class TEditColorThemeWindow extends TWindow {
         // Default to the color list
         activate(colorNames);
 
         // Default to the color list
         activate(colorNames);
 
+        // Add shortcut text
+        newStatusBar("Select Colors");
     }
 
     /**
     }
 
     /**
index f5e966581a6dbffb6026fe088c3d1a6246491e21..d6b817a845b651e1fcccef01f5e863d0bb6b86eb 100644 (file)
@@ -99,7 +99,8 @@ public final class TLabel extends TWidget {
     /**
      * Draw a static label.
      */
     /**
      * Draw a static label.
      */
-    @Override public void draw() {
+    @Override
+    public void draw() {
         // Setup my color
         CellAttributes color = new CellAttributes();
         color.setTo(getTheme().getColor(colorKey));
         // Setup my color
         CellAttributes color = new CellAttributes();
         color.setTo(getTheme().getColor(colorKey));
diff --git a/src/jexer/TStatusBar.java b/src/jexer/TStatusBar.java
new file mode 100644 (file)
index 0000000..d03d526
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2016 Kevin Lamonte
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import java.util.ArrayList;
+
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TCommandEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+
+/**
+ * TStatusBar implements a status line with clickable buttons.
+ */
+public final class TStatusBar extends TWidget {
+
+    /**
+     * A single shortcut key.
+     */
+    private class TStatusBarKey {
+
+        /**
+         * The keypress for this action.
+         */
+        public TKeypress key;
+
+        /**
+         * The command to issue.
+         */
+        public TCommand cmd;
+
+        /**
+         * The label text.
+         */
+        public String label;
+
+        /**
+         * If true, the mouse is on this key.
+         */
+        public boolean selected;
+
+        /**
+         * The left edge coordinate to draw this key with.
+         */
+        public int x = 0;
+
+        /**
+         * The width of this key on the screen.
+         *
+         * @return the number of columns this takes when drawn
+         */
+        public int width() {
+            return this.label.length() + this.key.toString().length() + 3;
+        }
+
+        /**
+         * Add a key to this status bar.
+         *
+         * @param key the key to trigger on
+         * @param cmd the command event to issue when key is pressed or this
+         * item is clicked
+         * @param label the label for this action
+         */
+        public TStatusBarKey(final TKeypress key, final TCommand cmd,
+            final String label) {
+
+            this.key    = key;
+            this.cmd    = cmd;
+            this.label  = label;
+        }
+
+    }
+
+    /**
+     * Remember mouse state.
+     */
+    private TMouseEvent mouse;
+
+    /**
+     * The text to display on the right side of the shortcut keys.
+     */
+    private String text = null;
+
+    /**
+     * The shortcut keys.
+     */
+    private ArrayList<TStatusBarKey> keys = new ArrayList<TStatusBarKey>();
+
+    /**
+     * Add a key to this status bar.
+     *
+     * @param key the key to trigger on
+     * @param cmd the command event to issue when key is pressed or this item
+     * is clicked
+     * @param label the label for this action
+     */
+    public void addShortcutKeypress(final TKeypress key, final TCommand cmd,
+        final String label) {
+
+        TStatusBarKey newKey = new TStatusBarKey(key, cmd, label);
+        if (keys.size() > 0) {
+            TStatusBarKey oldKey = keys.get(keys.size() - 1);
+            newKey.x = oldKey.x + oldKey.width();
+        }
+        keys.add(newKey);
+    }
+
+    /**
+     * Set the text to display on the right side of the shortcut keys.
+     *
+     * @param text the new text
+     */
+    public void setText(final String text) {
+        this.text = text;
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     * @param text text for the bar on the bottom row
+     */
+    public TStatusBar(final TWidget parent, final String text) {
+
+        // Set parent and window
+        super(parent, false, 0, 0, text.length(), 1);
+
+        this.text = text;
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     */
+    public TStatusBar(final TWidget parent) {
+        this(parent, "");
+    }
+
+    /**
+     * Draw the bar.
+     */
+    @Override
+    public void draw() {
+        CellAttributes barColor = new CellAttributes();
+        barColor.setTo(getTheme().getColor("tstatusbar.text"));
+        CellAttributes keyColor = new CellAttributes();
+        keyColor.setTo(getTheme().getColor("tstatusbar.button"));
+        CellAttributes selectedColor = new CellAttributes();
+        selectedColor.setTo(getTheme().getColor("tstatusbar.selected"));
+
+        // Status bar is weird.  Its draw() method is called directly by
+        // TApplication after everything is drawn, and after
+        // Screen.resetClipping().  So at this point we are drawing in
+        // absolute coordinates, not relative to our TWindow.
+        int row = getScreen().getHeight() - 1;
+        int width = getScreen().getWidth();
+
+        getScreen().hLineXY(0, row, width, ' ', barColor);
+
+        int col = 0;
+        for (TStatusBarKey key: keys) {
+            String keyStr = key.key.toString();
+            if (key.selected) {
+                getScreen().putCharXY(col++, row, ' ', selectedColor);
+                getScreen().putStringXY(col, row, keyStr, selectedColor);
+                col += keyStr.length();
+                getScreen().putCharXY(col++, row, ' ', selectedColor);
+                getScreen().putStringXY(col, row, key.label, selectedColor);
+                col += key.label.length();
+                getScreen().putCharXY(col++, row, ' ', selectedColor);
+            } else {
+                getScreen().putCharXY(col++, row, ' ', barColor);
+                getScreen().putStringXY(col, row, keyStr, keyColor);
+                col += keyStr.length() + 1;
+                getScreen().putStringXY(col, row, key.label, barColor);
+                col += key.label.length();
+                getScreen().putCharXY(col++, row, ' ', barColor);
+            }
+        }
+        if (text.length() > 0) {
+            if (keys.size() > 0) {
+                getScreen().putCharXY(col++, row, GraphicsChars.VERTICAL_BAR,
+                    barColor);
+            }
+            getScreen().putCharXY(col++, row, ' ', barColor);
+            getScreen().putStringXY(col, row, text, barColor);
+        }
+    }
+
+    /**
+     * Handle keypresses.
+     *
+     * @param keypress keystroke event
+     * @return true if this keypress was consumed
+     */
+    public boolean statusBarKeypress(final TKeypressEvent keypress) {
+        for (TStatusBarKey key: keys) {
+            if (keypress.equals(key.key)) {
+                getApplication().postMenuEvent(new TCommandEvent(key.cmd));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the button.
+     *
+     * @param statusBarKey the status bar item
+     * @return if true the mouse is currently on the button
+     */
+    private boolean mouseOnShortcut(final TStatusBarKey statusBarKey) {
+        if ((mouse != null)
+            && (mouse.getAbsoluteY() == getApplication().getDesktopBottom())
+            && (mouse.getAbsoluteX() >= statusBarKey.x)
+            && (mouse.getAbsoluteX() < statusBarKey.x + statusBarKey.width())
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Handle mouse button presses.
+     *
+     * @param mouse mouse button event
+     * @return true if this mouse event was consumed
+     */
+    public boolean statusBarMouseDown(final TMouseEvent mouse) {
+        this.mouse = mouse;
+
+        for (TStatusBarKey key: keys) {
+            if ((mouseOnShortcut(key)) && (mouse.isMouse1())) {
+                key.selected = true;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Handle mouse button releases.
+     *
+     * @param mouse mouse button release event
+     * @return true if this mouse event was consumed
+     */
+    public boolean statusBarMouseUp(final TMouseEvent mouse) {
+        this.mouse = mouse;
+
+        for (TStatusBarKey key: keys) {
+            if (key.selected && mouse.isMouse1()) {
+                key.selected = false;
+
+                // Dispatch the event
+                getApplication().postMenuEvent(new TCommandEvent(key.cmd));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Handle mouse movements.
+     *
+     * @param mouse mouse motion event
+     */
+    public void statusBarMouseMotion(final TMouseEvent mouse) {
+        this.mouse = mouse;
+
+        for (TStatusBarKey key: keys) {
+            if (!mouseOnShortcut(key)) {
+                key.selected = false;
+            }
+        }
+    }
+
+}
index c0af74bafa12ce9046277f0eab0746c7571bee43..50c87d6c6d4db7c1329dca206e1e2e97891565a5 100644 (file)
@@ -166,7 +166,7 @@ public class TTerminalWindow extends TWindow {
                 "script", "-fqe", "/dev/null"
             };
             String [] cmdShellBSD = {
                 "script", "-fqe", "/dev/null"
             };
             String [] cmdShellBSD = {
-                "script", "-qe", "-F", "/dev/null"
+                "script", "-q", "-F", "/dev/null"
             };
             // Spawn a shell and pass its I/O to the other constructor.
 
             };
             // Spawn a shell and pass its I/O to the other constructor.
 
@@ -200,6 +200,9 @@ public class TTerminalWindow extends TWindow {
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
+
+        // Add shortcut text
+        newStatusBar("Terminal session executing...");
     }
 
     /**
     }
 
     /**
@@ -263,6 +266,9 @@ public class TTerminalWindow extends TWindow {
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
+
+        // Add shortcut text
+        newStatusBar("Terminal session executing...");
     }
 
     /**
     }
 
     /**
@@ -358,7 +364,8 @@ public class TTerminalWindow extends TWindow {
     /**
      * Handle window close.
      */
     /**
      * Handle window close.
      */
-    @Override public void onClose() {
+    @Override
+    public void onClose() {
         emulator.close();
         if (shell != null) {
             terminateShellChildProcess();
         emulator.close();
         if (shell != null) {
             terminateShellChildProcess();
@@ -406,6 +413,8 @@ public class TTerminalWindow extends TWindow {
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
+                    statusBar.setText("Terminal session completed, exit " +
+                        "code " + rc + ".");
                 } catch (IllegalThreadStateException e) {
                     // The emulator thread has exited, but the shell Process
                     // hasn't figured that out yet.  Do nothing, we will see
                 } catch (IllegalThreadStateException e) {
                     // The emulator thread has exited, but the shell Process
                     // hasn't figured that out yet.  Do nothing, we will see
@@ -421,6 +430,8 @@ public class TTerminalWindow extends TWindow {
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
+                    statusBar.setText("Terminal session completed, exit " +
+                        "code " + rc + ".");
                 } catch (IllegalThreadStateException e) {
                     // The shell is still running, do nothing.
                 }
                 } catch (IllegalThreadStateException e) {
                     // The shell is still running, do nothing.
                 }
index 71032ab1944b0915dc9fc3125539be4c265a3f4c..4dfe5a14a890e3b4415232ab75d2d035ab20ded3 100644 (file)
@@ -30,7 +30,7 @@ package jexer;
 
 import java.io.IOException;
 import java.util.List;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.LinkedList;
+import java.util.ArrayList;
 
 import jexer.bits.ColorTheme;
 import jexer.event.TCommandEvent;
 
 import jexer.bits.ColorTheme;
 import jexer.event.TCommandEvent;
@@ -49,6 +49,10 @@ import static jexer.TKeypress.*;
  */
 public abstract class TWidget implements Comparable<TWidget> {
 
  */
 public abstract class TWidget implements Comparable<TWidget> {
 
+    // ------------------------------------------------------------------------
+    // Common widget attributes -----------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Every widget has a parent widget that it may be "contained" in.  For
      * example, a TWindow might contain several TTextFields, or a TComboBox
     /**
      * Every widget has a parent widget that it may be "contained" in.  For
      * example, a TWindow might contain several TTextFields, or a TComboBox
@@ -65,44 +69,6 @@ public abstract class TWidget implements Comparable<TWidget> {
         return parent;
     }
 
         return parent;
     }
 
-    /**
-     * Backdoor access for TWindow's constructor.  ONLY TWindow USES THIS.
-     *
-     * @param window the top-level window
-     * @param x column relative to parent
-     * @param y row relative to parent
-     * @param width width of window
-     * @param height height of window
-     */
-    protected final void setupForTWindow(final TWindow window,
-        final int x, final int y, final int width, final int height) {
-
-        this.parent = window;
-        this.window = window;
-        this.x      = x;
-        this.y      = y;
-        this.width  = width;
-        this.height = height;
-    }
-
-    /**
-     * Get this TWidget's parent TApplication.
-     *
-     * @return the parent TApplication
-     */
-    public TApplication getApplication() {
-        return window.getApplication();
-    }
-
-    /**
-     * Get the Screen.
-     *
-     * @return the Screen
-     */
-    public Screen getScreen() {
-        return window.getScreen();
-    }
-
     /**
      * Child widgets that this widget contains.
      */
     /**
      * Child widgets that this widget contains.
      */
@@ -368,6 +334,28 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.cursorY = cursorY;
     }
 
         this.cursorY = cursorY;
     }
 
+    // ------------------------------------------------------------------------
+    // TApplication integration -----------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get this TWidget's parent TApplication.
+     *
+     * @return the parent TApplication
+     */
+    public TApplication getApplication() {
+        return window.getApplication();
+    }
+
+    /**
+     * Get the Screen.
+     *
+     * @return the Screen
+     */
+    public Screen getScreen() {
+        return window.getScreen();
+    }
+
     /**
      * Comparison operator.  For various subclasses it sorts on:
      * <ul>
     /**
      * Comparison operator.  For various subclasses it sorts on:
      * <ul>
@@ -492,6 +480,12 @@ public abstract class TWidget implements Comparable<TWidget> {
         assert (getScreen() != null);
         Screen screen = getScreen();
 
         assert (getScreen() != null);
         Screen screen = getScreen();
 
+        // Special case: TStatusBar is drawn by TApplication, not anything
+        // else.
+        if (this instanceof TStatusBar) {
+            return;
+        }
+
         screen.setClipRight(width);
         screen.setClipBottom(height);
 
         screen.setClipRight(width);
         screen.setClipBottom(height);
 
@@ -533,11 +527,15 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
     }
 
         }
     }
 
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Default constructor for subclasses.
      */
     protected TWidget() {
     /**
      * Default constructor for subclasses.
      */
     protected TWidget() {
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
     }
 
     /**
     }
 
     /**
@@ -574,7 +572,7 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
         parent.addChild(this);
     }
 
         parent.addChild(this);
     }
 
@@ -594,7 +592,7 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
         parent.addChild(this);
 
         this.x = x;
         parent.addChild(this);
 
         this.x = x;
@@ -603,6 +601,30 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.height = height;
     }
 
         this.height = height;
     }
 
+    /**
+     * Backdoor access for TWindow's constructor.  ONLY TWindow USES THIS.
+     *
+     * @param window the top-level window
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of window
+     * @param height height of window
+     */
+    protected final void setupForTWindow(final TWindow window,
+        final int x, final int y, final int width, final int height) {
+
+        this.parent = window;
+        this.window = window;
+        this.x      = x;
+        this.y      = y;
+        this.width  = width;
+        this.height = height;
+    }
+
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Add a child widget to my list of children.  We set its tabOrder to 0
      * and increment the tabOrder of all other children.
     /**
      * Add a child widget to my list of children.  We set its tabOrder to 0
      * and increment the tabOrder of all other children.
@@ -752,6 +774,33 @@ public abstract class TWidget implements Comparable<TWidget> {
         return this;
     }
 
         return this;
     }
 
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Check if a mouse press/release event coordinate is contained in this
+     * widget.
+     *
+     * @param mouse a mouse-based event
+     * @return whether or not a mouse click would be sent to this widget
+     */
+    public final boolean mouseWouldHit(final TMouseEvent mouse) {
+
+        if (!enabled) {
+            return false;
+        }
+
+        if ((mouse.getAbsoluteX() >= getAbsoluteX())
+            && (mouse.getAbsoluteX() <  getAbsoluteX() + width)
+            && (mouse.getAbsoluteY() >= getAbsoluteY())
+            && (mouse.getAbsoluteY() <  getAbsoluteY() + height)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Method that subclasses can override to handle keystrokes.
      *
     /**
      * Method that subclasses can override to handle keystrokes.
      *
@@ -971,28 +1020,9 @@ public abstract class TWidget implements Comparable<TWidget> {
         return;
     }
 
         return;
     }
 
-    /**
-     * Check if a mouse press/release event coordinate is contained in this
-     * widget.
-     *
-     * @param mouse a mouse-based event
-     * @return whether or not a mouse click would be sent to this widget
-     */
-    public final boolean mouseWouldHit(final TMouseEvent mouse) {
-
-        if (!enabled) {
-            return false;
-        }
-
-        if ((mouse.getAbsoluteX() >= getAbsoluteX())
-            && (mouse.getAbsoluteX() <  getAbsoluteX() + width)
-            && (mouse.getAbsoluteY() >= getAbsoluteY())
-            && (mouse.getAbsoluteY() <  getAbsoluteY() + height)
-        ) {
-            return true;
-        }
-        return false;
-    }
+    // ------------------------------------------------------------------------
+    // Other TWidget constructors ---------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Convenience function to add a label to this container/window.
 
     /**
      * Convenience function to add a label to this container/window.
index 73026953eaa2aa5ffaa5985f6489d9fb1170e2e8..32d94b928a48add6a82c1caf690d97b3b41c7638 100644 (file)
@@ -48,30 +48,33 @@ import static jexer.TKeypress.*;
  */
 public class TWindow extends TWidget {
 
  */
 public class TWindow extends TWidget {
 
+    // ------------------------------------------------------------------------
+    // Public constants -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
     /**
-     * Window's parent TApplication.
+     * Window is resizable (default yes).
      */
      */
-    private TApplication application;
+    public static final int RESIZABLE   = 0x01;
 
     /**
 
     /**
-     * Get this TWindow's parent TApplication.
-     *
-     * @return this TWindow's parent TApplication
+     * Window is modal (default no).
      */
      */
-    @Override
-    public final TApplication getApplication() {
-        return application;
-    }
+    public static final int MODAL       = 0x02;
 
     /**
 
     /**
-     * Get the Screen.
-     *
-     * @return the Screen
+     * Window is centered (default no).
      */
      */
-    @Override
-    public final Screen getScreen() {
-        return application.getScreen();
-    }
+    public static final int CENTERED    = 0x04;
+
+    // ------------------------------------------------------------------------
+    // Common window attributes -----------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Window flags.  Note package private access.
+     */
+    int flags = RESIZABLE;
 
     /**
      * Window title.
 
     /**
      * Window title.
@@ -96,25 +99,34 @@ public class TWindow extends TWidget {
         this.title = title;
     }
 
         this.title = title;
     }
 
-    /**
-     * Window is resizable (default yes).
-     */
-    public static final int RESIZABLE   = 0x01;
+    // ------------------------------------------------------------------------
+    // TApplication integration -----------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
 
     /**
-     * Window is modal (default no).
+     * Window's parent TApplication.
      */
      */
-    public static final int MODAL       = 0x02;
+    private TApplication application;
 
     /**
 
     /**
-     * Window is centered (default no).
+     * Get this TWindow's parent TApplication.
+     *
+     * @return this TWindow's parent TApplication
      */
      */
-    public static final int CENTERED    = 0x04;
+    @Override
+    public final TApplication getApplication() {
+        return application;
+    }
 
     /**
 
     /**
-     * Window flags.
+     * Get the Screen.
+     *
+     * @return the Screen
      */
      */
-    private int flags = RESIZABLE;
+    @Override
+    public final Screen getScreen() {
+        return application.getScreen();
+    }
 
     /**
      * Z order.  Lower number means more in-front.
 
     /**
      * Z order.  Lower number means more in-front.
@@ -181,6 +193,37 @@ public class TWindow extends TWidget {
         return keyboardShortcuts.contains(key);
     }
 
         return keyboardShortcuts.contains(key);
     }
 
+    /**
+     * A window may have a status bar associated with it.  TApplication will
+     * draw this status bar last, and will also route events to it first
+     * before the window.
+     */
+    protected TStatusBar statusBar = null;
+
+    /**
+     * Get the window's status bar, or null if it does not have one.
+     *
+     * @return the status bar, or null
+     */
+    public TStatusBar getStatusBar() {
+        return statusBar;
+    }
+
+    /**
+     * Set the window's status bar to a new one.
+     *
+     * @param text the status bar text
+     * @return the status bar
+     */
+    public TStatusBar newStatusBar(final String text) {
+        statusBar = new TStatusBar(this, text);
+        return statusBar;
+    }
+
+    // ------------------------------------------------------------------------
+    // Window movement/resizing support ---------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * If true, then the user clicked on the title bar and is moving the
      * window.
     /**
      * If true, then the user clicked on the title bar and is moving the
      * window.
@@ -238,6 +281,55 @@ public class TWindow extends TWidget {
         this.maximumWindowWidth = maximumWindowWidth;
     }
 
         this.maximumWindowWidth = maximumWindowWidth;
     }
 
+    /**
+     * Recenter the window on-screen.
+     */
+    public final void center() {
+        if ((flags & CENTERED) != 0) {
+            if (getWidth() < getScreen().getWidth()) {
+                setX((getScreen().getWidth() - getWidth()) / 2);
+            } else {
+                setX(0);
+            }
+            setY(((application.getDesktopBottom()
+                    - application.getDesktopTop()) - getHeight()) / 2);
+            if (getY() < 0) {
+                setY(0);
+            }
+            setY(getY() + application.getDesktopTop());
+        }
+    }
+
+    /**
+     * Maximize window.
+     */
+    private void maximize() {
+        restoreWindowWidth = getWidth();
+        restoreWindowHeight = getHeight();
+        restoreWindowX = getX();
+        restoreWindowY = getY();
+        setWidth(getScreen().getWidth());
+        setHeight(application.getDesktopBottom() - 1);
+        setX(0);
+        setY(1);
+        maximized = true;
+    }
+
+    /**
+     * Restote (unmaximize) window.
+     */
+    private void restore() {
+        setWidth(restoreWindowWidth);
+        setHeight(restoreWindowHeight);
+        setX(restoreWindowX);
+        setY(restoreWindowY);
+        maximized = false;
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Public constructor.  Window will be located at (0, 0).
      *
     /**
      * Public constructor.  Window will be located at (0, 0).
      *
@@ -325,24 +417,9 @@ public class TWindow extends TWidget {
         application.addWindow(this);
     }
 
         application.addWindow(this);
     }
 
-    /**
-     * Recenter the window on-screen.
-     */
-    public final void center() {
-        if ((flags & CENTERED) != 0) {
-            if (getWidth() < getScreen().getWidth()) {
-                setX((getScreen().getWidth() - getWidth()) / 2);
-            } else {
-                setX(0);
-            }
-            setY(((application.getDesktopBottom()
-                    - application.getDesktopTop()) - getHeight()) / 2);
-            if (getY() < 0) {
-                setY(0);
-            }
-            setY(getY() + application.getDesktopTop());
-        }
-    }
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Returns true if this window is modal.
 
     /**
      * Returns true if this window is modal.
@@ -356,57 +433,6 @@ public class TWindow extends TWidget {
         return true;
     }
 
         return true;
     }
 
-    /**
-     * Returns true if the mouse is currently on the close button.
-     *
-     * @return true if mouse is currently on the close button
-     */
-    private boolean mouseOnClose() {
-        if ((mouse != null)
-            && (mouse.getAbsoluteY() == getY())
-            && (mouse.getAbsoluteX() == getX() + 3)
-        ) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the mouse is currently on the maximize/restore button.
-     *
-     * @return true if the mouse is currently on the maximize/restore button
-     */
-    private boolean mouseOnMaximize() {
-        if ((mouse != null)
-            && !isModal()
-            && (mouse.getAbsoluteY() == getY())
-            && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
-        ) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the mouse is currently on the resizable lower right
-     * corner.
-     *
-     * @return true if the mouse is currently on the resizable lower right
-     * corner
-     */
-    private boolean mouseOnResize() {
-        if (((flags & RESIZABLE) != 0)
-            && !isModal()
-            && (mouse != null)
-            && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
-            && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
-                || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
-        ) {
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Retrieve the background color.
      *
     /**
      * Retrieve the background color.
      *
@@ -491,30 +517,6 @@ public class TWindow extends TWidget {
         }
     }
 
         }
     }
 
-    /**
-     * Subclasses should override this method to cleanup resources.  This is
-     * called by application.closeWindow().
-     */
-    public void onClose() {
-        // Default: do nothing
-    }
-
-    /**
-     * Called by application.switchWindow() when this window gets the
-     * focus, and also by application.addWindow().
-     */
-    public void onFocus() {
-        // Default: do nothing
-    }
-
-    /**
-     * Called by application.switchWindow() when another window gets the
-     * focus.
-     */
-    public void onUnfocus() {
-        // Default: do nothing
-    }
-
     /**
      * Called by TApplication.drawChildren() to render on screen.
      */
     /**
      * Called by TApplication.drawChildren() to render on screen.
      */
@@ -582,6 +584,85 @@ public class TWindow extends TWidget {
         }
     }
 
         }
     }
 
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns true if the mouse is currently on the close button.
+     *
+     * @return true if mouse is currently on the close button
+     */
+    private boolean mouseOnClose() {
+        if ((mouse != null)
+            && (mouse.getAbsoluteY() == getY())
+            && (mouse.getAbsoluteX() == getX() + 3)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the maximize/restore button.
+     *
+     * @return true if the mouse is currently on the maximize/restore button
+     */
+    private boolean mouseOnMaximize() {
+        if ((mouse != null)
+            && !isModal()
+            && (mouse.getAbsoluteY() == getY())
+            && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the resizable lower right
+     * corner.
+     *
+     * @return true if the mouse is currently on the resizable lower right
+     * corner
+     */
+    private boolean mouseOnResize() {
+        if (((flags & RESIZABLE) != 0)
+            && !isModal()
+            && (mouse != null)
+            && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
+            && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
+                || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Subclasses should override this method to cleanup resources.  This is
+     * called by application.closeWindow().
+     */
+    public void onClose() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.switchWindow() when this window gets the
+     * focus, and also by application.addWindow().
+     */
+    public void onFocus() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.switchWindow() when another window gets the
+     * focus.
+     */
+    public void onUnfocus() {
+        // Default: do nothing
+    }
+
     /**
      * Handle mouse button presses.
      *
     /**
      * Handle mouse button presses.
      *
@@ -624,36 +705,17 @@ public class TWindow extends TWidget {
             return;
         }
 
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarMouseDown(mouse)) {
+                return;
+            }
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseDown(mouse);
     }
 
         // I didn't take it, pass it on to my children
         super.onMouseDown(mouse);
     }
 
-    /**
-     * Maximize window.
-     */
-    private void maximize() {
-        restoreWindowWidth = getWidth();
-        restoreWindowHeight = getHeight();
-        restoreWindowX = getX();
-        restoreWindowY = getY();
-        setWidth(getScreen().getWidth());
-        setHeight(application.getDesktopBottom() - 1);
-        setX(0);
-        setY(1);
-        maximized = true;
-    }
-
-    /**
-     * Restote (unmaximize) window.
-     */
-    private void restore() {
-        setWidth(restoreWindowWidth);
-        setHeight(restoreWindowHeight);
-        setX(restoreWindowX);
-        setY(restoreWindowY);
-        maximized = false;
-    }
-
     /**
      * Handle mouse button releases.
      *
     /**
      * Handle mouse button releases.
      *
@@ -697,6 +759,13 @@ public class TWindow extends TWidget {
             return;
         }
 
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarMouseUp(mouse)) {
+                return;
+            }
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseUp(mouse);
     }
         // I didn't take it, pass it on to my children
         super.onMouseUp(mouse);
     }
@@ -718,6 +787,10 @@ public class TWindow extends TWidget {
             if (getY() < application.getDesktopTop()) {
                 setY(application.getDesktopTop());
             }
             if (getY() < application.getDesktopTop()) {
                 setY(application.getDesktopTop());
             }
+            // Don't go below the status bar
+            if (getY() >= application.getDesktopBottom()) {
+                setY(application.getDesktopBottom() - 1);
+            }
             return;
         }
 
             return;
         }
 
@@ -766,6 +839,11 @@ public class TWindow extends TWidget {
             return;
         }
 
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            statusBar.statusBarMouseMotion(mouse);
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseMotion(mouse);
     }
         // I didn't take it, pass it on to my children
         super.onMouseMotion(mouse);
     }
@@ -841,6 +919,13 @@ public class TWindow extends TWidget {
             return;
         }
 
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarKeypress(keypress)) {
+                return;
+            }
+        }
+
         // These keystrokes will typically not be seen unless a subclass
         // overrides onMenu() due to how TApplication dispatches
         // accelerators.
         // These keystrokes will typically not be seen unless a subclass
         // overrides onMenu() due to how TApplication dispatches
         // accelerators.
index 626a6c8979bd81a451fdbf2c7ea5466cd0d42fdd..d3a468f416dd3d2986282f7b53653d097ffc4a90 100644 (file)
@@ -437,6 +437,23 @@ public final class ColorTheme {
         color.setBold(true);
         colors.put("tlist.inactive", color);
 
         color.setBold(true);
         colors.put("tlist.inactive", color);
 
+        // TStatusBar
+        color = new CellAttributes();
+        color.setForeColor(Color.BLACK);
+        color.setBackColor(Color.WHITE);
+        color.setBold(false);
+        colors.put("tstatusbar.text", color);
+        color = new CellAttributes();
+        color.setForeColor(Color.RED);
+        color.setBackColor(Color.WHITE);
+        color.setBold(false);
+        colors.put("tstatusbar.button", color);
+        color = new CellAttributes();
+        color.setForeColor(Color.WHITE);
+        color.setBackColor(Color.BLUE);
+        color.setBold(false);
+        colors.put("tstatusbar.selected", color);
+
         // TEditor
         color = new CellAttributes();
         color.setForeColor(Color.WHITE);
         // TEditor
         color = new CellAttributes();
         color.setForeColor(Color.WHITE);
index cd48fc8225fb193532a9765a9e25084ba6458566..8e7a883dd5815d6b5b39bc6d6a4381f79f8d0f24 100644 (file)
@@ -145,4 +145,5 @@ public final class GraphicsChars {
     public static final char WINDOW_RIGHT_TOP_DOUBLE    = CP437[0xBB];
     public static final char WINDOW_LEFT_BOTTOM_DOUBLE  = CP437[0xC8];
     public static final char WINDOW_RIGHT_BOTTOM_DOUBLE = CP437[0xBC];
     public static final char WINDOW_RIGHT_TOP_DOUBLE    = CP437[0xBB];
     public static final char WINDOW_LEFT_BOTTOM_DOUBLE  = CP437[0xC8];
     public static final char WINDOW_RIGHT_BOTTOM_DOUBLE = CP437[0xBC];
+    public static final char VERTICAL_BAR               = CP437[0xB3];
 }
 }
index 9ef561c3b1f64f108c4fa28e24d23dc10e37ef89..22d0cecba038515e649bab96faa060716a907c80 100644 (file)
@@ -29,6 +29,8 @@
 package jexer.demos;
 
 import jexer.*;
 package jexer.demos;
 
 import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
 
 /**
  * This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
@@ -78,6 +80,12 @@ public class DemoCheckboxWindow extends TWindow {
                 }
             }
         );
                 }
             }
         );
+
+        statusBar = newStatusBar("Radiobuttons and checkboxes");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 
 }
     }
 
 }
index 5d74d7df94ae7198d83760572edb358c32201eb3..febdb4096b7e27064d9a67a9c710dfa94ea9c2cf 100644 (file)
@@ -29,6 +29,8 @@
 package jexer.demos;
 
 import jexer.*;
 package jexer.demos;
 
 import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This is the main "demo" application window.  It makes use of the TTimer,
 
 /**
  * This is the main "demo" application window.  It makes use of the TTimer,
@@ -75,21 +77,19 @@ public class DemoMainWindow extends TWindow {
     private DemoMainWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it will be
         // centered on screen.
     private DemoMainWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it will be
         // centered on screen.
-        super(parent, "Demo Window", 0, 0, 60, 24, flags);
+        super(parent, "Demo Window", 0, 0, 60, 22, flags);
 
         int row = 1;
 
         // Add some widgets
 
         int row = 1;
 
         // Add some widgets
-        if (!isModal()) {
-            addLabel("Message Boxes", 1, row);
-            addButton("&MessageBoxes", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoMsgBoxWindow(getApplication());
-                    }
+        addLabel("Message Boxes", 1, row);
+        addButton("&MessageBoxes", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoMsgBoxWindow(getApplication());
                 }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         addLabel("Open me as modal", 1, row);
         row += 2;
 
         addLabel("Open me as modal", 1, row);
@@ -100,29 +100,26 @@ public class DemoMainWindow extends TWindow {
                 }
             }
         );
                 }
             }
         );
-
         row += 2;
 
         row += 2;
 
-        addLabel("Variable-width text field:", 1, row);
-        addField(35, row++, 15, false, "Field text");
-        addLabel("Fixed-width text field:", 1, row);
-        addField(35, row++, 15, true);
-        addLabel("Variable-width password:", 1, row);
-        addPasswordField(35, row++, 15, false);
-        addLabel("Fixed-width password:", 1, row);
-        addPasswordField(35, row++, 15, true, "hunter2");
-        row += 1;
+        addLabel("Text fields", 1, row);
+        addButton("Field&s", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoTextFieldWindow(getApplication());
+                }
+            }
+        );
+        row += 2;
 
 
-        if (!isModal()) {
-            addLabel("Radio buttons and checkboxes", 1, row);
-            addButton("&Checkboxes", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoCheckboxWindow(getApplication());
-                    }
+        addLabel("Radio buttons and checkboxes", 1, row);
+        addButton("&Checkboxes", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoCheckboxWindow(getApplication());
                 }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         /*
         row += 2;
 
         /*
@@ -137,56 +134,48 @@ public class DemoMainWindow extends TWindow {
         row += 2;
          */
 
         row += 2;
          */
 
-        if (!isModal()) {
-            addLabel("Text areas", 1, row);
-            addButton("&Text", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoTextWindow(getApplication());
-                    }
+        addLabel("Text areas", 1, row);
+        addButton("&Text", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoTextWindow(getApplication());
                 }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         row += 2;
 
-        if (!isModal()) {
-            addLabel("Tree views", 1, row);
-            addButton("Tree&View", 35, row,
-                new TAction() {
-                    public void DO() {
-                        try {
-                            new DemoTreeViewWindow(getApplication());
-                        } catch (Exception e) {
-                            e.printStackTrace();
-                        }
+        addLabel("Tree views", 1, row);
+        addButton("Tree&View", 35, row,
+            new TAction() {
+                public void DO() {
+                    try {
+                        new DemoTreeViewWindow(getApplication());
+                    } catch (Exception e) {
+                        e.printStackTrace();
                     }
                 }
                     }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         row += 2;
 
-        if (!isModal()) {
-            addLabel("Terminal", 1, row);
-            addButton("Termi&nal", 35, row,
-                new TAction() {
-                    public void DO() {
-                        getApplication().openTerminal(0, 0);
-                    }
+        addLabel("Terminal", 1, row);
+        addButton("Termi&nal", 35, row,
+            new TAction() {
+                public void DO() {
+                    getApplication().openTerminal(0, 0);
                 }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         row += 2;
 
-        if (!isModal()) {
-            addLabel("Color editor", 1, row);
-            addButton("Co&lors", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new TEditColorThemeWindow(getApplication());
-                    }
+        addLabel("Color editor", 1, row);
+        addButton("Co&lors", 35, row,
+            new TAction() {
+                public void DO() {
+                    new TEditColorThemeWindow(getApplication());
                 }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         progressBar = addProgressBar(1, row, 22, 0);
         row += 2;
 
         progressBar = addProgressBar(1, row, 22, 0);
@@ -205,5 +194,11 @@ public class DemoMainWindow extends TWindow {
                 }
             }
         );
                 }
             }
         );
+
+        statusBar = newStatusBar("Demo Main Window");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 }
     }
 }
index a56da43760e3b38176f90e4dc33d034530b70d4a..77bc33199d16208f351ebb16b08ac7ef63a9d206 100644 (file)
@@ -29,6 +29,8 @@
 package jexer.demos;
 
 import jexer.*;
 package jexer.demos;
 
 import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TMessageBox and TInputBox widgets.
 
 /**
  * This window demonstates the TMessageBox and TInputBox widgets.
@@ -53,7 +55,7 @@ public class DemoMsgBoxWindow extends TWindow {
     DemoMsgBoxWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it
         // will be centered on screen.
     DemoMsgBoxWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it
         // will be centered on screen.
-        super(parent, "Message Boxes", 0, 0, 60, 15, flags);
+        super(parent, "Message Boxes", 0, 0, 60, 16, flags);
 
         int row = 1;
 
 
         int row = 1;
 
@@ -154,6 +156,11 @@ public class DemoMsgBoxWindow extends TWindow {
                 }
             }
         );
                 }
             }
         );
+
+        statusBar = newStatusBar("Message boxes");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 }
     }
 }
-
diff --git a/src/jexer/demos/DemoTextFieldWindow.java b/src/jexer/demos/DemoTextFieldWindow.java
new file mode 100644 (file)
index 0000000..cd989b8
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2016 Kevin Lamonte
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
+
+/**
+ * This window demonstates the TField and TPasswordField widgets.
+ */
+public class DemoTextFieldWindow extends TWindow {
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     */
+    DemoTextFieldWindow(final TApplication parent) {
+        this(parent, TWindow.CENTERED | TWindow.RESIZABLE);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
+     */
+    DemoTextFieldWindow(final TApplication parent, final int flags) {
+        // Construct a demo window.  X and Y don't matter because it
+        // will be centered on screen.
+        super(parent, "Text Fields", 0, 0, 60, 10, flags);
+
+        int row = 1;
+
+        addLabel("Variable-width text field:", 1, row);
+        addField(35, row++, 15, false, "Field text");
+        addLabel("Fixed-width text field:", 1, row);
+        addField(35, row++, 15, true);
+        addLabel("Variable-width password:", 1, row);
+        addPasswordField(35, row++, 15, false);
+        addLabel("Fixed-width password:", 1, row);
+        addPasswordField(35, row++, 15, true, "hunter2");
+        row += 1;
+
+        addButton("&Close Window", (getWidth() - 14) / 2, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    getApplication().closeWindow(DemoTextFieldWindow.this);
+                }
+            }
+        );
+
+        statusBar = newStatusBar("Text fields");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
+    }
+}
index a2ab68b0ae34bb6c6cb5d9da01da93405584d952..0ca34b0b5bcd7ebcd37bbb4f33857963c3d08690 100644 (file)
@@ -31,6 +31,8 @@ package jexer.demos;
 import jexer.*;
 import jexer.event.*;
 import jexer.menu.*;
 import jexer.*;
 import jexer.event.*;
 import jexer.menu.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TText, THScroller, and TVScroller widgets.
 
 /**
  * This window demonstates the TText, THScroller, and TVScroller widgets.
@@ -54,6 +56,12 @@ public class DemoTextWindow extends TWindow {
 
         super(parent, title, 0, 0, 44, 20, RESIZABLE);
         textField = addText(text, 1, 1, 40, 16);
 
         super(parent, title, 0, 0, 44, 20, RESIZABLE);
         textField = addText(text, 1, 1, 40, 16);
+
+        statusBar = newStatusBar("Reflowable text window");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 
     /**
     }
 
     /**
index a1f4a6152cc342b60e693a846ae32dc62d1b7430..ad588a916acdca4f8957287fac54d24ba3df3381 100644 (file)
@@ -32,6 +32,8 @@ import java.io.IOException;
 
 import jexer.*;
 import jexer.event.*;
 
 import jexer.*;
 import jexer.event.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TTreeView widget.
 
 /**
  * This window demonstates the TTreeView widget.
@@ -55,6 +57,12 @@ public class DemoTreeViewWindow extends TWindow {
         // Load the treeview with "stuff"
         treeView = addTreeView(1, 1, 40, 12);
         new TDirectoryTreeItem(treeView, ".", true);
         // Load the treeview with "stuff"
         treeView = addTreeView(1, 1, 40, 12);
         new TDirectoryTreeItem(treeView, ".", true);
+
+        statusBar = newStatusBar("Treeview demonstration");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 
     /**
     }
 
     /**
index 3063bc4303ae688ba7466c324367fdcd700d0ddc..f7e7870b2030bf444959325193468dd97c46c571 100644 (file)
@@ -29,6 +29,6 @@
 
 /**
  * This package contains the user-facing I/O, including screen, keyboard, and
 
 /**
  * This package contains the user-facing I/O, including screen, keyboard, and
- * mouse handling..
+ * mouse handling classes.
  */
 package jexer.io;
  */
 package jexer.io;
index b3cb0ac556aded9743240714e087d738988d638c..a282058189fe0dec5eeda5a8725324879303ec71 100644 (file)
@@ -51,7 +51,7 @@ import jexer.io.TimeoutInputStream;
 import static jexer.TKeypress.*;
 
 /**
 import static jexer.TKeypress.*;
 
 /**
- * This implements a complex ANSI ECMA-48/ISO 6429/ANSI X3.64 type consoles,
+ * This implements a complex ECMA-48/ISO 6429/ANSI X3.64 type console,
  * including a scrollback buffer.
  *
  * <p>
  * including a scrollback buffer.
  *
  * <p>