wip II
[sdk] / ecere / src / gui / controls / EditBox.ec
index b9520b8..f8d2825 100644 (file)
@@ -47,28 +47,48 @@ public:
       byte b = (string)[0]; \
       int i; \
       byte mask = 0x7F; \
-      numBytes = 1; \
+      numBytes = b ? 1 : 0; \
       ch = 0; \
-      if(b & 0x80 && b & 0x40) \
+      if(b & 0x80) \
       { \
-         mask >>= 2; \
-         numBytes++; \
-         if(b & 0x20) \
+         if(b & 0x40) \
          { \
+            mask >>= 2; \
             numBytes++; \
-            mask >>= 1; \
-            if(b & 0x10) \
+            if(b & 0x20) \
             { \
                numBytes++; \
                mask >>= 1; \
+               if(b & 0x10) \
+               { \
+                  if(b & 0x08) { numBytes = 0; } \
+                  numBytes++; \
+                  mask >>= 1; \
+               } \
             } \
          } \
+         else \
+            numBytes = 0; \
       } \
       for(i = 0; i<numBytes; i++) \
       { \
          ch <<= 6; \
-         ch |= (string)[i] & mask; \
+         ch |= (b = (string)[i]) & mask; \
          mask = 0x3F; \
+         if(i > 1 && (!(b & 0x80) || (b & 0x40))) \
+         { \
+            numBytes = 0; \
+            ch = 0; \
+         } \
+      } \
+      if(i < numBytes || \
+         ch > 0x10FFFF || (ch >= 0xD800 && ch <= 0xDFFF) || \
+        (ch < 0x80 && numBytes > 1) || \
+        (ch < 0x800 && numBytes > 2) || \
+        (ch < 0x10000 && numBytes > 3))\
+      { \
+         ch = 0; \
+         numBytes = 0; \
       } \
       ch; \
    })
@@ -79,12 +99,13 @@ class EditBoxBits
    bool noCaret:1, noSelect:1, tabKey:1, useTab:1, tabSel:1, allCaps:1, syntax:1, wrap:1;
 
    // Syntax States
-   bool inMultiLineComment:1, inPrep:1, escaped:1;
+   bool inMultiLineComment:1, inPrep:1, escaped:1, continuedSingleLineComment:1;
 
    bool recomputeSyntax:1;
    bool cursorFollowsView:1;
    
    // bool lineNumbers:1;
+   bool autoSize:1;
 };
 
 /* TODO:
@@ -105,15 +126,13 @@ private:
 
 static class ArrayImpl
 {
-   uint size;
    Class type;
+   uint size;
    byte * array;
 };
 
 public class OldArray
 {
-   uint size;
-
    ~OldArray()
    {
       int c;
@@ -121,7 +140,7 @@ public class OldArray
       if(type.type == normalClass || type.type == noHeadClass)
       {
          for(c = 0; c<size; c++)
-            type._vTbl[__ecereVMethodID_class_OnFree](type, array[c]);
+            ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, array[c]);
       }
       // TODO: Call OnFree for structClass
       delete ((ArrayImpl)this).array;
@@ -152,6 +171,8 @@ public:
          memcpy(((ArrayImpl)this).array, value, type.typeSize * size);
       }
    }
+private:
+   uint size;
 };
 
 public class UndoAction : struct
@@ -170,16 +191,9 @@ public:
    }
 };
 
-
-class ArrayUndoActions : OldArray
-{
-   type = class(UndoAction);
-   UndoAction * _;
-};
-
 public class UndoBuffer
 {
-   ArrayUndoActions actions { size = 8 };
+   Array<UndoAction> actions { size = 8 };
 public:
    int count;
    int curAction;
@@ -188,13 +202,18 @@ public:
    bool insideRedo;
 
    dontRecord = 0;
+
+   ~UndoBuffer()
+   {
+      actions.Free();
+   }
    
    void Undo()
    {
       dontRecord++;
       if(curAction > 0)
       {
-         UndoAction action = actions._[--curAction];
+         UndoAction action = actions[--curAction];
 #ifdef _DEBUG
          /*Print("Undoing: ");
          action.Print(data);*/
