ecere/gui/EditBox: Fixed selection menu items after undo
[sdk] / ecere / src / gui / controls / EditBox.ec
index 3747439..c9408fe 100644 (file)
@@ -11,6 +11,16 @@ import "FindDialog"
 import "GoToDialog"
 import "Array"
 
+char * strchrmax(const char * s, int c, int max)
+{
+   int i;
+   char ch;
+   for(i = 0; i < max && (ch = s[i]); i++)
+      if(ch == c)
+         return (char *)s + i;
+   return null;
+}
+
 public class SyntaxColorScheme
 {
 public:
@@ -99,7 +109,7 @@ 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, continuedSingleLineComment:1, wasInMultiLine:1;
+   bool inMultiLineComment:1, inPrep:1, escaped:1, continuedSingleLineComment:1, wasInMultiLine:1, continuedString:1, continuedQuotes:1;
 
    bool recomputeSyntax:1;
    bool cursorFollowsView:1;
@@ -179,6 +189,8 @@ public class UndoAction : struct
 {
 public:
    subclass(UndoAction) type;
+   bool continued;
+
    virtual void Undo(void * data) { type.Undo(this, data); }
    virtual void Redo(void * data) { type.Redo(this, data); }
 #ifdef _DEBUG
@@ -200,6 +212,8 @@ public:
    void * data;
    int dontRecord;
    bool insideRedo;
+   bool recordAsOne;
+   bool firstEvent;
 
    dontRecord = 0;
 
@@ -210,35 +224,43 @@ public:
 
    void Undo()
    {
-      dontRecord++;
-      if(curAction > 0)
+      bool continued = true;
+      while(curAction > 0 && continued)
       {
          UndoAction action = actions[--curAction];
+         dontRecord++;
+
 #ifdef _DEBUG
          /*Print("Undoing: ");
          action.Print(data);*/
 #endif
          action.Undo(data);
+         dontRecord--;
+
+         continued = curAction > 0 && actions[curAction-1].continued;
       }
-      dontRecord--;
    }
 
    void Redo()
    {
-      dontRecord++;
-      insideRedo = true;
-      if(curAction < count)
+      bool continued = true;
+      while(curAction < count && continued)
       {
          UndoAction action = actions[curAction];
+         continued = action.continued;
+         dontRecord++;
+         insideRedo = true;
+
          curAction++;
 #ifdef _DEBUG
          /*Print("Redoing: ");
          action.Print(data);*/
 #endif
          action.Redo(data);
+
+         insideRedo = false;
+         dontRecord--;
       }
-      insideRedo = false;
-      dontRecord--;
    }
 
    void Record(UndoAction action)
@@ -261,6 +283,12 @@ public:
          /*Print("Recording: ");
          action.Print(data);*/
 #endif
+         if(recordAsOne)
+         {
+            if(!firstEvent && count > 0)
+               actions[count-1].continued = true;
+            firstEvent = false;
+         }
          actions[count++] = action;
          curAction = count;
 
@@ -349,7 +377,7 @@ static class DelTextAction : UndoAction
 {
    int y1, x1, y2, x2;
    char * string;
-   bool placeAfter;
+   bool placeAfter, noHighlight;
    int addedSpaces;
    type = class(DelTextAction);
 
@@ -367,8 +395,11 @@ static class DelTextAction : UndoAction
       if(!placeAfter)
       {
          editBox.GoToPosition(null, y1, x1);
-         editBox.selY = y2;
-         editBox.selX = x2;
+         if(!noHighlight)
+         {
+            editBox.selY = y2;
+            editBox.selX = x2;
+         }
          { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
          //editBox.SetViewToCursor(true);
 
@@ -377,8 +408,11 @@ static class DelTextAction : UndoAction
       }
       else
       {
-         editBox.selY = y1;
-         editBox.selX = x1;
+         if(!noHighlight)
+         {
+            editBox.selY = y1;
+            editBox.selX = x1;
+         }
          { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
          //editBox.SetViewToCursor(true);
 
@@ -504,7 +538,7 @@ public class EditLine : struct
    int length;
    EditBox editBox;
 public:
-   property char * text
+   property const char * text
    {
       set
       {
@@ -634,17 +668,18 @@ public struct BufferLocation
 
 public enum EditBoxFindResult { notFound, found, wrapped };
 
-static char * keyWords1[] =
+static const char * keyWords1[] =
 {
    // C
    "return","break","continue","default","switch","case","if","else","for","while", "do","long","short",
-   "void", "char","int","float","double","unsigned","static", "extern", "struct", "union", "typedef","enum",
+   "void", "char","int","float","double","signed","unsigned","static", "extern", "struct", "union", "typedef","enum",
    "const",   "sizeof",
    "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line",
    "__attribute__", "__stdcall", "_stdcall",
    "__declspec", "goto",
     "inline", "__inline__", "_inline", "__inline", "__typeof","__extension__",
    "asm", "__asm", "_asm", "volatile", "#cpu", "__stdcall__",
+   "__restrict__", "__restrict", "restrict",
 
    // eC
    "class", "private", "public",
@@ -671,12 +706,14 @@ static char * keyWords1[] =
    null
 };
 
-static char * keyWords2[] =
+static const char * keyWords2[] =
 {
-   "defined", "warning", null
+   "defined", "warning",
+   "include", "pragma", "elif", "ifdef", "ifndef", "endif", "undef", "line",
+   null
 };
 
-static char ** keyWords[] = { keyWords1, keyWords2 };
+static const char ** keyWords[] = { keyWords1, keyWords2 };
 #define NUM_KEYWORD_GROUPS (sizeof(keyWords) / sizeof(char **))
 //static int * keyLen[NUM_KEYWORD_GROUPS];
 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
@@ -769,7 +806,7 @@ public:
    property EditLine firstLine { get { return lines.first; } };      // Change these to a List<EditLine>... (this.lines[10].text)
    property EditLine lastLine  { get { return lines.last; } };
    property EditLine line { get { return this.line; } }; // TODO: Add Set   this.line = this.lines[10]
-   property char * contents
+   property const char * contents
    {
       property_category $"Data"
       set
@@ -861,7 +898,7 @@ public:
       return null;
    }
 
-   void SetLineText(char * text)
+   void SetLineText(const char * text)
    {
       if(this)
       {
@@ -872,6 +909,7 @@ public:
    property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
    property Color selectionText  { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
    property SyntaxColorScheme syntaxColorScheme { set { delete colorScheme; colorScheme = value; incref colorScheme; } }
+   property bool recordUndoEvent { set { undoBuffer.recordAsOne = value; undoBuffer.firstEvent = true; } get { return undoBuffer.recordAsOne; } };
 
    // selectionStart.line, selectionStart.column (With Set)
    // selection.line1, selection.line2, selection.column1, selection.column2  (Read only)
@@ -937,7 +975,7 @@ private:
 
    bool modified;
 
-   void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
+   void (* FontExtent)(Display display, Font font, const char * text, int len, int * width, int * height);
 
    Color backColor;
    bool rightButtonDown;
@@ -1104,7 +1142,7 @@ private:
             void NotifyDestroyed(Window window, DialogResult result)
             {
                ReplaceDialog dialog = (ReplaceDialog)window;
-               char * replace = dialog.replaceString;
+               const char * replace = dialog.replaceString;
                if(replace)
                   strcpy(replaceString, replace);
                strcpy(searchString, dialog.searchString);
@@ -1172,7 +1210,7 @@ private:
 
       FontExtent = Display::FontExtent;
       font = fontObject;
-      lines.offset = (uint)&((EditLine)0).prev;
+      lines.offset = (uint)(uintptr)&((EditLine)0).prev;
 
       style = EditBoxBits { hScroll = true };
 
@@ -1257,8 +1295,8 @@ private:
       }
    }
 
-   int CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected,
-                   Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, bool *overwrite)
+   bool CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected,
+                    Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, int *overwrite)
    {
       bool flush = false;
 
@@ -1282,7 +1320,7 @@ private:
          if((style.stuckCaret && wc == line.count && !line.next) ||
             (!mouseMove && line == this.line && wc == editX))
          {
-            *overwrite = true;
+            *overwrite = 1;
             flush = true;
          }
       }
@@ -1301,6 +1339,8 @@ private:
          bool inSingleLineComment = false;
          bool escaped = reset ? false : style.escaped;
          bool continuedSingleLineComment = reset ? false : style.continuedSingleLineComment;
+         bool continuedString = reset ? false : style.continuedString;
+         bool continuedQuotes = reset ? false : style.continuedQuotes;
 
          EditLine line = reset ? lines.first : firstLine;
          // int maxBackUp = 1000, c;
@@ -1315,8 +1355,8 @@ private:
             if(!escaped) inPrep = false;
             inSingleLineComment = continuedSingleLineComment;
             escaped = false;
-            inString = false;
-            inQuotes = false;
+            inString = continuedString;
+            inQuotes = continuedQuotes;
 
             firstWord = true;
             for(c = 0; (ch = text[c]); c++)
@@ -1382,10 +1422,23 @@ private:
                else if(ch != ' ' && ch != '\t')
                   firstWord = false;
             }
-            continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
+            if(line.count && line.text[line.count - 1] == '\\')
+            {
+               continuedSingleLineComment = inSingleLineComment;
+               continuedString = inString;
+               continuedQuotes = inQuotes;
+            }
+            else
+            {
+               continuedSingleLineComment = false;
+               continuedString = false;
+               continuedQuotes = false;
+            }
          }
 
          style.continuedSingleLineComment = continuedSingleLineComment;
+         style.continuedString = continuedString;
+         style.continuedQuotes = continuedQuotes;
          style.inMultiLineComment = inMultiLineComment;
          style.wasInMultiLine = wasInMultiLine;
          style.inPrep = inPrep;
@@ -1439,9 +1492,9 @@ private:
       bool opacity;
 
       // Overwrite Caret Stuff
-      bool overWrite = false;
-      int overWriteX, overWriteY;
-      byte overWriteCh;
+      int overWrite = 0;
+      int overWriteX = 0, overWriteY = 0;
+      char overWriteCh;
 
       // ****** SYNTAX STATES ******
       bool inMultiLineComment = style.inMultiLineComment;
@@ -1451,6 +1504,8 @@ private:
       bool inSingleLineComment = false;
       bool escaped = style.escaped;
       bool continuedSingleLineComment = style.continuedSingleLineComment;
+      bool continuedString = style.continuedString;
+      bool continuedQuotes = style.continuedQuotes;
       bool wasInMultiLine = style.wasInMultiLine;
       // ****** ************* ******
 
@@ -1525,8 +1580,8 @@ private:
          if(!escaped) inPrep = false;
          inSingleLineComment = continuedSingleLineComment;
          escaped = false;
-         inString = false;
-         inQuotes = false;
+         inString = continuedString;
+         inQuotes = continuedQuotes;
          // *********************************
 
    /*   === DEBUGGING TOOL FOR MAXLINE ===
@@ -1732,16 +1787,31 @@ private:
                            if(!wasEscaped)
                               escaped = true;
                         }
-                        else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && (isdigit(word[0]) || (word[0] == '.' && isdigit(word[1]))))
+                        else if(x < box.right && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && (isdigit(word[0]) || (word[0] == '.' && isdigit(word[1]))))
                         {
-                           char * dot = strchr(word, '.');
-                           char * exponent = strchr(word, 'E');
-                           bool isReal;
+                           char * dot = word[wordLen] == '.' ? word + wordLen : (word[0] == '.' && (word == line.buffer || word[-1] == '-' || isspace(word[-1])) ? word : null);
+                           bool isReal = dot != null;
                            char * s = null;
-                           if(dot && dot > word + wordLen) dot = null;
-                           isReal = dot || exponent;
+                           if(dot)
+                              isReal = true;
+                           else
+                           {
+                              char * exponent;
+                              bool isHex = (word[0] == '0' && (word[1] == 'x' || word[1] == 'X'));
+                              if(isHex)
+                              {
+                                 exponent = strchrmax(word, 'p', wordLen);
+                                 if(!exponent) exponent = strchrmax(word, 'P', wordLen);
+                              }
+                              else
+                              {
+                                 exponent = strchrmax(word, 'e', wordLen);
+                                 if(!exponent) exponent = strchrmax(word, 'E', wordLen);
+                              }
+                              isReal = exponent != null;
+                           }
                            if(isReal)
-                              strtod(word, &s);
+                              strtod(word, &s);      // strtod() seems to break on hex floats (e.g. 0x23e3p12, 0x1.fp3)
                            else
                               strtol(word, &s, 0);
                            if(s && s != word)
@@ -1790,7 +1860,7 @@ private:
                                  c += newWordLen - wordLen;
                                  wordLen = newWordLen;
                               }
-                              else if(dot && dot > word)
+                              else if(dot && dot > word && dot < s)
                                  newTextColor = colorScheme.numberColor;
                            }
                         }
@@ -1801,14 +1871,14 @@ private:
                               if(firstWord)
                               {
                                  inPrep = true;
-                                 newTextColor = colorScheme.preprocessorColor;
+                                 newTextColor = wordLen == 1 ? colorScheme.keywordColors[1] : colorScheme.preprocessorColor;
                               }
                            }
-                           if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
+                           if(x < box.right && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
                            {
                               for(g = 0; g < ((inPrep && word[0] != '#') ? 2 : 1); g++)
                               {
-                                 char ** keys = keyWords[g];
+                                 const char ** keys = keyWords[g];
                                  int * len = keyLen[g];
                                  for(ccc = 0; keys[ccc]; ccc++)
                                  {
@@ -1899,7 +1969,7 @@ private:
                {
                   flush = CheckColors(line, wc, selection, selX, editX, &selected, selectionForeground,
                      selectionBackground, textColor, &foreground, &background, &opacity, &overWrite);
-                  if(overWrite == true)
+                  if(overWrite == 1)
                   {
                      overWriteCh = (wc < line.count) ? line.buffer[wc] : ' ';
                      if(overWriteCh == '\t') overWriteCh = ' ';
@@ -1910,7 +1980,7 @@ private:
                      flagTrailingSpace = numSpaces && trailingSpace && style.syntax && start + bufferLen == line.count && line != this.line;
                      if(flagTrailingSpace) surface.SetBackground(red);
                      FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, flagTrailingSpace, box);
-                     if(overWrite == true)
+                     if(overWrite == 1)
                      {
                         overWriteX = x;
                         overWriteY = y;
@@ -1947,7 +2017,7 @@ private:
          if(CheckColors(line, c, selection, selX, editX, &selected, selectionForeground,
                         selectionBackground, textColor, &foreground, &background, &opacity, &overWrite))
          {
-            if(overWrite == true)
+            if(overWrite == 1)
             {
                overWriteX = x;
                overWriteY = y;
@@ -1974,8 +2044,18 @@ private:
             surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
          }
          */
-
-         continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
+         if(line.count && line.text[line.count - 1] == '\\')
+         {
+            continuedSingleLineComment = inSingleLineComment;
+            continuedString = inString;
+            continuedQuotes = inQuotes;
+         }
+         else
+         {
+            continuedSingleLineComment = false;
+            continuedString = false;
+            continuedQuotes = false;
+         }
 
          y+=this.space.h;
          if(y > box.bottom) // >=clientSize.h)
@@ -2020,8 +2100,6 @@ private:
    void ComputeLength(EditLine line)
    {
       int c;
-      int tabOccur = 0;
-      int tabWidth;
       int x = 0;
 
       for(c = 0; c < line.count; )
@@ -2121,9 +2199,9 @@ private:
       this.col = position;
    }
 
-   int DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
+   void DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
    {
-      return _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, null);
+      _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, true, null);
    }
 
    bool HasCommentOrEscape(EditLine line)
@@ -2148,7 +2226,7 @@ private:
       return hadComment;
    }
 
-   int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, int * addedSpacesPtr)
+   int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, bool highlight, int * addedSpacesPtr)
    {
       EditLine line = l1, next;
       char * buffer;
@@ -2197,7 +2275,7 @@ private:
 
          len = GetText(null, l1, y1, start, l2, y2, c2, false, false);
          string = new char[len];
-         action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter };
+         action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter, noHighlight = !highlight };
          GetText(string, l1, y1, start, l2, y2, c2, false, false);
          Record(action);
       }
