Add versioned about box, set title

This commit is contained in:
Kevin Lamonte 2017-03-17 14:18:42 -04:00
parent f5a270d545
commit 55d2b2c2b2
16 changed files with 334 additions and 40 deletions

View file

@ -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.

View file

@ -32,6 +32,7 @@
<!-- <property name="build.compiler" value="gcj"/> -->
<property name="version" value="0.0.4"/>
<property name="src.dir" value="src"/>
<property name="resources.dir" value="resources"/>
<property name="build.dir" value="build"/>
@ -62,6 +63,7 @@
<fileset dir="${src.dir}"/>
<manifest>
<attribute name="Main-Class" value="jexer.demos.Demo1"/>
<attribute name="Implementation-Version" value="${version}"/>
</manifest>
</jar>
</target>

95
docs/TODO.md Normal file
View file

@ -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
----------------------------

26
docs/worklog.md Normal file
View file

@ -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.

View file

@ -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.
*/

View file

@ -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<String, String> 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;
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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");
}
}

View file

@ -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();
}
}

View file

@ -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.

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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.