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