@@ -2222,14 +2300,16 @@ private:
          buffer = new char[line.size ? line.size : 1];
          */
          buffer = new char[line.size];
-         if(!buffer) return;
+         // TODO: Better handling of these allocation failures
+         if(!buffer) return extras;
          CopyBytes(buffer,l2.buffer,oldCount1 + 1/*line.count + 1*//*line.size*/);
       }
       else
          buffer = l2.buffer;
 
+      // TODO: Better handling of these allocation failures
       if(!line.AdjustBuffer(newLineCount))
-         return;
+         return extras;
 
 #ifdef _DEBUG
       /*if(newLineCount > 4000 || newLineCount < 0)
@@ -2369,28 +2449,28 @@ private:
       {
          if(this.selY < this.y)
          {
-            _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
+            _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, true, addedSpacesPtr);
             this.x = this.selX;
             this.y = this.selY;
             this.line = this.selLine;
          }
          else if(this.selY > this.y)
          {
-            _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
+            _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, true, addedSpacesPtr);
             this.selX = this.x;
             this.selY = this.y;
             this.selLine = this.line;
          }
          else if(this.selX < this.x)
          {
-            _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
+            _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, true, addedSpacesPtr);
             this.x = this.selX;
             this.y = this.selY;
             this.line = this.selLine;
          }
          else
          {
-            _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
+            _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, true, addedSpacesPtr);
             this.selX = this.x;
             this.selY = this.y;
             this.selLine = this.line;
@@ -2401,7 +2481,7 @@ private:
       return false;
    }
 
-   bool AddToLine(char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr)
+   bool AddToLine(const char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr)
    {
       bool hadComment = false;
       // Add the line here
@@ -2427,7 +2507,7 @@ private:
          {
             int w;
             int numBytes = 1;
-            char * string;
+            const char * string;
             if(c < Min(this.x, line.count))
                string = line.buffer + c;
             else if(c < endX)
@@ -2625,9 +2705,8 @@ private:
    // Returns true if it needs scrolling
    bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
    {
-      int w;
       int c;
-      int x, y;
+      int y;
       EditLine line;
       bool needHScroll = false;
 
@@ -2771,7 +2850,7 @@ private:
    {
       if(line)
       {
-         if(mouseMove || (!overwrite && !style.noCaret))
+         if(mouseMove || !style.noCaret)
          {
             int max = this.mouseMove ? this.dropX : this.x;
             int y = this.mouseMove ? this.dropY : this.y;
@@ -2827,11 +2906,15 @@ private:
             if(setCaret)
                caretX = x;
             caretY = y * this.space.h;
-            SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
+            if(!overwrite)
+               SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
+            else
+               SetCaret(0, 0, 0);
          }
          else
             SetCaret(0, 0, 0);
 
+         // TOFIX: Mismatch between NotifyCaretMove() and NotifyDropped() / GoToPosition()
          NotifyCaretMove(master, this, y + 1, x + 1);
 
          SelectionEnables();
@@ -2886,7 +2969,6 @@ private:
       while(true)
       {
          int start = c;
-         int numBytes = 1;
          int len = 1;
          int w;
          if(c < Min(max, line.count))
@@ -3031,7 +3113,7 @@ private:
    }
 
    /*
-   bool SaveFile(char * fileName)
+   bool SaveFile(const char * fileName)
    {
       File f = eFile_Open(fileName, FO_WRITE);
       if(f)
@@ -3213,7 +3295,9 @@ private:
 
       UpdateDirty();
       UpdateCaretPosition(true);
-      return true;
+      // Return false because DataBoxes automatically set EditBox editor's clickThrough to true for MouseMove events
+      // ( for tool tips -- see 95ee4962c4c7bc3fe0a04aa6a4f98cacada40884)
+      return false;
    }
 
    bool OnLeftButtonUp(int x, int y, Modifiers mods)
@@ -3265,12 +3349,16 @@ private:
                               moveX = this.selX - this.x;
                         }
                      }
+
+                     recordUndoEvent = true;
                      DelSel(null);
                      this.dropX -= moveX;
                      this.selX = this.x = this.dropX;
                      this.selY = this.y = this.dropY;
                      this.selLine = this.line = this.dropLine;
                      AddS(text);
+                     recordUndoEvent = false;
+
                      SetViewToCursor(true);
                      delete text;
                      Modified();
@@ -3320,7 +3408,9 @@ private:
          }
       }
       mouseMove = false;
-      return true;
+      // Return false because DataBoxes automatically set EditBox editor's clickThrough to true for MouseMove events
+      // ( for tool tips -- see 95ee4962c4c7bc3fe0a04aa6a4f98cacada40884)
+      return false;
    }
 
    bool OnMouseMove(int mx, int my, Modifiers mods)
@@ -3518,10 +3608,10 @@ private:
                int y;
                bool done = false;
                EditLine line = this.line;
-               int c;
+               int c = 0;
                for(y = this.y; y>= 0; y--)
                {
-                  c = (y == this.y) ? (this.x-1) : line.count-1;
+                  c = (y == this.y) ? (Min(this.x-1, line.count-1)) : line.count-1;
 
                   // Slow down when going on lines...
                   if(y != this.y) break;
@@ -3548,9 +3638,10 @@ private:
                   else
                      break;
                }
+
                //if(this.x != 0)
                {
-                  DelCh(line,y,c+1,this.line,this.y,this.x, true);
+                  _DelCh(line, y, c+1, this.line, this.y, this.x, true, false, null);
                   this.x = this.selX = Min(c+1, line.count);
                   this.y = this.selY = y;
                   this.line = this.selLine = line;
@@ -3581,51 +3672,52 @@ private:
                   SetViewToCursor(true);
                   Modified();
                }
-               // Delete word
-               else if(key.ctrl)
-               {
-                  if(this.x < this.line.count)
-                  {
-                     int i;
-                     int length;
-                     for(i = this.x; i < this.line.count; i++)
-                     {
-                        if(!IS_ALUNDER(this.line.buffer[i]))
-                           break;
-                     }
-
-                     for(; i < this.line.count; i++)
-                     {
-                        //Delete trailing whitespace
-                        if(IS_ALUNDER(this.line.buffer[i]))
-                           break;
-                     }
-                     DelCh(this.line, this.y, this.x, this.line, this.y, i, false);
-                     SetViewToCursor(true);
-                     Modified();
-                  }
-                  else if(this.line.next)
-                  {
-                     DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
-                     SetViewToCursor(true);
-                     Modified();
-                  }
-               }
                else
                {
-                  if(!(style.freeCaret))
+                  EditLine line1 = this.line, line2 = this.line;
+                  int x1, y1 = this.y, x2, y2 = this.y;
+                  if(!style.freeCaret)
                   {
                      this.selX = this.x = Min(this.x, this.line.count);
                      ComputeColumn();
                   }
-                  if(this.x < this.line.count)
+                  x1 = this.x;
+
+                  if(x1 < line1.count)
                   {
-                     DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
+                     // Delete word
+                     if(key.ctrl)
+                     {
+                        int i;
+                        char * buffer = line1.buffer;
+                        for(i = x1; i < line1.count; i++)
+                        {
+                           if(!IS_ALUNDER(buffer[i]))
+                              break;
+                        }
+
+                        for(; i < line1.count; i++)
+                        {
+                           // Delete trailing whitespace
+                           if(IS_ALUNDER(buffer[i]))
+                              break;
+                        }
+                        x2 = i;
+                     }
+                     else
+                        x2 = x1 + 1;
                   }
-                  else if(this.line.next)
+                  else
                   {
-                     DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
+                     // Avoid creating trailing spaces if there is no content on next line
+                     line2 = line1.next;
+                     y2++;
+                     if(line2 && !line2.count)
+                        x1 = line1.count;
+                     x2 = 0;
                   }
+                  if(line2)
+                     _DelCh(line1, y1, x1, line2, y2, x2, false, false, null);
                   SetViewToCursor(true);
                   Modified();
                }
@@ -3642,6 +3734,8 @@ private:
                bool stuffAfter = false;
                char * addString;
                int len = 0;
+               /*bool resetX = false;
+               int backX;*/
 
                if(style.stuckCaret) GoToEnd(true);
                if(style.readOnly) break;
