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
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
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.
0.0.4
-- TStatusBar
- - TMenu version
- - TWindow version
- - Click mouse
-
- TWindow
- "Smart placement" for new windows
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 {
+ // ------------------------------------------------------------------------
+ // Public constants -------------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* If true, emit thread stuff to System.err.
*/
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
lockoutHandleEvent = false;
}
+ // ------------------------------------------------------------------------
+ // TApplication attributes ------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* Access to the physical screen, keyboard, and mouse.
*/
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.
*
(new Thread(primaryEventHandler)).start();
}
+ // ------------------------------------------------------------------------
+ // Screen refresh loop ----------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* Invert the cell color at a position. This is used to track the mouse.
*
// 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();
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");
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;
repaint = false;
}
+ // ------------------------------------------------------------------------
+ // Main loop --------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* Run this application until it exits.
*/
}
}
- if (!windowWillShortcut) {
+ if (!windowWillShortcut && !modalWindowActive()) {
TKeypress keypressLowercase = keypress.getKey().toLowerCase();
TMenuItem item = null;
synchronized (accelerators) {
return;
}
}
- }
- // Handle the keypress
- if (onKeypress(keypress)) {
- return;
+ // Handle the keypress
+ if (onKeypress(keypress)) {
+ return;
+ }
}
}
}
}
- /**
- * 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
*/
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()) {
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.
}
}
- /**
- * 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.
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;
}
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;
}
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;
}
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;
}
+ // ------------------------------------------------------------------------
+ // 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;
}
/**
}
}
+ // ------------------------------------------------------------------------
+ // Other TWindow constructors ---------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* Convenience function to spawn a message box.
*
* 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.
*/
- public static final int OPEN = 2;
+ public static final int OPEN = 2;
/**
* Exit application.
*/
- public static final int EXIT = 3;
+ public static final int EXIT = 3;
/**
* Spawn OS shell window.
*/
- public static final int SHELL = 4;
+ public static final int SHELL = 4;
/**
* 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.
*/
- public static final int COPY = 6;
+ public static final int COPY = 6;
/**
* Paste from clipboard.
*/
- public static final int PASTE = 7;
+ public static final int PASTE = 7;
/**
* Clear selected text without copying it to the clipboard.
*/
- public static final int CLEAR = 8;
+ public static final int CLEAR = 8;
/**
* Tile windows.
*/
- public static final int TILE = 9;
+ public static final int TILE = 9;
/**
* Cascade windows.
*/
- public static final int CASCADE = 10;
+ public static final int CASCADE = 10;
/**
* Close all windows.
*/
- public static final int CLOSE_ALL = 11;
+ public static final int CLOSE_ALL = 11;
/**
* Move (move/resize) window.
*/
- public static final int WINDOW_MOVE = 12;
+ public static final int WINDOW_MOVE = 12;
/**
* Zoom (maximize/restore) window.
*/
- public static final int WINDOW_ZOOM = 13;
+ public static final int WINDOW_ZOOM = 13;
/**
* 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).
*/
- public static final int WINDOW_PREVIOUS = 15;
+ public static final int WINDOW_PREVIOUS = 15;
/**
* 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.
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 cmWindowClose = new TCommand(WINDOW_CLOSE);
+ public static final TCommand cmWindowClose = new TCommand(WINDOW_CLOSE);
+ public static final TCommand cmHelp = new TCommand(HELP);
}
// Default to the color list
activate(colorNames);
+ // Add shortcut text
+ newStatusBar("Select Colors");
}
/**
/**
* Draw a static label.
*/
- @Override public void draw() {
+ @Override
+ public void draw() {
// Setup my color
CellAttributes color = new CellAttributes();
color.setTo(getTheme().getColor(colorKey));
--- /dev/null
+/*
+ * 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;
+ }
+ }
+ }
+
+}
"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.
// Claim the keystrokes the emulator will need.
addShortcutKeys();
+
+ // Add shortcut text
+ newStatusBar("Terminal session executing...");
}
/**
// Claim the keystrokes the emulator will need.
addShortcutKeys();
+
+ // Add shortcut text
+ newStatusBar("Terminal session executing...");
}
/**
/**
* Handle window close.
*/
- @Override public void onClose() {
+ @Override
+ public void onClose() {
emulator.close();
if (shell != null) {
terminateShellChildProcess();
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
shell = null;
emulator.close();
clearShortcutKeypresses();
+ statusBar.setText("Terminal session completed, exit " +
+ "code " + rc + ".");
} catch (IllegalThreadStateException e) {
// The shell is still running, do nothing.
}
import java.io.IOException;
import java.util.List;
-import java.util.LinkedList;
+import java.util.ArrayList;
import jexer.bits.ColorTheme;
import jexer.event.TCommandEvent;
*/
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
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.
*/
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>
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);
}
}
+ // ------------------------------------------------------------------------
+ // Constructors -----------------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* Default constructor for subclasses.
*/
protected TWidget() {
- children = new LinkedList<TWidget>();
+ children = new ArrayList<TWidget>();
}
/**
this.enabled = enabled;
this.parent = parent;
this.window = parent.window;
- children = new LinkedList<TWidget>();
+ children = new ArrayList<TWidget>();
parent.addChild(this);
}
this.enabled = enabled;
this.parent = parent;
this.window = parent.window;
- children = new LinkedList<TWidget>();
+ children = new ArrayList<TWidget>();
parent.addChild(this);
this.x = x;
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.
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.
*
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.
*/
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.
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.
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.
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).
*
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.
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.
*
}
}
- /**
- * 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.
*/
}
}
+ // ------------------------------------------------------------------------
+ // 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.
*
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);
}
- /**
- * 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.
*
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);
}
if (getY() < application.getDesktopTop()) {
setY(application.getDesktopTop());
}
+ // Don't go below the status bar
+ if (getY() >= application.getDesktopBottom()) {
+ setY(application.getDesktopBottom() - 1);
+ }
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);
}
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.
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);
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];
}
package jexer.demos;
import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
/**
* This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
}
}
);
+
+ 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");
}
}
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,
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
- 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("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;
*/
- 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;
- 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;
- 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;
- 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);
}
}
);
+
+ 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");
}
}
package jexer.demos;
import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
/**
* This window demonstates the TMessageBox and TInputBox widgets.
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;
}
}
);
+
+ statusBar = newStatusBar("Message boxes");
+ statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+ statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+ statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+ statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
}
}
-
--- /dev/null
+/*
+ * 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");
+ }
+}
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.
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");
}
/**
import jexer.*;
import jexer.event.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
/**
* This window demonstates the TTreeView widget.
// 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");
}
/**
/**
* This package contains the user-facing I/O, including screen, keyboard, and
- * mouse handling..
+ * mouse handling classes.
*/
package jexer.io;
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>