@@ -210,7 +229,7 @@ public:
       insideRedo = true;
       if(curAction < count)
       {
-         UndoAction action = actions._[curAction];
+         UndoAction action = actions[curAction];
          curAction++;
 #ifdef _DEBUG
          /*Print("Redoing: ");
@@ -230,7 +249,7 @@ public:
          {
             int c;
             for(c = curAction; c < count; c++)
-               delete actions._[c];
+               delete actions[c];
          }
 
          count = curAction;
@@ -242,7 +261,7 @@ public:
          /*Print("Recording: ");
          action.Print(data);*/
 #endif
-         actions._[count++] = action;
+         actions[count++] = action;
          curAction = count;
 
          if(actions.size > count + count / 2 && count + count / 2 >= 8)
@@ -640,7 +659,7 @@ static char * keyWords1[] =
    "virtual", "thisclass","unichar", "dbtable", "dbindex", "database_open", "dbfield",
 
    // Types
-   "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64",
+   "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64", "uintptr", "intptr", "intsize", "uintsize",
 
    // Values
    "this", "true", "false", "null", "value",
@@ -662,23 +681,12 @@ static char ** keyWords[] = { keyWords1, keyWords2 };
 //static int * keyLen[NUM_KEYWORD_GROUPS];
 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
 
-/*
-static FileFilter filters[] =
-{
-   { "All files", null },
-   {
-      "Text Files (*.txt)",
-      "txt"
-   }
-};
-static FileListConfig fileListConfig = { "", "", filters, sizeof(filters), null, 0 };
-*/
 static char searchString[1025], replaceString[1025];
 static bool matchCase = false, wholeWord = false, searchUp = false;
 
 static GoToDialog goToDialog
 {
-   autoCreate = false, isModal = true, text = "Go To"
+   autoCreate = false, isModal = true, text = $"Go To"
 };
 
 public class EditBox : CommonControl
@@ -723,11 +731,11 @@ public:
    };
 
    // Properties