@@ -3656,6 +3750,17 @@ private:
                   else
                      break;
                }
+
+               // Prevent adding trailing spaces if at the head of a line
+               /*if(c && c == this.x && c < this.line.count && this.x == this.selX && this.y == this.selY)
+               {
+                  position = 0;
+                  backX = this.x;
+                  this.x = 0;
+                  this.selX = 0;
+                  resetX = true;
+               }*/
+
                if(!line.count)
                   position = x;
 
@@ -3663,12 +3768,13 @@ private:
                   stuffAfter = true;
 
                //If last character is a { indent one tab
-               if(this.line.buffer[this.x - 1] == '{')
+               c = Min(x, line.count);
+               if(c > 0 && line.buffer[c - 1] == '{')
                {
                   //Except if the next non space character is a }
                   bool indent = false;
                   int i;
-                  for(i = this.x; i < this.line.size; i++)
+                  for(i = c; i <= this.line.count; i++)   // indent will be set to true on nul terminating char
                      if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
                      {
                         if(this.line.buffer[i] != '}')
@@ -3700,9 +3806,27 @@ private:
                   }
                   addString[len] = '\0';
                }
+               recordUndoEvent = true;
                if(AddS(addString))
                {
-                  if(!stuffAfter && style.freeCaret)
+                  EditLine prevLine = this.line.prev;
+                  if(prevLine)
+                  {
+                     // Nuke spaces if that is all that is left on previous line
+                     int i;
+                     char * buffer = prevLine.buffer;
+                     for(i = 0; i < prevLine.count; i++)
+                        if(buffer[i] != ' ' && buffer[i] != '\t')
+                           break;
+                     if(i == prevLine.count)
+                        DelCh(prevLine, this.y - 1, 0, prevLine, this.y - 1, prevLine.count, false);
+                  }
+                  /*if(resetX)
+                  {
+                     this.x = this.selX = backX;
+                     ComputeColumn();
+                  }
+                  else */if(!stuffAfter && style.freeCaret)
                   {
                      this.x = this.selX = position;
                      ComputeColumn();
@@ -3711,6 +3835,7 @@ private:
                   SetViewToCursor(true);
                   Modified();
                }
+               recordUndoEvent = false;
                delete addString;
                return false;
             }
