ecere:gui:controls:EditBox: autoSize fix.
[sdk] / ecere / src / gui / controls / EditBox.ec
1 namespace gui::controls;
2
3 /*
4 selectionForeground = white;
5 disabled: defaultTextColor = Color { 85, 85, 85 };
6 */
7
8 import "Window"
9 import "ReplaceDialog"
10 import "FindDialog"
11 import "GoToDialog"
12 import "Array"
13
14 public class SyntaxColorScheme
15 {
16 public:
17    Color commentColor;
18    Color charLiteralColor;
19    Color stringLiteralColor;
20    Color preprocessorColor;
21    Color numberColor;
22    private Array<Color> keywordColors { };
23
24    public property Container<Color> keywordColors
25    {
26       set { keywordColors.Copy((void *)value); }
27       get { return keywordColors; }
28    }
29 };
30
31 #include <stdarg.h>
32
33 #define MAXWORDLEN   64
34
35 #define XOFFSET   (3 + (/*style.lineNumbers?6 * this.space.w:*/0))
36 // #define YOFFSET   1
37 #define YOFFSET   (style.multiLine ? 1 : ((clientSize.h + 1 - space.h) / 2))
38
39 #define IS_ALUNDER(ch) (/*(ch) == '_' || */CharMatchCategories((ch), letters|numbers|marks|connector))
40
41 #define UTF8_IS_FIRST(x)   (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || ((b) & 0x40)); }))
42 #define UTF8_NUM_BYTES(x)  (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
43 #define UTF8_GET_CHAR(string, numBytes) \
44    __extension__( \
45    { \
46       unichar ch; \
47       byte b = (string)[0]; \
48       int i; \
49       byte mask = 0x7F; \
50       numBytes = 1; \
51       ch = 0; \
52       if(b & 0x80 && b & 0x40) \
53       { \
54          mask >>= 2; \
55          numBytes++; \
56          if(b & 0x20) \
57          { \
58             numBytes++; \
59             mask >>= 1; \
60             if(b & 0x10) \
61             { \
62                numBytes++; \
63                mask >>= 1; \
64             } \
65          } \
66       } \
67       for(i = 0; i<numBytes; i++) \
68       { \
69          ch <<= 6; \
70          ch |= (string)[i] & mask; \
71          mask = 0x3F; \
72       } \
73       ch; \
74    })
75
76 class EditBoxBits
77 {
78    bool autoEmpty:1, readOnly:1, multiLine:1, stuckCaret:1, freeCaret:1, select:1, hScroll:1, vScroll:1, smartHome:1;
79    bool noCaret:1, noSelect:1, tabKey:1, useTab:1, tabSel:1, allCaps:1, syntax:1, wrap:1;
80
81    // Syntax States
82    bool inMultiLineComment:1, inPrep:1, escaped:1, continuedSingleLineComment:1;
83
84    bool recomputeSyntax:1;
85    bool cursorFollowsView:1;
86    
87    // bool lineNumbers:1;
88    bool autoSize:1;
89 };
90
91 /* TODO:
92 void UnregisterClass_EditBox()
93 {
94    int g;
95    for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
96    {
97       if(keyLen[g])
98          free(keyLen[g]);
99    }
100 }
101 */
102
103 default:
104 extern int __ecereVMethodID_class_OnFree;
105 private:
106
107 static class ArrayImpl
108 {
109    uint size;
110    Class type;
111    byte * array;
112 };
113
114 public class OldArray
115 {
116    uint size;
117
118    ~OldArray()
119    {
120       int c;
121       void ** array = (void **)((ArrayImpl)this).array;
122       if(type.type == normalClass || type.type == noHeadClass)
123       {
124          for(c = 0; c<size; c++)
125             type._vTbl[__ecereVMethodID_class_OnFree](type, array[c]);
126       }
127       // TODO: Call OnFree for structClass
128       delete ((ArrayImpl)this).array;
129    }
130
131 public:   
132    Class type;
133    property uint size
134    {
135       set
136       {
137          if(((ArrayImpl)this).array)
138          {
139             if(value == size) 
140                return;
141             ((ArrayImpl)this).array = renew0 ((ArrayImpl)this).array byte[type.typeSize * value];
142          }
143          else if(value)
144             ((ArrayImpl)this).array = new0 byte[value * type.typeSize];
145          size = value;
146       }
147       get { return size; }
148    }
149    property void * data
150    {
151       set
152       {
153          memcpy(((ArrayImpl)this).array, value, type.typeSize * size);
154       }
155    }
156 };
157
158 public class UndoAction : struct
159 {
160 public:
161    subclass(UndoAction) type;
162    virtual void Undo(void * data) { type.Undo(this, data); }
163    virtual void Redo(void * data) { type.Redo(this, data); }
164 #ifdef _DEBUG
165    virtual void Print(void * data)  { type.Print(this, data); }
166 #endif
167    ~UndoAction()
168    {
169       if(((Class)type).Destructor)
170          ((void (*)(void *))((Class)type).Destructor)(this);
171    }
172 };
173
174
175 class ArrayUndoActions : OldArray
176 {
177    type = class(UndoAction);
178    UndoAction * _;
179 };
180
181 public class UndoBuffer
182 {
183    ArrayUndoActions actions { size = 8 };
184 public:
185    int count;
186    int curAction;
187    void * data;
188    int dontRecord;
189    bool insideRedo;
190
191    dontRecord = 0;
192    
193    void Undo()
194    {
195       dontRecord++;
196       if(curAction > 0)
197       {
198          UndoAction action = actions._[--curAction];
199 #ifdef _DEBUG
200          /*Print("Undoing: ");
201          action.Print(data);*/
202 #endif
203          action.Undo(data);
204       }
205       dontRecord--;
206    }
207
208    void Redo()
209    {
210       dontRecord++;
211       insideRedo = true;
212       if(curAction < count)
213       {
214          UndoAction action = actions._[curAction];
215          curAction++;
216 #ifdef _DEBUG
217          /*Print("Redoing: ");
218          action.Print(data);*/
219 #endif
220          action.Redo(data);
221       }
222       insideRedo = false;
223       dontRecord--;
224    }
225
226    void Record(UndoAction action)
227    {
228       if(!dontRecord && !insideRedo)
229       {
230          if(curAction < count)
231          {
232             int c;
233             for(c = curAction; c < count; c++)
234                delete actions._[c];
235          }
236
237          count = curAction;
238
239          if(count >= actions.size)
240             actions.size += actions.size / 2;
241
242 #ifdef _DEBUG
243          /*Print("Recording: ");
244          action.Print(data);*/
245 #endif
246          actions._[count++] = action;
247          curAction = count;
248
249          if(actions.size > count + count / 2 && count + count / 2 >= 8)
250             actions.size = count + count / 2;
251       }
252       else
253          delete action;
254    }
255 };
256
257 static class AddCharAction : UndoAction
258 {
259    int y, x;
260    unichar ch;
261    int addedSpaces, addedTabs;
262    type = class(AddCharAction);
263
264    void Undo(EditBox editBox)
265    {
266       editBox.GoToPosition(null, (ch == '\n') ? (y + 1) : y, (ch == '\n') ? 0 : (x + 1));
267       editBox.BackSpace();
268       if(addedTabs || addedSpaces)
269          editBox.DelCh(editBox.line, y, x - (addedSpaces + addedTabs), editBox.line, y, x, false);
270       editBox.UpdateDirty();
271    }
272
273    void Redo(EditBox editBox)
274    {
275       editBox.GoToPosition(null, y, x);
276       editBox.PutCh(ch);
277       editBox.UpdateDirty();
278    }
279 #ifdef _DEBUG
280    void Print(EditBox editBox)
281    {
282       PrintLn("AddChar: y = ", y, "x = ", x, ", ch = ", ch, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
283    }
284 #endif
285 };
286
287 static class AddTextAction : UndoAction
288 {
289    int y1, x1, y2, x2;
290    char * string;
291    int addedSpaces, addedTabs;
292    type = class(AddTextAction);
293
294 #ifdef _DEBUG
295    void Print(EditBox editBox)
296    {
297       PrintLn("AddText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
298    }
299 #endif
300    ~AddTextAction()
301    {
302       delete string;
303    }
304
305    void Undo(EditBox editBox)
306    {
307       EditLine l1, l2;
308       int c;
309
310       editBox.GoToPosition(null, y1, x1);
311       l1 = editBox.line;
312       l2 = editBox.lines.first;
313       for(c = 0; c < y2 && l2; c++, l2 = l2.next);
314
315       editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
316       if(addedTabs || addedSpaces)
317          editBox.DelCh(editBox.line, y1, x1 - (addedSpaces + addedTabs), editBox.line, y1, x1, false);
318
319       editBox.SetViewToCursor(true);
320       editBox.Modified();
321    }
322
323    void Redo(EditBox editBox)
324    {
325       editBox.GoToPosition(null, y1, x1);
326       editBox.PutS(string);
327    }
328 };
329
330 static class DelTextAction : UndoAction
331 {
332    int y1, x1, y2, x2;
333    char * string;
334    bool placeAfter;
335    int addedSpaces;
336    type = class(DelTextAction);
337
338 #ifdef _DEBUG
339    void Print(EditBox editBox)
340    {
341       PrintLn("DelText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", placeAfter = ", placeAfter);   
342    }
343 #endif
344    void Undo(EditBox editBox)
345    {
346       editBox.GoToPosition(null, y1, x1);
347       editBox.PutS(string);
348
349       if(!placeAfter)
350       {
351          editBox.GoToPosition(null, y1, x1);
352          editBox.selY = y2;
353          editBox.selX = x2;
354          { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
355          //editBox.SetViewToCursor(true);
356
357          if(addedSpaces)
358             editBox.DelCh(editBox.line, y1, x1 - addedSpaces, editBox.line, y1, x1, false);
359       }
360       else
361       {  
362          editBox.selY = y1;
363          editBox.selX = x1;
364          { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }         
365          //editBox.SetViewToCursor(true);
366
367          if(addedSpaces)
368             editBox.DelCh(editBox.selLine, y1, x1 - addedSpaces, editBox.selLine, y1, x1, false);
369       }
370    }
371
372    void Redo(EditBox editBox)
373    {
374       EditLine l1, l2;
375       int c;
376
377       editBox.GoToPosition(null, y1, x1);
378       l1 = editBox.line;
379
380       l2 = editBox.lines.first;
381       for(c = 0; c < y2 && l2; c++, l2 = l2.next);
382
383       editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
384       editBox.SetViewToCursor(true);
385       editBox.Modified();
386    }
387
388    ~DelTextAction()
389    {
390       delete string;
391    }
392 };
393
394 static class ReplaceTextAction : UndoAction
395 {
396    int y1, x1, y2, x2, y3, x3;
397    char * oldString;
398    char * newString;
399    bool placeAfter;
400    int addedSpaces, addedTabs;
401
402    type = class(ReplaceTextAction);
403
404 #ifdef _DEBUG
405    void Print(EditBox editBox)
406    {
407       PrintLn("ReplaceText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", y3 = ", y3, ", x3 = ", x3, ", oldString = ", oldString, ", newString = ", newString, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs, ", placeAfter = ", placeAfter);   
408    }
409 #endif
410    void Undo(EditBox editBox)
411    {
412       EditLine l1, l3;
413       int c;
414
415       editBox.GoToPosition(null, y1, x1);
416       l1 = editBox.line;
417       l3 = editBox.lines.first;
418       for(c = 0; c < y3 && l3; c++, l3 = l3.next);
419
420       editBox.DelCh(l1, y1, x1, l3, y3, x3, true);
421
422       editBox.PutS(oldString);
423       if(addedSpaces || addedTabs)
424          editBox.DelCh(l1, y1, x1 - (addedSpaces + addedTabs), l1, y1, x1, false);
425
426       //editBox.PutS(oldString);
427       if(!placeAfter)
428       {
429          editBox.GoToPosition(null, y1, x1);
430          editBox.selY = y2;
431          editBox.selX = x2;
432          { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
433       }
434       else
435       {
436          editBox.selY = y1;
437          editBox.selX = x1;
438          { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
439       }
440    }
441
442    void Redo(EditBox editBox)
443    {
444       EditLine l1, l2;
445       int c;
446
447       editBox.GoToPosition(null, y1, x1);
448       l1 = editBox.line;
449       l2 = editBox.lines.first;
450       for(c = 0; c < y2 && l2; c++, l2 = l2.next);
451
452       editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
453
454       editBox.PutS(newString);
455    }
456
457    ~ReplaceTextAction()
458    {
459       delete oldString;
460       delete newString;
461    }
462 };
463 /*
464 static class MoveTextAction : UndoAction
465 {
466    int fy1, fx1, fy2, fx2; 
467    int ty, tx;
468    type = class(MoveTextAction);
469
470    void Undo(EditBox editBox)
471    {
472
473    }
474
475    void Redo(EditBox editBox)
476    {
477
478    }
479 };
480 */
481 public class EditLine : struct
482 {
483    EditLine prev, next;
484    char * buffer;
485    uint size, count;
486    int length;
487    EditBox editBox;
488 public:
489    property char * text
490    {
491       set
492       {
493          // Only works for single line edit for now...
494          EditBox editBox = this.editBox;
495          editBox.Clear();
496          editBox.PutS(text);
497       }
498       get { return this ? buffer : null; }
499    };
500    property EditLine prev { get { return this ? prev : null; } };
501    property EditLine next { get { return this ? next : null; } };
502    property int count { get { return this ? count : 0; } };
503 private:
504    void Free()
505    {
506       delete buffer;
507    }
508
509    // This makes sure the buffer always contains at least count characters
510    // Keeps a count/2 pad for avoiding always reallocating memory.
511    bool AdjustBuffer(int count)
512    {
513       char * buffer;
514       int newSize;
515       
516       // Adds '\0' byte
517       count = count+1;
518
519       newSize =  (count + (count >> 1));
520
521       // Shrink down the buffer
522       if(size > newSize)
523       {
524          buffer = new char[newSize];
525          if(!buffer) return false;
526
527          if(this.buffer)
528          {
529             CopyBytes(buffer, this.buffer, count);
530             delete this.buffer;
531          }
532          this.buffer = buffer;
533          size = newSize;
534          return true;
535       }
536       // Increase the buffer
537       else if(size < count)
538       {
539           buffer = new char[newSize];
540           if(!buffer) return false;
541
542           if(this.buffer)
543           {
544              CopyBytes(buffer, this.buffer, this.count + 1); // size);
545              delete this.buffer;
546           }
547           this.buffer = buffer;
548           size = newSize;
549           return true;
550       }
551       else
552          return true;
553    }
554 };
555
556 public struct BufferLocation
557 {
558    EditLine line;
559    int y, x;
560
561    void AdjustDelete(BufferLocation start, BufferLocation end)
562    {
563       // Location is before, nothing to do
564       if(y < start.y || (y == start.y && x < start.x))
565          return;
566       // Location is inside deleted bytes, point to the start
567       if((y >= start.y && (y > start.y || x >= start.x)) &&
568          (y >= end.y && (y > end.y || x >= end.x)))
569       {
570          // Location is after
571          if(y >= end.y)
572          {
573             // Location is on another line
574             if(y > end.y)
575                y -= end.y - start.y;
576             // Location is the last touched line
577             else 
578             {
579                if(x >= end.x)
580                {
581                   line = start.line;
582                   y = start.y;
583                   //if(start.line == end.line)
584                      x -= end.x - start.x;
585                }
586             }
587          }
588       }
589       else
590          this = start;
591    }
592
593    // Assuming no carriage return before first character ???? fixed?
594    void AdjustAdd(BufferLocation start, BufferLocation end)
595    {
596       int numLines = end.y - start.y;
597       if(y >= start.y)
598       {
599          if(y > start.y)
600             y += numLines;
601          else
602          {
603             if(x > start.x || (x == start.x /*&& (numLines ? true : false)*/))
604             {
605                int c;
606                for(c = 0, line = start.line; c<numLines; c++)
607                   line = line.next;
608                y += numLines;
609                //x += numLines ? end.x : (end.x - start.x);            
610                x += end.x - start.x;
611             }
612          }
613       }
614    }
615 };
616
617 public enum EditBoxFindResult { notFound, found, wrapped };
618
619 static char * keyWords1[] =
620 {
621    // C
622    "return","break","continue","default","switch","case","if","else","for","while", "do","long","short",
623    "void", "char","int","float","double","unsigned","static", "extern", "struct", "union", "typedef","enum",
624    "const",   "sizeof",
625    "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line",
626    "__attribute__", "__stdcall", "_stdcall",
627    "__declspec", "goto",
628     "inline", "__inline__", "_inline", "__inline", "__typeof","__extension__",
629    "asm", "__asm", "_asm", "volatile", "#cpu", "__stdcall__",
630
631    // eC
632    "class", "private", "public",
633    "property","import",
634    "delete", "new", "new0", "renew", "renew0", "define",
635    "get", "set",
636    "remote",
637    "dllexport", "dllimport", "stdcall",
638    "subclass", "__on_register_module", "namespace", "using",
639    "typed_object", "any_object", "incref", "register", "watch", "stopwatching", "firewatchers", "watchable", "class_designer",
640    "class_fixed", "class_no_expansion", "isset", "class_default_property", "property_category", "class_data", "class_property",
641    "virtual", "thisclass","unichar", "dbtable", "dbindex", "database_open", "dbfield",
642
643    // Types
644    "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64",
645
646    // Values
647    "this", "true", "false", "null", "value",
648
649
650    // C++
651    "protected",
652    /* "defined" */
653    null
654 };
655
656 static char * keyWords2[] =
657 {
658    "defined", "warning", null
659 };
660
661 static char ** keyWords[] = { keyWords1, keyWords2 };
662 #define NUM_KEYWORD_GROUPS (sizeof(keyWords) / sizeof(char **))
663 //static int * keyLen[NUM_KEYWORD_GROUPS];
664 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
665
666 static char searchString[1025], replaceString[1025];
667 static bool matchCase = false, wholeWord = false, searchUp = false;
668
669 static GoToDialog goToDialog
670 {
671    autoCreate = false, isModal = true, text = $"Go To"
672 };
673
674 public class EditBox : CommonControl
675 {
676    class_property(icon) = "<:ecere>controls/editBox.png";
677 public:
678
679    // Notifications
680    virtual bool Window::NotifyModified(EditBox editBox);
681    virtual void Window::NotifyCaretMove(EditBox editBox, int line, int charPos);
682    virtual void Window::NotifyUpdate(EditBox editBox);
683    virtual bool Window::NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods);
684    virtual void Window::NotifyOvrToggle(EditBox editBox, bool overwrite);
685    virtual bool Window::NotifyKeyDown(EditBox editBox, Key key, unichar ch);
686
687    virtual bool Window::NotifyCharsAdded(EditBox editBox, BufferLocation before, BufferLocation after, bool pasteOperation);
688    virtual bool Window::NotifyCharsDeleted(EditBox editBox, BufferLocation beforeLoc, BufferLocation after, bool pasteOperation);
689    virtual bool Window::NotifyDropped(EditBox editBox, int x, int y);
690
691    virtual bool Window::NotifyUnsetModified(EditBox editBox);
692
693    // Why was this commented out?
694    //     It is required otherwise updating font property from property sheet doesn't immediately reflect in form designer,
695    // and the scrollArea property isn't compared properly either.
696    watch(font)
697    {
698       font = fontObject;
699       if(font) ComputeFont();
700       SetInitSize(initSize);
701    };
702
703    watch(hasVertScroll)
704    {
705       if(hasVertScroll)
706          style.vScroll = true;
707    };
708
709    watch(hasHorzScroll)
710    {
711       if(hasHorzScroll)
712          style.hScroll = true;
713    };
714
715    // Properties
716    property bool textHorzScroll { property_category $"Behavior" set { style.hScroll = value; } get { return style.hScroll; } };      // Should cut the text on set to false
717    property bool textVertScroll { property_category $"Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
718    property bool readOnly
719    {
720       property_category $"Behavior" 
721       set
722       {
723          style.readOnly = value;
724          itemEditCut.disabled = value || !selection;
725          itemEditDelete.disabled = value || !selection;
726          itemEditPaste.disabled = value;
727       }
728       get { return style.readOnly; }
729    };
730    property bool multiLine { property_category $"Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
731    property bool freeCaret { property_category $"Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
732    property bool tabKey { property_category $"Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
733    property int tabSize { property_category $"Behavior" set { tabSize = value; } get { return tabSize; } };
734    property bool tabSelection { property_category $"Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
735    property bool smartHome { property_category $"Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
736    property bool autoEmpty { property_category $"Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
737    property bool noCaret { property_category $"Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
738    property int maxLineSize { property_category $"Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
739    property int maxNumLines { property_category $"Behavior" set { maxLines = value; } get { return maxLines; } };
740    property bool useTab { property_category $"Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
741    property bool syntaxHighlighting { property_category $"Appearance" set { style.syntax = value; } get { return style.syntax; } };
742    property bool noSelect { property_category $"Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
743    property bool allCaps { property_category $"Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
744    property bool autoSize { property_category $"Behavior" set { style.autoSize = value; } get { return style.autoSize; } };
745    property bool wrap { set { style.wrap = value; Update(null); } get { return style.wrap; } };
746    //property bool lineNumbers { set { style.lineNumbers = value; } get { return style.lineNumbers; } };
747    property int numLines { get { return this ? lineCount : 0; } };
748    property int lineNumber { get { return y; } };  // TODO: Change to property of EditLine    this.line.number
749    property int column { get { return col; } };   // TODO: Add Set
750    property int charPos { get { return x; } };   // TODO: Add Set
751    property EditLine firstLine { get { return lines.first; } };      // Change these to a List<EditLine>... (this.lines[10].text)
752    property EditLine lastLine  { get { return lines.last; } };
753    property EditLine line { get { return this.line; } }; // TODO: Add Set   this.line = this.lines[10]
754    property char * contents
755    {
756       property_category $"Data" 
757       set
758       {
759          if(this)
760          {
761             Deselect();
762             DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
763             if(value)
764                AddS(value);
765             //SetViewToCursor(true);
766             UpdateDirty();
767             Home();
768          }
769       }
770
771       get
772       {
773          char * buffer = null;
774          if(this)
775          {
776             /* Can't implement this right now because of memory leak... Need string reference counting...
777             if(style.multiLine)
778             {
779                
780                EditLine line;
781                int len = 0;
782
783                for(line = lines.first; line; line = line.next)
784                   len += strlen(line.buffer);
785
786                buffer = new char[len+1];
787                len = 0;
788                for(line = lines.first; line; line = line.next)
789                {
790                   int lineLen = strlen(line.buffer);
791                   memcpy(buffer + len, line.buffer, lineLen);
792                   len += lineLen;
793                }
794                buffer[len] = '\0';
795             else
796                */
797                buffer = this.line ? this.line.buffer : null;
798          }
799          return buffer;
800       }
801    };
802    property bool overwrite { get { return overwrite; } };
803    property bool caretFollowsScrolling { get { return style.cursorFollowsView; } set { style.cursorFollowsView = value; } }
804
805    property char * multiLineContents
806    {
807       get
808       {
809          char * buffer = null;
810          if(style.multiLine)
811          {
812             
813             EditLine line;
814             int len = 0;
815
816             for(line = lines.first; line; line = line.next)
817                len += strlen(line.buffer)+1;
818
819             buffer = new char[len+1];
820             len = 0;
821             for(line = lines.first; line; line = line.next)
822             {
823                int lineLen = strlen(line.buffer);
824                memcpy(buffer + len, line.buffer, lineLen);
825                len += lineLen;
826                if(line.next) buffer[len++] = '\n';
827             }
828             buffer[len] = '\0';         
829          }
830          return buffer;
831       }
832    }
833
834    /*
835    char * GetLineText()
836    {
837       if(this)
838       {
839          return this.line.buffer;
840       }
841       return null;
842    }
843
844    void SetLineText(char * text)
845    {
846       if(this)
847       {
848          EditLine_SetText(this.line, text);
849       }
850    }
851    */
852    property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
853    property Color selectionText  { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
854    property SyntaxColorScheme syntaxColorScheme { set { delete colorScheme; colorScheme = value; incref colorScheme; } }
855
856    // selectionStart.line, selectionStart.column (With Set)
857    // selection.line1, selection.line2, selection.column1, selection.column2  (Read only)
858
859    // Methods
860 private:
861    Font font;
862    EditBoxBits style;
863    int tabSize;
864    int maxLineSize;
865    int maxLines;
866    OldList lines;
867    int lineCount;
868    // Font Space size
869    Size space, large;
870    
871    // Position of Caret (Not necessarily displayed position)
872    int x,y;
873    int col;
874    // Position of beginning of block (Block ends at (x,y))
875    int selX, selY;
876    // line is line at carret, selLine is line at beginning of block
877    EditLine line, selLine, dropLine;
878    // Mouse selection Moving virtual caret
879    int dropX, dropY;
880
881    bool selection;
882
883    // ViewX is x offset in pixels, ViewY is y offset in lines
884    int viewX, viewY;
885    // viewLine is first displayed line
886    EditLine viewLine; 
887
888    // start and end of area to redraw
889    int startY, endY;
890
891    // MaxLine is the longest line
892    EditLine maxLine;
893    // MaxLength is the longest line's length
894    int maxLength;
895
896    // MouseSelect is true once button is pressed, overwrite is true if mode is on
897    bool mouseSelect, mouseMove, overwrite, wordSelect;
898    // Timer is used for mouse selection scrolling
899    Timer timer
900    {
901       window = this, delay = 0.1;
902
903       bool DelayExpired()
904       {
905          // LineDown();
906          /*
907          if(this.viewY > 200)
908             GoToLineNum(0);
909          */
910          OnMouseMove(mouseX, mouseY, -1);
911          return true;
912       }
913    };
914
915    // (mouseX,mouseY) is the position of the mouse in the edit box
916    int mouseX, mouseY;
917
918    bool modified;
919
920    void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
921
922    Color backColor;
923    bool rightButtonDown;
924    bool pasteOperation;
925    int caretX, caretY;
926    UndoBuffer undoBuffer { data = this };
927    int savedAction;
928    Color selectionColor, selectionText;
929    SyntaxColorScheme colorScheme { };
930
931    menu = Menu { };
932
933    // Edit Menu
934    Menu editMenu { menu, $"Edit", e };
935    MenuItem itemEditCut
936    {
937       editMenu, $"Cut\tCtrl+X", t, disabled = true;
938
939       bool NotifySelect(MenuItem item, Modifiers mods)
940       {
941          if(!(style.readOnly))
942             Cut();
943          return true;
944       }
945    };
946    MenuItem itemEditCopy
947    {
948       editMenu, $"Copy\tCtrl+C", c, disabled = true;
949
950       bool NotifySelect(MenuItem item, Modifiers mods)
951       {
952          Copy();
953          return true;
954       }
955    };
956    MenuItem itemEditPaste
957    {
958       editMenu, $"Paste\tCtrl+V", p;
959    
960       bool NotifySelect(MenuItem item, Modifiers mods)
961       {
962          if(!(style.readOnly))
963             Paste();
964          return true;
965       }
966    };
967    MenuItem itemEditDelete
968    {
969       editMenu, $"Delete\tDel", d, disabled = true;
970
971       bool NotifySelect(MenuItem item, Modifiers mods)
972       {
973          if(!(style.readOnly))
974             DeleteSelection();
975          return true;
976       }
977    };
978    MenuDivider { editMenu };
979    MenuItem itemEditSelectAll
980    {
981       editMenu, $"Select All\tCtrl+A", a;
982
983       bool NotifySelect(MenuItem item, Modifiers mods)
984       {
985          SelectAll();
986          return true;
987       }
988    };
989    MenuDivider { editMenu };
990    MenuItem itemEditUndo
991    {
992       editMenu, $"Undo\tCtrl+Z", u;
993       disabled = true;
994
995       bool NotifySelect(MenuItem item, Modifiers mods)
996       {
997          Undo();
998          return true;
999       }
1000    };
1001    MenuItem itemEditRedo
1002    {
1003       editMenu, $"Redo\tCtrl+Y", o;
1004       disabled = true;
1005
1006       bool NotifySelect(MenuItem item, Modifiers mods)
1007       {
1008          Redo();
1009          return true;
1010       }
1011    };
1012    MenuDivider { editMenu };
1013    MenuItem
1014    {
1015       editMenu, $"Find Previous\tShift-F3", e, shiftF3;
1016
1017       bool NotifySelect(MenuItem item, Modifiers mods)
1018       {
1019          if(searchString[0])
1020             Find(searchString, wholeWord, matchCase, false);
1021          else
1022             itemEditFind.NotifySelect(this, item, mods);
1023          return true;
1024       }
1025    };
1026    MenuItem
1027    {
1028       editMenu, $"Find Next\tF3", n, f3;
1029
1030       bool NotifySelect(MenuItem item, Modifiers mods)
1031       {
1032          if(searchString[0])
1033             Find(searchString, wholeWord, matchCase, true);
1034          else
1035             itemEditFind.NotifySelect(this, item, mods);
1036          return true;
1037       }
1038    };
1039    MenuItem itemEditFind
1040    {
1041       editMenu, $"Find...\tCtrl+F", f, ctrlF;
1042
1043       bool NotifySelect(MenuItem item, Modifiers mods)
1044       {
1045          FindDialog dialog
1046          {
1047             editBox = this, master = master, isModal = true, searchString = searchString, matchCase = matchCase, wholeWord = wholeWord,
1048             searchUp = searchUp;
1049
1050             // TODO:
1051             // Fix dialog from above shouldn't be visible
1052             // void NotifyDestroyed(FindDialog dialog, DialogResult result)
1053             void NotifyDestroyed(Window window, DialogResult result)
1054             {
1055                FindDialog dialog = (FindDialog) window;
1056                searchUp = dialog.searchUp;
1057                strcpy(searchString, dialog.searchString);
1058                matchCase = dialog.matchCase;
1059                wholeWord = dialog.wholeWord;
1060             }
1061          };
1062          dialog.Create();
1063          return true;
1064       }
1065    };
1066    MenuItem
1067    {
1068       editMenu, $"Replace...\tCtrl+R", r, ctrlR;
1069
1070       bool NotifySelect(MenuItem item, Modifiers mods)
1071       {
1072          ReplaceDialog dialog
1073          {
1074             master = master, 
1075             isModal = true,
1076             searchString = searchString,
1077             replaceString = replaceString,
1078             matchCase = matchCase,
1079             wholeWord = wholeWord,
1080             editBox = this;
1081
1082             // TODO:
1083             // void NotifyDestroyed(ReplaceDialog dialog, DialogResult result)
1084             void NotifyDestroyed(Window window, DialogResult result)
1085             {
1086                ReplaceDialog dialog = (ReplaceDialog)window;
1087                char * replace = dialog.replaceString;
1088                if(replace)
1089                   strcpy(replaceString, replace);
1090                strcpy(searchString, dialog.searchString);
1091                matchCase = dialog.matchCase;
1092                wholeWord = dialog.wholeWord;
1093             }
1094          };
1095          dialog.Create();
1096          return true;
1097       }
1098    };
1099    MenuDivider { editMenu };
1100    MenuItem
1101    {
1102       editMenu, $"Go To...\tCtrl+G", g, ctrlG;
1103
1104       bool NotifySelect(MenuItem item, Modifiers mods)
1105       {
1106          goToDialog.editBox = this;
1107          goToDialog.master = master;
1108          goToDialog.Create();
1109          return true;
1110       }
1111    };
1112    MenuDivider { editMenu };
1113    MenuItem itemEditInsertTab
1114    {
1115       editMenu, $"Insert Tabs", i, checkable = true;
1116
1117       bool NotifySelect(MenuItem item, Modifiers mods)
1118       {
1119          style.useTab = item.checked;
1120          return true;
1121       }
1122    };
1123
1124    borderStyle = deep;
1125    snapVertScroll = true;
1126    snapHorzScroll = true;
1127
1128    EditBox()
1129    {
1130       static bool syntaxInit = false;
1131       if(!syntaxInit) 
1132       {
1133          int g,c;
1134          syntaxInit = true;
1135          for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
1136          {
1137             for(c = 0; keyWords[g][c]; c++);
1138             //keyLen[g] = new int[c];
1139             for(c = 0; keyWords[g][c]; c++)
1140             {
1141                keyLen[g][c] = strlen(keyWords[g][c]);
1142             }
1143          }
1144
1145          colorScheme.commentColor = dimGray;
1146          colorScheme.charLiteralColor = crimson;
1147          colorScheme.stringLiteralColor = crimson;
1148          colorScheme.preprocessorColor = green;
1149          colorScheme.numberColor = teal;
1150          colorScheme.keywordColors = [ blue, blue ];
1151       }
1152
1153       FontExtent = Display::FontExtent;
1154       font = fontObject;
1155       lines.offset = (uint)&((EditLine)0).prev;
1156
1157       style = EditBoxBits { hScroll = true };
1158
1159       // cursor = guiApp.GetCursor(IBeam);
1160
1161       // Default Properties
1162       /*maxLines = 65536;
1163       maxLineSize = 1024;*/
1164
1165       maxLines = MAXINT;
1166       maxLineSize = MAXINT;
1167
1168       tabSize = 3;
1169       
1170       overwrite = false;
1171       mouseSelect = this.mouseMove = false;
1172       line = null;
1173       lineCount = 0;
1174       x = selX = selY = 0;
1175       col = 0;
1176       y = -1;
1177       line = selLine = null;
1178       viewX = 0;   
1179       viewY = 0;
1180       maxLength = 0;
1181       maxLine = null;
1182       startY = 0;
1183       endY = clientSize.h;
1184       wordSelect = false;
1185
1186       // Default space to 8 in case window is not constructed
1187       space = { 8, 8 };
1188
1189       undoBuffer.dontRecord++;
1190       AddCh('\n');
1191       undoBuffer.dontRecord--;
1192
1193       viewLine = lines.first;
1194       style.recomputeSyntax = true;
1195
1196       FindMaxLine();
1197
1198       FixScrollArea();
1199
1200       UpdateCaretPosition(true);
1201       return true;
1202    }
1203
1204    ~EditBox()
1205    {
1206       lines.Free(EditLine::Free);
1207    }
1208
1209    void FlushBuffer(Surface surface, EditLine line, int wc, int * renderStart, int * x, int y, int numSpaces, Box box)
1210    {
1211       int count = wc - *renderStart;
1212       if(count)
1213       {
1214          if(y + space.h >= box.top && y <= box.bottom)
1215          {
1216             int w;
1217
1218             if(!numSpaces)
1219             {
1220                //FontExtent(display, font, line.buffer + *renderStart, count, &w, null);
1221                surface.TextFont(font);
1222                surface.TextExtent(line.buffer + *renderStart, count, &w, null);
1223                if(*x + w + XOFFSET > 0)
1224                   surface.WriteText(XOFFSET + *x,y, line.buffer + *renderStart, count);
1225                *x += w;
1226             }
1227             else
1228             {
1229                w = numSpaces; // * space.w;
1230                if(*x + w + XOFFSET > 0 && surface.GetTextOpacity())
1231                   surface.Area(XOFFSET + *x - 1, y, XOFFSET + *x + w, y + space.h-1);
1232                   // WHATS UP WITH THIS...  surface.Area(XOFFSET + *x, y, XOFFSET + *x + w, y + space.h-1);
1233                *x += w;
1234             }      
1235          }
1236          *renderStart = wc;
1237       }
1238    }
1239
1240    int CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected, 
1241                    Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, bool *overwrite)
1242    {
1243       bool flush = false;
1244
1245       if(selection)
1246       {
1247          if((wc == selX && line == selLine) || (wc == editX && line == this.line))
1248          {
1249             *selected ^= 1;
1250
1251             *foreground = (*selected) ? selectionForeground : textColor;
1252             *background = selectionBackground;
1253             *opacity = *selected;
1254
1255             flush = true;
1256          }
1257       }
1258
1259       // Overwrite Carret
1260       if(this.overwrite && active)
1261       {
1262          if((style.stuckCaret && wc == line.count && !line.next) ||
1263             (!mouseMove && line == this.line && wc == editX))
1264          {
1265             *overwrite = true;
1266             flush = true;
1267          }
1268       }
1269       return flush;
1270    }
1271
1272    void FigureStartSyntaxStates(EditLine firstLine, bool reset)
1273    {
1274       if(style.syntax)
1275       {
1276          bool inMultiLineComment = reset ? false : style.inMultiLineComment;
1277          bool inString = false;
1278          bool inQuotes = false;
1279          bool inPrep = reset ? false : style.inPrep;
1280          bool inSingleLineComment = false;
1281          bool escaped = reset ? false : style.escaped;
1282          bool continuedSingleLineComment = reset ? false : style.continuedSingleLineComment;
1283
1284          EditLine line = reset ? lines.first : firstLine;
1285          // int maxBackUp = 1000, c;
1286          // for(c = 0, line = viewLine; c < maxBackUp && line && line.prev; line = line.prev);
1287          for(; line != viewLine; line = line.next)
1288          {
1289             char * text = line.buffer;
1290             int c;
1291             char ch;
1292             bool lastWasStar = false;
1293             bool firstWord = true;
1294             if(!escaped) inPrep = false;
1295             inSingleLineComment = continuedSingleLineComment;
1296             escaped = false;
1297             inString = false;
1298             inQuotes = false;
1299
1300             firstWord = true;
1301             for(c = 0; (ch = text[c]); c++)
1302             {
1303                bool wasEscaped = escaped;
1304                bool backLastWasStar = lastWasStar;
1305                escaped = false;
1306                lastWasStar = false;
1307                if(ch == '/')
1308                {
1309                   if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1310                   {
1311                      if(text[c+1] == '/')
1312                      {
1313                         inSingleLineComment = true;
1314                      }
1315                      else if(text[c+1] == '*')
1316                      {
1317                         inMultiLineComment = true;
1318                      }
1319                   }
1320                   else if(backLastWasStar)
1321                      inMultiLineComment = false;
1322                }
1323                else if(ch == '*')
1324                {
1325                   if(!c || text[c-1] != '/') lastWasStar = true;
1326                }
1327                else if(ch == '\"' && !inSingleLineComment && !inMultiLineComment && !inQuotes)
1328                {
1329                   if(inString && !wasEscaped)
1330                   {
1331                      inString = false;
1332                   }
1333                   else
1334                   {
1335                      inString = true;
1336                   }
1337                }
1338                else if(ch == '\'' && !inSingleLineComment && !inMultiLineComment && !inString)
1339                {
1340                   if(inQuotes && !wasEscaped)
1341                      inQuotes = false;
1342                   else
1343                   {
1344                      inQuotes = true;
1345                   }
1346                }
1347                else if(ch == '\\')
1348                {
1349                   if(!wasEscaped)
1350                      escaped = true;
1351                }
1352                else if(ch == '#' && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1353                {
1354                   if(firstWord)
1355                   {
1356                      inPrep = true;
1357                   }
1358                }
1359                else if(ch != ' ' && ch != '\t')
1360                   firstWord = false;
1361             }
1362             continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
1363          }
1364          
1365          style.continuedSingleLineComment = continuedSingleLineComment;
1366          style.inMultiLineComment = inMultiLineComment;
1367          style.inPrep = inPrep;
1368          style.escaped = escaped;
1369       }
1370    }
1371    
1372    /*void OnDrawOverChildren(Surface surface)
1373    {
1374       if(style.lineNumbers)
1375       {
1376          int currentLineNumber = this.viewY + 1;
1377          int i;
1378          char lineText[256];
1379          for(i = 0; i * space.h < box.height; i++)
1380          {
1381             // ********* LINE NUMBERING *********
1382             surface.SetForeground(Color{60, 60, 60});
1383             //Highlight current line
1384             if(this.caretY / space.h == currentLineNumber - 1)
1385                surface.SetBackground(Color{220, 220, 220});
1386             else
1387                surface.SetBackground(Color{230, 230, 230});
1388             surface.textOpacity = true;
1389             
1390             sprintf(lineText,"%5u ", currentLineNumber % 100000);
1391             if(currentLineNumber > this.lineCount)
1392                surface.WriteText(0,i*space.h+1,"      ",6);
1393             else
1394                surface.WriteText(0,i*space.h+1,lineText,6);
1395             
1396             currentLineNumber++;
1397          }
1398       }
1399    }*/
1400
1401    void OnRedraw(Surface surface)
1402    {
1403       EditLine line;
1404       int y = YOFFSET;
1405       bool selected = false, selection = true;
1406       int selX, editX;
1407       Color selectionBackground = selectionColor ? selectionColor : SELECTION_COLOR;
1408       Color selectionForeground = selectionText ? selectionText : SELECTION_TEXT;
1409       Color defaultTextColor = property::foreground;
1410       Color textColor;
1411       Box box;
1412       int maxW = clientSize.w;
1413       
1414       Color foreground, background;
1415       bool opacity;
1416
1417       // Overwrite Caret Stuff
1418       bool overWrite = false;
1419       int overWriteX, overWriteY;
1420       byte overWriteCh;
1421
1422       // ****** SYNTAX STATES ******
1423       bool inMultiLineComment = style.inMultiLineComment;
1424       bool inString = false;
1425       bool inQuotes = false;
1426       bool inPrep = style.inPrep;
1427       bool inSingleLineComment = false;
1428       bool escaped = style.escaped;
1429       bool continuedSingleLineComment = style.continuedSingleLineComment;
1430       // ****** ************* ******
1431
1432       if(!isEnabled)
1433          defaultTextColor = Color { 85, 85, 85 };
1434       textColor = defaultTextColor;
1435
1436       if(
1437          Abs(selectionBackground.r - property::background.r) + 
1438          Abs(selectionBackground.g - property::background.g) + 
1439          Abs(selectionBackground.b - property::background.b) < 92)
1440       {
1441          selectionBackground = formColor;
1442          selectionForeground = selectionColor ? selectionColor : SELECTION_COLOR;
1443       }
1444
1445       surface.TextFont(this.font);
1446    /*
1447       surface.SetBackground(GDefaultPalette()[RND_Get(1, 15)]);
1448       surface.Area(0,0,MAXINT,MAXINT);
1449    */
1450
1451       if(!isEnabled)
1452       {
1453          surface.SetBackground(formColor);
1454          surface.Area(0,0,clientSize.w, clientSize.h);
1455       }
1456
1457       if(this.selX == this.x && this.selY == this.y)
1458          selection = false;
1459
1460       if(style.freeCaret)
1461       {
1462          editX = this.x;
1463          selX  = this.selX;
1464       }
1465       else
1466       {  
1467          editX = Min(this.x,this.line.count);
1468          selX  = Min(this.selX,this.selLine.count);
1469       }
1470
1471       selected = (this.selY < this.viewY) ^ (this.y < this.viewY);
1472
1473       foreground = selected ? selectionForeground : textColor;
1474       background = selectionBackground;
1475       opacity = selected;
1476    /*
1477       opacity = true;
1478       background = WHITE;
1479    */
1480       surface.SetForeground(foreground);
1481       surface.SetBackground(background);
1482       surface.TextOpacity(opacity);
1483
1484       surface.GetBox(box);
1485
1486       for(line = this.viewLine; line; line = line.next)
1487       {
1488          int x = -this.viewX;
1489          int end;
1490          int c;
1491          int start = 0;
1492          Color newTextColor = textColor = defaultTextColor;
1493          bool lineComplete = false;
1494         
1495
1496          // ****** SYNTAX HIGHLIGHTING ******
1497          bool lastWasStar = false;
1498          bool firstWord = true;
1499          if(!escaped) inPrep = false;
1500          inSingleLineComment = continuedSingleLineComment;
1501          escaped = false;
1502          inString = false;
1503          inQuotes = false;
1504          // *********************************
1505
1506    /*   === DEBUGGING TOOL FOR MAXLINE ===
1507
1508          if(line == this.maxLine)
1509          {
1510             surface.SetBackground(GREEN|0xFF000000);
1511             surface.TextOpacity(true);
1512          }
1513          else
1514          {
1515             surface.TextOpacity(selected ? true : false);
1516             surface.SetBackground(selected ? SELECTION_COLOR|0xFF000000 : BLACK|0xFF000000);
1517          }
1518    */
1519          
1520          if(line == this.selLine && line == this.line)
1521          {
1522             end = Max(line.count, this.x);
1523             end = Max(end, this.selX);
1524          }
1525          else if(line == this.selLine)
1526             end = Max(line.count, this.selX);
1527          else if(line == this.line)
1528             end = Max(line.count, this.x);
1529          else
1530             end = line.count;
1531
1532          for(c=0; c<end; )
1533          {
1534             int bufferLen = 0;
1535             int wordLen = 0;
1536             bool spacing = true;
1537             bool cantHaveWords = false;
1538             int w = 0;
1539
1540             if(lineComplete)
1541             {
1542                x = -viewX;
1543                y += space.h;
1544                lineComplete = false;
1545             }
1546             
1547             textColor = newTextColor;
1548             if(!selected)
1549             {
1550                foreground = textColor;
1551                surface.SetForeground(textColor);
1552             }
1553
1554             // Look at words
1555             for(; c<end && !cantHaveWords;)
1556             {
1557                if(wordLen)
1558                {
1559                   bufferLen += wordLen;
1560                   wordLen = 0;
1561                }
1562 #ifdef _DEBUG
1563                /*if(line.count > 4000)
1564                {
1565                   printf("oh");
1566                   CheckMemory();
1567                }*/
1568 #endif
1569                // Parse Words
1570                for(; c<line.count; c++)
1571                {
1572                   unichar ch = line.buffer[c];
1573                   unichar bf = (wordLen == 1) ? line.buffer[c-1] : 0;
1574                   //if(ch == ' ' || ch == '\t' || (wordLen && (ch == '(' || ch == ')' || ch == ';' || ch == ':')) || (wordLen == 1 && line.buffer[c-1] == '('))
1575                   if(CharMatchCategories(ch, separators) || /*ch == ' ' ||*/ ch == '\t' ||
1576                      (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && ch != '#' /*&& ch != '_'*/) || 
1577                      (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && bf != '#' && bf != '\t' /*&& bf != '_' && bf != ' '*/))
1578                      break;
1579                   wordLen++;
1580                }
1581
1582                if(!wordLen)
1583                {
1584                
1585                   for(; c<line.count; c++)
1586                   {
1587                      unichar ch = line.buffer[c];
1588                      if(ch == '\t' || ch == ' ')
1589                      {
1590                         cantHaveWords = true;
1591                         if(bufferLen)
1592                            break;
1593                      }
1594                      if(ch != ' ' && ch != '\t')
1595                         break;
1596                      wordLen++;
1597                   }
1598
1599                   if(c == line.count && c < end)
1600                   {
1601                      if(bufferLen)
1602                      {
1603                         c -= wordLen;
1604                         break;
1605                      }
1606                      wordLen += end - c;
1607                      c = end;
1608                      cantHaveWords = true;
1609                   }
1610
1611                   if(c < end)
1612                      escaped = false;
1613                   lastWasStar = false;
1614                }
1615                else
1616                {
1617                   if(isEnabled)
1618                   {
1619                      bool backEscaped = escaped;
1620                      bool backLastWasStar = lastWasStar;
1621                      bool backInMultiLineComment = inMultiLineComment;
1622                      bool backInString = inString;
1623                      bool backInQuotes = inQuotes;
1624                      bool backInPrep = inPrep;
1625                      bool backInSingleLineComment = inSingleLineComment;
1626
1627                      char * word = line.buffer + c - wordLen;
1628                      int g,ccc;
1629                      bool wasEscaped = escaped;
1630                      escaped = false;
1631                      lastWasStar = false;
1632
1633                      // Determine Syntax Highlighting
1634                      newTextColor = defaultTextColor;
1635                      if(style.syntax)
1636                      {
1637                         if(inSingleLineComment || inMultiLineComment)
1638                         {
1639                            newTextColor = colorScheme.commentColor;
1640                         }
1641                         else if(inQuotes)
1642                         {
1643                            newTextColor = colorScheme.charLiteralColor;
1644                         }
1645                         else if(inString)
1646                         {
1647                            newTextColor = colorScheme.stringLiteralColor;
1648                         }
1649                         else if(inPrep)
1650                         {
1651                            newTextColor = colorScheme.preprocessorColor;
1652                         }
1653                         if(wordLen == 1 && word[0] == '/')
1654                         {
1655                            if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1656                            {
1657                               if(word[1] == '/')
1658                               {
1659                                  inSingleLineComment = true;
1660                                  newTextColor = colorScheme.commentColor;
1661                               }
1662                               else if(word[1] == '*')
1663                               {
1664                                  inMultiLineComment = true;
1665                                  newTextColor = colorScheme.commentColor;
1666                               }
1667                            }
1668                            else if(backLastWasStar)
1669                               inMultiLineComment = false;
1670                         }
1671                         else if(wordLen == 1 && word[0] == '*')
1672                         {
1673                            if(!c || word[-1] != '/')
1674                               lastWasStar = true;
1675                         }
1676                         else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && wordLen == 1 && word[0] == '\"')
1677                         {
1678                            if(inString && !wasEscaped)
1679                            {
1680                               inString = false;
1681                            }
1682                            else
1683                            {
1684                               inString = true;
1685                               newTextColor = colorScheme.stringLiteralColor;
1686                            }
1687                         }
1688                         else if(!inSingleLineComment && !inMultiLineComment && !inString && wordLen == 1 && word[0] == '\'')
1689                         {
1690                            if(inQuotes && !wasEscaped)
1691                               inQuotes = false;
1692                            else
1693                            {
1694                               inQuotes = true;
1695                               newTextColor = colorScheme.charLiteralColor;
1696                            }
1697                         }
1698                         else if(wordLen == 1 && word[0] == '\\')
1699                         {
1700                            if(!wasEscaped)
1701                               escaped = true;
1702                         }
1703                         else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && 
1704                            ( ( isdigit(word[0]) /*&& (!c || word[-1] == ' ' || word[-1] == '\t')*/ ) || (word[0] == '.' && isdigit(word[1]))))
1705                         {
1706                            newTextColor = colorScheme.numberColor;
1707                         }
1708                         else
1709                         {
1710                            if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && word[0] == '#')
1711                            {
1712                               if(firstWord)
1713                               {
1714                                  inPrep = true;
1715                                  newTextColor = colorScheme.preprocessorColor; 
1716                               }
1717                            }
1718                            if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1719                            {
1720                               for(g = 0; g < ((inPrep && word[0] != '#') ? 2 : 1); g++)
1721                               {
1722                                  char ** keys = keyWords[g];
1723                                  int * len = keyLen[g];
1724                                  for(ccc = 0; keys[ccc]; ccc++)
1725                                  {
1726                                     if(len[ccc] == wordLen && !strncmp(keys[ccc], word, wordLen))
1727                                     {
1728                                        newTextColor = colorScheme.keywordColors[g];
1729                                        break;
1730                                     }
1731                                  }
1732                               }
1733                            }
1734                         }
1735                         firstWord = false;
1736
1737                         // If highlighting for this word is different, break
1738                         if(newTextColor != textColor)
1739                         {
1740                            if(bufferLen)
1741                            {
1742                               // Better solution than going back?
1743                               c -= wordLen;
1744
1745                               // Reset syntax flags
1746                               escaped = backEscaped;
1747                               lastWasStar = backLastWasStar;
1748                               inMultiLineComment = backInMultiLineComment;
1749                               inString = backInString;
1750                               inQuotes = backInQuotes;
1751                               inPrep = backInPrep;
1752                               inSingleLineComment = backInSingleLineComment;
1753                               break;
1754                            }
1755                            else
1756                            {
1757                               textColor = newTextColor;
1758                               if(!selected)
1759                               {
1760                                  foreground = textColor;
1761                                  surface.SetForeground(textColor);
1762                               }
1763                            }
1764                         }
1765                      }
1766                   }
1767                
1768                   // If we're not breaking, this can't be rendered as spacing anymore
1769                   spacing = false;
1770
1771                   // Do word wrapping here
1772                   if(style.wrap)
1773                   {
1774                      //if(!numSpaces)
1775                      {
1776                         int tw;
1777                         FontExtent(display, font, line.buffer + start, bufferLen + wordLen, &tw, null);
1778                         w = tw;
1779                      }
1780                      /*else
1781                      {
1782                         w += numSpaces * space.w;
1783                      }*/
1784                      if(x + viewX + w > maxW)
1785                      {
1786                         c -= wordLen;
1787                         lineComplete = true;                        
1788                         break;
1789                      }
1790                   }
1791                }
1792                bufferLen += wordLen;
1793                wordLen = 0;
1794             }
1795
1796             {
1797                int renderStart = start;
1798                bool flush = false;
1799                int numSpaces = 0;
1800                int wc;
1801
1802                // Render checking if we need to split because of selection or to find where to draw insert caret
1803                for(wc = start; wc < start + bufferLen; wc++)
1804                {
1805                   flush = CheckColors(line, wc, selection, selX, editX, &selected, selectionForeground,
1806                      selectionBackground, textColor, &foreground, &background, &opacity, &overWrite);
1807                   if(overWrite == true)
1808                   {
1809                      overWriteCh = (wc < line.count) ? line.buffer[wc] : ' ';
1810                      if(overWriteCh == '\t') overWriteCh = ' ';
1811                   }
1812
1813                   if(flush)
1814                   {
1815                      FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1816                      if(overWrite == true)
1817                      {
1818                         overWriteX = x;
1819                         overWriteY = y;
1820                         overWrite = 2;
1821                      }
1822                      numSpaces = 0;
1823
1824                      surface.TextOpacity(opacity);
1825                      surface.SetBackground(background);
1826                      surface.SetForeground(foreground);
1827
1828                      flush = false;
1829                   }
1830
1831                   if(spacing)
1832                   {
1833                      if(wc < line.count && line.buffer[wc] == '\t')
1834                      {
1835                         numSpaces += (tabSize * space.w) - ((x + numSpaces + viewX) % (tabSize * space.w));
1836                      }
1837                      else
1838                      {
1839                         numSpaces += space.w;
1840                      }
1841                   }
1842                }
1843                FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1844                start += bufferLen;
1845             }
1846          }
1847
1848          if(CheckColors(line, c, selection, selX, editX, &selected, selectionForeground,
1849                         selectionBackground, textColor, &foreground, &background, &opacity, &overWrite))
1850          {
1851             if(overWrite == true)
1852             {
1853                overWriteX = x;
1854                overWriteY = y;
1855                overWriteCh = ' ';
1856                overWrite = 2;
1857             }
1858             surface.TextOpacity(opacity);
1859             surface.SetBackground(background);
1860             surface.SetForeground(foreground);
1861          }
1862
1863          if(style.freeCaret && selected)
1864          {
1865             surface.SetBackground(selectionBackground);
1866             surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1867             // TEST: surface.Area(x + XOFFSET,y,clientSize.w-1,y+this.space.h-1);
1868          }
1869
1870
1871          /*
1872          if(style.freeCaret && selected)
1873          {
1874             surface.SetBackground(selectionBackground);
1875             surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1876          }
1877          */
1878          
1879          continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
1880
1881          y+=this.space.h;
1882          if(y > box.bottom) // >=clientSize.h) 
1883             break;
1884       }
1885
1886       if(overWrite)
1887       {
1888          surface.TextOpacity(true);
1889          surface.SetForeground(black);
1890          surface.SetBackground(Color {255,255,85});
1891          surface.WriteText(XOFFSET + overWriteX,overWriteY, &overWriteCh, 1);
1892       }
1893    }
1894
1895    void FixScrollArea()
1896    {
1897       if(style.hScroll || style.vScroll)
1898       {
1899          int width = maxLength + XOFFSET;
1900          int height = lineCount * space.h;
1901          if(style.freeCaret && line)
1902          {
1903             if(x > selX)
1904             {
1905                if(x > line.count)
1906                   width = Max(line.length + (x - line.count) * space.w, maxLength);
1907             }
1908             else
1909             {
1910                if(selX > selLine.count)
1911                   width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength);
1912             }
1913          }
1914
1915          width += space.w;
1916          SetScrollLineStep(8, space.h);
1917          SetScrollArea(width, height, true);
1918       }
1919    }
1920
1921    void ComputeLength(EditLine line)
1922    {
1923       int c;
1924       int tabOccur = 0;
1925       int tabWidth;
1926       int x = 0;
1927
1928       for(c = 0; c < line.count; )
1929       {
1930          int len = 1;
1931          int start = c;
1932          int w;
1933          if(c < line.count)
1934          {
1935             byte ch = 0;
1936             int numBytes;
1937             for(len = 0; c < line.count; c += numBytes)
1938             {
1939                ch = line.buffer[c];
1940                numBytes = UTF8_NUM_BYTES(ch);
1941
1942                if(ch == ' ' || ch == '\t')
1943                {
1944                   if(!len) c++;
1945                   break;
1946                }
1947                len += numBytes;
1948             }
1949             if(!len && ch == ' ')
1950             {
1951                len = 1;
1952                w = space.w;
1953             }
1954             else if(!len && ch == '\t')
1955             {
1956                w = (tabSize * space.w) - (x % (tabSize * space.w));
1957                len = 1;
1958             }
1959             else
1960                FontExtent(display, font, line.buffer + start, len, &w, null); 
1961          }
1962          else
1963          {
1964             w = space.w;
1965             c++;
1966          }
1967          x += w;
1968       }               
1969       line.length = x;
1970
1971       if(line.length > this.maxLength)
1972       {
1973          this.maxLine = line;
1974          this.maxLength = line.length;
1975       }
1976    }
1977
1978    void FindMaxLine()
1979    {
1980       EditLine line;
1981
1982       this.maxLength = 0;
1983       this.maxLine = null;
1984
1985       for(line = lines.first; line; line = line.next)
1986       {
1987          if(line.length > this.maxLength)
1988          {
1989             this.maxLength = line.length;
1990             this.maxLine = line;
1991          }
1992       }
1993    }
1994
1995    void SelDirty()
1996    {
1997       if(this.selY != this.y)
1998          DirtyAll();
1999       else if(this.selX != this.x)  // commented out to erase caret: if(this.selX != this.x) 
2000          DirtyLine(this.y);
2001    }
2002
2003    void ComputeColumn()
2004    {
2005       // Adjust new edit X position according to tabs
2006       int c, position = 0;
2007       unichar ch;
2008       int nb;
2009       for(c = 0; c<this.line.count && c<this.x; c+= nb)
2010       {
2011          ch = UTF8_GET_CHAR(this.line.buffer + c, nb);
2012          // TODO: MIGHT WANT TO RETHINK WHAT COLUMN SHOULD BE REGARDING TABS
2013          if(ch == '\t')
2014             position += this.tabSize - (position % this.tabSize);
2015          else
2016             position ++;
2017       }
2018       position += this.x - c;
2019       this.col = position;
2020    }
2021
2022    int DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
2023    {
2024       return _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, null);
2025    }
2026    
2027    bool HasCommentOrEscape(EditLine line)
2028    {
2029       bool hadComment = strstr(line.buffer, "/*") || strstr(line.buffer, "*/");
2030       int c;
2031       
2032       if(!hadComment)
2033       {
2034          for(c = line.count-1; c >= 0; c--)
2035          {
2036             char ch = line.buffer[c];
2037             if(ch == '\\')
2038             {
2039                hadComment = true;
2040                break;
2041             }
2042             else //if(ch != ' ' && ch != '\t')
2043                break;
2044          }
2045       }
2046       return hadComment;
2047    }
2048    
2049    int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, int * addedSpacesPtr)
2050    {
2051       EditLine line = l1, next;
2052       char * buffer;
2053       int oldCount1, oldCount2;
2054       int y, firstViewY, firstY, firstDropY, firstSelY;
2055       int newLineCount;
2056       int extras = 0;
2057       DelTextAction action = null;
2058       bool hadComment = false;
2059       int start = c1;
2060
2061       if(style.syntax)
2062          hadComment = HasCommentOrEscape(line);
2063
2064       if(y2 > y1 || c2 > c1)
2065       {
2066          if(start < l1.count)
2067          {
2068             while(!UTF8_IS_FIRST(l1.buffer[start]) && start)
2069                start--;
2070          }
2071       }
2072       oldCount1 = l1.count;
2073       buffer = l1.buffer;
2074       while(c1 < oldCount1)
2075       {
2076          byte ch = buffer[c1];
2077          if(UTF8_IS_FIRST(ch)) break;
2078          c1--;         
2079          extras++;
2080       }
2081       oldCount2 = l2.count;
2082       buffer = l2.buffer;
2083       while(c2 < oldCount2)
2084       {
2085          byte ch = buffer[c2];
2086          if(UTF8_IS_FIRST(ch)) break;
2087          c2++;
2088          extras++;
2089       }
2090
2091       if(!undoBuffer.dontRecord && (y2 > y1 || c2 > c1))
2092       {
2093          int len;
2094          char * string;
2095          
2096          len = GetText(null, l1, y1, start, l2, y2, c2, false, false);
2097          string = new char[len];
2098          action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter };
2099          GetText(string, l1, y1, start, l2, y2, c2, false, false);
2100          Record(action);
2101       }
2102
2103       //oldCount1 = l1.count;
2104       //oldCount2 = l2.count;
2105
2106       {
2107          BufferLocation before = { l1,y1,c1}, after = { l2,y2,c2 };
2108          NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
2109       }
2110
2111       if(c2 > oldCount2) c2 = oldCount2;
2112       if(!(style.freeCaret))
2113          if(c1 > oldCount1) c1 = oldCount1;
2114       newLineCount = c1 + l2.count-c2;
2115       if(l1 == l2)
2116       {
2117          /*
2118          if(!line.size)
2119             printf("");
2120          buffer = new char[line.size ? line.size : 1];
2121          */
2122          buffer = new char[line.size];
2123          if(!buffer) return;
2124          CopyBytes(buffer,l2.buffer,oldCount1 + 1/*line.count + 1*//*line.size*/);
2125       }
2126       else
2127          buffer = l2.buffer;
2128
2129       if(!line.AdjustBuffer(newLineCount)) 
2130          return;
2131
2132 #ifdef _DEBUG
2133       /*if(newLineCount > 4000 || newLineCount < 0)
2134          printf("Warning");*/
2135 #endif
2136       line.count = newLineCount;
2137
2138       memmove(l1.buffer+c1, buffer+c2,line.count-c1);
2139       if(c1>oldCount1)
2140       {
2141          if(action)
2142          {
2143             action.addedSpaces = c1-oldCount1;
2144 #ifdef _DEBUG
2145             if(action.addedSpaces > action.x1)
2146             {
2147                printf("bug!");
2148             }
2149 #endif
2150          }
2151          if(addedSpacesPtr) *addedSpacesPtr = c1-oldCount1;
2152          FillBytes(l1.buffer+oldCount1,' ',c1-oldCount1);
2153       }
2154       line.buffer[line.count] = '\0';
2155
2156       if(l1 == l2)
2157       {
2158          delete buffer;
2159          DirtyLine(y1);
2160          /*
2161          this.dropX -= c2-c1;
2162          this.dropX = Max(this.dropX,0);
2163          this.x -= c2-c1-1;
2164          this.x = Max(this.x,0);
2165          */
2166       }
2167       else
2168          DirtyEnd(y1);
2169
2170       y = y1;
2171       firstViewY = this.viewY;
2172       firstY = this.y;
2173       firstDropY = this.dropY;
2174       firstSelY = this.selY;
2175       for(line = l1;line;line = next, y++)
2176       {
2177          next = line.next;
2178          if(line!=l1)
2179          {
2180             this.lineCount--;
2181             delete line.buffer;
2182        
2183             if(line == this.viewLine)
2184             {
2185                if(this.viewLine.next)
2186                {
2187                   this.viewLine = this.viewLine.next;
2188                   //this.viewY++;
2189                   style.recomputeSyntax = true;
2190                }
2191                else 
2192                {
2193                   this.viewLine = this.viewLine.prev;
2194                   this.viewY--;
2195                   style.recomputeSyntax = true;
2196                }
2197             }
2198             else if(y < firstViewY)
2199                this.viewY--;
2200             if(line == this.line)
2201             {
2202                if(this.line.next)
2203                {
2204                   this.line = this.line.next;
2205                   this.x = this.line.count;
2206                   //this.y++;
2207                }
2208                else 
2209                {
2210                   this.line = this.line.prev;
2211                   this.x = this.line.count;
2212                   this.y--;
2213                }
2214                ComputeColumn();
2215             }
2216             else if(y < firstY)
2217                this.y--;
2218             if(line == this.dropLine)
2219             {
2220                if(this.dropLine.next)
2221                {
2222                   this.dropLine = this.dropLine.next;
2223                   this.dropX = this.dropLine.count;
2224                }
2225                else 
2226                {
2227                   this.dropLine = this.dropLine.prev;
2228                   this.dropX = this.dropLine.count;
2229                   this.dropY--;
2230                }
2231             }
2232             else if(y < firstDropY)
2233                this.dropY--;
2234             if(line == this.selLine)
2235             {
2236                if(this.selLine.next)
2237                {
2238                   this.selLine = this.selLine.next;
2239                   this.selX = this.selLine.count;
2240                }
2241                else 
2242                {
2243                   this.selLine = this.selLine.prev;
2244                   this.selX = this.selLine.count;
2245                   this.selY--;
2246                }
2247             }
2248             else if(y < firstSelY)
2249                this.selY--;
2250             lines.Delete(line);
2251          }
2252          if(line == l2) break;
2253       }
2254       ComputeLength(l1);
2255       FindMaxLine();
2256       if(style.autoSize) AutoSize();
2257       if(style.syntax && (hadComment || HasCommentOrEscape(this.line)))
2258       {
2259          DirtyAll();
2260          style.recomputeSyntax = true;
2261       }
2262       return extras;
2263    }
2264
2265    bool DelSel(int * addedSpacesPtr)
2266    {
2267       if(this.line != this.selLine || this.x != this.selX)
2268       {
2269          if(this.selY < this.y)
2270          {
2271             _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2272             this.x = this.selX;
2273             this.y = this.selY;
2274             this.line = this.selLine;
2275          }
2276          else if(this.selY > this.y)
2277          {
2278             _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2279             this.selX = this.x;
2280             this.selY = this.y;
2281             this.selLine = this.line;
2282          }
2283          else if(this.selX < this.x)
2284          {
2285             _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2286             this.x = this.selX;
2287             this.y = this.selY;
2288             this.line = this.selLine;
2289          }
2290          else
2291          {
2292             _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2293             this.selX = this.x;
2294             this.selY = this.y;
2295             this.selLine = this.line;
2296          }
2297          ComputeColumn();
2298          return true;
2299       }
2300       return false;
2301    }
2302
2303    bool AddToLine(char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr)
2304    {
2305       bool hadComment = false;
2306       // Add the line here
2307       EditLine line = this.line;
2308          
2309       // The purpose of this is solely to lock a max number of characters if no HSCROLLING is present
2310       if(!style.hScroll && created)
2311       {
2312          int endX = (style.freeCaret) ? this.x : Min(this.x, line.count);
2313          int max;
2314          int x;
2315          int c;
2316
2317          // Lock if no place to display.
2318          if(LFComing)
2319             max = endX;
2320          else if(endX > this.x)
2321             max = Max(this.x, line.count);
2322          else
2323             max = line.count;
2324
2325          for(x = 0, c = 0; c < max+count; )
2326          {
2327             int w;
2328             int numBytes = 1;
2329             char * string;
2330             if(c < Min(this.x, line.count))
2331                string = line.buffer + c;
2332             else if(c < endX)
2333                string = " ";
2334             else if(c < endX + count)
2335                string = stringLine + c - endX;
2336             else
2337                string = line.buffer + c - endX - count;
2338
2339             if(*string == '\t')
2340             {
2341                w = (tabSize * space.w) - (x % (tabSize * space.w));
2342             }
2343             else
2344             {
2345                numBytes = UTF8_NUM_BYTES(*string);
2346                FontExtent(display, this.font, string, numBytes, &w, null);
2347             }
2348             x += w;
2349
2350             if(x >= clientSize.w) 
2351             {
2352                count = c - max;
2353                break;
2354             }
2355             c += numBytes; // - 1;
2356          }
2357       }
2358
2359       if(count > 0)
2360       {
2361          int addedSpaces = 0;
2362          int addedTabs = 0;
2363          
2364          // Add blank spaces if EES_FREECARET
2365          if(this.x > line.count)
2366          {
2367             if(style.freeCaret)
2368             {
2369                if(style.useTab)
2370                {
2371                   int wantedPosition;
2372                   int position = 0;
2373                   int c;
2374
2375                   for(c = 0; c<line.count; c++)
2376                   {
2377                      if(this.line.buffer[c] == '\t')
2378                         position += this.tabSize - (position % this.tabSize);
2379                      else
2380                         position ++;
2381                   }
2382                   wantedPosition = position + (this.x - line.count);
2383
2384                   // A tab is too much...
2385                   if(position + (this.tabSize - (position % this.tabSize)) > wantedPosition)
2386                      addedSpaces = wantedPosition - position;
2387                   else
2388                   {
2389                      // Put a first tab
2390                      addedTabs = 1;
2391                      position += this.tabSize - (position % this.tabSize);
2392                      // Add as many tabs as needed
2393                      addedTabs += (wantedPosition - position) / this.tabSize;
2394                      position += (addedTabs-1) * this.tabSize;
2395                      // Finish off with spaces
2396                      addedSpaces = wantedPosition - position;
2397                   }
2398                }
2399                else
2400                   addedSpaces = this.x - line.count;
2401             }
2402          }
2403          this.x = Min(this.x, line.count);
2404
2405          if(line.count + count + addedSpaces + addedTabs > this.maxLineSize)
2406             return false;
2407
2408          DirtyLine(this.y);
2409
2410          if(style.syntax)
2411             hadComment = HasCommentOrEscape(line);
2412
2413          // Adjust the size of the line
2414          if(!line.AdjustBuffer(line.count+count+addedTabs+addedSpaces))
2415             return false;
2416
2417          {
2418             BufferLocation before = { this.line, this.y, this.x }, after = { this.line, this.y, this.x };
2419             bool hasComment;
2420          
2421             memmove(line.buffer+this.x+count, line.buffer+this.x,line.count-this.x);
2422             CopyBytes(line.buffer + this.x + addedTabs + addedSpaces, stringLine, count);
2423             if(addedTabs)
2424             {
2425                *addedTabsPtr = addedTabs;
2426                FillBytes(line.buffer+line.count,'\t',addedTabs);
2427 #ifdef _DEBUG
2428       if(addedTabs > 4000 || addedTabs < 0)
2429          printf("Warning");
2430 #endif
2431                line.count += addedTabs;
2432             }
2433             else if(addedTabs)
2434                *addedTabsPtr = 0;
2435             if(addedSpaces)
2436             {
2437                FillBytes(line.buffer+line.count,' ',addedSpaces);
2438 #ifdef _DEBUG
2439       if(addedSpaces > 4000 || addedSpaces < 0)
2440          printf("Warning");
2441 #endif
2442                line.count += addedSpaces;
2443                if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
2444             }      
2445             else if(addedSpacesPtr)
2446                *addedSpacesPtr = 0;
2447 #ifdef _DEBUG
2448       if(count > 4000 || count < 0)
2449          printf("Warning");
2450 #endif
2451             line.count += count;
2452             this.x += count + addedTabs + addedSpaces;
2453             this.selX = this.x;
2454             this.selY = this.y;
2455             this.selLine = this.line;
2456
2457             line.buffer[line.count] = '\0';
2458
2459             ComputeLength(line);
2460             ComputeColumn();
2461
2462             after.x = this.x; 
2463
2464             hasComment = HasCommentOrEscape(line);
2465             if(!undoBuffer.insideRedo)
2466             {
2467                int backDontRecord = undoBuffer.dontRecord;
2468                undoBuffer.dontRecord = 0;
2469                NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
2470                if(style.autoSize) AutoSize();
2471                undoBuffer.dontRecord = backDontRecord;
2472             }
2473             if(style.syntax && (hadComment || hasComment || line != this.line))
2474             {
2475                style.recomputeSyntax = true;
2476                DirtyAll();
2477             }
2478          }
2479       }
2480       return true;
2481    }
2482
2483    void Emptyline(EditLine line, int y)
2484    {
2485       if(line.next)
2486          DelCh(line, y, 0, line.next, y+1, 0, true);
2487       else
2488          DelCh(line, y, 0, line, y, line.count, true);
2489    }
2490
2491    void GoToEnd(bool deselect)
2492    {
2493       if(this.line)
2494       {
2495          this.line = this.lines.last;
2496          if(this.y != this.lineCount-1)
2497             DirtyAll();
2498          else if (this.x != this.line.count)
2499             DirtyLine(this.lineCount-1);
2500          this.y = this.lineCount-1;
2501          this.x = this.line.count;
2502          ComputeColumn();
2503          if(deselect)
2504             Deselect();
2505       }
2506    }
2507
2508    void GoToHome(bool deselect)
2509    {
2510       if(this.line)
2511       {
2512          this.line = this.lines.first;
2513          if(this.y != 0)
2514             DirtyAll();
2515          else if (this.x !=0)
2516             DirtyLine(this.lineCount-1);
2517          this.y = 0;
2518          this.x = 0;
2519          this.col = 0;
2520          if(deselect)
2521             Deselect();
2522       }
2523    }
2524
2525    // Returns true if it needs scrolling
2526    bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
2527    {
2528       int w;
2529       int c;
2530       int x, y;
2531       EditLine line;
2532       bool needHScroll = false;
2533
2534       if(py < 0)
2535       {
2536          if(this.viewY > 0)
2537          {
2538             y = this.viewY-1;
2539             line = this.viewLine ? (void *)this.viewLine.prev : null;
2540          }
2541          else
2542          {
2543             y = 0;
2544             line = (void *)this.lines.first;
2545          }
2546       }
2547       else
2548       {
2549          py = Min(py, clientSize.h);
2550          py /= this.space.h;
2551          py = Min(py, this.lineCount);
2552          y = this.viewY;
2553          for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
2554          {
2555             y++;
2556          }
2557       }
2558
2559       if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
2560          needHScroll = true;
2561       px = Max(px,0);
2562       px = Min(px,clientSize.w+this.space.w);
2563
2564       if(tx && line)
2565       {
2566          *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
2567       }
2568
2569       if(tline) *tline = line;
2570       if(ty) *ty = y;
2571
2572       // Prevent divide by 0 from non valid this.font
2573       if(!this.space.h)
2574          return (y < this.viewY) || needHScroll;
2575       else
2576          return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
2577       return false;
2578    }
2579
2580    // Minimal Update Management Functions
2581    void DirtyAll()
2582    {
2583       this.startY = 0;
2584       this.endY = clientSize.h-1;
2585    //   ErrorLog("DirtyAll\n");
2586    }
2587
2588    void DirtyEnd(int y)
2589    {
2590       if((y - this.viewY)*this.space.h < this.startY)
2591          this.startY = (y - this.viewY)*this.space.h+ YOFFSET;
2592       this.endY = clientSize.h-1;
2593       //ErrorLog("DirtyEnd %d\n", y);
2594    }
2595         
2596    void DirtyLine(int y)
2597    {
2598       if(y >= this.viewY)
2599       {
2600          if((y - this.viewY)*this.space.h < this.startY)
2601             this.startY = (y - this.viewY)*this.space.h + YOFFSET;
2602          if((y - this.viewY+1)*this.space.h > this.endY)
2603             this.endY = (y - this.viewY+1)*this.space.h-1 + YOFFSET;
2604       }
2605       //ErrorLog("DirtyLine %d\n", y);
2606    }
2607
2608    void UpdateDirty()
2609    {
2610       Box box;
2611
2612       if(style.recomputeSyntax)
2613       {
2614          FigureStartSyntaxStates(lines.first, true);
2615          style.recomputeSyntax = false;
2616       }
2617       box.left  = 0;
2618       if(this.startY > this.endY) return;
2619       if(this.startY <= 0 && this.endY >= clientSize.h-1)
2620       {
2621          Update(null);
2622       }
2623       else
2624       {
2625          box.right = clientSize.w-1;
2626          box.top   = this.startY;
2627          box.bottom  = this.endY;
2628          Update(box);
2629       }
2630       this.startY = clientSize.h;
2631       this.endY = 0;
2632    }
2633
2634    bool IsMouseOnSelection()
2635    {
2636       bool mouseOnSelection = false;
2637
2638       int x, y;
2639       int minY = Min(this.selY, this.y);
2640       int maxY = Max(this.selY, this.y);
2641       int minX = Min(this.selX, this.x);
2642       int maxX = Max(this.selX, this.x);
2643
2644       FindMouse(this.mouseX - this.space.w / 2, this.mouseY, &x, &y, null, false);
2645
2646       if(maxX != minX || maxY != minY)
2647       {
2648          if(y > minY && y < maxY)
2649             mouseOnSelection = true;
2650          else if(y == minY && y == maxY)
2651             mouseOnSelection = (x < maxX && x >= minX);
2652          else if(y == minY)
2653          {
2654             if(y == this.selY)
2655                mouseOnSelection = (x >= this.selX);
2656             else if(y == this.y)
2657                mouseOnSelection = (x >= this.x);
2658          }
2659          else if(y == maxY)
2660          {
2661             if(y == this.selY)
2662                mouseOnSelection = (x < this.selX);
2663             else if(y == this.y)
2664                mouseOnSelection = (x < this.x);
2665          }
2666       }
2667       return mouseOnSelection;
2668    }
2669
2670    void UpdateCaretPosition(bool setCaret)
2671    {
2672       if(line)
2673       {
2674          if(mouseMove || (!overwrite && !style.noCaret))
2675          {
2676             int max = this.mouseMove ? this.dropX : this.x;
2677             int y = this.mouseMove ? this.dropY : this.y;
2678             EditLine line = this.mouseMove ? this.dropLine : this.line;
2679             int c, x = 0;
2680             if(!(style.freeCaret))
2681                max = Min(max, line.count);
2682
2683             if(FontExtent && display)
2684             {
2685                for(c = 0; c < max; )
2686                {
2687                   int len = 1;
2688                   int start = c;
2689                   int w;
2690                   if(c < line.count)
2691                   {
2692                      byte ch = 0;
2693                      int numBytes;
2694                      for(len = 0; c < Min(max, line.count); c += numBytes)
2695                      {
2696                         ch = line.buffer[c];
2697                         numBytes = UTF8_NUM_BYTES(ch);
2698
2699                         if(ch == ' ' || ch == '\t')
2700                         {
2701                            if(!len) c++;
2702                            break;
2703                         }
2704                         len += numBytes;
2705                      }
2706                      if(!len && ch == ' ')
2707                      {
2708                         w = space.w;
2709                         len = 1;
2710                      }
2711                      else if(!len && ch == '\t')
2712                      {
2713                         w = (tabSize * space.w) - (x % (tabSize * space.w));
2714                         len = 1;
2715                      }
2716                      else
2717                         FontExtent(display, this.font, line.buffer + start, len, &w, null); 
2718                   }
2719                   else
2720                   {
2721                      w = space.w;
2722                      c++;
2723                   }
2724                   x += w;
2725                }               
2726             }
2727             if(setCaret)
2728                caretX = x;
2729             caretY = y * this.space.h;
2730             SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
2731          }
2732          else
2733             SetCaret(0, 0, 0);
2734
2735          NotifyCaretMove(master, this, y + 1, x + 1);
2736
2737          SelectionEnables();
2738       }
2739    }
2740
2741    void SelectionEnables()
2742    {
2743       if((x != selX || y != selY) && !selection)
2744       {
2745          if(!style.readOnly)
2746          {
2747             itemEditCut.disabled = false;
2748             itemEditDelete.disabled = false;
2749          }
2750          itemEditCopy.disabled = false;
2751
2752          this.selection = true;
2753       }
2754       else if((x == selX && y == selY) && selection)
2755       {
2756          itemEditCut.disabled = true;
2757          itemEditCopy.disabled = true;
2758          itemEditDelete.disabled = true;
2759
2760          this.selection = false;
2761       }
2762    }
2763
2764    void SetSelectCursor()
2765    {
2766       if(!inactive || !style.noSelect)
2767       {
2768          if(this.mouseMove)
2769             cursor = guiApp.GetCursor(arrow);
2770          else if(this.mouseSelect)
2771             cursor = guiApp.GetCursor(iBeam);
2772          else
2773          {
2774             if(IsMouseOnSelection())
2775                cursor = guiApp.GetCursor(arrow);
2776             else
2777                cursor = guiApp.GetCursor(iBeam);
2778          }
2779       }
2780    }
2781
2782    int AdjustXPosition(EditLine line, int position, bool half, int * px, int max, int sc)
2783    {
2784       int c = sc;
2785       int x = px ? *px : 0;
2786       while(true)
2787       {
2788          int start = c;
2789          int numBytes = 1;
2790          int len = 1;
2791          int w;
2792          if(c < Min(max, line.count))
2793          {
2794             byte ch = 0;
2795             int numBytes;
2796             for(len = 0; c < Min(max, line.count); c += numBytes)
2797             {
2798                ch = line.buffer[c];
2799                numBytes = UTF8_NUM_BYTES(ch);
2800
2801                if(ch == ' ' || ch == '\t')
2802                {
2803                   if(!len) c++;
2804                   break;
2805                }
2806                len += numBytes;
2807             }
2808             if(!len && ch == ' ')
2809             {
2810                w = space.w;
2811                len = 1;
2812             }
2813             else if(!len && ch == '\t')
2814             {
2815                w = (tabSize * space.w) - (x % (tabSize * space.w));
2816                len = 1;
2817             }
2818             else
2819                FontExtent(display, font, line.buffer + start, len, &w, null); 
2820          }
2821          else 
2822          {
2823             if(style.freeCaret && c < max)
2824                w = space.w;
2825             else 
2826             {
2827                if(px) *px = x;
2828                return c;
2829             }
2830             c++;
2831          }
2832          if(x + (((half && len == 1) ? (w / 2) : w)) >= position) 
2833          {
2834             int lastW;
2835             while(len > 0)
2836             {
2837                int a = start + len;
2838                lastW = w;
2839                if(a <= line.count)
2840                   while(a > 0 && !UTF8_IS_FIRST(line.buffer[--a]));
2841                else
2842                   a--;
2843                if(a > start)
2844                   FontExtent(display, font, line.buffer + start, a - start, &w, null);
2845                else
2846                   w = 0;
2847                if(position > x + (half ? ((w + lastW) / 2) : lastW)) break;
2848                len = a - start;
2849             }
2850             if(px) *px = x + w;
2851             return Min(this.maxLineSize - 1, start + len);
2852          }
2853          x += w;
2854       }
2855    }
2856
2857    void SetCursorToViewX()
2858    {
2859       bool selecting = this.x != selX || y != selY;
2860
2861       // Horizontal Adjustment
2862       int x = 0;
2863       int c = AdjustXPosition(line, viewX, false, &x, MAXINT, 0);
2864       if(this.x < c)
2865          this.x = x;
2866       else
2867       { 
2868          c = AdjustXPosition(line, viewX + clientSize.w - 1, false, &x, MAXINT, c);
2869          if(this.x > c)
2870             this.x = c;
2871      }
2872
2873       if(!selecting)
2874       {
2875          selX = this.x;
2876          selY = y;
2877          selLine = line;
2878       }
2879
2880       UpdateCaretPosition(false);
2881
2882       UpdateDirty();
2883       SetSelectCursor();
2884    }
2885
2886    void SetCursorToViewY()
2887    {
2888       int c, numLines;
2889       EditLine oldLine = this.line;
2890       
2891       bool selecting = this.x != this.selX || this.y != this.selY;
2892
2893       numLines = clientSize.h / this.space.h;
2894
2895       // Vertical Adjustment
2896       if(this.viewY > this.y)
2897       {
2898          this.y = this.viewY;
2899          this.line = this.viewLine;
2900       }
2901
2902       if(this.viewY + numLines <=  this.y)
2903       {
2904          EditLine line;
2905
2906          this.y = this.viewY-1;
2907          for(c = 0, line = this.viewLine; line && c<numLines; line = line.next, c++)
2908          {
2909             this.line = line;
2910             this.y++;
2911          }
2912       }
2913
2914       if(this.line != oldLine)
2915       {
2916          this.x = AdjustXPosition(this.line, caretX, true, null, MAXINT, 0);
2917          ComputeColumn();
2918       }
2919
2920       if(!selecting)
2921       {
2922          this.selX = this.x;
2923          this.selY = this.y;
2924          this.selLine = this.line;
2925       }
2926
2927       UpdateCaretPosition(false);
2928
2929       UpdateDirty();
2930       SetSelectCursor();
2931    }
2932
2933    /*
2934    bool SaveFile(char * fileName)
2935    {
2936       File f = eFile_Open(fileName, FO_WRITE);
2937       if(f)
2938       {
2939          Save(f, false);
2940          eWindow_SetModified(false);
2941          eInstance_Delete(f);
2942          return true;
2943       }
2944       return false;
2945    }
2946    */
2947
2948    bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
2949    {
2950       if(line)
2951       {
2952          if(!style.multiLine)
2953          {
2954             x = (active && this.active && !style.readOnly) ? line.count : 0;
2955             selX = 0;
2956             ComputeColumn();
2957             SetViewToCursor(true);
2958             DirtyLine(0);
2959             UpdateDirty();
2960          }
2961          // Update(null);
2962          if(!active && modified)
2963          {
2964             modified = false;
2965             if(!NotifyModified(master, this))
2966             {
2967                modified = true;
2968                *goOnWithActivation = false;
2969             }
2970          }
2971       }
2972       if(!active)
2973       {
2974          ReleaseCapture();
2975          if(timer) timer.Stop();
2976
2977          mouseSelect = false;
2978          wordSelect = false;
2979       }
2980       return true;
2981    }
2982
2983    void AutoSize()
2984    {
2985       //if(created)
2986       {
2987          if(multiLine)
2988          {
2989             // todo: resize width based on largest on-screen-line extent...
2990             int sh = 0;
2991             display.FontExtent(font, " ", 1, null, &sh);
2992             if(sh)
2993             {
2994                int nh = 0;
2995                nh = lineCount * sh + 2;
2996                size.h = nh < minClientSize.h ? minClientSize.h : nh;
2997             }
2998          }
2999          else
3000          {
3001             int tw = 0;
3002             int sh = 0;
3003             int nw = 0;
3004             int nh = 0;
3005             MinMaxValue dw = 0;
3006             MinMaxValue dh = 0;
3007             int len = line ? strlen(line.text) : 0;
3008             GetDecorationsSize(&dw, &dh);
3009             display.FontExtent(font, " ", 1, null, &sh);
3010             if(len) display.FontExtent(font, line.text, len, &tw, null);
3011             nw = dw+tw+12;
3012             if(nw < minClientSize.w) nw = minClientSize.w;
3013             nh = dh+sh+4;
3014             if(nh < minClientSize.h) nh = minClientSize.h;
3015             size = { nw, nh };
3016          }
3017       }
3018    }
3019
3020    bool OnResizing(int *w, int *h)
3021    {
3022       if(!*h)
3023          *h = space.h + 2;
3024       if(!*w)
3025          //*w = 80;
3026          *w = space.h * 80 / 14;
3027       return true;
3028    }
3029
3030    void OnResize(int w, int h)
3031    {
3032    /*
3033       if(!hasHorzScroll && !hasVertScroll && viewLine)
3034          SetViewToCursor(true);
3035    */
3036       //if(!hasHorzScroll && !hasVertScroll && viewLine)
3037       if(viewLine)
3038          SetViewToCursor(true);
3039       //else
3040    //     FixScrollArea();
3041    }
3042
3043    bool OnMiddleButtonDown(int x, int y, Modifiers mods)
3044    {
3045       if(style.readOnly) return true;
3046       // We really shouldn't be pasting here, Unix middle button paste is for the selection (not the clipboard), good for the terminal
3047       // Middle button already serves as a dragger as well.
3048       // Paste();
3049       return true;
3050    }
3051
3052    bool OnRightButtonDown(int x, int y, Modifiers mods)
3053    {
3054       this.rightButtonDown = true;
3055       // Copy();
3056       return true;
3057    }
3058
3059    bool OnRightButtonUp(int x, int y, Modifiers mods)
3060    {
3061       // Context Menu
3062       if(!parent.inactive && rightButtonDown)
3063       {
3064          PopupMenu popup;
3065          Menu contextMenu { };
3066
3067          MenuItem { contextMenu, $"Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
3068          MenuItem { contextMenu, $"Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
3069          MenuItem { contextMenu, $"Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
3070          MenuItem { contextMenu, $"Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
3071          MenuDivider { contextMenu };
3072          MenuItem { contextMenu, $"Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
3073
3074          popup = PopupMenu { master = this, menu = contextMenu,
3075    /*
3076             nonClient = true, interim = false, parent = parent, 
3077             position = { x + clientStart.x + parent.clientStart.x + position.x, y + cientStart.y + parent.sy + clientStart.y + position.y };
3078    */
3079             position = { x + clientStart.x + absPosition.x - guiApp.desktop.position.x, y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
3080          };
3081          popup.Create();
3082       }
3083       rightButtonDown = false;
3084       return true;
3085    }
3086
3087    bool OnLeftButtonDown(int mx, int my, Modifiers mods)
3088    {
3089       int x,y;
3090       EditLine line;
3091
3092       if(style.noSelect) return true;
3093
3094       // Should we have a separate 'selectOnActivate' style?
3095       if(!mods.isActivate || (style.readOnly && style.multiLine))
3096       {
3097          Capture();
3098          mouseSelect = true;
3099       }
3100
3101       mouseX = mx - XOFFSET;
3102       mouseY = my;
3103
3104       FindMouse(mouseX, mouseY, &x, &y, &line, true);
3105 #ifdef _DEBUG
3106       //PrintLn("OnLeftButtonDown: ", x, ", ", y);
3107 #endif
3108       if(!style.readOnly)
3109       {
3110          if(wordSelect)
3111             mouseMove = false;
3112          else if(IsMouseOnSelection() && !mods.isActivate)
3113          {
3114             DirtyLine(this.y);
3115             mouseMove = true;
3116             dropX = x;
3117             dropY = y;
3118             dropLine = line;
3119          }
3120       }
3121
3122       if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
3123       {
3124          if(mods.shift && !mods.isActivate)
3125          {
3126             this.x = x;
3127             this.y = y;
3128             this.line = line;
3129             DirtyAll();
3130          }
3131          else
3132          {
3133             SelDirty();
3134             DirtyLine(this.y);
3135             this.x = x;
3136             this.y = y;
3137             this.line = line;
3138             DirtyLine(this.y);
3139             this.selLine = this.line;
3140             this.selX = this.x;
3141             this.selY = this.y;
3142             //Deselect();
3143          }
3144          ComputeColumn();
3145       }
3146       
3147       UpdateDirty();
3148       UpdateCaretPosition(true);
3149       return true;
3150    }
3151
3152    bool OnLeftButtonUp(int x, int y, Modifiers mods)
3153    {
3154       timer.Stop();
3155
3156       mouseSelect = false;
3157       wordSelect = false;
3158       
3159       x -= XOFFSET;
3160       
3161       ReleaseCapture();
3162       if(!style.readOnly)
3163       {
3164          if(mouseMove)
3165          {
3166             EditLine line;
3167             FindMouse(mouseX, mouseY, &x, &y, &line, true);
3168 #ifdef _DEBUG
3169             //PrintLn("MouseMove: ", x, ", ", y);
3170 #endif
3171             dropX = x;
3172             dropY = y;
3173             dropLine = line;
3174
3175             mouseMove = IsMouseOnSelection();
3176
3177             if(!mouseMove)
3178             {
3179                int size = SelSize();
3180                if(size)
3181                {
3182                   char * text = new char[size+1];
3183                   if(text)
3184                   {
3185                      int moveX = 0;
3186                      GetSel(text, false);
3187
3188                      if(Max(selY, this.y) == dropY)
3189                      {
3190                         if(this.x > selX)
3191                         {
3192                            if(this.dropX > this.selX)
3193                               moveX = this.x - this.selX;
3194                         }
3195                         else
3196                         {
3197                            if(this.dropX > this.x)
3198                               moveX = this.selX - this.x;
3199                         }
3200                      }
3201                      DelSel(null);
3202                      this.dropX -= moveX;
3203                      this.selX = this.x = this.dropX;
3204                      this.selY = this.y = this.dropY;
3205                      this.selLine = this.line = this.dropLine;
3206                      AddS(text);
3207                      SetViewToCursor(true);
3208                      delete text;
3209                      Modified();
3210 #ifdef _DEBUG
3211                    /*  {
3212                         byte * c = ((EditBox)this).multiLineContents, * c2;
3213                         int l1 = c ? strlen(c) : 0, l2;
3214                         Undo();
3215                         Redo();
3216                         c2 = ((EditBox)this).multiLineContents;
3217                         l2 = c2 ? strlen(c2) : 0;
3218                         if(l1 != l2 || (l1 && strcmp(c, c2)))
3219                         {
3220                            PrintLn("Fail!");
3221                         }
3222                         delete c;
3223                      }
3224                      */
3225 #endif
3226                   }
3227                }
3228             }
3229             else
3230             {
3231                SelDirty();
3232                DirtyLine(this.y);
3233                this.x = x;
3234                this.y = y;
3235                this.line = line;
3236                ComputeColumn();
3237                DirtyLine(this.y);
3238                Deselect();
3239                UpdateDirty();
3240             }
3241          }
3242          else
3243          {
3244             EditLine line;
3245             mouseX = x;
3246             mouseY = y;
3247
3248             FindMouse(mouseX, mouseY, &x, &y, &line, true);
3249 #ifdef _DEBUG
3250             //PrintLn("Dropped: ", x, ", ", y);
3251 #endif
3252             NotifyDropped(master, this, x, y);
3253          }
3254       }
3255       mouseMove = false;
3256       return true;
3257    }
3258
3259    bool OnMouseMove(int mx, int my, Modifiers mods)
3260    {
3261       int x,y;
3262       EditLine line;
3263       bool needScroll;
3264
3265       if(mods != -1 && mods.isSideEffect) 
3266       { 
3267          SetSelectCursor(); 
3268          return true; 
3269       }
3270       if(style.noSelect) return true;
3271       if(wordSelect) return true;
3272       mouseX = mx - XOFFSET;
3273       mouseY = my;
3274
3275       needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
3276
3277       if(this.mouseMove || this.mouseSelect)
3278       {
3279          if(!needScroll)
3280             timer.Stop();
3281          else 
3282          {
3283             if(needScroll)
3284                timer.Start();
3285             if(mods != -1 && 
3286                ((style.hScroll) || (style.vScroll)))
3287                return true;
3288          }
3289       }
3290
3291       if(this.mouseMove)
3292       {
3293          DirtyLine(this.dropY);
3294          this.dropX = x;
3295          this.dropY = y;
3296          DirtyLine(this.dropY);
3297          this.dropLine = line;
3298          SetViewToCursor(true);
3299 #ifdef _DEBUG
3300          //PrintLn("MouseMove: ", "dropX = ", x, ", dropY = ", y);
3301 #endif
3302       }
3303       else if(this.mouseSelect)
3304       {
3305          DirtyLine(this.selY);
3306          DirtyLine(this.y);
3307          this.x = x;
3308          this.y = y;
3309          ComputeColumn();
3310          DirtyLine(this.y);
3311          this.line = line;
3312          SetViewToCursor(true);
3313          UpdateDirty();
3314 #ifdef _DEBUG
3315          //PrintLn("MouseSelect: ", "x = ", x, ", y = ", y);
3316 #endif
3317       }
3318       SetSelectCursor();
3319       return true;
3320    }
3321
3322    bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
3323    {
3324       int x,y;
3325       EditLine line;
3326 #ifdef _DEBUG
3327       //PrintLn("OnLeftDoubleClick: ", mx, ", ", my, ", mods = ", mods);
3328 #endif
3329       mx -= XOFFSET;
3330
3331       if(style.noSelect) return true;
3332       FindMouse(mx, my, &x, &y, &line, false);
3333       if(!NotifyDoubleClick(master, this, line, mods))
3334          return false;
3335       if(x < line.count)
3336       {
3337          int c;
3338          int start = -1;
3339          int numBytes;
3340          for(c = x; c >= 0; c--)
3341          {
3342             unichar ch;
3343             while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
3344             ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3345             if(!IS_ALUNDER(ch))
3346                break;
3347             start = c;
3348          }
3349          if(start != -1)
3350          {
3351             for(c = start; c<line.count; c += numBytes)
3352             {
3353                unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3354                if(!IS_ALUNDER(ch))
3355                   break;
3356             }
3357             SelDirty();
3358             DirtyLine(this.y);
3359             this.y = y;
3360             DirtyLine(this.y);
3361             this.selX = start;
3362             this.x = c;
3363             ComputeColumn();
3364             this.line = this.selLine = line;
3365             this.wordSelect = (c != start);
3366             UpdateDirty();
3367          }
3368       }
3369       return true;
3370    }
3371
3372    bool OnPostCreate()
3373    {
3374       /*if(isDocument)
3375       {
3376          Menu fileMenu { menu, "File", F };
3377          saveDialog = fileDialog;
3378          MenuItem { fileMenu, $"Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
3379          MenuItem { fileMenu, $"Save As...", A, NotifySelect = MenuFileSaveAs };
3380       }*/
3381       if(style.autoSize) AutoSize();
3382       return true;
3383    }
3384
3385    void ComputeFont()
3386    {
3387       if(FontExtent)
3388       {
3389          FontExtent(display, font, " ", 1, (int *)&space.w, (int *)&space.h);
3390          FontExtent(display, font, "W", 1, (int *)&large.w, (int *)&large.h);
3391
3392          space.w = Max(space.w, 1);
3393          large.w = Max(large.w, 1);
3394          space.h = Max(space.h, 1);
3395          large.h = Max(large.h, 1);
3396
3397          {
3398             EditLine line;
3399             for(line = lines.first; line; line = line.next)
3400                ComputeLength(line);
3401             FindMaxLine();
3402          }
3403
3404          if(viewLine)
3405             SetViewToCursor(true);
3406
3407          FixScrollArea();
3408
3409          Update(null);
3410       }
3411    }
3412
3413    bool OnLoadGraphics()
3414    {
3415       FontExtent = Display::FontExtent;
3416       font = fontObject;
3417       ComputeFont();
3418       // UpdateCaretPosition(true);
3419       return true;
3420    }
3421
3422    void OnUnloadGraphics()
3423    {
3424       this.font = null;
3425    }
3426
3427    bool OnKeyHit(Key key, unichar ch)
3428    {
3429       bool shift = (key.shift) ? true : false;
3430 #ifdef _DEBUG
3431       //PrintLn("OnKeyHit: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
3432 #endif
3433       if(!ch && !key.alt && !key.ctrl)
3434       {
3435          key.code = (SmartKey)key.code;
3436       }
3437
3438       switch(key.code) //(ch || key.alt || key.ctrl) ? key.code : (Key)(SmartKey)key.code)
3439       {
3440          case backSpace:
3441             if(style.readOnly) break;
3442             if(style.stuckCaret) GoToEnd(true);
3443             if(!(style.freeCaret))
3444             {
3445                this.x = Min(this.x, this.line.count);
3446             }
3447             if(key.ctrl)
3448             {
3449                int y;
3450                bool done = false;
3451                EditLine line = this.line;
3452                int c;
3453                for(y = this.y; y>= 0; y--)
3454                {
3455                   c = (y == this.y) ? (this.x-1) : line.count-1;
3456
3457                   // Slow down when going on lines...
3458                   if(y != this.y) break;
3459
3460                   for(; c>=0; c--)
3461                   {
3462                      //if(this.line.buffer[c] != '\t' && this.line.buffer[c] != ' ')
3463                      if(IS_ALUNDER(line.buffer[c]))
3464                         break;
3465                   }
3466                   for(; c>=0; c--)
3467                   {
3468                      if(!IS_ALUNDER(line.buffer[c]))
3469                      {
3470                      //if(this.line.buffer[c] == '\t' || this.line.buffer[c] == ' ')
3471                         done = true;
3472                         break;
3473                      }
3474                   }
3475                   if(done)
3476                      break;
3477                   if(line.prev)
3478                      line = line.prev;
3479                   else
3480                      break;
3481                }
3482                //if(this.x != 0)
3483                {
3484                   DelCh(line,y,c+1,this.line,this.y,this.x, true);
3485                   this.x = this.selX = Min(c+1, line.count);
3486                   this.y = this.selY = y;
3487                   this.line = this.selLine = line;
3488                   SetViewToCursor(true);
3489                }
3490                ComputeColumn();
3491             }
3492             else
3493             {
3494                BackSpace();
3495             }
3496             return false;
3497             //break;
3498          case del:
3499             if(style.readOnly) break;
3500             if(style.stuckCaret) break;
3501
3502             if(shift)
3503             {
3504                Cut();
3505             }
3506             else
3507             {
3508                // Delete selection
3509                if(this.line != this.selLine || this.x != this.selX)
3510                {
3511                   DelSel(null);
3512                   SetViewToCursor(true);
3513                   Modified();
3514                }
3515                // Delete word
3516                else if(key.ctrl)
3517                {
3518                   if(this.x < this.line.count)
3519                   {
3520                      int i;
3521                      int length;
3522                      for(i = this.x; i < this.line.count; i++)
3523                      {
3524                         if(!IS_ALUNDER(this.line.buffer[i]))
3525                            break;
3526                      }
3527                      
3528                      for(; i < this.line.count; i++)
3529                      {
3530                         //Delete trailing whitespace
3531                         if(IS_ALUNDER(this.line.buffer[i]))
3532                            break;
3533                      }
3534                      DelCh(this.line, this.y, this.x, this.line, this.y, i, false);
3535                      SetViewToCursor(true);
3536                      Modified();
3537                   }
3538                   else if(this.line.next)
3539                   {
3540                      DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3541                      SetViewToCursor(true);
3542                      Modified();
3543                   }
3544                }
3545                else 
3546                {
3547                   if(!(style.freeCaret))
3548                   {
3549                      this.selX = this.x = Min(this.x, this.line.count);
3550                      ComputeColumn();
3551                   }
3552                   if(this.x < this.line.count)
3553                   {
3554                      DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3555                   }
3556                   else if(this.line.next)
3557                   {
3558                      DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3559                   }
3560                   SetViewToCursor(true);
3561                   Modified();
3562                }
3563             }
3564             return false;
3565             //break;
3566          case enter:
3567          case keyPadEnter:
3568          {
3569             if(!key.alt && !key.ctrl)
3570             {
3571                int c;
3572                int position = 0;
3573                bool stuffAfter = false;
3574                char * addString;
3575                int len = 0;
3576
3577                if(style.stuckCaret) GoToEnd(true);
3578                if(style.readOnly) break;
3579                if(!(style.multiLine)) break;
3580
3581                for(c = 0; c<this.line.count && c<this.x; c++)
3582                {
3583                   if(this.line.buffer[c] == '\t')
3584                      position += this.tabSize - (position % this.tabSize);
3585                   else if(this.line.buffer[c] == ' ')
3586                      position++;
3587                   else
3588                      break;
3589                }
3590                if(!line.count)
3591                   position = x;
3592
3593                if(this.x < this.line.count)
3594                   stuffAfter = true;
3595                   
3596                //If last character is a { indent one tab
3597                if(this.line.buffer[this.x - 1] == '{')
3598                {
3599                   //Except if the next non space character is a }
3600                   bool indent = false;
3601                   int i;
3602                   for(i = this.x; i < this.line.size; i++)
3603                      if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
3604                      {
3605                         if(this.line.buffer[i] != '}')
3606                            indent = true;
3607                         break;
3608                      }
3609                   if(indent)
3610                      position += this.tabSize;
3611                }
3612
3613                addString = new char[position + 2];
3614                addString[len++] = '\n';
3615                addString[len] = '\0';
3616
3617                if(stuffAfter || !style.freeCaret)
3618                {
3619                   for(c = 0; c<position; )
3620                   {
3621                      if(style.useTab && c + this.tabSize <= position)
3622                      {
3623                         addString[len++] = '\t';
3624                         c += this.tabSize;
3625                      }
3626                      else
3627                      {
3628                         addString[len++] = ' ';
3629                         c++;
3630                      }
3631                   }
3632                   addString[len] = '\0';
3633                }
3634                if(AddS(addString))
3635                {
3636                   if(!stuffAfter && style.freeCaret)
3637                   {
3638                      this.x = this.selX = position;
3639                      ComputeColumn();
3640                   }
3641                   FindMaxLine();
3642                   SetViewToCursor(true);
3643                   Modified();
3644                }
3645                delete addString;
3646                return false;
3647             }
3648             break;
3649          }
3650          case left:
3651          {
3652             if(style.stuckCaret) break;
3653             if(!(style.freeCaret))
3654             {
3655                this.x = Min(this.x, this.line.count);
3656                this.selX = Min(this.selX, this.selLine.count);
3657                ComputeColumn();
3658             }
3659             if(!shift) SelDirty();
3660             if(key.ctrl)
3661             {
3662                bool foundAlpha = false;
3663                bool found = false;
3664                int y = this.y;
3665                EditLine line, lastLine;
3666                int lastC, lastY;
3667
3668                for(line = this.line; (line && !found); line = line.prev, y--)
3669                {
3670                   int start;
3671                   int c;
3672
3673                   if(this.x == 0 && line != this.line)
3674                   {
3675                      foundAlpha = true;
3676                      lastC = line.count;
3677                      lastY = y;
3678                      lastLine = line;
3679                      break;
3680                   }
3681
3682                   if(line == this.line) start = this.x -1; else start = line.count-1;
3683                   start = Min(start, line.count-1);
3684
3685                   for(c = start; c >= 0;)
3686                   {
3687                      int numBytes;
3688                      unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3689                      if(IS_ALUNDER(ch))
3690                      {
3691                         foundAlpha = true;
3692                         lastC = c;
3693                         lastY = y;
3694                         lastLine = line;
3695                      }
3696                      else
3697                      {
3698                         if(foundAlpha)
3699                         {
3700                            found = true;
3701                            break;
3702                         }
3703                      }
3704                      while(--c)
3705                      {
3706                         byte ch = line.buffer[c];
3707                         if(UTF8_IS_FIRST(ch)) break;
3708                      } 
3709
3710                   }
3711                   // No next word found, 
3712                   if(!found && ( this.x > 0 || (!line.count && this.x)))
3713                   {
3714                      foundAlpha = true;
3715                      lastC = 0;
3716                      lastY = y;
3717                      lastLine = line;
3718                      break;
3719                   }
3720                }
3721                if(foundAlpha)
3722                {
3723                   DirtyLine(this.y);
3724                   this.x = lastC;
3725                   this.y = lastY;
3726                   this.line = lastLine;
3727                   DirtyLine(this.y);
3728                   ComputeColumn();
3729                }
3730             }
3731             else
3732             {
3733                if(x > 0)
3734                {
3735                   if(x <= line.count)
3736                   {
3737                      byte * buffer = line.buffer;
3738                      while(--x)
3739                      {
3740                         byte ch = buffer[x];
3741                         if(UTF8_IS_FIRST(ch)) break;
3742                      } 
3743                   }
3744                   else
3745                      x--;
3746                   DirtyLine(y);
3747                }
3748                else if(line.prev)
3749                {
3750                   line = line.prev;
3751                   DirtyLine(y);
3752                   x = line.count;
3753                   y--;
3754                   DirtyLine(y);
3755                }
3756                ComputeColumn();
3757             }
3758             if(!shift) Deselect();
3759             SetViewToCursor(true);
3760             //break;
3761             return false;
3762          }
3763          case right:
3764          {
3765             if(style.stuckCaret) break;
3766             if(!(style.freeCaret))
3767             {
3768                this.x = Min(this.x, this.line.count);
3769                this.selX = Min(this.selX, this.selLine.count);
3770                ComputeColumn();
3771             }
3772             if(!shift) SelDirty();
3773             if(!shift && (this.x != this.selX || this.y != this.selY));
3774             else if(key.ctrl)
3775             {
3776                bool onAChar = false;
3777                if(this.selX != this.x || this.selY != this.y)
3778                   onAChar = true;
3779                if(this.x<this.line.count)
3780                   if(this.line.buffer[this.x] != '\t' && this.line.buffer[this.x] !=' ')
3781                      onAChar = true;
3782                if(key.shift && onAChar &&
3783                   ((this.y > this.selY)||((this.selY == this.y)&&(this.x >= this.selX))))
3784                {
3785                   bool foundAlpha = false;
3786                   bool found = false;
3787                   EditLine line, lastLine;
3788                   int y = this.y;
3789                   int lastC, lastY, lastNumBytes;
3790                   
3791                   for(line = this.line; (line && !found); line = line.next, y++)
3792                   {
3793                      int start = (line == this.line) ? this.x : 0;
3794                      int c;
3795                      int numBytes;
3796                      for(c = start; c < line.count; c += numBytes)
3797                      {
3798                         unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3799                         if(IS_ALUNDER(ch))
3800                         {
3801                            foundAlpha = true;
3802                            lastC = c;
3803                            lastNumBytes = numBytes;
3804                            lastY = y;
3805                            lastLine = line;
3806                         }
3807                         else if(foundAlpha)
3808                         {
3809                            found = true;
3810                            break;
3811                         }
3812                      }
3813                      if(!found && (c != this.x || line != this.line))
3814                      {
3815                         found = true;
3816                         lastLine = line;
3817                         lastC = line.count;
3818                         lastNumBytes = 0;
3819                         lastY = y;
3820                         break;
3821                      }
3822                   }  
3823                   if(found)
3824                   {
3825                      DirtyLine(this.y);
3826                      this.x = lastC + lastNumBytes;
3827                      this.y = lastY;
3828                      this.line = lastLine;
3829                      DirtyLine(this.y);
3830                      ComputeColumn();
3831                   }
3832                }
3833                else
3834                {
3835                   bool foundAlpha = false;
3836                   bool found = false;
3837                   EditLine line;
3838                   int y = this.y;
3839
3840                   for(line = this.line; (line && !found); line = line.next, y++)
3841                   {
3842                      int start = (line == this.line) ? this.x : 0;
3843                      int c;
3844                      int numBytes;
3845                      for(c = start; c < line.count; c += numBytes)
3846                      {
3847                         unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3848                         if(!IS_ALUNDER(ch))
3849                            foundAlpha = true;
3850                         else if(foundAlpha)
3851                         {
3852                            found = true;
3853                            DirtyLine(this.y);
3854                            this.x = c;
3855                            this.y = y;
3856                            this.line = line;
3857                            DirtyLine(this.y);
3858                            ComputeColumn();
3859                            break;
3860                         }
3861                      }
3862                      // No next word found, 
3863                      if(!found && (c != this.x || line != this.line))
3864                      {
3865                         found = true;
3866                         DirtyLine(this.y);
3867                         this.x = line.count;
3868                         this.y = y;
3869                         this.line = line;
3870                         DirtyLine(this.y);
3871                         ComputeColumn();
3872                      }
3873                      foundAlpha = true;
3874                   }
3875                }
3876             }
3877             else
3878             {
3879                if(x < line.count || (style.freeCaret && line.count < maxLineSize))
3880                {
3881                   if(x < line.count)
3882                   {
3883                      byte * buffer = line.buffer;
3884                      while(++x)
3885                      {
3886                         byte ch = buffer[x];
3887                         if(UTF8_IS_FIRST(ch)) break;
3888                      } 
3889                   }
3890                   else
3891                      x++;
3892                   ComputeColumn();
3893                   DirtyLine(y);
3894                }
3895                else
3896                {
3897                   if(line.next)
3898                   {
3899                      DirtyLine(y);
3900                      line = line.next;
3901                      y++;
3902                      x = 0;
3903                      col = 0;
3904                      DirtyLine(y);
3905                   }
3906                }
3907             }
3908             if(!shift) Deselect();
3909             SetViewToCursor(true);
3910             // break;
3911             return false;
3912          }
3913          case up:
3914             if(key.ctrl)
3915             {
3916                if(!style.vScroll || hasVertScroll) break;
3917                LineUp();
3918                return false;
3919             }
3920             else
3921             {
3922                if(style.stuckCaret) break;
3923                
3924                if(!shift) SelDirty();
3925                DirtyLine(this.y);
3926
3927                if(style.wrap)
3928                {
3929                }
3930                else if(line.prev)
3931                {
3932                   line = line.prev;
3933                   this.y--;
3934                   this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
3935                }
3936                
3937                DirtyLine(this.y);
3938                if(!shift) Deselect();
3939                ComputeColumn();
3940                SetViewToCursor(false);
3941
3942                /*
3943                if(caretY == this.y * space.h)
3944                {
3945                   if(line.prev)
3946                   {
3947                      line = line.prev;
3948                      this.y--;
3949                      if(!style.freeCaret)
3950                         this.x = Min(this.x, line.count);
3951                      caretY = MAXINT;
3952                   }
3953                   else
3954                      return false;
3955                }
3956
3957                {
3958                   int th = space.h;
3959                   int textPos = 0;
3960                   int sx = 0, sy = this.y * space.h;
3961                   char * text = line.text;
3962                   int maxW = clientSize.w - sx;
3963                   display.FontExtent(font, " ", 1, null, &th);
3964
3965                   do
3966                   {
3967                      int startPos = textPos;
3968                      int width = 0;
3969                      int x = 0;
3970                      bool lineComplete = false;
3971
3972                      if(!style.wrap && caretY == MAXINT)
3973                      {
3974                         caretY = sy + th;
3975                         //textPos = line.count;
3976                         //lineComplete = true;
3977                      }
3978
3979                      for(; (style.freeCaret || textPos < line.count) && !lineComplete;)
3980                      {
3981                         int w = 0;
3982                         int len;
3983                         char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
3984
3985                         if(nextSpace)
3986                            len = (nextSpace - (text + textPos));
3987                         else
3988                            len = line.count - textPos;
3989                         
3990                         if(textPos < line.count)
3991                         {
3992                            display.FontExtent(font, text + textPos, len, &w, null);
3993                         }
3994                         if(nextSpace) { w += space.w; len++; }
3995
3996                         if(style.wrap && x + width + w > maxW && x > 0)
3997                         {
3998                            lineComplete = true;
3999                            break;
4000                         }
4001                         textPos += len;
4002                         width += w;
4003                         if(nextSpace)
4004                         {
4005                            x += width;
4006                            width = 0;
4007                         }
4008                         if((!style.freeCaret && textPos >= line.count) || (sy == caretY - th && caretX <= x + width + sx))
4009                         {
4010                            x += width;
4011                            this.x = textPos;
4012                            while(this.x > 0 && x + sx > caretX && this.x > startPos)
4013                            {
4014                               int len;
4015                               if(this.x > line.count)
4016                                  this.x--;
4017                               else
4018                                  while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4019                               len = this.x - startPos;
4020                               display.FontExtent(font, text + startPos, len, &x, null);
4021                            }
4022
4023                            DirtyLine(this.y);
4024                            if(!shift) Deselect();
4025                            ComputeColumn();
4026                            SetViewToCursor(false);
4027                            return false;
4028                         }
4029                      }
4030                      if(sy == caretY - th || textPos >= line.count)
4031                      {
4032                         if(textPos >= line.count)
4033                         {
4034                            int c = textPos - 1;
4035                            while(c > 0 && text[c] == ' ') c--;
4036                            this.x = c + 1;
4037                         }
4038                         else
4039                            this.x = line.count;
4040
4041                         DirtyLine(this.y);
4042                         if(!shift) Deselect();
4043                         ComputeColumn();
4044                         SetViewToCursor(false);
4045                         return false;
4046                      }
4047                      sy += th;
4048                      sx = 0;
4049                   } while(textPos < line.count);
4050
4051                   DirtyLine(this.y);
4052                   if(!shift) Deselect();
4053                   ComputeColumn();
4054                   SetViewToCursor(false);
4055                   return false;
4056                }
4057                */
4058                
4059                // PREVIOUS CODE
4060                /*
4061                if(this.line.prev)
4062                {
4063                   int x = AdjustXPosition(this.line, this.line.prev, true, null, MAXINT, 0);
4064                   if(!shift) SelDirty();
4065                   this.line = this.line.prev;
4066                   DirtyLine(this.y);
4067                   this.y--;
4068
4069                   DirtyLine(this.y);
4070                   this.x = x;
4071                   if(!shift) Deselect();
4072
4073                   ComputeColumn();
4074
4075                   SetViewToCursor(false);
4076                }
4077                */
4078
4079             }
4080             // break;
4081             return style.multiLine ? false : true;
4082          case down:
4083             if(key.ctrl)
4084             {
4085                if(!style.vScroll || hasVertScroll)
4086                   break;
4087                LineDown();
4088                return false;
4089             }
4090             else
4091             {
4092                if(style.stuckCaret) break;
4093                {
4094                   int th = space.h;
4095                   int textPos = 0;
4096                   int sx = 0, sy = this.y * this.space.h;
4097                   int maxW = clientSize.w - sx;
4098                   char * text = line.buffer;
4099
4100                   if(!shift) SelDirty();
4101                   DirtyLine(this.y);
4102                   
4103                   if(style.wrap)
4104                   {
4105                      /*
4106                      if(AdjustXPosition(line, maxW, this.x, line.count, true, null, MAXINT, 0) <= line.count)
4107                      {
4108
4109                      }
4110                      */
4111                   }
4112                   else if(line.next)
4113                   {
4114                      line = line.next;
4115                      this.y++;
4116                      this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
4117                   }
4118
4119                   if(!shift) Deselect();
4120                   ComputeColumn();
4121                   if(this.selX != this.x || this.selY != this.y)
4122                      DirtyLine(this.y);
4123                   SetViewToCursor(false);
4124                   
4125                   /*
4126                   while(!textPos || (style.freeCaret || textPos<line.count))
4127                   {
4128                      int startPos = textPos;
4129                      int width = 0;
4130                      int x = 0;
4131                      bool lineComplete = false;
4132                      if(!style.wrap && sy <= caretY)
4133                      {
4134                         textPos = line.count;
4135                         lineComplete = true;
4136                      }
4137                      for(; (style.freeCaret || textPos<line.count) && !lineComplete;)
4138                      {
4139                         int w = 0;
4140                         int len;
4141                         char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4142
4143                         if(nextSpace)
4144                            len = (nextSpace - (text + textPos));
4145                         else
4146                            len = line.count - textPos;
4147                         
4148                         if(textPos < line.count)
4149                         {
4150                            display.FontExtent(font, text + textPos, len, &w, &th);
4151                         }
4152                         if(nextSpace) { w += space.w; len++; }
4153                         if(style.wrap && x + width + w > maxW && x > 0)
4154                         {
4155                            lineComplete = true;
4156                            break;
4157                         }
4158                         textPos += len;
4159                         width += w;
4160                         if(nextSpace)
4161                         {
4162                            x += width;
4163                            width = 0;
4164                         }
4165                         if(sy > caretY && ((!style.freeCaret && textPos >= line.count) || caretX <= x + width + sx))
4166                         {
4167                            this.x = textPos;
4168                            x += width;
4169                            while(this.x > 0 && x + sx > caretX && textPos > startPos)
4170                            {
4171                               int len;
4172                               if(this.x > line.count)
4173                                  this.x--;
4174                               else
4175                                  while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4176                                  
4177                               len = this.x - startPos;
4178                               display.FontExtent(font, text + startPos, len, &x, null);
4179                            }
4180                            
4181                            if(!shift) Deselect();
4182                            ComputeColumn();
4183
4184                            SetViewToCursor(false);
4185                            return false;
4186                         }
4187                      }
4188                      if(sy > caretY)
4189                      {
4190                         this.x = line.count;
4191
4192                         DirtyLine(this.y);
4193                         if(!shift) Deselect();
4194                         ComputeColumn();
4195
4196                         SetViewToCursor(false);
4197                         return false;
4198                      } 
4199                      else if(textPos >= line.count && line.next)
4200                      {
4201                         startPos = 0;
4202                         textPos = 0;
4203                         line = line.next;
4204                         this.y++;
4205                         sy = this.y * this.space.h;
4206                         sx = 0; //textBlock.startX;
4207                         text = line.buffer;
4208                      }
4209                      else
4210                      {
4211                         sy += th;
4212                         sx = 0; //textBlock.startX;
4213                      }
4214                   }
4215                   */
4216                   /*
4217                   if(line.next)
4218                   {
4219                      line = line.next;
4220                      this.x = Min(this.x, line.count);
4221                      //int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4222                      this.y++;
4223                      if(!shift) Deselect();
4224                      ComputeColumn();
4225
4226                      if(this.selX != this.x || this.selY != this.y)
4227                         DirtyLine(this.y);
4228
4229                      SetViewToCursor(false);
4230                   }
4231                   */
4232                }
4233                /* PREVIOUS CODE
4234                if(this.line.next)
4235                {
4236                   int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4237                   if(!shift) SelDirty();
4238                   this.line = this.line.next;
4239                   DirtyLine(this.y);
4240                   this.y++;
4241                   this.x = x;
4242                   if(!shift) Deselect();
4243                   ComputeColumn();
4244
4245                   if(this.selX != this.x || this.selY != this.y)
4246                      DirtyLine(this.y);
4247
4248                   SetViewToCursor();
4249                }
4250                */
4251             }
4252             // break;
4253             return style.multiLine ? false : true;
4254          case home:
4255          {
4256             if(style.stuckCaret) break;
4257             if(!(style.freeCaret))
4258                this.selX = Min(this.selX, this.selLine.count);
4259
4260             if(!shift) SelDirty();
4261             if(key.ctrl)
4262             {
4263                this.line = this.lines.first;
4264                if(this.y != 0 || this.x != 0)
4265                   DirtyAll();
4266                this.y = 0;
4267                this.x = 0;
4268                this.col = 0;
4269             }
4270             else
4271             {
4272                if(style.smartHome)
4273                {
4274                   EditLine line = this.line;
4275                   int c;
4276                   for(c=0; line.buffer[c]; c++)
4277                      if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
4278                         break;
4279                   if(c != 0 || this.x)
4280                      DirtyLine(this.y);
4281                   if(this.x != c) 
4282                      this.x = c;
4283                   else
4284                      this.x = 0;
4285                }
4286                else
4287                {
4288                   /*if(this.x != 0)
4289                      DirtyLine(this.y);*/
4290                   this.x = 0;
4291                }
4292                ComputeColumn();
4293             }
4294             if(!shift) Deselect();
4295             SetViewToCursor(true);
4296             //break;
4297             return false;
4298          }
4299          case end:
4300          {
4301             if(style.stuckCaret) break;
4302             if(!(style.freeCaret))
4303                this.selX = Min(this.selX, this.selLine.count);
4304
4305             if(!shift) SelDirty();
4306             if(key.ctrl)
4307             {
4308                GoToEnd(false);
4309             }
4310             else if(this.x != this.line.count)
4311             {
4312                this.x = this.line.count;
4313                //DirtyLine(this.y);
4314                ComputeColumn();
4315             }
4316             if(!shift) Deselect();
4317             SetViewToCursor(true);
4318             //break;
4319             return false;
4320          }
4321          case tab:
4322             if(style.tabKey && !key.ctrl)
4323             {
4324                if(this.selY != this.y && style.tabSel)
4325                {
4326                   EditLine firstLine, lastLine;
4327                   EditLine line;
4328                   int y, x;
4329
4330                   // Do multi line selection tabbing here
4331                   if(this.selY < this.y)
4332                   {
4333                      firstLine = this.selLine;
4334                      lastLine = this.line;
4335                      y = this.selY;
4336                      x = this.x;
4337                   }
4338                   else
4339                   {
4340                      // Selecting going up
4341                      firstLine = this.line;
4342                      lastLine = this.selLine;
4343                      y = this.y;
4344                      x = this.selX;
4345                   }
4346                   ComputeColumn();
4347                   if(shift)
4348                   {
4349                      for(line = firstLine; line; line = line.next, y++)
4350                      {
4351                         if(line != lastLine || x)
4352                         {
4353                            int c;
4354                            int lastC = 0;
4355                            BufferLocation before = { line, y, 0 }, after = { line, y, 0 };
4356
4357                            for(c=0; c<line.count && lastC < this.tabSize; c++, lastC++)
4358                            {
4359                               if(line.buffer[c] == '\t')
4360                               {
4361                                  lastC++;
4362                                  break;
4363                               }
4364                               else if(line.buffer[c] != ' ')
4365                                  break;
4366                            }
4367                            after.x = lastC;
4368
4369                            NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
4370                            if(lastC)
4371                            {
4372                               int len = GetText(null, line, y, 0, line, y, lastC, false, false);
4373                               char * string = new char[len];
4374                               DelTextAction action { y1 = y, x1 = 0, y2 = y, x2 = lastC, string = string, placeAfter = true };
4375                               GetText(string, line, y, 0, line, y, lastC, false, false);
4376                               Record(action);
4377                            }
4378                            memmove(line.buffer,line.buffer+lastC,line.size-lastC);
4379                            if(!line.AdjustBuffer(line.count-lastC)) 
4380                               break;
4381                            line.count-=lastC;
4382                            if(style.autoSize) AutoSize();
4383                            DirtyLine(y);
4384                         }
4385
4386                         if(line == lastLine) break;
4387                      }
4388                   }
4389                   else
4390                   {
4391                      for(line = firstLine; line; line = line.next, y++)
4392                      {
4393                         if(line.count)
4394                         {
4395                            if(line != lastLine || x)
4396                            {
4397                               if(style.useTab)
4398                               {
4399                                  if(line.count + 1 <= this.maxLineSize)
4400                                  {
4401                                     BufferLocation before = { line, y, 0 }, after = { line, y, 1 };
4402
4403                                     if(!line.AdjustBuffer(line.count+1)) 
4404                                        break;
4405
4406                                     NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4407                                     if(style.autoSize) AutoSize();
4408                                     {
4409                                        AddCharAction action { ch = '\t', x = 0, y = y };
4410                                        Record(action);
4411                                     }
4412
4413                                     memmove(line.buffer+1,line.buffer,line.size-1);
4414                                     line.count++;
4415                                     line.buffer[0] = '\t';
4416                                  }
4417                               }
4418                               else
4419                               {
4420                                  if(line.count + this.tabSize <= this.maxLineSize)
4421                                  {
4422                                     int c;
4423                                     BufferLocation before = { line, y, 0 }, after = { line, y, this.tabSize };
4424                                     NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4425                                     if(style.autoSize) AutoSize();
4426
4427                                     if(!line.AdjustBuffer(line.count+this.tabSize)) 
4428                                        break;
4429
4430                                     {
4431                                        char * string = new char[this.tabSize + 1];
4432                                        memset(string, ' ', this.tabSize);
4433                                        string[this.tabSize] = '\0';
4434                                        Record(AddTextAction { string = string, x1 = 0, y1 = y, x2 = this.tabSize, y2 = y });
4435                                     }
4436
4437                                     memmove(line.buffer+this.tabSize,line.buffer,line.size-(this.tabSize));
4438                                     line.count+=this.tabSize;
4439                                     for(c=0; c<this.tabSize; c++)
4440                                        line.buffer[c] = ' ';
4441                                  }
4442                               }
4443                               DirtyLine(y);
4444                            }
4445                         }
4446                         if(line == lastLine) break;
4447                      }
4448                   }
4449                }
4450                else
4451                {
4452                   if(style.useTab)
4453                   {
4454                      // Insert character
4455                      AddCh('\t');
4456                   }
4457                   else
4458                   {
4459                      int start, c;
4460                      char * addString = new char[this.tabSize + 1];
4461                      int len = 0;
4462                      if(!(style.freeCaret))
4463                      {
4464                         this.x = Min(this.x, this.line.count);
4465                         ComputeColumn();
4466                      }
4467                      // Insert spaces
4468                      start = this.x;
4469                      for(c=start; ((c == start) || ((c) % this.tabSize)); c++)
4470                      {
4471                         addString[len++] = ' ';
4472                      }
4473                      addString[len] = 0;
4474                      AddS(addString);
4475                      delete addString;
4476                   }
4477                }
4478                Modified();
4479                SetViewToCursor(true);
4480                return false;
4481             }
4482             break;
4483          case pageDown:
4484             if(key.ctrl)
4485             {
4486                if(!(style.hScroll) || hasHorzScroll) break;
4487                if(this.viewX < this.maxLength)
4488                {
4489                   //this.viewX+=this.space.w*this.tabSize;
4490                   //DirtyAll();
4491                   SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
4492                }
4493             }
4494             else
4495             {
4496                PageDown();
4497                DirtyAll();
4498                if(!shift) Deselect();
4499                SetCursorToViewX();
4500                SetCursorToViewY();
4501             }
4502             return false;
4503             // break;
4504          case pageUp:
4505             if(key.ctrl)
4506             {
4507                if(!(style.hScroll) || hasHorzScroll) break;
4508                if(this.viewX > 0)
4509                {
4510                   //this.viewX-=this.space.w*this.tabSize;
4511                   //this.viewX = Max(this.viewX,0);
4512                   //DirtyAll();
4513                   SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
4514                   // SetCursorToView();
4515                }
4516             }
4517             else
4518             {
4519                PageUp();
4520                DirtyAll();
4521                if(!shift) Deselect();
4522                SetCursorToViewX();
4523                SetCursorToViewY();
4524             }
4525             // break;
4526             return false;
4527          case insert:
4528             if(key.ctrl)
4529             {
4530                Copy();
4531                return false;
4532             }
4533             else if(key.shift)
4534             {
4535                if(!(style.readOnly))
4536                   Paste();
4537                return false;
4538             }
4539             else
4540             {
4541                this.overwrite ^= 1;
4542                UpdateCaretPosition(true);
4543                if(this.overwrite)
4544                   SetCaret(0,0,0);
4545                DirtyLine(this.y);
4546                UpdateDirty();
4547                NotifyOvrToggle(master, this, this.overwrite);
4548             }
4549             break;
4550          case hotKey: 
4551             break;
4552          default:
4553             
4554             switch(key)
4555             {
4556                case ctrlA:
4557                   // Select All
4558                   if(style.noSelect) break;
4559
4560                   {
4561                      //Save current view position
4562                      int tempX = this.viewX;
4563                      int tempY = this.viewY;
4564                      
4565                      this.selX = 0;
4566                      this.selY = 0;
4567                      this.selLine = this.lines.first;
4568                      this.y = this.lineCount-1;
4569                      this.line = this.lines.last;
4570                      this.x = this.line.count;
4571                      ComputeColumn();
4572                      DirtyAll();
4573                      SetViewToCursor(true);
4574                      
4575                      //Restore previous view position
4576                      SetScrollPosition(tempX, tempY * this.space.h);
4577                      
4578                      UpdateDirty();
4579                      SetSelectCursor();
4580                      SelectionEnables();
4581                   }
4582                   // TOCHECK: Was there any good reason why we weren't returning false here?
4583                   return false; // break;
4584                case ctrlC:
4585                   Copy();
4586                   return false;
4587                case ctrlV:
4588                   if(!(style.readOnly))
4589                   {
4590                      Paste();
4591                      return false;
4592                   }
4593                   break;
4594                case ctrlX:
4595                {
4596                   if(style.readOnly) break;
4597                   Cut();
4598                   return false;
4599                }
4600                case ctrlL:
4601                {
4602                   if(style.readOnly) break;
4603                   ClearLine();
4604                   return false;
4605                }
4606                case ctrlZ:
4607                   if(style.readOnly) break;
4608                   Undo();
4609                   return false;
4610                case ctrlY:
4611                   if(style.readOnly) break;
4612                   Redo();
4613                   return false;
4614                default:
4615                   if(style.readOnly) break;
4616                   if(key.shift && key.code == rightBracket)
4617                   {
4618                      //Only indent back if you are exactly at one tab.
4619                      {
4620                         bool whitespace = true;
4621                         int i;
4622                         char * newline;
4623                         int putsize;
4624                         
4625                         int indentwidth;
4626                         EditLine line = this.line;
4627                         
4628                         //Only remove one tab if there is nothing else on the line.
4629                         for(i = 0; i < this.line.count; i++)
4630                         {
4631                            if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
4632                            {
4633                               PutCh(ch);
4634                               return false;
4635                            }
4636                         }
4637                         
4638                         //Find opening {
4639                         i = 1;
4640                         while(i && line)
4641                         {
4642                            int pos;
4643                            
4644                            indentwidth = 0;
4645                            for(pos = line.count - 1; pos >= 0; pos--)
4646                            {
4647                               char c = line.buffer[pos];
4648                               if(c == ' ')
4649                                  indentwidth++;
4650                               else if(c == '\t')
4651                                  indentwidth += this.tabSize;
4652                               else
4653                                  //Counting backwards, so when you find a character, indentation is reset
4654                                  indentwidth = 0;
4655                               if(i && c == '}')
4656                                  i++;
4657                               if(i && c == '{')
4658                                  i--;
4659                            }
4660                            line = line.prev;
4661                         }
4662                         
4663                         //Place the } to get an undo:
4664                         PutCh(ch);
4665                         
4666                         this.x = this.line.count;
4667                         this.selX = 0;
4668                         
4669                         if(!style.useTab)
4670                            putsize = indentwidth;
4671                         else
4672                            putsize = indentwidth / this.tabSize + indentwidth % this.tabSize;
4673                         
4674                         newline = new char[putsize+2];
4675                         newline[putsize] = '}';
4676                         newline[putsize+1] = '\0';
4677                         
4678                         i = 0;
4679                         if(style.useTab)
4680                            for(; i < indentwidth / this.tabSize; i++)
4681                               newline[i] = '\t';
4682                         for(;i < putsize; i++)
4683                            newline[i] = ' ';
4684                         
4685                         AddS(newline);
4686                         
4687                         delete newline;
4688                      }
4689                      return false;
4690                   } else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
4691                   {
4692                      PutCh(ch);
4693                      return false;
4694                   }
4695                   break;
4696             }
4697             break;
4698       }
4699       return true;
4700    }
4701
4702    void OnHScroll(ScrollBarAction action, int position, Key key)
4703    {
4704 #ifdef _DEBUG
4705       //PrintLn("OnHScroll: ", action, ", pos = ", position, ", key = ", key);
4706 #endif
4707       this.viewX = position;
4708       if(action != setRange)
4709       {
4710          if(!this.mouseMove && style.cursorFollowsView)
4711             SetCursorToViewX();
4712       }
4713       DirtyAll();
4714       UpdateDirty();
4715    }
4716
4717    void OnVScroll(ScrollBarAction action, int position, Key key)
4718    {
4719       int oldViewY = this.viewY;
4720
4721 #ifdef _DEBUG
4722       //PrintLn("OnVScroll: ", action, ", pos = ", position, ", key = ", key);
4723 #endif
4724       position /= this.space.h;
4725
4726       if(position < this.viewY)
4727       {
4728          for(; position < this.viewY && this.viewLine.prev; this.viewLine = this.viewLine.prev, this.viewY--);
4729          style.recomputeSyntax = true;
4730       }
4731       else if(position > this.viewY)
4732       {
4733          EditLine oldViewLine = viewLine;
4734          for(; position > this.viewY && this.viewLine.next; this.viewLine = this.viewLine.next, this.viewY++);
4735          FigureStartSyntaxStates(oldViewLine, false);
4736       }
4737       
4738       if(action != setRange)
4739       {
4740          if(!this.mouseMove && style.cursorFollowsView && !SelSize()) SetCursorToViewY();
4741       }
4742
4743       if(this.x != this.selX || this.y != this.selY)
4744          DirtyLine(this.y);
4745
4746       {
4747          Scroll(0, (this.viewY - oldViewY) * this.space.h);
4748
4749          /*
4750          int numLines = clientSize.h / this.space.h;
4751          int y;
4752          if(Abs(this.viewY - oldViewY) < numLines)
4753             
4754          
4755          if(this.viewY > oldViewY)
4756          {
4757             for(y = oldViewY; y <this.viewY; y++)
4758                DirtyLine(y + numLines);
4759          }
4760          else
4761          {
4762             for(y = this.viewY; y <oldViewY; y++)
4763                DirtyLine(y + numLines);
4764          }*/
4765       }
4766       //DirtyAll();
4767
4768       // Fix dirt of stuff before first line...
4769       if(this.viewY - oldViewY > 0)
4770       {
4771          Box box { 0,0, clientSize.w-1, YOFFSET-1 };
4772          Update(box);
4773       }
4774       
4775       UpdateDirty();
4776    }
4777
4778    bool _AddCh(unichar ch, int * addedSpacesPtr, int * addedTabsPtr)
4779    {
4780       EditLine line;
4781       int length, endX;
4782       bool result;
4783       ReplaceTextAction replaceAction = null;
4784       AddCharAction addCharAction = null;
4785       int addedSpaces = 0, addedTabs = 0;
4786
4787       if(ch == '\r') return true;
4788       if(style.stuckCaret /*|EES_READONLY)*/ ) 
4789          GoToEnd(true);
4790    
4791       if(ch == '\n' && !(style.multiLine) && this.line) return false;
4792       
4793       if(!undoBuffer.dontRecord)
4794       {
4795          if(selX != x || selY != y)
4796          {
4797             char buffer[5];
4798             char * newString;
4799             char * oldString;
4800             int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
4801             oldString = new char[len];
4802             UTF32toUTF8Len(&ch, 1, buffer, 4);
4803             newString = CopyString(buffer);
4804             GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
4805
4806             replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
4807             if(selY < y || (selY == y && selX < x))
4808             {
4809                replaceAction.x1 = selX;
4810                replaceAction.y1 = selY;
4811                replaceAction.x2 = x;
4812                replaceAction.y2 = y;
4813             }
4814             else
4815             {
4816                replaceAction.x1 = x;
4817                replaceAction.y1 = y;
4818                replaceAction.x2 = selX;
4819                replaceAction.y2 = selY;
4820             }
4821             Record(replaceAction);
4822             undoBuffer.dontRecord++;
4823          }
4824          else
4825          {
4826             addCharAction = AddCharAction { y = y, x = x, ch = ch };
4827             Record(addCharAction);
4828          }
4829       }
4830
4831       if(ch == '\n')
4832       {
4833          DelSel(&addedSpaces);
4834          if(this.lineCount+1 > this.maxLines)
4835          {
4836             if(style.autoEmpty)
4837                Emptyline(this.lines.first,0);
4838             else
4839                return false;
4840          }
4841          if(!(style.vScroll)) 
4842          {
4843             // Make sure it fits, but we need a default line is this.font is too big for window
4844             if(this.space.h * (this.lineCount+1) > clientSize.h && this.line)
4845                return false;
4846          }
4847          if((this.y >= 0) && this.y < this.viewY)
4848          {
4849             this.viewY++;
4850             DirtyAll();
4851          }
4852          line = EditLine { };
4853          if(!line)
4854             return false;
4855          lines.Insert(this.line, line);
4856          line.editBox = this;
4857          line.buffer = null;
4858          line.count = 0;
4859          line.size = 0;
4860          length = 0;
4861
4862          // If we're displacing the lines from a current line ...
4863          if(this.line)
4864          {
4865             if(this.line.buffer)
4866             {
4867                endX = this.x;
4868                if(this.line.count < endX) endX = this.line.count;
4869                length = this.line.count - endX;
4870             }
4871          }
4872          if(!line.AdjustBuffer(length)) 
4873             return false;
4874          if(this.line)
4875          {
4876             if(this.line.buffer)
4877             {
4878                CopyBytes(line.buffer,this.line.buffer+endX, length+1);
4879 #ifdef _DEBUG
4880       if(endX > 4000 || endX < 0)
4881          printf("Warning");
4882 #endif
4883                this.line.count = endX;
4884                this.line.buffer[this.line.count] = '\0';
4885                this.line.AdjustBuffer(this.line.count);
4886                ComputeLength(this.line);
4887             }
4888          }
4889          {
4890             BufferLocation before = { this.line, this.y, this.x }, after;
4891
4892             this.line = line;
4893             this.x = 0;
4894             this.col = 0;
4895
4896             ComputeLength(this.line);
4897
4898 #ifdef _DEBUG
4899       if(length > 4000 || length < 0)
4900          printf("Warning");
4901 #endif
4902             line.count = length;
4903             DirtyEnd(this.y);
4904             this.y++;
4905             this.lineCount++;
4906             line.buffer[line.count] = '\0';
4907             result = true;
4908
4909             after.line = this.line, after.y = this.y, after.x = this.x;
4910
4911             NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4912             if(style.autoSize) AutoSize();
4913          }
4914       }
4915       else
4916       {
4917          char string[5];
4918          int count = UTF32toUTF8Len(&ch, 1, string, 5);
4919          DelSel(&addedSpaces);
4920          result = AddToLine(string, count, false, addedSpaces ? null : &addedSpaces, &addedTabs);
4921          if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
4922          if(addedTabsPtr) *addedTabsPtr = addedTabs;
4923       }
4924       this.selX = this.x;
4925       this.selY = this.y;
4926       this.selLine = this.line;
4927
4928       if(replaceAction)
4929       {
4930          replaceAction.x3 = x;
4931          replaceAction.y3 = y;
4932          replaceAction.addedSpaces = addedSpaces;
4933          replaceAction.addedTabs = addedTabs;
4934          undoBuffer.dontRecord--;
4935       }
4936       if(addCharAction)
4937       {
4938          addCharAction.addedSpaces = addedSpaces;
4939          addCharAction.addedTabs = addedTabs;
4940       }
4941       return result;
4942    }
4943
4944 public:
4945
4946    /****************************************************************************
4947                                  EDIT BOX METHODS
4948    ****************************************************************************/
4949
4950
4951    bool AddCh(unichar ch)
4952    {
4953       return _AddCh(ch, null, null);
4954    }
4955
4956    void Modified()
4957    {
4958       this.modified = true;
4959       NotifyUpdate(master, this);
4960       modifiedDocument = true;
4961    }
4962
4963    void Delete(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
4964    {
4965       Deselect();
4966       DelCh(line1, y1, x1, line2, y2, x2, false);
4967       SetViewToCursor(true);
4968       UpdateDirty();
4969       Modified();
4970    }
4971
4972    void Undo()
4973    {
4974       undoBuffer.Undo();
4975       itemEditUndo.disabled = undoBuffer.curAction == 0;
4976       itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4977       if(savedAction == undoBuffer.curAction)
4978       {
4979          modifiedDocument = false;
4980          SetModified(false);
4981          NotifyUnsetModified(master, this);
4982       }
4983    }
4984
4985    void Redo()
4986    {
4987       undoBuffer.Redo();
4988       itemEditUndo.disabled = undoBuffer.curAction == 0;
4989       itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4990       if(savedAction == undoBuffer.curAction)
4991       {
4992          modifiedDocument = false;
4993          SetModified(false);
4994          NotifyUnsetModified(master, this);
4995       }
4996    }
4997
4998    void Record(UndoAction action)
4999    {
5000       if(!undoBuffer.dontRecord)
5001       {
5002          undoBuffer.Record(action);
5003          itemEditUndo.disabled = undoBuffer.curAction == 0;
5004          itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5005       }
5006    }
5007
5008    void SelectAll()
5009    {
5010       Select(
5011          this.lines.first, 0,0,
5012          this.lines.last, this.lines.count-1, strlen(((EditLine)this.lines.last).buffer));
5013    }
5014
5015    void Select(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
5016    {
5017       SelDirty();
5018       this.selX = x1;
5019       this.selY = y1;
5020       this.selLine = line1 ? (EditLine)line1 : this.lines.first;
5021       this.x = line2 ? x2 : ((EditLine)this.lines.last).count;
5022       this.y = line2 ? y2 : (this.lineCount-1);
5023       this.line = line2 ? (EditLine)line2 : this.lines.last;
5024       ComputeColumn();
5025       SelDirty();
5026       SetViewToCursor(true);
5027       UpdateDirty();
5028    }
5029
5030    // TODO: Fix this vs modifiedDocument window property
5031    void SetModified(bool flag)
5032    {
5033       if(this)
5034       {
5035          this.modified = false;
5036          if(flag && !NotifyModified(master, this))
5037             this.modified = true;
5038       }
5039    }
5040
5041    // BASIC OUTPUT
5042    bool AddS(char * string)
5043    {
5044       if(this)
5045       {
5046          bool ret = true;
5047          char * line;
5048          int c, count;
5049          int addedSpaces = 0, addedTabs = 0;
5050          AddTextAction action = null;
5051          ReplaceTextAction replaceAction = null;
5052
5053          this.pasteOperation = true;
5054
5055          if(style.stuckCaret /*|EES_READONLY)*/ ) 
5056             GoToEnd(true);
5057
5058          if(!undoBuffer.dontRecord)
5059          {
5060             char * placeString = CopyString(string);
5061             if(!style.multiLine)
5062             {
5063                int i;
5064                char ch; 
5065                for(i = 0; (ch = placeString[i]); i++)
5066                   if(ch == '\n')
5067                   {
5068                      placeString[i] = '\0';
5069                      placeString = renew placeString byte[i+1];
5070                      break;
5071                   }
5072             }
5073             
5074             if(selX != x || selY != y)
5075             {
5076                char * newString;
5077                char * oldString;
5078                int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
5079                oldString = new char[len];
5080                newString = placeString;
5081                GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
5082
5083                replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5084                if(selY < y || (selY == y && selX < x))
5085                {
5086                   replaceAction.x1 = selX;
5087                   replaceAction.y1 = selY;
5088                   replaceAction.x2 = x;
5089                   replaceAction.y2 = y;
5090                }
5091                else
5092                {
5093                   replaceAction.x1 = x;
5094                   replaceAction.y1 = y;
5095                   replaceAction.x2 = selX;
5096                   replaceAction.y2 = selY;
5097                }
5098                Record(replaceAction);
5099             }
5100             else if(string[0])
5101             {
5102                if(string[0] == '\n')
5103                   action = AddTextAction { y1 = y, x1 = Min(this.line.count, x), string = placeString };
5104                else
5105                   action = AddTextAction { y1 = y, x1 = x, string = placeString };
5106                
5107                Record(action);
5108             }
5109             else
5110                delete placeString;
5111          }
5112
5113          undoBuffer.dontRecord++;
5114          DelSel(&addedSpaces);
5115
5116          count = 0;
5117          line = string;
5118          for(c = 0; string[c]; c++)
5119          {
5120             if(string[c] == '\n' || string[c] == '\r')
5121             {
5122                if(!AddToLine(line,count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5123                {
5124                   ret = false;
5125                   break;
5126                }
5127                if(string[c] == '\n')
5128                {
5129                   if(!AddCh('\n'))
5130                   {
5131                      count = 0;
5132                      ret = false;
5133                      break;
5134                   }
5135                }
5136                // Reset for next line
5137                count = 0;
5138                line = string+c+1;
5139                /*
5140                if(string[c] == '\r' && *line == '\n') 
5141                {
5142                   line++;
5143                   c++;
5144                }*/
5145             }
5146             else
5147             {
5148                count++;
5149             }
5150          }
5151
5152          // Why was this here?
5153          // FindMaxLine();
5154
5155          // Add the line here
5156          if(ret && count)
5157             if(!AddToLine(line,count,false, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5158             {
5159                ret = false;
5160             }
5161          FindMaxLine();
5162
5163          undoBuffer.dontRecord--;
5164          if(action)
5165          {
5166             action.y2 = y;
5167             action.x2 = x;
5168             action.addedSpaces = addedSpaces;
5169             action.addedTabs = addedTabs;
5170          }
5171          else if(replaceAction)
5172          {
5173             replaceAction.y3 = y;
5174             replaceAction.x3 = x;
5175             replaceAction.addedSpaces = addedSpaces;
5176             replaceAction.addedTabs = addedTabs;
5177          }
5178
5179          UpdateCaretPosition(true);
5180
5181          this.pasteOperation = false;
5182
5183          return ret;
5184       }
5185       return false;
5186    }
5187
5188    // CONSOLE OUTPUT
5189    void Clear()
5190    {
5191       if(this)
5192       {
5193          Deselect();
5194          DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5195          SetViewToCursor(true);
5196          UpdateDirty();
5197          Modified();
5198       }
5199    }
5200
5201    void PutCh(unichar ch)
5202    {
5203       bool result;
5204       
5205       if((ch >= 32 /*&& ch <=126*/) || ch == '\n')
5206       //if((ch >= 32) || ch == '\n')
5207       {
5208          int addedSpaces = 0, addedTabs = 0;
5209          ReplaceTextAction replaceAction = null;
5210          AddCharAction addCharAction = null;
5211
5212          if(style.allCaps)
5213             ch = (ch < 128) ? toupper(ch) : ch;     // TODO: UNICODE TO UPPER
5214
5215          if(this.x < this.line.count && this.overwrite)
5216          {
5217             char buffer[5];
5218             char * newString;
5219             char * oldString;
5220             int len = GetText(null, line, y, x, line, y, x+1, false, false);
5221             oldString = new char[len];
5222             UTF32toUTF8Len(&ch, 1, buffer, 4);
5223             newString = CopyString(buffer);
5224             GetText(oldString, line, y, x, line, y, x+1, false, false);
5225             replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5226             replaceAction.x1 = x;
5227             replaceAction.y1 = y;
5228             replaceAction.x2 = x+1;
5229             replaceAction.y2 = y;
5230             Record(replaceAction);
5231
5232             undoBuffer.dontRecord++;
5233             DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, true);
5234             undoBuffer.dontRecord--;
5235          }
5236          else if(!undoBuffer.dontRecord)
5237          {
5238             if(selX != x || selY != y)
5239             {
5240                char buffer[5];
5241                char * newString;
5242                char * oldString;
5243                int len = GetText(null, line, y, x, selLine, selY, selX, false, false);
5244                oldString = new char[len];
5245                UTF32toUTF8Len(&ch, 1, buffer, 4);
5246                newString = CopyString(buffer);
5247                GetText(oldString, line, y, x, selLine, selY, selX, false, false);
5248                replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = true };
5249                if(selY < y || (selY == y && selX < x))
5250                {
5251                   replaceAction.x1 = selX;
5252                   replaceAction.y1 = selY;
5253                   replaceAction.x2 = x;
5254                   replaceAction.y2 = y;
5255                }
5256                else
5257                {
5258                   replaceAction.x1 = x;
5259                   replaceAction.y1 = y;
5260                   replaceAction.x2 = selX;
5261                   replaceAction.y2 = selY;
5262                }
5263                Record(replaceAction);
5264             }
5265             else
5266             {
5267                addCharAction = AddCharAction { y = y, x = x, ch = ch };
5268                Record(addCharAction);
5269             }
5270          }
5271          undoBuffer.dontRecord++;
5272          result = _AddCh(ch, &addedSpaces, &addedTabs);
5273          if(replaceAction)
5274          {
5275             replaceAction.x3 = x;
5276             replaceAction.y3 = y;
5277             replaceAction.addedSpaces = addedSpaces;
5278             replaceAction.addedTabs = addedTabs;
5279          }  
5280          if(addCharAction)
5281          {
5282             addCharAction.addedSpaces = addedSpaces;
5283             addCharAction.addedTabs = addedTabs;
5284          }
5285          undoBuffer.dontRecord--;
5286          if(ch == '\n')
5287             FindMaxLine();
5288          Modified();
5289          if(result) SetViewToCursor(true);
5290       }
5291    }
5292
5293    void PutS(char * string)
5294    {
5295       if(this)
5296       {
5297          AddS(string);
5298          SetViewToCursor(true);
5299          Modified();
5300       }
5301    }
5302
5303    void Printf(char * format, ...)
5304    {
5305       if(this)
5306       {
5307          char temp[MAX_F_STRING];
5308          va_list args;
5309          va_start(args, format);
5310          vsprintf(temp, format, args);
5311          va_end(args);
5312          PutS(temp);
5313       }
5314    }
5315
5316    void SetContents(char * format, ...)
5317    {
5318       if(this)
5319       {
5320          Deselect();
5321          DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5322          if(format)
5323          {
5324             char temp[MAX_F_STRING];
5325             va_list args;
5326             va_start(args, format);
5327             vsprintf(temp, format, args);
5328             va_end(args);
5329
5330             AddS(temp);
5331          }
5332          UpdateDirty();
5333          Home();
5334       }
5335    }
5336
5337    void BackSpace()
5338    {
5339       if(!DelSel(null))
5340       {
5341          if(x > 0)
5342          {
5343             x -= 1 + DelCh(line, y, x-1, line, y, x, true);
5344             Modified();
5345          }
5346          else if(this.line.prev)
5347          {
5348             EditLine line = this.line.prev;
5349             int x = line.count;
5350             int y = this.y;
5351
5352             DelCh(line, this.y-1, x, this.line, this.y, this.x, true);
5353             this.line = line;
5354             this.y = y-1;
5355             this.x = x;
5356             Modified();
5357          }
5358          this.selX = this.x;
5359          this.selY = this.y;
5360          this.selLine = this.line;
5361          ComputeColumn();
5362       }
5363       else
5364          Modified();
5365       SetViewToCursor(true);
5366    }
5367
5368    void ClearLine()
5369    {
5370       Emptyline(this.line,this.y);
5371       this.selX = this.x = 0;
5372       this.col = 0;
5373       this.selY = this.y;
5374       this.selLine = this.line;
5375
5376       SetViewToCursor(true);
5377       Modified();
5378    }
5379
5380    // CARET CONTROL
5381    void End()
5382    {
5383       if(this)
5384       {
5385          GoToEnd(true);
5386          SetViewToCursor(true);
5387       }
5388    }
5389    void Home()
5390    {
5391       if(this)
5392       {
5393          GoToHome(true);
5394          SetViewToCursor(true);
5395       }
5396    }
5397
5398    bool GoToLineNum(int lineNum)
5399    {
5400       if(this.line)
5401       {
5402          int c;
5403          EditLine line = this.lines.first;
5404          for(c = 0; c < lineNum && line; c++, line = line.next);
5405          if(line)
5406          {
5407             if(this.y != c)
5408                DirtyAll();
5409             else
5410                DirtyLine(c);
5411             this.y = c;
5412             this.line = line;
5413             Deselect();
5414             SetViewToCursor(true);
5415             return true;
5416          }
5417       }
5418       return false;
5419    }
5420
5421    bool GoToPosition(EditLine line, int y, int x)
5422    {
5423       /*
5424       if(!line)
5425       {
5426          line = this.line;
5427          y = this.y;
5428       }
5429       */
5430       if(!line)
5431       {
5432          int c;
5433          for(line = this.lines.first, c = 0; c<y && line; c++, line = line.next);
5434       }
5435
5436       if(line)
5437       {
5438          if(this.y != y)
5439             DirtyAll();
5440          else
5441             DirtyLine(y);
5442          this.x = x;
5443          this.y = y;
5444          this.line = line;
5445          ComputeColumn();
5446          Deselect();
5447          SetViewToCursor(true);
5448          return true;
5449       }
5450       return false;
5451    }
5452
5453    // VIEW POSITIONING
5454    void SetViewToCursor(bool setCaret)
5455    {
5456       if(created)
5457       {
5458          int w;
5459          int c, numLines;
5460          EditLine line;
5461          int x;
5462          int checkX, checkY;
5463          EditLine checkLine;
5464          bool dontScroll = false;
5465          bool selected;
5466          int viewX, viewY;
5467
5468          FixScrollArea();
5469
5470          selected = selX != this.x || selY != y;
5471          
5472          viewX = this.viewX;
5473          viewY = this.viewY;
5474
5475          if(mouseMove)
5476          {
5477             checkLine = dropLine;
5478             checkX = dropX;
5479             checkY = dropY;
5480          }
5481          else
5482          {
5483             checkLine = this.line;
5484             checkX = this.x;
5485             checkY = y;
5486          }
5487
5488          numLines = clientSize.h / space.h;
5489
5490          // This is broken. The EditBox now doesn't do anything different when adding to it,
5491          // regardless of the previous scrolling position. It should be read and then set again
5492          // if one wishes to preserve it.
5493          /*  // Don't scroll view to cursor if we're in a EES_NOCARET box
5494          if(style.noCaret && this.viewY < lineCount - numLines - 1)
5495             dontScroll = true;
5496          */
5497
5498          // Horizontal Adjustment
5499          if(!dontScroll && checkLine)
5500          {
5501             x = 0;
5502             if(mouseMove)
5503                dropX = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5504             else
5505             {
5506                this.x = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5507                ComputeColumn();
5508             }
5509
5510             if(style.hScroll)
5511             {
5512                if(x + space.w >= this.viewX + clientSize.w && clientSize.w >= space.w)
5513                   viewX = x - clientSize.w+space.w;
5514                if(x < this.viewX + clientSize.w/2 - space.w)
5515                   viewX = Max(0, x - clientSize.w/2 + space.w);
5516             }
5517          }
5518
5519          if(!dontScroll) 
5520          {
5521             if(style.vScroll)
5522             {
5523                // Vertical Adjustment
5524                if(viewY > checkY) viewY = checkY;
5525                if(viewY + numLines <= checkY)
5526                {
5527                   if(clientSize.h >= space.h)
5528                   {
5529                      for(line = viewLine; line && (viewY + numLines <= checkY); line = line.next)
5530                         viewY++;
5531                   }
5532                   else
5533                      viewY = checkY;
5534                }
5535             }
5536             else
5537             {
5538                if(mouseMove)
5539                   for(;dropLine && dropLine.prev && dropY >= numLines;)
5540                   {
5541                      dropLine = dropLine.prev;
5542                      dropY--;                  
5543                   }
5544                else
5545                   for(;this.line && this.line.prev && this.y >= numLines;)
5546                   {
5547                      this.line = this.line.prev;
5548                      y--;                  
5549                   }
5550             }
5551
5552             SetScrollPosition(viewX, viewY * this.space.h);
5553
5554             UpdateCaretPosition(setCaret);
5555
5556             if(!selected)
5557             {
5558                selX = this.x;
5559                selY = this.y;
5560                selLine = this.line;
5561             }
5562          }
5563
5564          UpdateDirty();
5565          SetSelectCursor();
5566          SelectionEnables();
5567       }
5568    }
5569
5570    void CenterOnCursor()
5571    {
5572       int numLines = clientSize.h / this.space.h;
5573       int y = this.y - numLines / 2;
5574       int viewY;
5575       bool figureSyntax = false;
5576       EditLine oldViewLine = viewLine;
5577       if(y > this.lineCount - numLines) y = this.lineCount-numLines;
5578       if(y < 0) y = 0;
5579
5580       viewY = y;
5581
5582       for(;y < this.viewY; y++)
5583       {
5584          this.viewLine = this.viewLine.prev;
5585          style.recomputeSyntax = true;
5586       }
5587       for(;y > this.viewY; y--)
5588       {
5589          this.viewLine = this.viewLine.next;
5590          figureSyntax = true;
5591       }
5592       if(figureSyntax)
5593          FigureStartSyntaxStates(oldViewLine, false);
5594
5595       this.viewY = viewY;
5596
5597       SetScrollPosition(this.viewX, viewY * this.space.h);
5598       UpdateCaretPosition(true);
5599       UpdateDirty();
5600    }
5601
5602    void SetCursorToView()
5603    {
5604       SetCursorToViewX();
5605       SetCursorToViewY();
5606    }
5607
5608    void PageDown()
5609    {
5610       int c, numLines;
5611       EditLine line;
5612
5613       numLines = clientSize.h / this.space.h;
5614
5615       if(style.noCaret)
5616       {
5617          for(c=0, line = this.viewLine.next; line && c<numLines && (this.viewY < this.lineCount - numLines); line = line.next, c++);
5618          SetScrollPosition(this.viewX, (this.viewY + c) * this.space.h);
5619       }
5620       else
5621       {
5622          EditLine oldLine = this.line;
5623          bool lastOne = false;
5624          EditLine oldViewLine = this.viewLine;
5625          bool figureSyntax = false;
5626
5627          if(this.y >= this.lineCount-1) return;
5628
5629          for(c=0, line = this.line.next; line && c<numLines; line = line.next, c++)
5630          {
5631             if(this.viewY + numLines < this.lines.count)
5632             {
5633                this.viewLine = this.viewLine.next;
5634                this.viewY++;
5635                figureSyntax = true;
5636             }
5637             else if(c && !lastOne)
5638                break;
5639             else
5640                lastOne = true;
5641
5642             this.line = line;
5643             this.y++;
5644          }
5645          if(figureSyntax)
5646             FigureStartSyntaxStates(oldViewLine, false);
5647          this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5648          ComputeColumn();
5649
5650          SetViewToCursor(false);
5651       }
5652    }
5653
5654    void PageUp()
5655    {
5656       int c, numLines;
5657       EditLine line;
5658       
5659       if(this.y == 0) return;
5660
5661       numLines = clientSize.h / this.space.h;
5662
5663       if(style.noCaret)
5664       {
5665          for(c=0, line = this.viewLine.prev; line && c<numLines; line = line.prev, c++);
5666          SetScrollPosition(this.viewX, (this.viewY - c) * this.space.h);
5667       }
5668       else
5669       {
5670          EditLine oldLine = this.line;
5671
5672          for(c=0, line = this.line.prev; line && c<numLines; line = line.prev, c++)
5673          {
5674             this.line = line;
5675             this.y--;
5676
5677             if(this.viewLine.prev)
5678             {
5679                this.viewLine = this.viewLine.prev;
5680                this.viewY--;
5681                style.recomputeSyntax = true;
5682             }
5683          }
5684
5685          this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5686          ComputeColumn();
5687
5688          SetViewToCursor(false);
5689       }
5690    }
5691
5692    void LineUp()
5693    {
5694
5695       if(this.viewLine.prev)
5696       {
5697          //DirtyAll();
5698          // this.viewLine = this.viewLine.prev;
5699          //this.viewY--;
5700
5701          SetScrollPosition(this.viewX, (this.viewY - 1) * this.space.h);
5702       }
5703    }
5704
5705
5706    void LineDown()
5707    {
5708       if(this.viewLine.next)
5709       {
5710          //DirtyAll();
5711          // this.viewLine = this.viewLine.next;
5712          // this.viewY++;
5713          
5714          SetScrollPosition(this.viewX, (this.viewY + 1) * this.space.h);
5715       }
5716    }
5717
5718    // Selection
5719    uint SelSize()
5720    {
5721       EditLine l1, l2, line;
5722       int x1, x2, nx1, nx2;
5723       int count;
5724       int start, end;
5725       int size;
5726
5727       if(!this.selLine) return 0;
5728       if(this.selLine == this.line && this.selX == this.x) return 0;
5729       if(this.selY < this.y)
5730       {
5731          l1 = this.selLine;
5732          x1 = this.selX;
5733          l2 = this.line;
5734          x2 = this.x;
5735       }
5736       else if(this.selY > this.y)
5737       {
5738          l1 = this.line;
5739          x1 = this.x;
5740          l2 = this.selLine;
5741          x2 = this.selX;
5742       }
5743       else if(this.selX < this.x)
5744       {
5745          l1 = l2 = this.line;
5746          x1 = this.selX;
5747          x2 = this.x;
5748       }
5749       else
5750       {
5751          l1 = l2 = this.line;
5752          x1 = this.x;
5753          x2 = this.selX;
5754       }
5755       nx1 = Min(x1,l1.count);
5756       nx2 = Min(x2,l2.count);
5757       
5758       // Find Number of Bytes Needed
5759       size = 0;
5760       for(line = l1; line; line = line.next)
5761       {  
5762          if(line == l1) start = nx1; else start = 0;
5763          if(line == l2) end = nx2; else end = line.count;
5764          size += end-start;
5765          if(style.freeCaret && line == l2)
5766          {
5767             if(l1 == l2)
5768                count = Max(x2-Max(x1,l1.count),0);
5769             else
5770                count = Max(x2-l2.count,0);
5771             size+=count;
5772          }
5773
5774          if(line == l2) break;
5775          // Add Carriage Return / line Feed
5776          size++;
5777          size++;
5778       }
5779       return size;
5780    }
5781
5782    void GetSelPos(EditLine * l1, int *y1, int *x1, EditLine * l2, int * y2, int * x2, bool reorder)
5783    {
5784       if(this)
5785       {
5786          if(!reorder || this.selY < this.y)
5787          {
5788             if(l1) *l1 = this.selLine;
5789             if(y1) *y1 = this.selY;
5790             if(x1) *x1 = this.selX;
5791             if(l2) *l2 = this.line;
5792             if(y2) *y2 = this.y;
5793             if(x2) *x2 = this.x;
5794          }
5795          else if(this.selY > this.y)
5796          {
5797             if(l1) *l1 = this.line;
5798             if(y1) *y1 = this.y;
5799             if(x1) *x1 = this.x;
5800             if(l2) *l2 = this.selLine;
5801             if(y2) *y2 = this.selY;
5802             if(x2) *x2 = this.selX;
5803          }
5804          else if(this.selX < this.x)
5805          {
5806             if(l1) *l1 = this.line;
5807             if(y1) *y1 = this.selY;
5808             if(x1) *x1 = this.selX;
5809             if(l2) *l2 = this.line;
5810             if(y2) *y2 = this.y;
5811             if(x2) *x2 = this.x;
5812          }
5813          else
5814          {
5815             if(l1) *l1 = this.line;
5816             if(y1) *y1 = this.y;
5817             if(x1) *x1 = this.x;
5818             if(l2) *l2 = this.line;
5819             if(y2) *y2 = this.selY;
5820             if(x2) *x2 = this.selX;
5821          }
5822       }
5823    }
5824
5825    void SetSelPos(EditLine l1, int y1, int x1, EditLine l2, int y2, int x2)
5826    {
5827       if(this && (this.selY != y1 || this.selX != x1 || this.y != y2 || this.x != x2))
5828       {
5829          this.selLine = (EditLine)l1;
5830          this.selY = y1;
5831          this.selX = x1;
5832          this.line = (EditLine)l2;
5833          this.y = y2;
5834          this.x = x2;
5835          ComputeColumn();
5836          SetViewToCursor(true);
5837       }
5838    }
5839
5840    int GetText(char * text, EditLine _l1, int _y1, int _x1, EditLine _l2, int _y2, int _x2, bool addCr, bool addSpaces)
5841    {
5842       EditLine l1, l2, line;
5843       int x1, x2, nx1, nx2;
5844       int count;
5845       int start, end;
5846       int numChars = 0;
5847
5848       if(_y2 < _y1)
5849       {
5850          l1 = _l2;
5851          x1 = _x2;
5852          l2 = _l1;
5853          x2 = _x1;
5854       }
5855       else if(_y2 > _y1)
5856       {
5857          l1 = _l1;
5858          x1 = _x1;
5859          l2 = _l2;
5860          x2 = _x2;
5861       }
5862       else if(_x2 < _x1)
5863       {
5864          l1 = l2 = _l1;
5865          x1 = _x2;
5866          x2 = _x1;
5867       }
5868       else
5869       {
5870          l1 = l2 = _l1;
5871          x1 = _x1;
5872          x2 = _x2;
5873       }
5874       nx1 = Min(x1,l1.count);
5875       nx2 = Min(x2,l2.count);
5876
5877       // Copy text
5878       for(line = l1; line; line = line.next)
5879       {  
5880          if(line == l1) start = nx1; else start = 0;
5881          if(line == l2) end = nx2; else end = line.count;
5882          if(text)
5883          {
5884             CopyBytes(text, line.buffer + start, end - start);
5885             text += end-start;
5886          } 
5887          numChars += end-start;
5888          
5889          if(style.freeCaret && line == l2 && addSpaces)
5890          {
5891             if(l1 == l2)
5892                count = Max(x2-Max(x1,l1.count),0);
5893             else
5894                count = Max(x2-l2.count,0);
5895             if(text)
5896             {
5897                FillBytes(text,' ',count);
5898                text += count;
5899             }
5900             else
5901                numChars += count;
5902          }
5903          if(line == l2) break;
5904          // Add line Feed
5905          if(addCr)
5906          {
5907             if(text)
5908                *(text++) = '\r';
5909             numChars++;
5910          } 
5911          if(text)
5912             *(text++) = '\n';
5913          numChars++; 
5914       }
5915       // '\0' terminate Terminate
5916       if(text)
5917          *(text) = '\0';
5918       numChars++;
5919       return numChars;
5920    }
5921
5922    void GetSel(char * text, bool addCr)
5923    {
5924       GetText(text, line, y, x, selLine, selY, selX, addCr, true);
5925    }
5926
5927    void Deselect()
5928    {
5929       SelDirty();
5930       this.selLine = this.line;
5931       this.selX = this.x;
5932       this.selY = this.y;
5933    }
5934
5935    // CLIPBOARD
5936    void Copy()
5937    {
5938       if(this)
5939       {
5940          int size = SelSize();
5941          if(size)
5942          {
5943             // Try to allocate memory
5944             ClipBoard clipBoard { };
5945             if(clipBoard.Allocate(size+1))
5946             {
5947                GetSel(clipBoard.memory, true);   
5948                // Save clipboard
5949                clipBoard.Save();
5950             }
5951             delete clipBoard;
5952          }
5953       }
5954    }
5955
5956    void Paste()
5957    {
5958       if(this)
5959       {
5960          ClipBoard clipBoard { };
5961          if(clipBoard.Load())
5962             PutS(clipBoard.memory);
5963          delete clipBoard;
5964       }
5965    }
5966
5967    void Cut()
5968    {
5969       if(this)
5970       {
5971          Copy();
5972          DelSel(null);
5973          SetViewToCursor(true);
5974          Modified();
5975       }
5976    }
5977
5978    void DeleteSelection()
5979    {
5980       if(this)
5981       {
5982          DelSel(null);
5983          SetViewToCursor(true);
5984          Modified();
5985       }
5986    }
5987
5988    // FILE INTERFACE
5989    void Save(File f, bool cr)
5990    {
5991       EditLine line;
5992       savedAction = undoBuffer.curAction;
5993
5994       for(line = this.lines.first; line; line = line.next)
5995       {  
5996          f.Write(line.buffer, line.count,1);
5997          if(line.next)
5998          {
5999             if(cr) f.Putc('\r');
6000             f.Putc('\n');
6001          }
6002       }
6003    }
6004
6005    #define BUFFER_SIZE  16384
6006    void Load(File f)
6007    {
6008       undoBuffer.dontRecord++;
6009       if(f)
6010       {
6011          char buffer[BUFFER_SIZE];
6012      
6013          for(;;)
6014          {
6015             int count = f.Read(buffer, 1, BUFFER_SIZE-1);
6016             buffer[count] = '\0';
6017             AddS(buffer);
6018             if(!count) break;
6019          }
6020          Home();
6021       }
6022       undoBuffer.dontRecord--;
6023       undoBuffer.count = 0;
6024       undoBuffer.curAction = 0;
6025       itemEditUndo.disabled = undoBuffer.curAction == 0;
6026       itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
6027    }
6028
6029    EditBoxFindResult Find(char * text, bool matchWord, bool matchCase, bool isSearchDown)
6030    {
6031       EditLine line;
6032       int num;
6033       bool firstPass = true;
6034       EditBoxFindResult result = found;
6035
6036       if(!this.line) return notFound;
6037       num = this.y;
6038
6039       for(line = this.line;;)
6040       {
6041          char * string;
6042
6043          if(!line) 
6044          {
6045             if(isSearchDown)
6046             {
6047                line = this.lines.first;
6048                num = 0;
6049                result = wrapped;
6050             }
6051             else
6052             {
6053                line = this.lines.last;
6054                num = this.lineCount - 1;
6055                result = wrapped;
6056             }
6057          }
6058          
6059          if(isSearchDown)
6060             string = SearchString(line.buffer, firstPass ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6061          else
6062             string = RSearchString(line.buffer,text,firstPass ? (Min(this.x,line.count)-1) : line.count,matchCase,matchWord);
6063
6064          if(string)
6065          {
6066             Select((void *)line,num,string - line.buffer,(void *)line,num,string - line.buffer + strlen(text));
6067             return result;
6068          } 
6069          if(line == this.line && !firstPass) break;
6070
6071          if(isSearchDown)
6072          {
6073             line = line.next;
6074             num++;
6075          }
6076          else
6077          {
6078             line = line.prev;
6079             num--;
6080          }
6081
6082          firstPass = false;
6083       }
6084       return notFound;
6085    }
6086
6087    EditBoxFindResult FindInSelection(char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
6088    {
6089       EditLine line;
6090       int y;
6091       int searchLen = strlen(text);
6092       for(line = (EditLine)this.line, y = this.y; y <= y2; line = line.next, y++)
6093       {
6094          char * string = SearchString(line.buffer, (y == this.y) ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6095          if(string && (y < y2 || (string - line.buffer) + searchLen <= x2))
6096          {
6097             Select((void *)line,y,string - line.buffer,(void *)line,y,string - line.buffer + strlen(text));
6098             return found;
6099          } 
6100       }
6101       return notFound;
6102    }
6103
6104    bool OnKeyDown(Key key, unichar ch)
6105    {
6106 #ifdef _DEBUG
6107       //PrintLn("OnKeyDown: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
6108 #endif
6109       if(!NotifyKeyDown(master, this, key, ch))
6110          return false;
6111       else 
6112       {
6113          switch(key)
6114          {
6115             case escape:
6116             {
6117                if(this.mouseMove)
6118                {
6119                   this.mouseMove = false;
6120                   OnLeftButtonUp(0,0,0);
6121                   SetViewToCursor(true);
6122                   return false;
6123                }
6124             }
6125          }
6126       }
6127       return true;
6128    }
6129 };
6130
6131 public class EditBoxStream : File
6132 {
6133    EditBox editBox;
6134    BufferLocation start, sel;
6135    uint pos;
6136    byte utf8Bytes[5];
6137    int numBytes;
6138
6139    ~EditBoxStream()
6140    {
6141       EditBox editBox = this.editBox;
6142
6143       editBox.x = start.x;
6144       editBox.y = start.y;
6145       editBox.line = start.line;
6146
6147       editBox.selX = sel.x;
6148       editBox.selY = sel.y;
6149       editBox.selLine = sel.line;
6150
6151       editBox.SetViewToCursor(true);
6152       //editBox.ComputeColumn();
6153    }
6154
6155 public:
6156    property EditBox editBox
6157    {
6158       set
6159       {
6160          editBox = value;
6161
6162          start.x = value.x;
6163          start.y = value.y;
6164          start.line = value.line;
6165
6166          sel.x = value.selX;
6167          sel.y = value.selY;
6168          sel.line = value.selLine;
6169          numBytes = 0;
6170
6171          value.GoToHome(true);
6172       }
6173
6174       get { return editBox; }
6175    }
6176
6177    uint Read(byte * buffer, uint size, uint count)
6178    {
6179       uint read = 0;
6180       EditBox editBox = this.editBox;
6181       EditLine line = editBox.line;
6182       int x = editBox.x;
6183       int y = editBox.y;
6184
6185       count *= size;
6186
6187       for(;read < count && line; line = (*&line.next))
6188       {
6189          int numBytes = Min(count - read, (*&line.count) - x);
6190          if(numBytes > 0)
6191          {
6192             memcpy(buffer + read, (*&line.buffer) + x, numBytes);
6193             read += numBytes;
6194             x += numBytes;
6195          }
6196          /*
6197          for(;read < count && x < (*&line.count);)
6198          {
6199             buffer[read++] = (*&line.buffer)[x++];
6200          }
6201          */
6202          if((*&line.next))
6203          {
6204             if(read < count)
6205             {
6206                buffer[read++] = '\n';
6207             }
6208             else
6209                break;
6210          }
6211          if(x == (*&line.count) && (*&line.next))
6212          {
6213             x = 0;
6214             y++;
6215          }
6216          else
6217             break;         
6218       }
6219
6220       editBox.line = editBox.selLine = line;
6221       editBox.x = editBox.selX = x;
6222       editBox.y = editBox.selY = y;
6223       pos += read;
6224       return read / size;
6225    }
6226
6227    bool Seek(int pos, FileSeekMode mode)
6228    {
6229       bool result = true;
6230       EditBox editBox = this.editBox;
6231       EditLine line = editBox.line;
6232       numBytes = 0;
6233       if(mode == FileSeekMode::start)
6234       {
6235          pos = pos - this.pos;
6236          mode = current;
6237       }
6238       if(mode == current)
6239       {
6240          uint read = 0;
6241          int x = editBox.x;
6242          int y = editBox.y;
6243          if(pos > 0)
6244          {
6245             for(;read < pos && line; line = (*&line.next))
6246             {
6247                int numBytes = Min(pos - read, (*&line.count) - x);
6248                if(numBytes > 0)
6249                {
6250                   read += numBytes; x += numBytes;
6251                }
6252
6253                /*for(;read < pos && x < (*&line.count);)
6254                {
6255                   read++; x++;
6256                }
6257                */
6258                if((*&line.next))
6259                {
6260                   if(read < pos)
6261                   {
6262                      read++;
6263                      x = 0;
6264                   }
6265                   else
6266                      break;
6267                }
6268                else
6269                {
6270                   if(read<pos)
6271                      result = false;
6272                   break;
6273                }
6274                y++;
6275             }
6276             this.pos += read;
6277          }
6278          else if(pos < 0)
6279          {
6280             pos = -pos;
6281             for(;read < pos && line; line = (*&line.prev))
6282             {
6283                int numBytes = Min(pos - read, x);
6284                if(numBytes > 0)
6285                {
6286                   read += numBytes;
6287                   x -= numBytes;
6288                }
6289                /*for(;read < pos && x > 0;)
6290                {
6291                   read++; x--;
6292                }
6293                */
6294                if((*&line.prev))
6295                {
6296                   if(read < pos)
6297                   {
6298                      read++;
6299                      x = (*&(*&line.prev).count);
6300                   }
6301                   else
6302                      break;
6303                }
6304                else
6305                {
6306                   if(read<pos)
6307                      result = false;
6308                   break;
6309                }
6310                y--;
6311             }
6312             this.pos -= read;
6313          }
6314
6315          editBox.line = editBox.selLine = line;
6316          editBox.x = editBox.selX = x;
6317          editBox.y = editBox.selY = y;
6318       }
6319       return result;
6320    }
6321    
6322    bool Puts(char * string)
6323    {
6324       EditBox editBox = this.editBox;
6325       BufferLocation start { editBox.line, editBox.y, editBox.x };
6326       BufferLocation pos;
6327       
6328       numBytes = 0;
6329       editBox.AddS(string);
6330
6331       pos.line = editBox.line;
6332       pos.y = editBox.y;
6333       pos.x = editBox.x;
6334
6335       this.start.AdjustAdd(start, pos);
6336       sel.AdjustAdd(start, pos);
6337
6338       return true;
6339    }
6340
6341    // NOTE: BYTE, NOT UNICODE CHARACTER!
6342    bool Putc(char ch)
6343    {
6344       EditBox editBox = this.editBox;
6345       BufferLocation start = { editBox.line, editBox.y, editBox.x };
6346       BufferLocation pos;
6347
6348       if(numBytes < 4)
6349       {
6350          utf8Bytes[numBytes++] = ch;
6351          utf8Bytes[numBytes] = 0;
6352          if(UTF8Validate(utf8Bytes))
6353          {
6354             editBox.AddCh(UTF8_GET_CHAR(utf8Bytes, numBytes));
6355             numBytes = 0;
6356             pos.line = editBox.line;
6357             pos.y = editBox.y;
6358             pos.x = editBox.x;
6359             this.start.AdjustAdd(start, pos);
6360             sel.AdjustAdd(start, pos);
6361          }
6362          return true;
6363       }
6364       return false; 
6365    }
6366
6367    bool Getc(char * ch)
6368    {
6369       return Read(ch, 1, 1) ? true : false;
6370    }
6371
6372    void DeleteBytes(uint count)
6373    {
6374       EditBox editBox = this.editBox;
6375       if(count)
6376       {
6377          BufferLocation pos { editBox.line, editBox.y, editBox.x };
6378          BufferLocation end = pos;
6379
6380          uint c = 0;
6381          for(;;)
6382          {
6383             for(;c < count && end.line && end.x < end.line.count;)
6384             {
6385                end.x++;
6386                c++;
6387             }
6388             if(c < count && end.line && end.line.next)
6389             {
6390                end.line = end.line.next;
6391                end.y++;
6392                c++;
6393                end.x = 0;
6394             }
6395             else
6396                break;
6397          }
6398
6399          start.AdjustDelete(pos, end);
6400          sel.AdjustDelete(pos, end);
6401
6402          editBox.DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true);
6403       }
6404    }
6405 };