TEditor working
[nikiroo-utils.git] / src / jexer / teditor / Document.java
index cae6f47106db0ae9a3656dd1d318f7916771be12..c8ab746096f20971871fe8da99efbe67dd4fcade 100644 (file)
  */
 package jexer.teditor;
 
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.util.ArrayList;
 import java.util.List;
 
+import jexer.bits.CellAttributes;
+
 /**
  * A Document represents a text file, as a collection of lines.
  */
@@ -52,6 +57,21 @@ public class Document {
      */
     private boolean overwrite = false;
 
+    /**
+     * If true, the document has been edited.
+     */
+    private boolean dirty = false;
+
+    /**
+     * The default color for the TEditor class.
+     */
+    private CellAttributes defaultColor = null;
+
+    /**
+     * The text highlighter to use.
+     */
+    private Highlighter highlighter = new Highlighter();
+
     /**
      * Get the overwrite flag.
      *
@@ -61,6 +81,41 @@ public class Document {
         return overwrite;
     }
 
+    /**
+     * Get the dirty value.
+     *
+     * @return true if the buffer is dirty
+     */
+    public boolean isDirty() {
+        return dirty;
+    }
+
+    /**
+     * Save contents to file.
+     *
+     * @param filename file to save to
+     * @throws IOException if a java.io operation throws
+     */
+    public void saveToFilename(final String filename) throws IOException {
+        OutputStreamWriter output = null;
+        try {
+            output = new OutputStreamWriter(new FileOutputStream(filename),
+                "UTF-8");
+
+            for (Line line: lines) {
+                output.write(line.getRawString());
+                output.write("\n");
+            }
+
+            dirty = false;
+        }
+        finally {
+            if (output != null) {
+                output.close();
+            }
+        }
+    }
+
     /**
      * Set the overwrite flag.
      *
@@ -81,6 +136,15 @@ public class Document {
         return lineNumber;
     }
 
+    /**
+     * Get the current editing line.
+     *
+     * @return the line
+     */
+    public Line getCurrentLine() {
+        return lines.get(lineNumber);
+    }
+
     /**
      * Get a specific line by number.
      *
@@ -100,19 +164,65 @@ public class Document {
      */
     public void setLineNumber(final int n) {
         if ((n < 0) || (n > lines.size())) {
-            throw new IndexOutOfBoundsException("Line size is " + lines.size() +
-                ", requested index " + n);
+            throw new IndexOutOfBoundsException("Lines array size is " +
+                lines.size() + ", requested index " + n);
         }
         lineNumber = n;
     }
 
+    /**
+     * Get the current cursor position of the editing line.
+     *
+     * @return the cursor position
+     */
+    public int getCursor() {
+        return lines.get(lineNumber).getCursor();
+    }
+
+    /**
+     * Set the current cursor position of the editing line.  0-based.
+     *
+     * @param cursor the new cursor position
+     */
+    public void setCursor(final int cursor) {
+        lines.get(lineNumber).setCursor(cursor);
+    }
+
+    /**
+     * Construct a new Document from an existing text string.
+     *
+     * @param str the text string
+     * @param defaultColor the color for unhighlighted text
+     */
+    public Document(final String str, final CellAttributes defaultColor) {
+        this.defaultColor = defaultColor;
+
+        // TODO: set different colors based on file extension
+        highlighter.setJavaColors();
+
+        String [] rawLines = str.split("\n");
+        for (int i = 0; i < rawLines.length; i++) {
+            lines.add(new Line(rawLines[i], this.defaultColor, highlighter));
+        }
+    }
+
     /**
      * Increment the line number by one.  If at the last line, do nothing.
+     *
+     * @return true if the editing line changed
      */
-    public void down() {
+    public boolean down() {
         if (lineNumber < lines.size() - 1) {
+            int x = lines.get(lineNumber).getCursor();
             lineNumber++;
+            if (x > lines.get(lineNumber).getDisplayLength()) {
+                lines.get(lineNumber).end();
+            } else {
+                lines.get(lineNumber).setCursor(x);
+            }
+            return true;
         }
+        return false;
     }
 
     /**
@@ -120,21 +230,42 @@ public class Document {
      * increment only to the last line.
      *
      * @param n the number of lines to increment by
+     * @return true if the editing line changed
      */
-    public void down(final int n) {
-        lineNumber += n;
-        if (lineNumber > lines.size() - 1) {
-            lineNumber = lines.size() - 1;
+    public boolean down(final int n) {
+        if (lineNumber < lines.size() - 1) {
+            int x = lines.get(lineNumber).getCursor();
+            lineNumber += n;
+            if (lineNumber > lines.size() - 1) {
+                lineNumber = lines.size() - 1;
+            }
+            if (x > lines.get(lineNumber).getDisplayLength()) {
+                lines.get(lineNumber).end();
+            } else {
+                lines.get(lineNumber).setCursor(x);
+            }
+            return true;
         }
+        return false;
     }
 
     /**
      * Decrement the line number by one.  If at the first line, do nothing.
+     *
+     * @return true if the editing line changed
      */
-    public void up() {
+    public boolean up() {
         if (lineNumber > 0) {
+            int x = lines.get(lineNumber).getCursor();
             lineNumber--;
+            if (x > lines.get(lineNumber).getDisplayLength()) {
+                lines.get(lineNumber).end();
+            } else {
+                lines.get(lineNumber).setCursor(x);
+            }
+            return true;
         }
+        return false;
     }
 
     /**
@@ -142,54 +273,139 @@ public class Document {
      * decrement only to the first line.
      *
      * @param n the number of lines to decrement by
+     * @return true if the editing line changed
      */
-    public void up(final int n) {
-        lineNumber -= n;
-        if (lineNumber < 0) {
-            lineNumber = 0;
+    public boolean up(final int n) {
+        if (lineNumber > 0) {
+            int x = lines.get(lineNumber).getCursor();
+            lineNumber -= n;
+            if (lineNumber < 0) {
+                lineNumber = 0;
+            }
+            if (x > lines.get(lineNumber).getDisplayLength()) {
+                lines.get(lineNumber).end();
+            } else {
+                lines.get(lineNumber).setCursor(x);
+            }
+            return true;
         }
+        return false;
     }
 
     /**
      * Decrement the cursor by one.  If at the first column, do nothing.
+     *
+     * @return true if the cursor position changed
      */
-    public void left() {
-        lines.get(lineNumber).left();
+    public boolean left() {
+        if (!lines.get(lineNumber).left()) {
+            // We are on the leftmost column, wrap
+            if (up()) {
+                end();
+            } else {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
      * Increment the cursor by one.  If at the last column, do nothing.
+     *
+     * @return true if the cursor position changed
      */
-    public void right() {
-        lines.get(lineNumber).right();
+    public boolean right() {
+        if (!lines.get(lineNumber).right()) {
+            // We are on the rightmost column, wrap
+            if (down()) {
+                home();
+            } else {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
      * Go to the first column of this line.
+     *
+     * @return true if the cursor position changed
      */
-    public void home() {
-        lines.get(lineNumber).home();
+    public boolean home() {
+        return lines.get(lineNumber).home();
     }
 
     /**
      * Go to the last column of this line.
+     *
+     * @return true if the cursor position changed
      */
-    public void end() {
-        lines.get(lineNumber).end();
+    public boolean end() {
+        return lines.get(lineNumber).end();
     }
 
     /**
      * Delete the character under the cursor.
      */
     public void del() {
-        lines.get(lineNumber).del();
+        dirty = true;
+        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);
+        }
     }
 
     /**
      * Delete the character immediately preceeding the cursor.
      */
     public void backspace() {
-        lines.get(lineNumber).backspace();
+        dirty = true;
+        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();
     }
 
     /**
@@ -199,7 +415,12 @@ public class Document {
      * @param ch the character to replace or insert
      */
     public void addChar(final char ch) {
-        lines.get(lineNumber).addChar(ch);
+        dirty = true;
+        if (overwrite) {
+            lines.get(lineNumber).replaceChar(ch);
+        } else {
+            lines.get(lineNumber).addChar(ch);
+        }
     }
 
     /**
@@ -235,16 +456,4 @@ public class Document {
         return n;
     }
 
-    /**
-     * Construct a new Document from an existing text string.
-     *
-     * @param str the text string
-     */
-    public Document(final String str) {
-        String [] rawLines = str.split("\n");
-        for (int i = 0; i < rawLines.length; i++) {
-            lines.add(new Line(rawLines[i]));
-        }
-    }
-
 }