@@ -3731,8 +3856,8 @@ private:
                bool foundAlpha = false;
                bool found = false;
                int y = this.y;
-               EditLine line, lastLine;
-               int lastC, lastY;
+               EditLine line, lastLine = null;
+               int lastC = 0, lastY = 0;
 
                for(line = this.line; (line && !found); line = line.prev, y--)
                {
@@ -3770,7 +3895,7 @@ private:
                            break;
                         }
                      }
-                     while(--c)
+                     while(--c >= 0)
                      {
                         byte ch = line.buffer[c];
                         if(UTF8_IS_FIRST(ch)) break;
@@ -3803,7 +3928,7 @@ private:
                {
                   if(x <= line.count)
                   {
-                     byte * buffer = line.buffer;
+                     byte * buffer = (byte *)line.buffer;
                      while(--x)
                      {
                         byte ch = buffer[x];
@@ -3853,9 +3978,9 @@ private:
                {
                   bool foundAlpha = false;
                   bool found = false;
-                  EditLine line, lastLine;
+                  EditLine line = null, lastLine = null;
                   int y = this.y;
-                  int lastC, lastY, lastNumBytes;
+                  int lastC = 0, lastY = 0, lastNumBytes = 0;
 
                   for(line = this.line; (line && !found); line = line.next, y++)
                   {
@@ -3949,7 +4074,7 @@ private:
                {
                   if(x < line.count)
                   {
-                     byte * buffer = line.buffer;
+                     byte * buffer = (byte *)line.buffer;
                      while(++x)
                      {
                         byte ch = buffer[x];
@@ -4160,11 +4285,13 @@ private:
             {
                if(style.stuckCaret) break;
                {
+                  /*
                   int th = space.h;
                   int textPos = 0;
                   int sx = 0, sy = this.y * this.space.h;
                   int maxW = clientSize.w - sx;
                   char * text = line.buffer;
+                  */
 
                   if(!shift) SelDirty();
                   DirtyLine(this.y);
@@ -4345,7 +4472,7 @@ private:
                   for(c=0; line.buffer[c]; c++)
                      if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
                         break;
-                  if(shift && (c != 0 || this.x))
+                  if(overwrite || (shift && (c != 0 || this.x)))
                      DirtyLine(this.y);
                   if(this.x != c)
                      this.x = c;
@@ -4354,7 +4481,7 @@ private:
                }
                else
                {
-                  if(shift && this.x != 0)
+                  if(overwrite || (shift && this.x != 0))
                      DirtyLine(this.y);
                   this.x = 0;
                }
@@ -4380,7 +4507,7 @@ private:
             else if(this.x != this.line.count)
             {
                this.x = this.line.count;
-               if(shift)
+               if(overwrite || shift)
                   DirtyLine(this.y);
                ComputeColumn();
             }
@@ -4605,21 +4732,24 @@ private:
                Copy();
                return false;
             }
-            else if(key.shift)
+            else if(!style.readOnly)
             {
-               if(!(style.readOnly))
-                  Paste();
-               return false;
-            }
-            else
-            {
-               this.overwrite ^= 1;
-               UpdateCaretPosition(true);
-               if(this.overwrite)
-                  SetCaret(0,0,0);
-               DirtyLine(this.y);
-               UpdateDirty();
-               NotifyOvrToggle(master, this, this.overwrite);
+               if(key.shift)
+               {
+                  if(!(style.readOnly))
+                     Paste();
+                  return false;
+               }
+               else
+               {
+                  this.overwrite ^= 1;
+                  UpdateCaretPosition(true);
+                  if(this.overwrite)
+                     SetCaret(0,0,0);
+                  DirtyLine(this.y);
+                  UpdateDirty();
+                  NotifyOvrToggle(master, this, this.overwrite);
+               }
             }
             break;
          case hotKey:
@@ -4692,12 +4822,12 @@ private:
                   {
                      //Only indent back if you are exactly at one tab.
                      {
-                        bool whitespace = true;
+                        //bool whitespace = true;
                         int i;
                         char * newline;
                         int putsize;
 
-                        int indentwidth;
+                        int indentwidth = 0;
                         EditLine line = this.line;
 
                         //Only remove one tab if there is nothing else on the line.
@@ -4854,7 +4984,7 @@ private:
    bool _AddCh(unichar ch, int * addedSpacesPtr, int * addedTabsPtr)
    {
       EditLine line;
-      int length, endX;
+      int length, endX = 0;
       bool result;
       ReplaceTextAction replaceAction = null;
       AddCharAction addCharAction = null;
@@ -5039,7 +5169,7 @@ public:
    void Delete(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
    {
       Deselect();
-      DelCh(line1, y1, x1, line2, y2, x2, false);
+      _DelCh(line1, y1, x1, line2, y2, x2, false, false, null);
       SetViewToCursor(true);
       UpdateDirty();
       Modified();
@@ -5050,6 +5180,11 @@ public:
       undoBuffer.Undo();
       itemEditUndo.disabled = undoBuffer.curAction == 0;
       itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
+
+      UpdateDirty();
+      SetSelectCursor();
+      SelectionEnables();
+
       if(savedAction == undoBuffer.curAction)
       {
          modifiedDocument = false;
@@ -5063,6 +5198,11 @@ public:
       undoBuffer.Redo();
       itemEditUndo.disabled = undoBuffer.curAction == 0;
       itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
+
+      UpdateDirty();
+      SetSelectCursor();
+      SelectionEnables();
+
       if(savedAction == undoBuffer.curAction)
       {
          modifiedDocument = false;
@@ -5115,12 +5255,12 @@ public:
    }
 
    // BASIC OUTPUT
-   bool AddS(char * string)
+   bool AddS(const char * string)
    {
       if(this)
       {
          bool ret = true;
-         char * line;
+         const char * line;
          int c, count;
          int addedSpaces = 0, addedTabs = 0;
          AddTextAction action = null;
@@ -5195,7 +5335,7 @@ public:
          {
             if(string[c] == '\n' || string[c] == '\r')
             {
-               if(!AddToLine(line,count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
+               if(!AddToLine(line, count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
                {
                   ret = false;
                   break;
@@ -5367,7 +5507,7 @@ public:
       }
    }
 
-   void PutS(char * string)
+   void PutS(const char * string)
    {
       if(this)
       {
@@ -5377,7 +5517,7 @@ public:
       }
    }
 
-   void Printf(char * format, ...)
+   void Printf(const char * format, ...)
    {
       if(this)
       {
@@ -5391,7 +5531,7 @@ public:
       }
    }
 
-   void SetContents(char * format, ...)
+   void SetContents(const char * format, ...)
    {
       if(this)
       {
@@ -5421,7 +5561,10 @@ public:
       {
          if(x > 0)
          {
-            x -= 1 + DelCh(line, y, x-1, line, y, x, true);
+            if(x > line.count)
+               x--;
+            else
+               x -= 1 + _DelCh(line, y, x-1, line, y, x, true, false, null);
             Modified();
          }
          else if(this.line.prev)
@@ -5430,7 +5573,7 @@ public:
             int x = line.count;
             int y = this.y;
 
-            DelCh(line, this.y-1, x, this.line, this.y, this.x, true);
+            _DelCh(line, this.y-1, x, this.line, this.y, this.x, true, false, null);
             this.line = line;
             this.y = y-1;
             this.x = x;
@@ -5499,6 +5642,7 @@ public:
       return false;
    }
 
+   // NOTE: Mismatch with NotifyCaretMove() for x/y + 1
    bool GoToPosition(EditLine line, int y, int x)
    {
       /*
@@ -5536,8 +5680,7 @@ public:
    {
       if(created)
       {
-         int w;
-         int c, numLines;
+         int numLines;
          EditLine line;
          int x;
          int checkX, checkY;
@@ -5700,7 +5843,7 @@ public:
       }
       else
       {
-         EditLine oldLine = this.line;
+         //EditLine oldLine = this.line;
          bool lastOne = false;
          EditLine oldViewLine = this.viewLine;
          bool figureSyntax = false;
@@ -5748,8 +5891,6 @@ public:
       }
       else
       {
-         EditLine oldLine = this.line;
-
          for(c=0, line = this.line.prev; line && c<numLines; line = line.prev, c++)
          {
             this.line = line;
@@ -6081,21 +6222,7 @@ public:
 
       for(line = this.lines.first; line; line = line.next)
       {
-         if(style.syntax && line.count && isspace(line.buffer[line.count-1]))
-         {
-            int c = 0;
-            for(c=line.count-2; c>=-1; c--)
-            {
-               if(c == -1 || !isspace(line.buffer[c]))
-               {
-                  c++;
-                  line.buffer[c] = '\0';
-                  line.count -= (line.count - c);
-                  break;
-               }
-            }
-         }
-         f.Write(line.buffer, line.count,1);
+         f.Write(line.buffer, line.count, 1);
          if(line.next)
          {
             if(cr) f.Putc('\r');
@@ -6128,7 +6255,7 @@ public:
       itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
    }
 
-   EditBoxFindResult Find(char * text, bool matchWord, bool matchCase, bool isSearchDown)
+   EditBoxFindResult Find(const char * text, bool matchWord, bool matchCase, bool isSearchDown)
    {
       EditLine line;
       int num;
@@ -6186,7 +6313,7 @@ public:
       return notFound;
    }
 
-   EditBoxFindResult FindInSelection(char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
+   EditBoxFindResult FindInSelection(const char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
    {
       EditLine line;
       int y;
@@ -6421,7 +6548,7 @@ public:
       return result;
    }
 
-   bool Puts(char * string)
+   bool Puts(const char * string)
    {
       EditBox editBox = this.editBox;
       BufferLocation start { editBox.line, editBox.y, editBox.x };
@@ -6451,7 +6578,7 @@ public:
       {
          utf8Bytes[numBytes++] = ch;
          utf8Bytes[numBytes] = 0;
-         if(UTF8Validate(utf8Bytes))
+         if(UTF8Validate((char *)utf8Bytes))
          {
             editBox.AddCh(UTF8_GET_CHAR(utf8Bytes, numBytes));
             numBytes = 0;
@@ -6501,7 +6628,7 @@ public:
          start.AdjustDelete(pos, end);
          sel.AdjustDelete(pos, end);
 
-         editBox.DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true);
+         editBox._DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true, false, null);
       }
    }
 };