Merge branch 'subtree'
[fanfix.git] / src / jexer / TEditorWindow.java
index 5bf664d254e515f95d41418ef383ae01559dcdb4..a28376ba3a18e8fcdfd75a300a32c180c2946385 100644 (file)
@@ -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"),
@@ -30,6 +30,8 @@ package jexer;
 
 import java.io.File;
 import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
 import java.util.Scanner;
 
 import jexer.TApplication;
@@ -42,8 +44,10 @@ import jexer.bits.CellAttributes;
 import jexer.bits.GraphicsChars;
 import jexer.event.TCommandEvent;
 import jexer.event.TKeypressEvent;
+import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
 import jexer.event.TResizeEvent;
+import jexer.menu.TMenu;
 import static jexer.TCommand.*;
 import static jexer.TKeypress.*;
 
@@ -52,6 +56,15 @@ import static jexer.TKeypress.*;
  */
 public class TEditorWindow extends TScrollableWindow {
 
+    /**
+     * Translated strings.
+     */
+    private static final ResourceBundle i18n = ResourceBundle.getBundle(TEditorWindow.class.getName());
+
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Hang onto my TEditor so I can resize it with the window.
      */
@@ -63,24 +76,19 @@ public class TEditorWindow extends TScrollableWindow {
     private String filename = "";
 
     /**
-     * Setup other fields after the editor is created.
+     * If true, hide the mouse after typing a keystroke.
      */
-    private void setupAfterEditor() {
-        hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
-        vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
-        setMinimumWindowWidth(25);
-        setMinimumWindowHeight(10);
-        setTopValue(1);
-        setBottomValue(editField.getMaximumRowNumber());
-        setLeftValue(1);
-        setRightValue(editField.getMaximumColumnNumber());
+    private boolean hideMouseWhenTyping = true;
 
-        statusBar = newStatusBar("Editor");
-        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
-        statusBar.addShortcutKeypress(kbF2, cmSave, "Save");
-        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
-        statusBar.addShortcutKeypress(kbF10, cmMenu, "Menu");
-    }
+    /**
+     * If true, the mouse should not be displayed because a keystroke was
+     * typed.
+     */
+    private boolean typingHidMouse = false;
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Public constructor sets window title.
@@ -91,7 +99,7 @@ public class TEditorWindow extends TScrollableWindow {
     public TEditorWindow(final TApplication parent, final String title) {
 
         super(parent, title, 0, 0, parent.getScreen().getWidth(),
-            parent.getScreen().getHeight() - 2, RESIZABLE);
+            parent.getDesktopBottom() - parent.getDesktopTop(), RESIZABLE);
 
         editField = addEditor("", 0, 0, getWidth() - 2, getHeight() - 2);
         setupAfterEditor();
@@ -108,7 +116,7 @@ public class TEditorWindow extends TScrollableWindow {
         final String contents) {
 
         super(parent, title, 0, 0, parent.getScreen().getWidth(),
-            parent.getScreen().getHeight() - 2, RESIZABLE);
+            parent.getDesktopBottom() - parent.getDesktopTop(), RESIZABLE);
 
         filename = title;
         editField = addEditor(contents, 0, 0, getWidth() - 2, getHeight() - 2);
@@ -126,7 +134,7 @@ public class TEditorWindow extends TScrollableWindow {
         final File file) throws IOException {
 
         super(parent, file.getName(), 0, 0, parent.getScreen().getWidth(),
-            parent.getScreen().getHeight() - 2, RESIZABLE);
+            parent.getDesktopBottom() - parent.getDesktopTop(), RESIZABLE);
 
         filename = file.getName();
         String contents = readFileData(file);
@@ -140,79 +148,31 @@ public class TEditorWindow extends TScrollableWindow {
      * @param parent the main application
      */
     public TEditorWindow(final TApplication parent) {
-        this(parent, "New Text Document");
+        this(parent, i18n.getString("newTextDocument"));
     }
 
-    /**
-     * Read file data into a string.
-     *
-     * @param file the file to open
-     * @return the file contents
-     * @throws IOException if a java.io operation throws
-     */
-    private String readFileData(final File file) throws IOException {
-        StringBuilder fileContents = new StringBuilder();
-        Scanner scanner = new Scanner(file);
-        String EOL = System.getProperty("line.separator");
-
-        try {
-            while (scanner.hasNextLine()) {
-                fileContents.append(scanner.nextLine() + EOL);
-            }
-            return fileContents.toString();
-        } finally {
-            scanner.close();
-        }
-    }
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
-     * Read file data into a string.
-     *
-     * @param filename the file to open
-     * @return the file contents
-     * @throws IOException if a java.io operation throws
+     * Called by application.switchWindow() when this window gets the
+     * focus, and also by application.addWindow().
      */
-    private String readFileData(final String filename) throws IOException {
-        return readFileData(new File(filename));
+    public void onFocus() {
+        super.onFocus();
+        getApplication().enableMenuItem(TMenu.MID_UNDO);
+        getApplication().enableMenuItem(TMenu.MID_REDO);
     }
 
     /**
-     * Draw the window.
+     * Called by application.switchWindow() when another window gets the
+     * focus.
      */
-    @Override
-    public void draw() {
-        // Draw as normal.
-        super.draw();
-
-        // Add the row:col on the bottom row
-        CellAttributes borderColor = getBorder();
-        String location = String.format(" %d:%d ",
-            editField.getEditingRowNumber(),
-            editField.getEditingColumnNumber());
-        int colon = location.indexOf(':');
-        putStringXY(10 - colon, getHeight() - 1, location, borderColor);
-
-        if (editField.isDirty()) {
-            putCharXY(2, getHeight() - 1, GraphicsChars.OCTOSTAR, borderColor);
-        }
-    }
-
-    /**
-     * Check if a mouse press/release/motion event coordinate is over the
-     * editor.
-     *
-     * @param mouse a mouse-based event
-     * @return whether or not the mouse is on the editor
-     */
-    private final boolean mouseOnEditor(final TMouseEvent mouse) {
-        if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
-            && (mouse.getAbsoluteX() <  getAbsoluteX() + getWidth() - 1)
-            && (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
-            && (mouse.getAbsoluteY() <  getAbsoluteY() + getHeight() - 1)
-        ) {
-            return true;
-        }
-        return false;
+    public void onUnfocus() {
+        super.onUnfocus();
+        getApplication().disableMenuItem(TMenu.MID_UNDO);
+        getApplication().disableMenuItem(TMenu.MID_REDO);
     }
 
     /**
@@ -225,16 +185,20 @@ public class TEditorWindow extends TScrollableWindow {
         // Use TWidget's code to pass the event to the children.
         super.onMouseDown(mouse);
 
+        if (hideMouseWhenTyping) {
+            typingHidMouse = false;
+        }
+
         if (mouseOnEditor(mouse)) {
             // The editor might have changed, update the scollbars.
             setBottomValue(editField.getMaximumRowNumber());
-            setVerticalValue(editField.getEditingRowNumber());
+            setVerticalValue(editField.getVisibleRowNumber());
             setRightValue(editField.getMaximumColumnNumber());
             setHorizontalValue(editField.getEditingColumnNumber());
         } else {
             if (mouse.isMouseWheelUp() || mouse.isMouseWheelDown()) {
                 // Vertical scrollbar actions
-                editField.setEditingRowNumber(getVerticalValue());
+                editField.setVisibleRowNumber(getVerticalValue());
             }
         }
     }
@@ -249,12 +213,19 @@ public class TEditorWindow extends TScrollableWindow {
         // Use TWidget's code to pass the event to the children.
         super.onMouseUp(mouse);
 
+        if (hideMouseWhenTyping) {
+            typingHidMouse = false;
+        }
+
         if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
             // Clicked on vertical scrollbar
-            editField.setEditingRowNumber(getVerticalValue());
+            editField.setVisibleRowNumber(getVerticalValue());
+        }
+        if (mouse.isMouse1() && mouseOnHorizontalScroller(mouse)) {
+            // Clicked on horizontal scrollbar
+            editField.setVisibleColumnNumber(getHorizontalValue());
+            setHorizontalValue(editField.getVisibleColumnNumber());
         }
-
-        // TODO: horizontal scrolling
     }
 
     /**
@@ -267,19 +238,26 @@ public class TEditorWindow extends TScrollableWindow {
         // Use TWidget's code to pass the event to the children.
         super.onMouseMotion(mouse);
 
+        if (hideMouseWhenTyping) {
+            typingHidMouse = false;
+        }
+
         if (mouseOnEditor(mouse) && mouse.isMouse1()) {
             // The editor might have changed, update the scollbars.
             setBottomValue(editField.getMaximumRowNumber());
-            setVerticalValue(editField.getEditingRowNumber());
+            setVerticalValue(editField.getVisibleRowNumber());
             setRightValue(editField.getMaximumColumnNumber());
             setHorizontalValue(editField.getEditingColumnNumber());
         } else {
             if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
                 // Clicked/dragged on vertical scrollbar
-                editField.setEditingRowNumber(getVerticalValue());
+                editField.setVisibleRowNumber(getVerticalValue());
+            }
+            if (mouse.isMouse1() && mouseOnHorizontalScroller(mouse)) {
+                // Clicked/dragged on horizontal scrollbar
+                editField.setVisibleColumnNumber(getHorizontalValue());
+                setHorizontalValue(editField.getVisibleColumnNumber());
             }
-
-            // TODO: horizontal scrolling
         }
 
     }
@@ -291,12 +269,16 @@ public class TEditorWindow extends TScrollableWindow {
      */
     @Override
     public void onKeypress(final TKeypressEvent keypress) {
+        if (hideMouseWhenTyping) {
+            typingHidMouse = true;
+        }
+
         // Use TWidget's code to pass the event to the children.
         super.onKeypress(keypress);
 
         // The editor might have changed, update the scollbars.
         setBottomValue(editField.getMaximumRowNumber());
-        setVerticalValue(editField.getEditingRowNumber());
+        setVerticalValue(editField.getVisibleRowNumber());
         setRightValue(editField.getMaximumColumnNumber());
         setHorizontalValue(editField.getEditingColumnNumber());
     }
@@ -340,13 +322,15 @@ public class TEditorWindow extends TScrollableWindow {
                         String contents = readFileData(filename);
                         new TEditorWindow(getApplication(), filename, contents);
                     } catch (IOException e) {
-                        messageBox("Error", "Error reading file: " +
-                            e.getMessage());
+                        messageBox(i18n.getString("errorDialogTitle"),
+                            MessageFormat.format(i18n.
+                                getString("errorReadingFile"), e.getMessage()));
                     }
                 }
             } catch (IOException e) {
-                messageBox("Error", "Error opening file dialog: " +
-                    e.getMessage());
+                messageBox(i18n.getString("errorDialogTitle"),
+                    MessageFormat.format(i18n.
+                        getString("errorOpeningFileDialog"), e.getMessage()));
             }
             return;
         }
@@ -356,7 +340,9 @@ public class TEditorWindow extends TScrollableWindow {
                 try {
                     editField.saveToFilename(filename);
                 } catch (IOException e) {
-                    messageBox("Error", "Error saving file: " + e.getMessage());
+                messageBox(i18n.getString("errorDialogTitle"),
+                    MessageFormat.format(i18n.
+                        getString("errorSavingFile"), e.getMessage()));
                 }
             }
             return;
@@ -366,4 +352,144 @@ public class TEditorWindow extends TScrollableWindow {
         super.onCommand(command);
     }
 
+    /**
+     * Handle posted menu events.
+     *
+     * @param menu menu event
+     */
+    @Override
+    public void onMenu(final TMenuEvent menu) {
+        switch (menu.getId()) {
+        case TMenu.MID_UNDO:
+            editField.undo();
+            break;
+        case TMenu.MID_REDO:
+            editField.redo();
+            break;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // TWindow ----------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Draw the window.
+     */
+    @Override
+    public void draw() {
+        // Draw as normal.
+        super.draw();
+
+        // Add the row:col on the bottom row
+        CellAttributes borderColor = getBorder();
+        String location = String.format(" %d:%d ",
+            editField.getEditingRowNumber(),
+            editField.getEditingColumnNumber());
+        int colon = location.indexOf(':');
+        putStringXY(10 - colon, getHeight() - 1, location, borderColor);
+
+        if (editField.isDirty()) {
+            putCharXY(2, getHeight() - 1, GraphicsChars.OCTOSTAR, borderColor);
+        }
+    }
+
+    /**
+     * 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() {
+        return (super.hasHiddenMouse() || typingHidMouse);
+    }
+
+    // ------------------------------------------------------------------------
+    // TEditorWindow ----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Setup other fields after the editor is created.
+     */
+    private void setupAfterEditor() {
+        hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
+        vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
+        setMinimumWindowWidth(25);
+        setMinimumWindowHeight(10);
+        setTopValue(1);
+        setBottomValue(editField.getMaximumRowNumber());
+        setLeftValue(1);
+        setRightValue(editField.getMaximumColumnNumber());
+
+        statusBar = newStatusBar(i18n.getString("statusBar"));
+        statusBar.addShortcutKeypress(kbF1, cmHelp,
+            i18n.getString("statusBarHelp"));
+        statusBar.addShortcutKeypress(kbF2, cmSave,
+            i18n.getString("statusBarSave"));
+        statusBar.addShortcutKeypress(kbF3, cmOpen,
+            i18n.getString("statusBarOpen"));
+        statusBar.addShortcutKeypress(kbF10, cmMenu,
+            i18n.getString("statusBarMenu"));
+
+        // Hide mouse when typing option
+        if (System.getProperty("jexer.TEditor.hideMouseWhenTyping",
+                "true").equals("false")) {
+
+            hideMouseWhenTyping = false;
+        }
+    }
+
+    /**
+     * Read file data into a string.
+     *
+     * @param file the file to open
+     * @return the file contents
+     * @throws IOException if a java.io operation throws
+     */
+    private String readFileData(final File file) throws IOException {
+        StringBuilder fileContents = new StringBuilder();
+        Scanner scanner = new Scanner(file);
+        String EOL = System.getProperty("line.separator");
+
+        try {
+            while (scanner.hasNextLine()) {
+                fileContents.append(scanner.nextLine() + EOL);
+            }
+            return fileContents.toString();
+        } finally {
+            scanner.close();
+        }
+    }
+
+    /**
+     * Read file data into a string.
+     *
+     * @param filename the file to open
+     * @return the file contents
+     * @throws IOException if a java.io operation throws
+     */
+    private String readFileData(final String filename) throws IOException {
+        return readFileData(new File(filename));
+    }
+
+    /**
+     * Check if a mouse press/release/motion event coordinate is over the
+     * editor.
+     *
+     * @param mouse a mouse-based event
+     * @return whether or not the mouse is on the editor
+     */
+    private boolean mouseOnEditor(final TMouseEvent mouse) {
+        if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
+            && (mouse.getAbsoluteX() <  getAbsoluteX() + getWidth() - 1)
+            && (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
+            && (mouse.getAbsoluteY() <  getAbsoluteY() + getHeight() - 1)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
 }