From: Kevin Lamonte Date: Fri, 17 Mar 2017 18:18:42 +0000 (-0400) Subject: Add versioned about box, set title X-Git-Tag: fanfix-swing-0.0.1~12^2~13^2~277 X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=55d2b2c2b29ce51f4f910448a115073371deeae8;p=fanfix-swing.git Add versioned about box, set title --- diff --git a/README.md b/README.md index ab6069ea..2f146cc4 100644 --- a/README.md +++ b/README.md @@ -211,34 +211,5 @@ ambiguous. This section describes such issues. Roadmap ------- -Many tasks remain before calling this version 1.0: - -0.0.4 - -- TStatusBar -- TEditor -- TWindow - - "Smart placement" for new windows - -0.0.5: BUG HUNT - -- Swing performance is better, triple-buffering appears to have helped. - -0.1.0: BETA RELEASE - -- TSpinner -- TComboBox -- TCalendar - -Wishlist features (2.0): - -- TTerminal - - Handle resize events (pass to child process) -- Screen - - Allow complex characters in putCharXY() and detect them in putStringXY(). -- Drag and drop - - TEditor - - TField - - TText - - TTerminal - - TComboBox +Many tasks remain before calling this version 1.0. See docs/TODO.md +for the complete list of tasks. diff --git a/build.xml b/build.xml index 45014d95..5894ef15 100644 --- a/build.xml +++ b/build.xml @@ -32,6 +32,7 @@ + @@ -62,6 +63,7 @@ + diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 00000000..47f07d05 --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,95 @@ +Jexer TODO List +=============== + + +Roadmap +------- + +0.0.4 + +- TStatusBar + - TMenu version + - TWindow version + - Click mouse + +- TWindow + - "Smart placement" for new windows + +0.0.5 + +- TEditor + +0.0.6 + +- TSpinner +- TComboBox +- TCalendar + +0.0.7 + +- THelpWindow + - TText + clickable links + - Index + +0.1.0: BETA RELEASE and BUG HUNT + +- Verify vttest in multiple tterminals. + +1.0.0 + + + +1.1.0 Wishlist +-------------- + +- TTerminal + - Handle resize events (pass to child process) + +- Screen + - Allow complex characters in putCharXY() and detect them in putStringXY(). + +- Drag and drop + - TEditor + - TField + - TText + - TTerminal + - TComboBox + + + +Regression Checklist +-------------------- + + TTerminal + No hang when closing, whether or not script is running + No dead script children lying around + vttest passing + + + +Release Checklist √ +------------------- + +Fix all marked TODOs in code + +Eliminate DEBUG, System.err prints + +Version in: + +Update written by date to current year: + All code headers + VERSION + +Tag github + +Upload to SF + + + +Brainstorm Wishlist +------------------- + + + +Bugs Noted In Other Programs +---------------------------- diff --git a/docs/worklog.md b/docs/worklog.md new file mode 100644 index 00000000..89cbef56 --- /dev/null +++ b/docs/worklog.md @@ -0,0 +1,26 @@ +Jexer Work Log +============== + +March 17, 2017 + +Jexer is coming back to active development status. I had a lot of +other projects ahead of it in the queue, mostly Qodem but also Jermit +and of course lots of actual day job work keeping me too tired for +afterhours stuff. But here we are now, and I want to get Jexer to its +1.0.0 release before the end of 2018. After that it will be a +critical bit of function for IWP and NIB, if I ever get those going. +I need to re-organize the demo app a bit so that it fits within 80x25, +and then get to TStatusBar. + +A status bar will be an optional part of TWindow. If it exists, then +it will be drawn last by TApplication and get events routed to it from +TWindow's event handlers. This will have the nice effect that the +status bar can change depending on which window is active, without any +real extra work on TApplication's part. + +Putting together a proper TODO now, with release and regression +checklists. I think I will see if jexer is available at SourceForge, +and if so grab it. Perhaps I can put together some good Turbo Vision +resources too. At the very least direct people to the Borland-derived +C++ releases and Free Vision. + diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 971f6f8f..91d2e98d 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -370,6 +370,15 @@ public class TApplication implements Runnable { */ private Backend backend; + /** + * Get the Backend. + * + * @return the Backend + */ + public final Backend getBackend() { + return backend; + } + /** * Get the Screen. * @@ -1514,6 +1523,15 @@ public class TApplication implements Runnable { 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. @@ -1549,6 +1567,10 @@ public class TApplication implements Runnable { closeAllWindows(); return true; } + if (menu.getId() == TMenu.MID_ABOUT) { + showAboutDialog(); + return true; + } return false; } @@ -1752,6 +1774,24 @@ public class TApplication implements Runnable { return windowMenu; } + /** + * Convenience function to add a default "Help" menu. + * + * @return the new menu + */ + public final TMenu addHelpMenu() { + TMenu helpMenu = addMenu("&Help"); + helpMenu.addDefaultItem(TMenu.MID_HELP_CONTENTS); + helpMenu.addDefaultItem(TMenu.MID_HELP_INDEX); + helpMenu.addDefaultItem(TMenu.MID_HELP_SEARCH); + helpMenu.addDefaultItem(TMenu.MID_HELP_PREVIOUS); + helpMenu.addDefaultItem(TMenu.MID_HELP_HELP); + helpMenu.addDefaultItem(TMenu.MID_HELP_ACTIVE_FILE); + helpMenu.addSeparator(); + helpMenu.addDefaultItem(TMenu.MID_ABOUT); + return helpMenu; + } + /** * Close all open windows. */ diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 6d97531a..c0af74ba 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -32,6 +32,7 @@ import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -158,17 +159,27 @@ public class TTerminalWindow extends TWindow { // You cannot run a login shell in a bare Process interactively, // due to libc's behavior of buffering when stdin/stdout aren't a - // tty. Use 'script' instead to run a shell in a pty. - String [] cmdShell = { + // tty. Use 'script' instead to run a shell in a pty. And + // because BSD and GNU differ on the '-f' vs '-F' flags, we need + // two different commands. Lovely. + String [] cmdShellGNU = { "script", "-fqe", "/dev/null" }; + String [] cmdShellBSD = { + "script", "-qe", "-F", "/dev/null" + }; // Spawn a shell and pass its I/O to the other constructor. ProcessBuilder pb; if (System.getProperty("os.name").startsWith("Windows")) { pb = new ProcessBuilder(cmdShellWindows); + } else if (System.getProperty("os.name").startsWith("Mac")) { + pb = new ProcessBuilder(cmdShellBSD); + } else if (System.getProperty("os.name").startsWith("Linux")) { + pb = new ProcessBuilder(cmdShellGNU); } else { - pb = new ProcessBuilder(cmdShell); + // When all else fails, assume GNU. + pb = new ProcessBuilder(cmdShellGNU); } Map env = pb.environment(); env.put("TERM", ECMA48.deviceTypeTerm(deviceType)); @@ -191,6 +202,39 @@ public class TTerminalWindow extends TWindow { addShortcutKeys(); } + /** + * Terminate the child of the 'script' process used on POSIX. This may + * or may not work. + */ + private void terminateShellChildProcess() { + int pid = -1; + if (shell.getClass().getName().equals("java.lang.UNIXProcess")) { + /* get the PID on unix/linux systems */ + try { + Field field = shell.getClass().getDeclaredField("pid"); + field.setAccessible(true); + pid = field.getInt(shell); + } catch (Throwable e) { + // SQUASH, this didn't work. Just bail out quietly. + return; + } + } + if (pid != -1) { + // shell.destroy() works successfully at killing this side of + // 'script'. But we need to make sure the other side (child + // process) is also killed. + String [] cmdKillIt = { + "pkill", "-P", Integer.toString(pid) + }; + try { + Runtime.getRuntime().exec(cmdKillIt); + } catch (Throwable e) { + // SQUASH, this didn't work. Just bail out quietly. + return; + } + } + } + /** * Public constructor. * @@ -317,7 +361,7 @@ public class TTerminalWindow extends TWindow { @Override public void onClose() { emulator.close(); if (shell != null) { - // System.err.println("shell.destroy()"); + terminateShellChildProcess(); shell.destroy(); shell = null; } diff --git a/src/jexer/backend/Backend.java b/src/jexer/backend/Backend.java index bb29b578..dbc7c973 100644 --- a/src/jexer/backend/Backend.java +++ b/src/jexer/backend/Backend.java @@ -89,4 +89,11 @@ public abstract class Backend { */ public abstract void shutdown(); + /** + * Subclasses must provide an implementation that sets the window title. + * + * @param title the new title + */ + public abstract void setTitle(final String title); + } diff --git a/src/jexer/backend/ECMA48Backend.java b/src/jexer/backend/ECMA48Backend.java index 62fae34b..85798556 100644 --- a/src/jexer/backend/ECMA48Backend.java +++ b/src/jexer/backend/ECMA48Backend.java @@ -160,4 +160,14 @@ public final class ECMA48Backend extends Backend { terminal.shutdown(); } + /** + * Set the window title. + * + * @param title the new title + */ + @Override + public void setTitle(final String title) { + ((ECMA48Screen) screen).setTitle(title); + } + } diff --git a/src/jexer/backend/SwingBackend.java b/src/jexer/backend/SwingBackend.java index 3dbf0177..e7f4312b 100644 --- a/src/jexer/backend/SwingBackend.java +++ b/src/jexer/backend/SwingBackend.java @@ -91,4 +91,14 @@ public final class SwingBackend extends Backend { ((SwingScreen) screen).shutdown(); } + /** + * Set the window title. + * + * @param title the new title + */ + @Override + public void setTitle(final String title) { + ((SwingScreen) screen).setTitle(title); + } + } diff --git a/src/jexer/demos/Demo1.java b/src/jexer/demos/Demo1.java index 522baa14..1efe89f1 100644 --- a/src/jexer/demos/Demo1.java +++ b/src/jexer/demos/Demo1.java @@ -48,6 +48,9 @@ public class Demo1 { if (System.getProperty("os.name").startsWith("Windows")) { backendType = TApplication.BackendType.SWING; } + if (System.getProperty("os.name").startsWith("Mac")) { + backendType = TApplication.BackendType.SWING; + } if (System.getProperty("jexer.Swing") != null) { if (System.getProperty("jexer.Swing", "false").equals("true")) { backendType = TApplication.BackendType.SWING; diff --git a/src/jexer/demos/DemoApplication.java b/src/jexer/demos/DemoApplication.java index 7e7126fb..b07c783d 100644 --- a/src/jexer/demos/DemoApplication.java +++ b/src/jexer/demos/DemoApplication.java @@ -74,6 +74,7 @@ public class DemoApplication extends TApplication { item = subMenu.addItem(2002, "&Normal (sub)"); addWindowMenu(); + addHelpMenu(); } /** @@ -93,6 +94,8 @@ public class DemoApplication extends TApplication { final OutputStream output) throws UnsupportedEncodingException { super(input, output); addAllWidgets(); + + getBackend().setTitle("Jexer Demo Application"); } /** @@ -111,6 +114,8 @@ public class DemoApplication extends TApplication { final PrintWriter writer, final boolean setRawMode) { super(input, reader, writer, setRawMode); addAllWidgets(); + + getBackend().setTitle("Jexer Demo Application"); } /** @@ -183,5 +188,6 @@ public class DemoApplication extends TApplication { public DemoApplication(final BackendType backendType) throws Exception { super(backendType); addAllWidgets(); + getBackend().setTitle("Jexer Demo Application"); } } diff --git a/src/jexer/io/ECMA48Screen.java b/src/jexer/io/ECMA48Screen.java index a00a1806..71878e96 100644 --- a/src/jexer/io/ECMA48Screen.java +++ b/src/jexer/io/ECMA48Screen.java @@ -282,4 +282,15 @@ public final class ECMA48Screen extends Screen { terminal.getOutput().write(result); terminal.flush(); } + + /** + * Set the window title. + * + * @param title the new title + */ + public void setTitle(final String title) { + terminal.getOutput().write(terminal.setTitle(title)); + terminal.flush(); + } + } diff --git a/src/jexer/io/ECMA48Terminal.java b/src/jexer/io/ECMA48Terminal.java index f448ac44..d567a429 100644 --- a/src/jexer/io/ECMA48Terminal.java +++ b/src/jexer/io/ECMA48Terminal.java @@ -1228,6 +1228,17 @@ public final class ECMA48Terminal implements Runnable { return "\033[?1036l"; } + /** + * Create an xterm OSC sequence to change the window title. Note package + * private access. + * + * @param title the new title + * @return the string to emit to xterm + */ + String setTitle(final String title) { + return "\033]2;" + title + "\007"; + } + /** * Create a SGR parameter sequence for a single color change. Note * package private access. diff --git a/src/jexer/io/SwingScreen.java b/src/jexer/io/SwingScreen.java index b7c84109..3d0729e4 100644 --- a/src/jexer/io/SwingScreen.java +++ b/src/jexer/io/SwingScreen.java @@ -165,6 +165,11 @@ public final class SwingScreen extends Screen { */ SwingScreen screen; + /** + * If true, we were successful getting Terminus. + */ + private boolean gotTerminus = false; + /** * Width of a character cell. */ @@ -329,8 +334,9 @@ public final class SwingScreen extends Screen { getContextClassLoader(); InputStream in = loader.getResourceAsStream(FONTFILE); Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in); - Font terminus = terminusRoot.deriveFont(Font.PLAIN, 22); + Font terminus = terminusRoot.deriveFont(Font.PLAIN, 20); setFont(terminus); + gotTerminus = true; } catch (Exception e) { e.printStackTrace(); // setFont(new Font("Liberation Mono", Font.PLAIN, 24)); @@ -375,15 +381,24 @@ public final class SwingScreen extends Screen { Rectangle2D bounds = fm.getMaxCharBounds(gr); int leading = fm.getLeading(); textWidth = (int)Math.round(bounds.getWidth()); - textHeight = (int)Math.round(bounds.getHeight()) - maxDescent; - // This also produces the same number, but works better for ugly + // textHeight = (int)Math.round(bounds.getHeight()) - maxDescent; + + // This produces the same number, but works better for ugly // monospace. textHeight = fm.getMaxAscent() + maxDescent - leading; + if (gotTerminus == true) { + textHeight++; + } + if (System.getProperty("os.name").startsWith("Windows")) { textAdjustY = -1; textAdjustX = 0; } + if (System.getProperty("os.name").startsWith("Mac")) { + textAdjustY = -1; + textAdjustX = 0; + } } /** @@ -857,4 +872,13 @@ public final class SwingScreen extends Screen { return ((y - frame.top) / frame.textHeight); } + /** + * Set the window title. + * + * @param title the new title + */ + public void setTitle(final String title) { + frame.setTitle(title); + } + } diff --git a/src/jexer/menu/TMenu.java b/src/jexer/menu/TMenu.java index b0f99ae2..a0a97865 100644 --- a/src/jexer/menu/TMenu.java +++ b/src/jexer/menu/TMenu.java @@ -88,6 +88,15 @@ public final class TMenu extends TWindow { public static final int MID_WINDOW_PREVIOUS = 26; public static final int MID_WINDOW_CLOSE = 27; + // Help menu + public static final int MID_HELP_CONTENTS = 40; + public static final int MID_HELP_INDEX = 41; + public static final int MID_HELP_SEARCH = 42; + public static final int MID_HELP_PREVIOUS = 43; + public static final int MID_HELP_HELP = 44; + public static final int MID_HELP_ACTIVE_FILE = 45; + public static final int MID_ABOUT = 46; + /** * Public constructor. * @@ -439,6 +448,31 @@ public final class TMenu extends TWindow { key = kbCtrlW; break; + case MID_HELP_CONTENTS: + label = "&Contents"; + break; + case MID_HELP_INDEX: + label = "&Index"; + key = kbShiftF1; + break; + case MID_HELP_SEARCH: + label = "&Topic search"; + key = kbCtrlF1; + break; + case MID_HELP_PREVIOUS: + label = "&Previous topic"; + key = kbAltF1; + break; + case MID_HELP_HELP: + label = "&Help on help"; + break; + case MID_HELP_ACTIVE_FILE: + label = "Active &file..."; + break; + case MID_ABOUT: + label = "&About..."; + break; + default: throw new IllegalArgumentException("Invalid menu ID: " + id); } diff --git a/src/jexer/session/SwingSessionInfo.java b/src/jexer/session/SwingSessionInfo.java index 55a6d8d4..5b613868 100644 --- a/src/jexer/session/SwingSessionInfo.java +++ b/src/jexer/session/SwingSessionInfo.java @@ -66,12 +66,12 @@ public final class SwingSessionInfo implements SessionInfo { /** * Text window width. */ - private int windowWidth = 132; + private int windowWidth = 80; /** * Text window height. */ - private int windowHeight = 40; + private int windowHeight = 25; /** * Username getter.