terminal paste
[nikiroo-utils.git] / src / jexer / TTerminalWidget.java
index fb4b68bb68e0648c16a5e8daaed4a8c6132fc386..8c5ed9dc71dd8f4e48fb43c1c20d6b1ed7ce0708 100644 (file)
@@ -49,6 +49,7 @@ import jexer.backend.MultiScreen;
 import jexer.backend.SwingTerminal;
 import jexer.bits.Cell;
 import jexer.bits.CellAttributes;
+import jexer.event.TCommandEvent;
 import jexer.event.TKeypressEvent;
 import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
@@ -57,13 +58,14 @@ import jexer.menu.TMenu;
 import jexer.tterminal.DisplayLine;
 import jexer.tterminal.DisplayListener;
 import jexer.tterminal.ECMA48;
+import static jexer.TCommand.*;
 import static jexer.TKeypress.*;
 
 /**
  * TTerminalWidget exposes a ECMA-48 / ANSI X3.64 style terminal in a widget.
  */
 public class TTerminalWidget extends TScrollableWidget
-                             implements DisplayListener {
+                             implements DisplayListener, EditMenuUser {
 
     /**
      * Translated strings.
@@ -358,6 +360,10 @@ public class TTerminalWidget extends TScrollableWidget
         // Let TWidget set my size.
         super.onResize(resize);
 
+        if (emulator == null) {
+            return;
+        }
+
         // Synchronize against the emulator so we don't stomp on its reader
         // thread.
         synchronized (emulator) {
@@ -418,7 +424,7 @@ public class TTerminalWidget extends TScrollableWidget
             return;
         }
 
-        if (emulator.isReading()) {
+        if ((emulator != null) && (emulator.isReading())) {
             // Get out of scrollback
             setVerticalValue(0);
             emulator.addUserEvent(keypress);
@@ -453,25 +459,27 @@ public class TTerminalWidget extends TScrollableWidget
             typingHidMouse = false;
         }
 
-        // If the emulator is tracking mouse buttons, it needs to see wheel
-        // events.
-        if (emulator.getMouseProtocol() == ECMA48.MouseProtocol.OFF) {
-            if (mouse.isMouseWheelUp()) {
-                verticalDecrement();
-                dirty = true;
-                return;
+        if (emulator != null) {
+            // If the emulator is tracking mouse buttons, it needs to see
+            // wheel events.
+            if (emulator.getMouseProtocol() == ECMA48.MouseProtocol.OFF) {
+                if (mouse.isMouseWheelUp()) {
+                    verticalDecrement();
+                    dirty = true;
+                    return;
+                }
+                if (mouse.isMouseWheelDown()) {
+                    verticalIncrement();
+                    dirty = true;
+                    return;
+                }
             }
-            if (mouse.isMouseWheelDown()) {
-                verticalIncrement();
-                dirty = true;
+            if (mouseOnEmulator(mouse)) {
+                emulator.addUserEvent(mouse);
+                readEmulatorState();
                 return;
             }
         }
-        if (mouseOnEmulator(mouse)) {
-            emulator.addUserEvent(mouse);
-            readEmulatorState();
-            return;
-        }
 
         // Emulator didn't consume it, pass it on
         super.onMouseDown(mouse);
@@ -488,7 +496,7 @@ public class TTerminalWidget extends TScrollableWidget
             typingHidMouse = false;
         }
 
-        if (mouseOnEmulator(mouse)) {
+        if ((emulator != null) && (mouseOnEmulator(mouse))) {
             emulator.addUserEvent(mouse);
             readEmulatorState();
             return;
@@ -509,7 +517,7 @@ public class TTerminalWidget extends TScrollableWidget
             typingHidMouse = false;
         }
 
-        if (mouseOnEmulator(mouse)) {
+        if ((emulator != null) && (mouseOnEmulator(mouse))) {
             emulator.addUserEvent(mouse);
             readEmulatorState();
             return;
@@ -519,6 +527,32 @@ public class TTerminalWidget extends TScrollableWidget
         super.onMouseMotion(mouse);
     }
 
+    /**
+     * Handle posted command events.
+     *
+     * @param command command event
+     */
+    @Override
+    public void onCommand(final TCommandEvent command) {
+        if (emulator == null) {
+            return;
+        }
+
+        if (command.equals(cmPaste)) {
+            // Paste text from clipboard.
+            String text = getClipboard().pasteText();
+            if (text != null) {
+                for (int i = 0; i < text.length(); ) {
+                    int ch = text.codePointAt(i);
+                    emulator.addUserEvent(new TKeypressEvent(false, 0, ch,
+                            false, false, false));
+                    i += Character.charCount(ch);
+                }
+            }
+            return;
+        }
+    }
+
     // ------------------------------------------------------------------------
     // TScrollableWidget ------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -528,12 +562,14 @@ public class TTerminalWidget extends TScrollableWidget
      */
     @Override
     public void draw() {
+        if (emulator == null) {
+            return;
+        }
+
         int width = getDisplayWidth();
 
         boolean syncEmulator = false;
-        if ((System.currentTimeMillis() - lastUpdateTime >= 20)
-            && (dirty == true)
-        ) {
+        if (System.currentTimeMillis() - lastUpdateTime >= 50) {
             // Too much time has passed, draw it all.
             syncEmulator = true;
         } else if (emulator.isReading() && (dirty == false)) {
@@ -682,7 +718,9 @@ public class TTerminalWidget extends TScrollableWidget
      */
     @Override
     public void close() {
-        emulator.close();
+        if (emulator != null) {
+            emulator.close();
+        }
         if (shell != null) {
             terminateShellChildProcess();
             shell.destroy();
@@ -695,6 +733,9 @@ public class TTerminalWidget extends TScrollableWidget
      */
     @Override
     public void reflowData() {
+        if (emulator == null) {
+            return;
+        }
 
         // Synchronize against the emulator so we don't stomp on its reader
         // thread.
@@ -733,6 +774,9 @@ public class TTerminalWidget extends TScrollableWidget
      * cursor drawn over it
      */
     public boolean hasHiddenMouse() {
+        if (emulator == null) {
+            return false;
+        }
         return (emulator.hasHiddenMousePointer() || typingHidMouse);
     }
 
@@ -743,6 +787,9 @@ public class TTerminalWidget extends TScrollableWidget
      * side
      */
     public boolean isReading() {
+        if (emulator == null) {
+            return false;
+        }
         return emulator.isReading();
     }
 
@@ -875,6 +922,10 @@ public class TTerminalWidget extends TScrollableWidget
      * screen.
      */
     private void readEmulatorState() {
+        if (emulator == null) {
+            return;
+        }
+
         // Synchronize against the emulator so we don't stomp on its reader
         // thread.
         synchronized (emulator) {
@@ -940,6 +991,9 @@ public class TTerminalWidget extends TScrollableWidget
      * @return whether or not the mouse is on the emulator
      */
     private boolean mouseOnEmulator(final TMouseEvent mouse) {
+        if (emulator == null) {
+            return false;
+        }
 
         if (!emulator.isReading()) {
             return false;
@@ -1097,7 +1151,17 @@ public class TTerminalWidget extends TScrollableWidget
      * Called by emulator when fresh data has come in.
      */
     public void displayChanged() {
-        dirty = true;
+        if (emulator != null) {
+            // Force sync here: EMCA48.run() thread might be setting
+            // dirty=true while TTerminalWdiget.draw() is setting
+            // dirty=false.  If these writes start interleaving, the display
+            // stops getting updated.
+            synchronized (emulator) {
+                dirty = true;
+            }
+        } else {
+            dirty = true;
+        }
         getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
     }
 
@@ -1125,4 +1189,44 @@ public class TTerminalWidget extends TScrollableWidget
         return 24;
     }
 
+    // ------------------------------------------------------------------------
+    // EditMenuUser -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Check if the cut menu item should be enabled.
+     *
+     * @return true if the cut menu item should be enabled
+     */
+    public boolean isEditMenuCut() {
+        return false;
+    }
+
+    /**
+     * Check if the copy menu item should be enabled.
+     *
+     * @return true if the copy menu item should be enabled
+     */
+    public boolean isEditMenuCopy() {
+        return false;
+    }
+
+    /**
+     * Check if the paste menu item should be enabled.
+     *
+     * @return true if the paste menu item should be enabled
+     */
+    public boolean isEditMenuPaste() {
+        return true;
+    }
+
+    /**
+     * Check if the clear menu item should be enabled.
+     *
+     * @return true if the clear menu item should be enabled
+     */
+    public boolean isEditMenuClear() {
+        return false;
+    }
+
 }