X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTTerminalWindow.java;h=e96c50c9921d99603da342e80802c27d0da02e86;hb=12b90437b5f22c2ae6e9b9b14c3b62b60f6143e5;hp=d1936535c63e5a41bc0a1a06eede35a20e7c5a4c;hpb=00fbfc382425d1f20a84cd8897919836cc6254ee;p=fanfix.git diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index d193653..e96c50c 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (C) 2017 Kevin Lamonte + * Copyright (C) 2019 Kevin Lamonte * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -28,17 +28,32 @@ */ package jexer; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import java.io.InputStream; import java.io.IOException; import java.lang.reflect.Field; -import java.util.LinkedList; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.ResourceBundle; +import jexer.backend.ECMA48Terminal; +import jexer.backend.GlyphMaker; +import jexer.backend.MultiScreen; +import jexer.backend.SwingTerminal; import jexer.bits.Cell; import jexer.bits.CellAttributes; import jexer.event.TKeypressEvent; +import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; +import jexer.menu.TMenu; import jexer.tterminal.DisplayLine; import jexer.tterminal.DisplayListener; import jexer.tterminal.ECMA48; @@ -47,406 +62,183 @@ import static jexer.TKeypress.*; /** * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window. */ -public class TTerminalWindow extends TScrollableWindow - implements DisplayListener { +public class TTerminalWindow extends TScrollableWindow { /** - * The emulator. + * Translated strings. */ - private ECMA48 emulator; + private static final ResourceBundle i18n = ResourceBundle.getBundle(TTerminalWindow.class.getName()); + + // ------------------------------------------------------------------------ + // Variables -------------------------------------------------------------- + // ------------------------------------------------------------------------ /** - * The Process created by the shell spawning constructor. + * The terminal. */ - private Process shell; + private TTerminalWidget terminal; /** - * If true, we are using the ptypipe utility to support dynamic window - * resizing. ptypipe is available at - * https://github.com/klamonte/ptypipe . + * If true, close the window when the shell exits. */ - private boolean ptypipe = false; + private boolean closeOnExit = false; + + // ------------------------------------------------------------------------ + // Constructors ----------------------------------------------------------- + // ------------------------------------------------------------------------ /** - * Claim the keystrokes the emulator will need. + * Public constructor spawns a custom command line. + * + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param commandLine the command line to execute */ - private void addShortcutKeys() { - addShortcutKeypress(kbCtrlA); - addShortcutKeypress(kbCtrlB); - addShortcutKeypress(kbCtrlC); - addShortcutKeypress(kbCtrlD); - addShortcutKeypress(kbCtrlE); - addShortcutKeypress(kbCtrlF); - addShortcutKeypress(kbCtrlG); - addShortcutKeypress(kbCtrlH); - addShortcutKeypress(kbCtrlU); - addShortcutKeypress(kbCtrlJ); - addShortcutKeypress(kbCtrlK); - addShortcutKeypress(kbCtrlL); - addShortcutKeypress(kbCtrlM); - addShortcutKeypress(kbCtrlN); - addShortcutKeypress(kbCtrlO); - addShortcutKeypress(kbCtrlP); - addShortcutKeypress(kbCtrlQ); - addShortcutKeypress(kbCtrlR); - addShortcutKeypress(kbCtrlS); - addShortcutKeypress(kbCtrlT); - addShortcutKeypress(kbCtrlU); - addShortcutKeypress(kbCtrlV); - addShortcutKeypress(kbCtrlW); - addShortcutKeypress(kbCtrlX); - addShortcutKeypress(kbCtrlY); - addShortcutKeypress(kbCtrlZ); - addShortcutKeypress(kbF1); - addShortcutKeypress(kbF2); - addShortcutKeypress(kbF3); - addShortcutKeypress(kbF4); - addShortcutKeypress(kbF5); - addShortcutKeypress(kbF6); - addShortcutKeypress(kbF7); - addShortcutKeypress(kbF8); - addShortcutKeypress(kbF9); - addShortcutKeypress(kbF10); - addShortcutKeypress(kbF11); - addShortcutKeypress(kbF12); - addShortcutKeypress(kbAltA); - addShortcutKeypress(kbAltB); - addShortcutKeypress(kbAltC); - addShortcutKeypress(kbAltD); - addShortcutKeypress(kbAltE); - addShortcutKeypress(kbAltF); - addShortcutKeypress(kbAltG); - addShortcutKeypress(kbAltH); - addShortcutKeypress(kbAltU); - addShortcutKeypress(kbAltJ); - addShortcutKeypress(kbAltK); - addShortcutKeypress(kbAltL); - addShortcutKeypress(kbAltM); - addShortcutKeypress(kbAltN); - addShortcutKeypress(kbAltO); - addShortcutKeypress(kbAltP); - addShortcutKeypress(kbAltQ); - addShortcutKeypress(kbAltR); - addShortcutKeypress(kbAltS); - addShortcutKeypress(kbAltT); - addShortcutKeypress(kbAltU); - addShortcutKeypress(kbAltV); - addShortcutKeypress(kbAltW); - addShortcutKeypress(kbAltX); - addShortcutKeypress(kbAltY); - addShortcutKeypress(kbAltZ); + public TTerminalWindow(final TApplication application, final int x, + final int y, final String commandLine) { + + this(application, x, y, RESIZABLE, commandLine.split("\\s+"), + System.getProperty("jexer.TTerminal.closeOnExit", + "false").equals("true")); } /** - * Public constructor spawns a shell. + * Public constructor spawns a custom command line. * * @param application TApplication that manages this window * @param x column relative to parent * @param y row relative to parent - * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param commandLine the command line to execute + * @param closeOnExit if true, close the window when the command exits */ public TTerminalWindow(final TApplication application, final int x, - final int y, final int flags) { - - super(application, "Terminal", x, y, 80 + 2, 24 + 2, flags); - - vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); - setBottomValue(0); - - // Assume XTERM - ECMA48.DeviceType deviceType = ECMA48.DeviceType.XTERM; - - try { - String [] cmdShellWindows = { - "cmd.exe" - }; - - // You cannot run a login shell in a bare Process interactively, - // due to libc's behavior of buffering when stdin/stdout aren't a - // tty. Use 'script' instead to run a shell in a pty. And - // because BSD and GNU differ on the '-f' vs '-F' flags, we need - // two different commands. Lovely. - String [] cmdShellGNU = { - "script", "-fqe", "/dev/null" - }; - String [] cmdShellBSD = { - "script", "-q", "-F", "/dev/null" - }; - String [] cmdShellPtypipe = { - "ptypipe", "/bin/bash", "--login" - }; - // Spawn a shell and pass its I/O to the other constructor. - - ProcessBuilder pb; - if ((System.getProperty("jexer.TTerminal.ptypipe") != null) - && (System.getProperty("jexer.TTerminal.ptypipe"). - equals("true")) - ) { - pb = new ProcessBuilder(cmdShellPtypipe); - ptypipe = true; - } else if (System.getProperty("os.name").startsWith("Windows")) { - pb = new ProcessBuilder(cmdShellWindows); - } else if (System.getProperty("os.name").startsWith("Mac")) { - pb = new ProcessBuilder(cmdShellBSD); - } else if (System.getProperty("os.name").startsWith("Linux")) { - pb = new ProcessBuilder(cmdShellGNU); - } else { - // When all else fails, assume GNU. - pb = new ProcessBuilder(cmdShellGNU); - } - Map env = pb.environment(); - env.put("TERM", ECMA48.deviceTypeTerm(deviceType)); - env.put("LANG", ECMA48.deviceTypeLang(deviceType, "en")); - env.put("COLUMNS", "80"); - env.put("LINES", "24"); - pb.redirectErrorStream(true); - shell = pb.start(); - emulator = new ECMA48(deviceType, shell.getInputStream(), - shell.getOutputStream(), this); - } catch (IOException e) { - messageBox("Error", "Error launching shell: " + e.getMessage()); - } - - // Setup the scroll bars - onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(), - getHeight())); - - // Claim the keystrokes the emulator will need. - addShortcutKeys(); + final int y, final String commandLine, final boolean closeOnExit) { - // Add shortcut text - newStatusBar("Terminal session executing..."); + this(application, x, y, RESIZABLE, commandLine.split("\\s+"), + closeOnExit); } /** - * Terminate the child of the 'script' process used on POSIX. This may - * or may not work. + * Public constructor spawns a custom command line. + * + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param command the command line to execute */ - 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 TTerminalWindow(final TApplication application, final int x, + final int y, final int flags, final String [] command) { + + this(application, x, y, flags, command, + System.getProperty("jexer.TTerminal.closeOnExit", + "false").equals("true")); } /** - * Draw the display buffer. + * Public constructor spawns a custom command line. + * + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param command the command line to execute + * @param closeOnExit if true, close the window when the command exits */ - @Override - public void draw() { - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { + public TTerminalWindow(final TApplication application, final int x, + final int y, final int flags, final String [] command, + final boolean closeOnExit) { - // Update the scroll bars - reflowData(); + super(application, i18n.getString("windowTitle"), x, y, + 80 + 2, 24 + 2, flags); - // Draw the box using my superclass - super.draw(); - - List scrollback = emulator.getScrollbackBuffer(); - List display = emulator.getDisplayBuffer(); - - // Put together the visible rows - int visibleHeight = getHeight() - 2; - int visibleBottom = scrollback.size() + display.size() - + getVerticalValue(); - assert (visibleBottom >= 0); - - List preceedingBlankLines = new LinkedList(); - int visibleTop = visibleBottom - visibleHeight; - if (visibleTop < 0) { - for (int i = visibleTop; i < 0; i++) { - preceedingBlankLines.add(emulator.getBlankDisplayLine()); - } - visibleTop = 0; - } - assert (visibleTop >= 0); - - List displayLines = new LinkedList(); - displayLines.addAll(scrollback); - displayLines.addAll(display); - - List visibleLines = new LinkedList(); - visibleLines.addAll(preceedingBlankLines); - visibleLines.addAll(displayLines.subList(visibleTop, - visibleBottom)); - - visibleHeight -= visibleLines.size(); - assert (visibleHeight >= 0); - - // Now draw the emulator screen - int row = 1; - for (DisplayLine line: visibleLines) { - int widthMax = emulator.getWidth(); - if (line.isDoubleWidth()) { - widthMax /= 2; - } - if (widthMax > getWidth() - 2) { - widthMax = getWidth() - 2; - } - for (int i = 0; i < widthMax; i++) { - Cell ch = line.charAt(i); - Cell newCell = new Cell(); - newCell.setTo(ch); - boolean reverse = line.isReverseColor() ^ ch.isReverse(); - newCell.setReverse(false); - if (reverse) { - newCell.setBackColor(ch.getForeColor()); - newCell.setForeColor(ch.getBackColor()); - } - if (line.isDoubleWidth()) { - getScreen().putCharXY((i * 2) + 1, row, newCell); - getScreen().putCharXY((i * 2) + 2, row, ' ', newCell); - } else { - getScreen().putCharXY(i + 1, row, newCell); - } - } - row++; - if (row == getHeight() - 1) { - // Don't overwrite the box edge - break; - } - } - CellAttributes background = new CellAttributes(); - // Fill in the blank lines on bottom - for (int i = 0; i < visibleHeight; i++) { - getScreen().hLineXY(1, i + row, getWidth() - 2, ' ', - background); - } + // Require at least one line for the display. + setMinimumWindowHeight(3); - } // synchronized (emulator) + this.closeOnExit = closeOnExit; + vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); - } + // Claim the keystrokes the emulator will need. + addShortcutKeys(); - /** - * Called by emulator when fresh data has come in. - */ - public void displayChanged() { - doRepaint(); + // Add shortcut text + newStatusBar(i18n.getString("statusBarRunning")); + + // Spin it up + terminal = new TTerminalWidget(this, 0, 0, new TAction() { + public void DO() { + onShellExit(); + } + }); } /** - * Function to call to obtain the display width. + * Public constructor spawns a shell. * - * @return the number of columns in the display + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE */ - public int getDisplayWidth() { - if (ptypipe) { - return getWidth() - 2; - } - return 80; + public TTerminalWindow(final TApplication application, final int x, + final int y, final int flags) { + + this(application, x, y, flags, + System.getProperty("jexer.TTerminal.closeOnExit", + "false").equals("true")); + } /** - * Function to call to obtain the display height. + * Public constructor spawns a shell. * - * @return the number of rows in the display + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param closeOnExit if true, close the window when the shell exits */ - public int getDisplayHeight() { - if (ptypipe) { - return getHeight() - 2; - } - return 24; + public TTerminalWindow(final TApplication application, final int x, + final int y, final int flags, final boolean closeOnExit) { + + super(application, i18n.getString("windowTitle"), x, y, + 80 + 2, 24 + 2, flags); + + // Require at least one line for the display. + setMinimumWindowHeight(3); + + this.closeOnExit = closeOnExit; + vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); + + // Claim the keystrokes the emulator will need. + addShortcutKeys(); + + // Add shortcut text + newStatusBar(i18n.getString("statusBarRunning")); + + // Spin it up + terminal = new TTerminalWidget(this, 0, 0, new TAction() { + public void DO() { + onShellExit(); + } + }); } + // ------------------------------------------------------------------------ + // TScrollableWindow ------------------------------------------------------ + // ------------------------------------------------------------------------ + /** - * Handle window close. + * Draw the display buffer. */ @Override - public void onClose() { - emulator.close(); - if (shell != null) { - terminateShellChildProcess(); - shell.destroy(); - shell = null; + public void draw() { + if (terminal != null) { + setTitle(terminal.getTitle()); } - } - - /** - * Copy out variables from the emulator that TTerminal has to expose on - * screen. - */ - private void readEmulatorState() { - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { - - setCursorX(emulator.getCursorX() + 1); - setCursorY(emulator.getCursorY() + 1 - + (getHeight() - 2 - emulator.getHeight()) - - getVerticalValue()); - setCursorVisible(emulator.isCursorVisible()); - if (getCursorX() > getWidth() - 2) { - setCursorVisible(false); - } - if ((getCursorY() > getHeight() - 2) || (getCursorY() < 0)) { - setCursorVisible(false); - } - if (emulator.getScreenTitle().length() > 0) { - // Only update the title if the shell is still alive - if (shell != null) { - setTitle(emulator.getScreenTitle()); - } - } - - // Check to see if the shell has died. - if (!emulator.isReading() && (shell != null)) { - try { - int rc = shell.exitValue(); - // The emulator exited on its own, all is fine - setTitle(String.format("%s [Completed - %d]", - getTitle(), rc)); - 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 - // this in a future tick. - } - } else if (emulator.isReading() && (shell != null)) { - // The shell might be dead, let's check - try { - int rc = shell.exitValue(); - // If we got here, the shell died. - setTitle(String.format("%s [Completed - %d]", - getTitle(), rc)); - shell = null; - emulator.close(); - clearShortcutKeypresses(); - statusBar.setText("Terminal session completed, exit " + - "code " + rc + "."); - } catch (IllegalThreadStateException e) { - // The shell is still running, do nothing. - } - } - - } // synchronized (emulator) + reflowData(); + super.draw(); } /** @@ -456,30 +248,17 @@ public class TTerminalWindow extends TScrollableWindow */ @Override public void onResize(final TResizeEvent resize) { - - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { - - if (resize.getType() == TResizeEvent.Type.WIDGET) { - // Resize the scroll bars - reflowData(); - placeScrollbars(); - - // Get out of scrollback - setVerticalValue(0); - - if (ptypipe) { - emulator.setWidth(getWidth() - 2); - emulator.setHeight(getHeight() - 2); - - emulator.writeRemote("\033[8;" + (getHeight() - 2) + ";" + - (getWidth() - 2) + "t"); - } + if (resize.getType() == TResizeEvent.Type.WIDGET) { + if (terminal != null) { + terminal.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, + getWidth() - 2, getHeight() - 2)); } - return; - } // synchronized (emulator) + // Resize the scroll bars + reflowData(); + placeScrollbars(); + } + return; } /** @@ -487,46 +266,14 @@ public class TTerminalWindow extends TScrollableWindow */ @Override public void reflowData() { - - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { - - // Pull cursor information - readEmulatorState(); - - // Vertical scrollbar - setTopValue(getHeight() - 2 - - (emulator.getScrollbackBuffer().size() - + emulator.getDisplayBuffer().size())); - setVerticalBigChange(getHeight() - 2); - - } // synchronized (emulator) - } - - /** - * Check if a mouse press/release/motion event coordinate is over the - * emulator. - * - * @param mouse a mouse-based event - * @return whether or not the mouse is on the emulator - */ - private final boolean mouseOnEmulator(final TMouseEvent mouse) { - - synchronized (emulator) { - if (!emulator.isReading()) { - return false; - } + // Vertical scrollbar + if (terminal != null) { + terminal.reflowData(); + setTopValue(terminal.getTopValue()); + setBottomValue(terminal.getBottomValue()); + setVerticalBigChange(terminal.getVerticalBigChange()); + setVerticalValue(terminal.getVerticalValue()); } - - if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1) - && (mouse.getAbsoluteX() < getAbsoluteX() + getWidth() - 1) - && (mouse.getAbsoluteY() >= getAbsoluteY() + 1) - && (mouse.getAbsoluteY() < getAbsoluteY() + getHeight() - 1) - ) { - return true; - } - return false; } /** @@ -536,46 +283,11 @@ public class TTerminalWindow extends TScrollableWindow */ @Override public void onKeypress(final TKeypressEvent keypress) { - - // Scrollback up/down - if (keypress.equals(kbShiftPgUp) - || keypress.equals(kbCtrlPgUp) - || keypress.equals(kbAltPgUp) - ) { - bigVerticalDecrement(); - return; - } - if (keypress.equals(kbShiftPgDn) - || keypress.equals(kbCtrlPgDn) - || keypress.equals(kbAltPgDn) - ) { - bigVerticalIncrement(); - return; - } - - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { - if (emulator.isReading()) { - // Get out of scrollback - setVerticalValue(0); - emulator.keypress(keypress.getKey()); - - // UGLY HACK TIME! cmd.exe needs CRLF, not just CR, so if - // this is kBEnter then also send kbCtrlJ. - if (System.getProperty("os.name").startsWith("Windows")) { - if (keypress.equals(kbEnter)) { - emulator.keypress(kbCtrlJ); - } - } - - readEmulatorState(); - return; - } + if ((terminal != null) && (terminal.isReading())) { + terminal.onKeypress(keypress); + } else { + super.onKeypress(keypress); } - - // Process is closed, honor "normal" TUI keystrokes - super.onKeypress(keypress); } /** @@ -591,25 +303,6 @@ public class TTerminalWindow extends TScrollableWindow return; } - if (mouse.isMouseWheelUp()) { - verticalDecrement(); - return; - } - if (mouse.isMouseWheelDown()) { - verticalIncrement(); - return; - } - if (mouseOnEmulator(mouse)) { - synchronized (emulator) { - mouse.setX(mouse.getX() - 1); - mouse.setY(mouse.getY() - 1); - emulator.mouse(mouse); - readEmulatorState(); - return; - } - } - - // Emulator didn't consume it, pass it on super.onMouseDown(mouse); } @@ -626,18 +319,14 @@ public class TTerminalWindow extends TScrollableWindow return; } - if (mouseOnEmulator(mouse)) { - synchronized (emulator) { - mouse.setX(mouse.getX() - 1); - mouse.setY(mouse.getY() - 1); - emulator.mouse(mouse); - readEmulatorState(); - return; + super.onMouseUp(mouse); + + if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) { + // Clicked on vertical scrollbar + if (terminal != null) { + terminal.setVerticalValue(getVerticalValue()); } } - - // Emulator didn't consume it, pass it on - super.onMouseUp(mouse); } /** @@ -653,18 +342,114 @@ public class TTerminalWindow extends TScrollableWindow return; } - if (mouseOnEmulator(mouse)) { - synchronized (emulator) { - mouse.setX(mouse.getX() - 1); - mouse.setY(mouse.getY() - 1); - emulator.mouse(mouse); - readEmulatorState(); - return; + super.onMouseMotion(mouse); + + if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) { + // Clicked/dragged on vertical scrollbar + if (terminal != null) { + terminal.setVerticalValue(getVerticalValue()); } } + } - // Emulator didn't consume it, pass it on - super.onMouseMotion(mouse); + // ------------------------------------------------------------------------ + // TTerminalWindow -------------------------------------------------------- + // ------------------------------------------------------------------------ + + /** + * Returns true if this window does not want the application-wide mouse + * cursor drawn over it. + * + * @return true if this window does not want the application-wide mouse + * cursor drawn over it + */ + @Override + public boolean hasHiddenMouse() { + if (terminal != null) { + return terminal.hasHiddenMouse(); + } + return false; + } + + /** + * Claim the keystrokes the emulator will need. + */ + private void addShortcutKeys() { + addShortcutKeypress(kbCtrlA); + addShortcutKeypress(kbCtrlB); + addShortcutKeypress(kbCtrlC); + addShortcutKeypress(kbCtrlD); + addShortcutKeypress(kbCtrlE); + addShortcutKeypress(kbCtrlF); + addShortcutKeypress(kbCtrlG); + addShortcutKeypress(kbCtrlH); + addShortcutKeypress(kbCtrlU); + addShortcutKeypress(kbCtrlJ); + addShortcutKeypress(kbCtrlK); + addShortcutKeypress(kbCtrlL); + addShortcutKeypress(kbCtrlM); + addShortcutKeypress(kbCtrlN); + addShortcutKeypress(kbCtrlO); + addShortcutKeypress(kbCtrlP); + addShortcutKeypress(kbCtrlQ); + addShortcutKeypress(kbCtrlR); + addShortcutKeypress(kbCtrlS); + addShortcutKeypress(kbCtrlT); + addShortcutKeypress(kbCtrlU); + addShortcutKeypress(kbCtrlV); + addShortcutKeypress(kbCtrlW); + addShortcutKeypress(kbCtrlX); + addShortcutKeypress(kbCtrlY); + addShortcutKeypress(kbCtrlZ); + addShortcutKeypress(kbF1); + addShortcutKeypress(kbF2); + addShortcutKeypress(kbF3); + addShortcutKeypress(kbF4); + addShortcutKeypress(kbF5); + addShortcutKeypress(kbF6); + addShortcutKeypress(kbF7); + addShortcutKeypress(kbF8); + addShortcutKeypress(kbF9); + addShortcutKeypress(kbF10); + addShortcutKeypress(kbF11); + addShortcutKeypress(kbF12); + addShortcutKeypress(kbAltA); + addShortcutKeypress(kbAltB); + addShortcutKeypress(kbAltC); + addShortcutKeypress(kbAltD); + addShortcutKeypress(kbAltE); + addShortcutKeypress(kbAltF); + addShortcutKeypress(kbAltG); + addShortcutKeypress(kbAltH); + addShortcutKeypress(kbAltU); + addShortcutKeypress(kbAltJ); + addShortcutKeypress(kbAltK); + addShortcutKeypress(kbAltL); + addShortcutKeypress(kbAltM); + addShortcutKeypress(kbAltN); + addShortcutKeypress(kbAltO); + addShortcutKeypress(kbAltP); + addShortcutKeypress(kbAltQ); + addShortcutKeypress(kbAltR); + addShortcutKeypress(kbAltS); + addShortcutKeypress(kbAltT); + addShortcutKeypress(kbAltU); + addShortcutKeypress(kbAltV); + addShortcutKeypress(kbAltW); + addShortcutKeypress(kbAltX); + addShortcutKeypress(kbAltY); + addShortcutKeypress(kbAltZ); + } + + /** + * Hook for subclasses to be notified of the shell termination. + */ + public void onShellExit() { + if (closeOnExit) { + close(); + } + clearShortcutKeypresses(); + getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT)); } }