make private
[fanfix.git] / src / jexer / TTerminalWidget.java
index 6efa6490c6383e81564128fae4a96d4fc2899f73..b1ff8b9b0d8e90995229d94fb3c80242f09f4d5c 100644 (file)
@@ -60,7 +60,7 @@ import jexer.tterminal.ECMA48;
 import static jexer.TKeypress.*;
 
 /**
- * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a widget.
+ * TTerminalWidget exposes a ECMA-48 / ANSI X3.64 style terminal in a widget.
  */
 public class TTerminalWidget extends TScrollableWidget
                              implements DisplayListener {
@@ -119,12 +119,7 @@ public class TTerminalWidget extends TScrollableWidget
     private boolean haveTimer = false;
 
     /**
-     * The last seen scrollback lines.
-     */
-    private List<DisplayLine> scrollback;
-
-    /**
-     * The last seen display lines.
+     * The last seen visible display.
      */
     private List<DisplayLine> display;
 
@@ -159,6 +154,11 @@ public class TTerminalWidget extends TScrollableWidget
      */
     private String title = "";
 
+    /**
+     * Action to perform when the terminal exits.
+     */
+    private TAction closeAction = null;
+
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -171,8 +171,8 @@ public class TTerminalWidget extends TScrollableWidget
      * @param y row relative to parent
      * @param commandLine the command line to execute
      */
-    public TTerminalWidget(final TWidget parent, final int x,
-        final int y, final String commandLine) {
+    public TTerminalWidget(final TWidget parent, final int x, final int y,
+        final String commandLine) {
 
         this(parent, x, y, commandLine.split("\\s+"));
     }
@@ -185,10 +185,45 @@ public class TTerminalWidget extends TScrollableWidget
      * @param y row relative to parent
      * @param command the command line to execute
      */
-    public TTerminalWidget(final TWidget parent, final int x,
-        final int y, final String [] command) {
+    public TTerminalWidget(final TWidget parent, final int x, final int y,
+        final String [] command) {
+
+        this(parent, x, y, command, null);
+    }
+
+    /**
+     * Public constructor spawns a custom command line.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param command the command line to execute
+     * @param closeAction action to perform when the shell sxits
+     */
+    public TTerminalWidget(final TWidget parent, final int x, final int y,
+        final String [] command, final TAction closeAction) {
+
+        this(parent, x, y, 80, 24, command, closeAction);
+    }
+
+    /**
+     * Public constructor spawns a custom command line.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of widget
+     * @param height height of widget
+     * @param command the command line to execute
+     * @param closeAction action to perform when the shell sxits
+     */
+    public TTerminalWidget(final TWidget parent, final int x, final int y,
+        final int width, final int height, final String [] command,
+        final TAction closeAction) {
+
+        super(parent, x, y, width, height);
 
-        super(parent, x, y, 80, 24);
+        this.closeAction = closeAction;
 
         String [] fullCommand;
 
@@ -234,8 +269,39 @@ public class TTerminalWidget extends TScrollableWidget
      * @param y row relative to parent
      */
     public TTerminalWidget(final TWidget parent, final int x, final int y) {
+        this(parent, x, y, (TAction) null);
+    }
+
+    /**
+     * Public constructor spawns a shell.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param closeAction action to perform when the shell sxits
+     */
+    public TTerminalWidget(final TWidget parent, final int x, final int y,
+        final TAction closeAction) {
 
-        super(parent, x, y, 80, 24);
+        this(parent, x, y, 80, 24, closeAction);
+    }
+
+    /**
+     * Public constructor spawns a shell.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of widget
+     * @param height height of widget
+     * @param closeAction action to perform when the shell sxits
+     */
+    public TTerminalWidget(final TWidget parent, final int x, final int y,
+        final int width, final int height, final TAction closeAction) {
+
+        super(parent, x, y, width, height);
+
+        this.closeAction = closeAction;
 
         if (System.getProperty("jexer.TTerminal.shell") != null) {
             String shell = System.getProperty("jexer.TTerminal.shell");
@@ -288,8 +354,9 @@ public class TTerminalWidget extends TScrollableWidget
     @Override
     public void draw() {
         int width = getDisplayWidth();
+
         boolean syncEmulator = false;
-        if ((System.currentTimeMillis() - lastUpdateTime >= 25)
+        if ((System.currentTimeMillis() - lastUpdateTime >= 20)
             && (dirty == true)
         ) {
             // Too much time has passed, draw it all.
@@ -303,7 +370,6 @@ public class TTerminalWidget extends TScrollableWidget
         }
 
         if ((syncEmulator == true)
-            || (scrollback == null)
             || (display == null)
         ) {
             // We want to minimize the amount of time we have the emulator
@@ -312,49 +378,27 @@ public class TTerminalWidget extends TScrollableWidget
                 // Update the scroll bars
                 reflowData();
 
-                if ((scrollback == null) || emulator.isReading()) {
-                    scrollback = copyBuffer(emulator.getScrollbackBuffer());
-                    display = copyBuffer(emulator.getDisplayBuffer());
+                if (!isDrawable()) {
+                    // We lost the connection, onShellExit() called an action
+                    // that ultimately removed this widget from the UI
+                    // hierarchy, so no one cares if we update the display.
+                    // Bail out.
+                    return;
+                }
+
+                if ((display == null) || emulator.isReading()) {
+                    display = emulator.getVisibleDisplay(getHeight(),
+                        -getVerticalValue());
+                    assert (display.size() == getHeight());
                 }
                 width = emulator.getWidth();
             }
             dirty = false;
         }
 
-        // Draw the box using my superclass
-        super.draw();
-
-        // Put together the visible rows
-        int visibleHeight = getHeight();
-        int visibleBottom = scrollback.size() + display.size()
-                + getVerticalValue();
-        assert (visibleBottom >= 0);
-
-        List<DisplayLine> preceedingBlankLines = new ArrayList<DisplayLine>();
-        int visibleTop = visibleBottom - visibleHeight;
-        if (visibleTop < 0) {
-            for (int i = visibleTop; i < 0; i++) {
-                preceedingBlankLines.add(emulator.getBlankDisplayLine());
-            }
-            visibleTop = 0;
-        }
-        assert (visibleTop >= 0);
-
-        List<DisplayLine> displayLines = new ArrayList<DisplayLine>();
-        displayLines.addAll(scrollback);
-        displayLines.addAll(display);
-
-        List<DisplayLine> visibleLines = new ArrayList<DisplayLine>();
-        visibleLines.addAll(preceedingBlankLines);
-        visibleLines.addAll(displayLines.subList(visibleTop,
-                visibleBottom));
-
-        visibleHeight -= visibleLines.size();
-        assert (visibleHeight >= 0);
-
         // Now draw the emulator screen
         int row = 0;
-        for (DisplayLine line: visibleLines) {
+        for (DisplayLine line: display) {
             int widthMax = width;
             if (line.isDoubleWidth()) {
                 widthMax /= 2;
@@ -394,21 +438,11 @@ public class TTerminalWidget extends TScrollableWidget
                 }
             }
             row++;
-            if (row == getHeight()) {
-                // 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++) {
-            hLineXY(0, i + row, getWidth(), ' ', background);
         }
-
     }
 
     /**
-     * Handle window close.
+     * Handle widget close.
      */
     @Override
     public void close() {
@@ -427,6 +461,8 @@ public class TTerminalWidget extends TScrollableWidget
      */
     @Override
     public void onResize(final TResizeEvent resize) {
+        // Let TWidget set my size.
+        super.onResize(resize);
 
         // Synchronize against the emulator so we don't stomp on its reader
         // thread.
@@ -447,6 +483,12 @@ public class TTerminalWidget extends TScrollableWidget
                     emulator.writeRemote("\033[8;" + getHeight() + ";" +
                         getWidth() + "t");
                 }
+
+                // Pass the correct text cell width/height to the emulator
+                if (getScreen() != null) {
+                    emulator.setTextWidth(getScreen().getTextWidth());
+                    emulator.setTextHeight(getScreen().getTextHeight());
+                }
             }
             return;
 
@@ -492,6 +534,7 @@ public class TTerminalWidget extends TScrollableWidget
             || keypress.equals(kbAltPgUp)
         ) {
             bigVerticalDecrement();
+            dirty = true;
             return;
         }
         if (keypress.equals(kbShiftPgDn)
@@ -499,6 +542,7 @@ public class TTerminalWidget extends TScrollableWidget
             || keypress.equals(kbAltPgDn)
         ) {
             bigVerticalIncrement();
+            dirty = true;
             return;
         }
 
@@ -542,10 +586,12 @@ public class TTerminalWidget extends TScrollableWidget
         if (emulator.getMouseProtocol() == ECMA48.MouseProtocol.OFF) {
             if (mouse.isMouseWheelUp()) {
                 verticalDecrement();
+                dirty = true;
                 return;
             }
             if (mouse.isMouseWheelDown()) {
                 verticalIncrement();
+                dirty = true;
                 return;
             }
         }
@@ -615,10 +661,10 @@ public class TTerminalWidget extends TScrollableWidget
     }
 
     /**
-     * Returns true if this window does not want the application-wide mouse
+     * Returns true if this widget does not want the application-wide mouse
      * cursor drawn over it.
      *
-     * @return true if this window does not want the application-wide mouse
+     * @return true if this widget does not want the application-wide mouse
      * cursor drawn over it
      */
     public boolean hasHiddenMouse() {
@@ -696,10 +742,6 @@ public class TTerminalWidget extends TScrollableWidget
         onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(),
                 getHeight()));
 
-        // Pass the correct text cell width/height to the emulator
-        emulator.setTextWidth(getScreen().getTextWidth());
-        emulator.setTextHeight(getScreen().getTextHeight());
-
         // Hide mouse when typing option
         if (System.getProperty("jexer.TTerminal.hideMouseWhenTyping",
                 "true").equals("false")) {
@@ -745,10 +787,22 @@ public class TTerminalWidget extends TScrollableWidget
      * Hook for subclasses to be notified of the shell termination.
      */
     public void onShellExit() {
-        if (getParent() instanceof TTerminalWindow) {
-            ((TTerminalWindow) getParent()).onShellExit();
+        TApplication app = getApplication();
+        if (app != null) {
+            if (closeAction != null) {
+                // We have to put this action inside invokeLater() because it
+                // could be executed during draw() when syncing with ECMA48.
+                app.invokeLater(new Runnable() {
+                    public void run() {
+                        closeAction.DO(TTerminalWidget.this);
+                    }
+                });
+            }
+            if (getApplication() != null) {
+                getApplication().postEvent(new TMenuEvent(
+                    TMenu.MID_REPAINT));
+            }
         }
-        getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
     }
 
     /**
@@ -836,20 +890,6 @@ public class TTerminalWidget extends TScrollableWidget
         return false;
     }
 
-    /**
-     * Copy a display buffer.
-     *
-     * @param buffer the buffer to copy
-     * @return a deep copy of the buffer's data
-     */
-    private List<DisplayLine> copyBuffer(final List<DisplayLine> buffer) {
-        ArrayList<DisplayLine> result = new ArrayList<DisplayLine>(buffer.size());
-        for (DisplayLine line: buffer) {
-            result.add(new DisplayLine(line));
-        }
-        return result;
-    }
-
     /**
      * Draw glyphs for a double-width or double-height VT100 cell to two
      * screen cells.