TEditor working
authorKevin Lamonte <kevin.lamonte@gmail.com>
Tue, 15 Aug 2017 01:26:58 +0000 (21:26 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Tue, 15 Aug 2017 01:26:58 +0000 (21:26 -0400)
docs/TODO.md
docs/worklog.md
src/jexer/TEditorWidget.java
src/jexer/TEditorWindow.java
src/jexer/TWindow.java
src/jexer/demos/DemoApplication.java
src/jexer/demos/DemoMainWindow.java
src/jexer/teditor/Document.java

index 20ce1fe6a49b4ea5c0e9710648e2be03ed949606..95e1add9ad12ff36d3d174f9318484cfb497c8a6 100644 (file)
@@ -8,21 +8,12 @@ Roadmap
 BUG: TTreeView.reflow() doesn't keep the vertical dot within the
      scrollbar.
 
-0.0.5
-
-- TEditor
-  - Document
-    - Filename
-    - Pick appropriate Highlighter: plain, Java, XML, ...
-  - TEditorWidget:
-    - Cut and Paste
-  - TTextArea extends TScrollableWidget
-
 0.0.6
 
 - TEditor
   - True tokenization and syntax highlighting: Java, C, Clojure, XML
   - Tab character support
+  - Cut and Paste
 
 - Finish up multiscreen support:
   - cmAbort to cmScreenDisconnected
index 570404097cc2a5a1be28f3e90a24c07955945539..6dde360a0ca9ba5492b3f328ed375bb263b690f5 100644 (file)
@@ -1,6 +1,16 @@
 Jexer Work Log
 ==============
 
+August 14, 2017
+
+TEditor is basically done.  Mouse movement, keyboard movement,
+backspace / delete / enter / etc. are all in.  Things are starting to
+look pretty good.
+
+I'm going to prep for a final cut and release tag tomorrow or the next
+evening.  I need to take a break and get some meatspace life dealt
+with.
+
 August 12, 2017
 
 TEditor is stubbed in about 50% complete now.  I have a Highlighter
@@ -180,4 +190,3 @@ 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.
-
index cf4d887fd3cba5525637f406e4b776e517950d54..8d2a0104b4c598aec5de23f06a4e879e4871171a 100644 (file)
@@ -112,7 +112,6 @@ public final class TEditorWidget extends TWidget {
                 }
             }
         }
-
     }
 
     /**
@@ -141,14 +140,14 @@ public final class TEditorWidget extends TWidget {
             // Set the row and column
             int newLine = topLine + mouse.getY();
             int newX = leftColumn + mouse.getX();
-            if (newLine > document.getLineCount()) {
+            if (newLine > document.getLineCount() - 1) {
                 // Go to the end
                 document.setLineNumber(document.getLineCount() - 1);
                 document.end();
-                if (document.getLineCount() > getHeight()) {
-                    setCursorY(getHeight() - 1);
+                if (newLine > document.getLineCount() - 1) {
+                    setCursorY(document.getLineCount() - 1 - topLine);
                 } else {
-                    setCursorY(document.getLineCount() - 1);
+                    setCursorY(mouse.getY());
                 }
                 alignCursor();
                 return;
@@ -160,6 +159,7 @@ public final class TEditorWidget extends TWidget {
                 document.end();
                 alignCursor();
             } else {
+                document.setCursor(newX);
                 setCursorX(mouse.getX());
             }
             return;
@@ -206,6 +206,7 @@ public final class TEditorWidget extends TWidget {
      */
     private void alignDocument(final boolean topLineIsTop) {
         int line = document.getLineNumber();
+        int cursor = document.getCursor();
 
         if ((line < topLine) || (line > topLine + getHeight() - 1)) {
             // Need to move document to ensure it fits view.
@@ -214,6 +215,9 @@ public final class TEditorWidget extends TWidget {
             } else {
                 document.setLineNumber(topLine + (getHeight() - 1));
             }
+            if (cursor < document.getCurrentLine().getDisplayLength()) {
+                document.setCursor(cursor);
+            }
         }
 
         /*
@@ -244,7 +248,8 @@ public final class TEditorWidget extends TWidget {
         /*
         System.err.println("document cursor " + document.getCursor() +
             " leftColumn " + leftColumn);
-         */
+        */
+
 
         setCursorX(document.getCursor() - leftColumn);
     }