-   property bool textHorzScroll { property_category "Behavior" set { style.hScroll = value; } get { return style.hScroll; } };      // Should cut the text on set to false
-   property bool textVertScroll { property_category "Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
+   property bool textHorzScroll { property_category $"Behavior" set { style.hScroll = value; } get { return style.hScroll; } };      // Should cut the text on set to false
+   property bool textVertScroll { property_category $"Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
    property bool readOnly
    {
-      property_category "Behavior" 
+      property_category $"Behavior" 
       set
       {
          style.readOnly = value;
@@ -737,20 +745,21 @@ public:
       }
       get { return style.readOnly; }
    };
-   property bool multiLine { property_category "Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
-   property bool freeCaret { property_category "Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
-   property bool tabKey { property_category "Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
-   property int tabSize { property_category "Behavior" set { tabSize = value; } get { return tabSize; } };
-   property bool tabSelection { property_category "Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
-   property bool smartHome { property_category "Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
-   property bool autoEmpty { property_category "Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
-   property bool noCaret { property_category "Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
-   property int maxLineSize { property_category "Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
-   property int maxNumLines { property_category "Behavior" set { maxLines = value; } get { return maxLines; } };
-   property bool useTab { property_category "Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
-   property bool syntaxHighlighting { property_category "Appearance" set { style.syntax = value; } get { return style.syntax; } };
-   property bool noSelect { property_category "Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
-   property bool allCaps { property_category "Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
+   property bool multiLine { property_category $"Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
+   property bool freeCaret { property_category $"Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
+   property bool tabKey { property_category $"Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
+   property int tabSize { property_category $"Behavior" set { tabSize = value; } get { return tabSize; } };
+   property bool tabSelection { property_category $"Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
+   property bool smartHome { property_category $"Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
+   property bool autoEmpty { property_category $"Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
+   property bool noCaret { property_category $"Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
+   property int maxLineSize { property_category $"Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
+   property int maxNumLines { property_category $"Behavior" set { maxLines = value; } get { return maxLines; } };
+   property bool useTab { property_category $"Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
+   property bool syntaxHighlighting { property_category $"Appearance" set { style.syntax = value; } get { return style.syntax; } };
+   property bool noSelect { property_category $"Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
+   property bool allCaps { property_category $"Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
+   property bool autoSize { property_category $"Behavior" set { style.autoSize = value; } get { return style.autoSize; } };
    property bool wrap { set { style.wrap = value; Update(null); } get { return style.wrap; } };
    //property bool lineNumbers { set { style.lineNumbers = value; } get { return style.lineNumbers; } };
    property int numLines { get { return this ? lineCount : 0; } };
@@ -762,11 +771,12 @@ public:
    property EditLine line { get { return this.line; } }; // TODO: Add Set   this.line = this.lines[10]
    property char * contents
    {
-      property_category "Data" 
+      property_category $"Data" 
       set
       {
          if(this)
          {
+            undoBuffer.dontRecord++;
             Deselect();
             DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
             if(value)
@@ -774,6 +784,7 @@ public:
             //SetViewToCursor(true);
             UpdateDirty();
             Home();
+            undoBuffer.dontRecord--;
          }
       }
 
@@ -934,16 +945,16 @@ private:
    int caretX, caretY;
    UndoBuffer undoBuffer { data = this };
    int savedAction;
-   Color selectionColor, selectionText;
+   ColorAlpha selectionColor, selectionText;
    SyntaxColorScheme colorScheme { };
 
    menu = Menu { };
 
    // Edit Menu
-   Menu editMenu { menu, "Edit", e };
+   Menu editMenu { menu, $"Edit", e };
    MenuItem itemEditCut
    {
-      editMenu, "Cut\tCtrl+X", t, disabled = true;
+      editMenu, $"Cut\tCtrl+X", t, disabled = true;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -954,7 +965,7 @@ private:
    };
    MenuItem itemEditCopy
    {
-      editMenu, "Copy\tCtrl+C", c, disabled = true;
+      editMenu, $"Copy\tCtrl+C", c, disabled = true;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -964,7 +975,7 @@ private:
    };
    MenuItem itemEditPaste
    {
-      editMenu, "Paste\tCtrl+V", p;
+      editMenu, $"Paste\tCtrl+V", p;
    
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -975,7 +986,7 @@ private:
    };
    MenuItem itemEditDelete
    {
-      editMenu, "Delete\tDel", d, disabled = true;
+      editMenu, $"Delete\tDel", d, disabled = true;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -987,7 +998,7 @@ private:
    MenuDivider { editMenu };
    MenuItem itemEditSelectAll
    {
-      editMenu, "Select All\tCtrl+A", a;
+      editMenu, $"Select All\tCtrl+A", a;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -998,7 +1009,7 @@ private:
    MenuDivider { editMenu };
    MenuItem itemEditUndo
    {
-      editMenu, "Undo\tCtrl+Z", u;
+      editMenu, $"Undo\tCtrl+Z", u;
       disabled = true;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
@@ -1009,7 +1020,7 @@ private:
    };
    MenuItem itemEditRedo
    {
-      editMenu, "Redo\tCtrl+Y", o;
+      editMenu, $"Redo\tCtrl+Y", o;
       disabled = true;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
@@ -1021,7 +1032,7 @@ private:
    MenuDivider { editMenu };
    MenuItem
    {
-      editMenu, "Find Previous\tShift-F3", e, shiftF3;
+      editMenu, $"Find Previous\tShift-F3", e, shiftF3;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -1034,7 +1045,7 @@ private:
    };
    MenuItem
    {
-      editMenu, "Find Next\tF3", n, f3;
+      editMenu, $"Find Next\tF3", n, f3;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -1047,7 +1058,7 @@ private:
    };
    MenuItem itemEditFind
    {
-      editMenu, "Find...\tCtrl+F", f, ctrlF;
+      editMenu, $"Find...\tCtrl+F", f, ctrlF;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -1074,7 +1085,7 @@ private:
    };
    MenuItem
    {
-      editMenu, "Replace...\tCtrl+R", r, ctrlR;
+      editMenu, $"Replace...\tCtrl+R", r, ctrlR;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -1108,7 +1119,7 @@ private:
    MenuDivider { editMenu };
    MenuItem
    {
-      editMenu, "Go To...\tCtrl+G", g, ctrlG;
+      editMenu, $"Go To...\tCtrl+G", g, ctrlG;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -1121,7 +1132,7 @@ private:
    MenuDivider { editMenu };
    MenuItem itemEditInsertTab
    {
-      editMenu, "Insert Tabs", i, checkable = true;
+      editMenu, $"Insert Tabs", i, checkable = true;
 
       bool NotifySelect(MenuItem item, Modifiers mods)
       {
@@ -1288,7 +1299,7 @@ private:
          bool inPrep = reset ? false : style.inPrep;
          bool inSingleLineComment = false;
          bool escaped = reset ? false : style.escaped;
-         bool continuedSingleLineComment = false;
+         bool continuedSingleLineComment = reset ? false : style.continuedSingleLineComment;
 
          EditLine line = reset ? lines.first : firstLine;
          // int maxBackUp = 1000, c;
@@ -1333,7 +1344,7 @@ private:
                {
                   if(!c || text[c-1] != '/') lastWasStar = true;
                }
-               else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && ch == '\"')
+               else if(ch == '\"' && !inSingleLineComment && !inMultiLineComment && !inQuotes)
                {
                   if(inString && !wasEscaped)
                   {
@@ -1344,7 +1355,7 @@ private:
                      inString = true;
                   }
                }
-               else if(!inSingleLineComment && !inMultiLineComment && !inString && ch == '\'')
+               else if(ch == '\'' && !inSingleLineComment && !inMultiLineComment && !inString)
                {
                   if(inQuotes && !wasEscaped)
                      inQuotes = false;
@@ -1358,22 +1369,20 @@ private:
                   if(!wasEscaped)
                      escaped = true;
                }
-               else
+               else if(ch == '#' && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
                {
-                  if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && ch == '#')
+                  if(firstWord)
                   {
-                     if(firstWord)
-                     {
-                        inPrep = true;
-                     }
+                     inPrep = true;
                   }
                }
-               firstWord = false;
+               else if(ch != ' ' && ch != '\t')
+                  firstWord = false;
             }
+            continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
          }
          
-         continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
-
+         style.continuedSingleLineComment = continuedSingleLineComment;
          style.inMultiLineComment = inMultiLineComment;
          style.inPrep = inPrep;
          style.escaped = escaped;
@@ -1437,7 +1446,7 @@ private:
       bool inPrep = style.inPrep;
       bool inSingleLineComment = false;
       bool escaped = style.escaped;
-      bool continuedSingleLineComment = false;
+      bool continuedSingleLineComment = style.continuedSingleLineComment;
       // ****** ************* ******
 
       if(!isEnabled)
@@ -1449,7 +1458,7 @@ private:
          Abs(selectionBackground.g - property::background.g) + 
          Abs(selectionBackground.b - property::background.b) < 92)
       {
-         selectionBackground = activeBorder;
+         selectionBackground = formColor;
          selectionForeground = selectionColor ? selectionColor : SELECTION_COLOR;
       }
 
@@ -1461,7 +1470,7 @@ private:
 
       if(!isEnabled)
       {
-         surface.SetBackground(activeBorder);
+         surface.SetBackground(formColor);
          surface.Area(0,0,clientSize.w, clientSize.h);
       }
 
@@ -1557,7 +1566,10 @@ private:
             
             textColor = newTextColor;
             if(!selected)
+            {
+               foreground = textColor;
                surface.SetForeground(textColor);
+            }
 
             // Look at words
             for(; c<end && !cantHaveWords;)
@@ -1678,7 +1690,7 @@ private:
                         }
                         else if(wordLen == 1 && word[0] == '*')
                         {
-                           if(!c || word[-1] != '/')
+                           if(c < 2 || word[-1] != '/')
                               lastWasStar = true;
                         }
                         else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && wordLen == 1 && word[0] == '\"')
@@ -1708,10 +1720,26 @@ private:
                            if(!wasEscaped)
                               escaped = true;
                         }
-                        else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && 
-                           ( ( isdigit(word[0]) /*&& (!c || word[-1] == ' ' || word[-1] == '\t')*/ ) || (word[0] == '.' && isdigit(word[1]))))
+                        else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && (isdigit(word[0]) || (word[0] == '.' && isdigit(word[1]))))
                         {
-                           newTextColor = colorScheme.numberColor;
+                           char * dot = strchr(word, '.');
+                           char * s = null;
+                           if(dot)
+                              strtod((dot == word + wordLen) ? (dot+1) : word, &s);
+                           else
+                              strtol(word, &s, 0);
+                           if(s && s != word)
+                           {
+                              if((dot && *s == 'f' && !isalnum(s[1]) && s[1] != '_') || (!isalpha(*s) && *s != '_'))
+                              {
+                                 int newWordLen = s + ((*s == 'f') ? 1 : 0) - word;
+                                 newTextColor = colorScheme.numberColor;
+                                 c += newWordLen - wordLen;
+                                 wordLen = newWordLen;
+                              }
+                              else if(dot && dot > word && (isalpha(dot[1]) || dot[1] == '_'))
+                                 newTextColor = colorScheme.numberColor;
+                           }
                         }
                         else
                         {
@@ -1764,7 +1792,10 @@ private:
                            {
                               textColor = newTextColor;
                               if(!selected)
+                              {
+                                 foreground = textColor;
                                  surface.SetForeground(textColor);
+                              }
                            }
                         }
                      }
@@ -1908,12 +1939,12 @@ private:
             if(x > selX)
             {
                if(x > line.count)
-                  width = Max(line.length + (x - line.count) * space.w, maxLength);
+                  width = Max(line.length + (x - line.count) * space.w, maxLength + XOFFSET);
             }
             else
             {
                if(selX > selLine.count)
-                  width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength);
+                  width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength + XOFFSET);
             }
          }
 
@@ -2011,9 +2042,8 @@ private:
       int c, position = 0;
       unichar ch;
       int nb;
-      for(c = 0; c<this.line.count && c<this.x; c+= nb)
+      for(c = 0; c<this.line.count && c<this.x && (ch = UTF8_GET_CHAR(this.line.buffer + c, nb)); c+= nb)
       {
-         ch = UTF8_GET_CHAR(this.line.buffer + c, nb);
          // TODO: MIGHT WANT TO RETHINK WHAT COLUMN SHOULD BE REGARDING TABS
          if(ch == '\t')
             position += this.tabSize - (position % this.tabSize);
@@ -2258,6 +2288,7 @@ private:
       }
       ComputeLength(l1);
       FindMaxLine();
+      if(style.autoSize) AutoSize();
       if(style.syntax && (hadComment || HasCommentOrEscape(this.line)))
       {
          DirtyAll();
@@ -2471,6 +2502,7 @@ private:
                int backDontRecord = undoBuffer.dontRecord;
                undoBuffer.dontRecord = 0;
                NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
+               if(style.autoSize) AutoSize();
                undoBuffer.dontRecord = backDontRecord;
             }
             if(style.syntax && (hadComment || hasComment || line != this.line))
@@ -2983,6 +3015,43 @@ private:
       return true;
    }
 
+   void AutoSize()
+   {
+      //if(created)
+      {
+         if(multiLine)
+         {
+            // todo: resize width based on largest on-screen-line extent...
+            int sh = 0;
+            display.FontExtent(font, " ", 1, null, &sh);
+            if(sh)
+            {
+               int nh = 0;
+               nh = lineCount * sh + 2;
+               size.h = nh < minClientSize.h ? minClientSize.h : nh;
+            }
+         }
+         else
+         {
+            int tw = 0;
+            int sh = 0;
+            int nw = 0;
+            int nh = 0;
+            MinMaxValue dw = 0;
+            MinMaxValue dh = 0;
+            int len = line ? strlen(line.text) : 0;
+            GetDecorationsSize(&dw, &dh);
+            display.FontExtent(font, " ", 1, null, &sh);
+            if(len) display.FontExtent(font, line.text, len, &tw, null);
+            nw = dw+tw+12;
+            if(nw < minClientSize.w) nw = minClientSize.w;
+            nh = dh+sh+4;
+            if(nh < minClientSize.h) nh = minClientSize.h;
+            size = { nw, nh };
+         }
+      }
+   }
+
    bool OnResizing(int *w, int *h)
    {
       if(!*h)
@@ -3030,12 +3099,12 @@ private:
          PopupMenu popup;
          Menu contextMenu { };
 
-         MenuItem { contextMenu, "Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
-         MenuItem { contextMenu, "Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
-         MenuItem { contextMenu, "Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
-         MenuItem { contextMenu, "Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
+         MenuItem { contextMenu, $"Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
+         MenuItem { contextMenu, $"Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
+         MenuItem { contextMenu, $"Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
+         MenuItem { contextMenu, $"Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
          MenuDivider { contextMenu };
-         MenuItem { contextMenu, "Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
+         MenuItem { contextMenu, $"Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
 
          popup = PopupMenu { master = this, menu = contextMenu,
    /*
@@ -3317,7 +3386,7 @@ private:
             for(c = start; c<line.count; c += numBytes)
             {
                unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
-               if(!IS_ALUNDER(ch))
+               if(!ch || !IS_ALUNDER(ch))
                   break;
             }
             SelDirty();
@@ -3335,19 +3404,18 @@ private:
       return true;
    }
 
-   /*
    bool OnPostCreate()
    {
-      if(isDocument)
+      /*if(isDocument)
       {
          Menu fileMenu { menu, "File", F };
          saveDialog = fileDialog;
-         MenuItem { fileMenu, "Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
-         MenuItem { fileMenu, "Save As...", A, NotifySelect = MenuFileSaveAs };
-      }
+         MenuItem { fileMenu, $"Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
+         MenuItem { fileMenu, $"Save As...", A, NotifySelect = MenuFileSaveAs };
+      }*/
+      if(style.autoSize) AutoSize();
       return true;
    }
-   */
 
    void ComputeFont()
    {
@@ -3401,6 +3469,8 @@ private:
       {
          key.code = (SmartKey)key.code;
       }
+      else if(!ch && key.alt)
+         key.code = 0;
 
       switch(key.code) //(ch || key.alt || key.ctrl) ? key.code : (Key)(SmartKey)key.code)
       {
@@ -3486,13 +3556,10 @@ private:
                   {
                      int i;
                      int length;
-                     //Can't delete right away as that would change the line.count
                      for(i = this.x; i < this.line.count; i++)
                      {
                         if(!IS_ALUNDER(this.line.buffer[i]))
                            break;
-                        DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
-                        i--;
                      }
                      
                      for(; i < this.line.count; i++)
@@ -3500,9 +3567,8 @@ private:
                         //Delete trailing whitespace
                         if(IS_ALUNDER(this.line.buffer[i]))
                            break;
-                        DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
-                        i--;
                      }
+                     DelCh(this.line, this.y, this.x, this.line, this.y, i, false);
                      SetViewToCursor(true);
                      Modified();
                   }
@@ -3764,9 +3830,9 @@ private:
                      int start = (line == this.line) ? this.x : 0;
                      int c;
                      int numBytes;
-                     for(c = start; c < line.count; c += numBytes)
+                     unichar ch;
+                     for(c = start; c < line.count && (ch = UTF8_GET_CHAR(line.buffer + c, numBytes)); c += numBytes)
                      {
-                        unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
                         if(IS_ALUNDER(ch))
                         {
                            foundAlpha = true;
@@ -3813,9 +3879,9 @@ private:
                      int start = (line == this.line) ? this.x : 0;
                      int c;
                      int numBytes;
-                     for(c = start; c < line.count; c += numBytes)
+                     unichar ch;
+                     for(c = start; c < line.count && (ch = UTF8_GET_CHAR(line.buffer + c, numBytes)); c += numBytes)
                      {
-                        unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
                         if(!IS_ALUNDER(ch))
                            foundAlpha = true;
                         else if(foundAlpha)
@@ -4225,6 +4291,7 @@ private:
          case home:
          {
             if(style.stuckCaret) break;
+            if(!style.multiLine && key.ctrl) break;
             if(!(style.freeCaret))
                this.selX = Min(this.selX, this.selLine.count);
 
@@ -4247,7 +4314,7 @@ private:
                   for(c=0; line.buffer[c]; c++)
                      if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
                         break;
-                  if(c != 0 || this.x)
+                  if(shift && (c != 0 || this.x))
                      DirtyLine(this.y);
                   if(this.x != c) 
                      this.x = c;
@@ -4256,8 +4323,8 @@ private:
                }
                else
                {
-                  /*if(this.x != 0)
-                     DirtyLine(this.y);*/
+                  if(shift && this.x != 0)
+                     DirtyLine(this.y);
                   this.x = 0;
                }
                ComputeColumn();
@@ -4270,7 +4337,8 @@ private:
          case end:
          {
             if(style.stuckCaret) break;
-            if(!(style.freeCaret))
+            if(!style.multiLine && key.ctrl) break;
+            if(!style.freeCaret)
                this.selX = Min(this.selX, this.selLine.count);
 
             if(!shift) SelDirty();
@@ -4281,7 +4349,8 @@ private:
             else if(this.x != this.line.count)
             {
                this.x = this.line.count;
-               //DirtyLine(this.y);
+               if(shift)
+                  DirtyLine(this.y);
                ComputeColumn();
             }
             if(!shift) Deselect();
@@ -4350,6 +4419,7 @@ private:
                            if(!line.AdjustBuffer(line.count-lastC)) 
                               break;
                            line.count-=lastC;
+                           if(style.autoSize) AutoSize();
                            DirtyLine(y);
                         }
 
@@ -4374,6 +4444,7 @@ private:
                                        break;
 
                                     NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
+                                    if(style.autoSize) AutoSize();
                                     {
                                        AddCharAction action { ch = '\t', x = 0, y = y };
                                        Record(action);
@@ -4391,6 +4462,7 @@ private:
                                     int c;
                                     BufferLocation before = { line, y, 0 }, after = { line, y, this.tabSize };
                                     NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
+                                    if(style.autoSize) AutoSize();
 
                                     if(!line.AdjustBuffer(line.count+this.tabSize)) 
                                        break;
@@ -4433,7 +4505,7 @@ private:
                         ComputeColumn();
                      }
                      // Insert spaces
-                     start = this.x;
+                     start = Min(this.x, this.selX);
                      for(c=start; ((c == start) || ((c) % this.tabSize)); c++)
                      {
                         addString[len++] = ' ';
@@ -4449,49 +4521,55 @@ private:
             }
             break;
          case pageDown:
-            if(key.ctrl)
+            if(style.multiLine)
             {
-               if(!(style.hScroll) || hasHorzScroll) break;
-               if(this.viewX < this.maxLength)
+               if(key.ctrl)
                {
-                  //this.viewX+=this.space.w*this.tabSize;
-                  //DirtyAll();
-                  SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
+                  if(!(style.hScroll) || hasHorzScroll) break;
+                  if(this.viewX < this.maxLength)
+                  {
+                     //this.viewX+=this.space.w*this.tabSize;
+                     //DirtyAll();
+                     SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
+                  }
                }
+               else
+               {
+                  PageDown();
+                  DirtyAll();
+                  if(!shift) Deselect();
+                  SetCursorToViewX();
+                  SetCursorToViewY();
+               }
+               return false;
             }
-            else
-            {
-               PageDown();
-               DirtyAll();
-               if(!shift) Deselect();
-               SetCursorToViewX();
-               SetCursorToViewY();
-            }
-            return false;
-            // break;
+            break;
          case pageUp:
-            if(key.ctrl)
+            if(style.multiLine)
             {
-               if(!(style.hScroll) || hasHorzScroll) break;
-               if(this.viewX > 0)
+               if(key.ctrl)
                {
-                  //this.viewX-=this.space.w*this.tabSize;
-                  //this.viewX = Max(this.viewX,0);
-                  //DirtyAll();
-                  SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
-                  // SetCursorToView();
+                  if(!(style.hScroll) || hasHorzScroll) break;
+                  if(this.viewX > 0)
+                  {
+                     //this.viewX-=this.space.w*this.tabSize;
+                     //this.viewX = Max(this.viewX,0);
+                     //DirtyAll();
+                     SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
+                     // SetCursorToView();
+                  }
                }
+               else
+               {
+                  PageUp();
+                  DirtyAll();
+                  if(!shift) Deselect();
+                  SetCursorToViewX();
+                  SetCursorToViewY();
+               }
+               return false;
             }
-            else
-            {
-               PageUp();
-               DirtyAll();
-               if(!shift) Deselect();
-               SetCursorToViewX();
-               SetCursorToViewY();
-            }
-            // break;
-            return false;
+            break;
          case insert:
             if(key.ctrl)
             {
@@ -4655,7 +4733,8 @@ private:
                         delete newline;
                      }
                      return false;
-                  } else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
+                  }
+                  else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
                   {
                      PutCh(ch);
                      return false;
@@ -4860,14 +4939,14 @@ private:
             this.line = line;
             this.x = 0;
             this.col = 0;
-
-            ComputeLength(this.line);
+            line.count = length;
 
 #ifdef _DEBUG
       if(length > 4000 || length < 0)
          printf("Warning");
 #endif
-            line.count = length;
+            ComputeLength(this.line);
+
             DirtyEnd(this.y);
             this.y++;
             this.lineCount++;
@@ -4877,6 +4956,7 @@ private:
             after.line = this.line, after.y = this.y, after.x = this.x;
 
             NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
+            if(style.autoSize) AutoSize();
          }
       }
       else
@@ -4902,6 +4982,7 @@ private:
       }
       if(addCharAction)
       {
+         addCharAction.x -= addedTabs * (tabSize-1);
          addCharAction.addedSpaces = addedSpaces;
          addCharAction.addedTabs = addedTabs;
       }
@@ -5179,7 +5260,7 @@ public:
          if(style.allCaps)
             ch = (ch < 128) ? toupper(ch) : ch;     // TODO: UNICODE TO UPPER
 
-         if(this.x < this.line.count && this.overwrite)
+         if(this.overwrite && selX == x && selY == y && this.x < this.line.count)
          {
             char buffer[5];
             char * newString;
@@ -5246,6 +5327,7 @@ public:
          }  
          if(addCharAction)
          {
+            addCharAction.x -= addedTabs * (tabSize-1);
             addCharAction.addedSpaces = addedSpaces;
             addCharAction.addedTabs = addedTabs;
          }
@@ -5274,7 +5356,8 @@ public:
          char temp[MAX_F_STRING];
          va_list args;
          va_start(args, format);
-         vsprintf(temp, format, args);
+         vsnprintf(temp, sizeof(temp), format, args);
+         temp[sizeof(temp)-1] = 0;
          va_end(args);
          PutS(temp);
       }
@@ -5284,6 +5367,7 @@ public:
    {
       if(this)
       {
+         undoBuffer.dontRecord++;
          Deselect();
          DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
          if(format)
@@ -5291,13 +5375,15 @@ public:
             char temp[MAX_F_STRING];
             va_list args;
             va_start(args, format);
-            vsprintf(temp, format, args);
+            vsnprintf(temp, sizeof(temp), format, args);
+            temp[sizeof(temp)-1] = 0;
             va_end(args);
 
             AddS(temp);
          }
          UpdateDirty();
          Home();
+         undoBuffer.dontRecord--;
       }
    }
 
@@ -5936,9 +6022,11 @@ public:
       if(this)
       {
          Copy();
-         DelSel(null);
-         SetViewToCursor(true);
-         Modified();
+         if(DelSel(null))
+         {
+            SetViewToCursor(true);
+            Modified();
+         }
       }
    }