@@ -257,13 +262,11 @@ public final class TEditorWidget extends TWidget {
     @Override
     public void onKeypress(final TKeypressEvent keypress) {
         if (keypress.equals(kbLeft)) {
-            if (document.left()) {
-                alignCursor();
-            }
+            document.left();
+            alignTopLine(false);
         } else if (keypress.equals(kbRight)) {
-            if (document.right()) {
-                alignCursor();
-            }
+            document.right();
+            alignTopLine(true);
         } else if (keypress.equals(kbUp)) {
             document.up();
             alignTopLine(false);
@@ -302,14 +305,21 @@ public final class TEditorWidget extends TWidget {
         } else if (keypress.equals(kbIns)) {
             document.setOverwrite(!document.getOverwrite());
         } else if (keypress.equals(kbDel)) {
-            // TODO: join lines
             document.del();
             alignCursor();
         } else if (keypress.equals(kbBackspace)) {
             document.backspace();
+            alignTopLine(false);
+        } else if (keypress.equals(kbTab)) {
+            // TODO: tab character.  For now just add spaces until we hit
+            // modulo 8.
+            for (int i = document.getCursor(); (i + 1) % 8 != 0; i++) {
+                document.addChar(' ');
+            }
             alignCursor();
         } else if (keypress.equals(kbEnter)) {
-            // TODO: split lines
+            document.enter();
+            alignTopLine(true);
         } else if (!keypress.getKey().isFnKey()
             && !keypress.getKey().isAlt()
             && !keypress.getKey().isCtrl()
index f96b177f6f4d1ccbddd44a08ad5d87c5b73afe09..69e254b9df70f2b4303edc9a9806b64bacd90eb1 100644 (file)
@@ -114,6 +114,25 @@ public class TEditorWindow extends TScrollableWindow {
         setupAfterEditor();
     }
 
+    /**
+     * Public constructor opens a file.
+     *
+     * @param parent the main application
+     * @param file the file to open
+     * @throws IOException if a java.io operation throws
+     */
+    public TEditorWindow(final TApplication parent,
+        final File file) throws IOException {
+
+        super(parent, file.getName(), 0, 0, parent.getScreen().getWidth(),
+            parent.getScreen().getHeight() - 2, RESIZABLE);
+
+        filename = file.getName();
+        String contents = readFileData(file);
+        editField = addEditor(contents, 0, 0, getWidth() - 2, getHeight() - 2);
+        setupAfterEditor();
+    }
+
     /**
      * Public constructor.
      *
@@ -123,6 +142,39 @@ public class TEditorWindow extends TScrollableWindow {
         this(parent, "New Text Document");
     }
 
+    /**
+     * 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));
+    }
+
     /**
      * Draw the window.
      */
@@ -169,17 +221,18 @@ public class TEditorWindow extends TScrollableWindow {
      */
     @Override
     public void onMouseDown(final TMouseEvent mouse) {
+        // Use TWidget's code to pass the event to the children.
+        super.onMouseDown(mouse);
+
         if (mouseOnEditor(mouse)) {
-            editField.onMouseDown(mouse);
+            // The editor might have changed, update the scollbars.
             setBottomValue(editField.getMaximumRowNumber());
             setVerticalValue(editField.getEditingRowNumber());
             setRightValue(editField.getMaximumColumnNumber());
             setHorizontalValue(editField.getEditingColumnNumber());
         } else {
-            // Let the scrollbars get the event
-            super.onMouseDown(mouse);
-
             if (mouse.isMouseWheelUp() || mouse.isMouseWheelDown()) {
+                // Vertical scrollbar actions
                 editField.setEditingRowNumber(getVerticalValue());
             }
             // TODO: horizontal scrolling
@@ -220,30 +273,18 @@ public class TEditorWindow extends TScrollableWindow {
         if (command.equals(cmOpen)) {
             try {
                 String filename = fileOpenBox(".");
-                 if (filename != null) {
-                     try {
-                         File file = new File(filename);
-                         StringBuilder fileContents = new StringBuilder();
-                         Scanner scanner = new Scanner(file);
-                         String EOL = System.getProperty("line.separator");
-
-                         try {
-                             while (scanner.hasNextLine()) {
-                                 fileContents.append(scanner.nextLine() + EOL);
-                             }
-                             new TEditorWindow(getApplication(), filename,
-                                 fileContents.toString());
-                         } finally {
-                             scanner.close();
-                         }
-                     } catch (IOException e) {
-                         // TODO: make this a message box
-                         e.printStackTrace();
-                     }
-                 }
+                if (filename != null) {
+                    try {
+                        String contents = readFileData(filename);
+                        new TEditorWindow(getApplication(), filename, contents);
+                    } catch (IOException e) {
+                        messageBox("Error", "Error reading file: " +
+                            e.getMessage());
+                    }
+                }
             } catch (IOException e) {
-                // TODO: make this a message box
-                e.printStackTrace();
+                messageBox("Error", "Error opening file dialog: " +
+                    e.getMessage());
             }
             return;
         }
@@ -253,8 +294,7 @@ public class TEditorWindow extends TScrollableWindow {
                 try {
                     editField.saveToFilename(filename);
                 } catch (IOException e) {
-                    // TODO: make this a message box
-                    e.printStackTrace();
+                    messageBox("Error", "Error saving file: " + e.getMessage());
                 }
             }
             return;
index 962130560af8c7ed9cd3f9367db389c7b7bbc9a4..406eb7897a21a152329dbfc9c6ddee3bd52251aa 100644 (file)
@@ -1384,5 +1384,4 @@ public class TWindow extends TWidget {
         getScreen().hLineXY(x, y, n, ch, attr);
     }
 
-
 }
index 4d8671f92ef5c6a9290afadf131c51ed522078ae..5036dd017ff71ce0ae45a2590a1fcdb4d0890f02 100644 (file)
@@ -29,7 +29,6 @@
 package jexer.demos;
 
 import java.io.*;
-import java.util.*;
 
 import jexer.*;
 import jexer.event.*;
@@ -187,20 +186,7 @@ public class DemoApplication extends TApplication {
                 String filename = fileOpenBox(".");
                  if (filename != null) {
                      try {
-                         File file = new File(filename);
-                         StringBuilder fileContents = new StringBuilder();
-                         Scanner scanner = new Scanner(file);
-                         String EOL = System.getProperty("line.separator");
-
-                         try {
-                             while (scanner.hasNextLine()) {
-                                 fileContents.append(scanner.nextLine() + EOL);
-                             }
-                             new DemoTextWindow(this, filename,
-                                 fileContents.toString());
-                         } finally {
-                             scanner.close();
-                         }
+                         new TEditorWindow(this, new File(filename));
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
index 8840605e017ef90a00fcc210d86fafd42ae250df..85d03414307465fe3930f881d979c2e4fc7b81f3 100644 (file)
  */
 package jexer.demos;
 
+import java.io.*;
+
 import jexer.*;
+import jexer.event.*;
 import static jexer.TCommand.*;
 import static jexer.TKeypress.*;
 
@@ -206,4 +209,34 @@ public class DemoMainWindow extends TWindow {
         statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
         statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
+
+    /**
+     * Method that subclasses can override to handle posted command events.
+     *
+     * @param command command event
+     */
+    @Override
+    public void onCommand(final TCommandEvent command) {
+        if (command.equals(cmOpen)) {
+            try {
+                String filename = fileOpenBox(".");
+                if (filename != null) {
+                    try {
+                        new TEditorWindow(getApplication(), new File(filename));
+                    } catch (IOException e) {
+                        messageBox("Error", "Error reading file: " +
+                            e.getMessage());
+                    }
+                }
+            } catch (IOException e) {
+                messageBox("Error", "Error opening file dialog: " +
+                    e.getMessage());
+            }
+            return;
+        }
+
+        // Didn't handle it, let children get it instead
+        super.onCommand(command);
+    }
+
 }
index 42082e55ad21a670b31a91e8ba6e9b7e0950344e..c8ab746096f20971871fe8da99efbe67dd4fcade 100644 (file)
@@ -298,7 +298,15 @@ public class Document {
      * @return true if the cursor position changed
      */
     public boolean left() {
-        return lines.get(lineNumber).left();
+        if (!lines.get(lineNumber).left()) {
+            // We are on the leftmost column, wrap
+            if (up()) {
+                end();
+            } else {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -307,7 +315,15 @@ public class Document {
      * @return true if the cursor position changed
      */
     public boolean right() {
-        return lines.get(lineNumber).right();
+        if (!lines.get(lineNumber).right()) {
+            // We are on the rightmost column, wrap
+            if (down()) {
+                home();
+            } else {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -333,7 +349,19 @@ public class Document {
      */
     public void del() {
         dirty = true;
-        lines.get(lineNumber).del();
+        int cursor = lines.get(lineNumber).getCursor();
+        if (cursor < lines.get(lineNumber).getDisplayLength() - 1) {
+            lines.get(lineNumber).del();
+        } else if (lineNumber < lines.size() - 2) {
+            // Join two lines
+            StringBuilder newLine = new StringBuilder(lines.
+                get(lineNumber).getRawString());
+            newLine.append(lines.get(lineNumber + 1).getRawString());
+            lines.set(lineNumber, new Line(newLine.toString(),
+                    defaultColor, highlighter));
+            lines.get(lineNumber).setCursor(cursor);
+            lines.remove(lineNumber + 1);
+        }
     }
 
     /**
@@ -341,7 +369,43 @@ public class Document {
      */
     public void backspace() {
         dirty = true;
-        lines.get(lineNumber).backspace();
+        int cursor = lines.get(lineNumber).getCursor();
+        if (cursor > 0) {
+            lines.get(lineNumber).backspace();
+        } else if (lineNumber > 0) {
+            // Join two lines
+            lineNumber--;
+            String firstLine = lines.get(lineNumber).getRawString();
+            if (firstLine.length() > 0) {
+                // Backspacing combining two lines
+                StringBuilder newLine = new StringBuilder(firstLine);
+                newLine.append(lines.get(lineNumber + 1).getRawString());
+                lines.set(lineNumber, new Line(newLine.toString(),
+                        defaultColor, highlighter));
+                lines.get(lineNumber).setCursor(firstLine.length());
+                lines.remove(lineNumber + 1);
+            } else {
+                // Backspacing an empty line
+                lines.remove(lineNumber);
+                lines.get(lineNumber).setCursor(0);
+            }
+        }
+    }
+
+    /**
+     * Split the current line into two, like pressing the enter key.
+     */
+    public void enter() {
+        dirty = true;
+        int cursor = lines.get(lineNumber).getCursor();
+        String original = lines.get(lineNumber).getRawString();
+        String firstLine = original.substring(0, cursor);
+        String secondLine = original.substring(cursor);
+        lines.add(lineNumber + 1, new Line(secondLine, defaultColor,
+                highlighter));
+        lines.set(lineNumber, new Line(firstLine, defaultColor, highlighter));
+        lineNumber++;
+        lines.get(lineNumber).home();
     }
 
     /**