1 namespace gui::controls;
4 selectionForeground = white;
5 disabled: defaultTextColor = Color { 85, 85, 85 };
14 char * strchrmax(const char * s, int c, int max)
18 for(i = 0; i < max && (ch = s[i]); i++)
24 public class SyntaxColorScheme
28 Color charLiteralColor;
29 Color stringLiteralColor;
30 Color preprocessorColor;
32 private Array<Color> keywordColors { };
34 public property Container<Color> keywordColors
36 set { keywordColors.Copy((void *)value); }
37 get { return keywordColors; }
45 #define XOFFSET (3 + (/*style.lineNumbers?6 * this.space.w:*/0))
47 #define YOFFSET (style.multiLine ? 1 : ((clientSize.h + 1 - space.h) / 2))
49 #define IS_ALUNDER(ch) (/*(ch) == '_' || */CharMatchCategories((ch), letters|numbers|marks|connector))
51 #define UTF8_IS_FIRST(x) (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || ((b) & 0x40)); }))
52 #define UTF8_NUM_BYTES(x) (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
53 #define UTF8_GET_CHAR(string, numBytes) \
57 byte b = (string)[0]; \
60 numBytes = b ? 1 : 0; \
74 if(b & 0x08) { numBytes = 0; } \
83 for(i = 0; i<numBytes; i++) \
86 ch |= (b = (string)[i]) & mask; \
88 if(i > 1 && (!(b & 0x80) || (b & 0x40))) \
95 ch > 0x10FFFF || (ch >= 0xD800 && ch <= 0xDFFF) || \
96 (ch < 0x80 && numBytes > 1) || \
97 (ch < 0x800 && numBytes > 2) || \
98 (ch < 0x10000 && numBytes > 3))\
108 bool autoEmpty:1, readOnly:1, multiLine:1, stuckCaret:1, freeCaret:1, select:1, hScroll:1, vScroll:1, smartHome:1;
109 bool noCaret:1, noSelect:1, tabKey:1, useTab:1, tabSel:1, allCaps:1, syntax:1, wrap:1;
112 bool inMultiLineComment:1, inPrep:1, escaped:1, continuedSingleLineComment:1, wasInMultiLine:1, continuedString:1, continuedQuotes:1;
114 bool recomputeSyntax:1;
115 bool cursorFollowsView:1;
117 // bool lineNumbers:1;
122 void UnregisterClass_EditBox()
125 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
134 extern int __ecereVMethodID_class_OnFree;
137 static class ArrayImpl
144 public class OldArray
149 void ** array = (void **)((ArrayImpl)this).array;
150 if(type.type == normalClass || type.type == noHeadClass)
152 for(c = 0; c<size; c++)
153 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, array[c]);
155 // TODO: Call OnFree for structClass
156 delete ((ArrayImpl)this).array;
165 if(((ArrayImpl)this).array)
169 ((ArrayImpl)this).array = renew0 ((ArrayImpl)this).array byte[type.typeSize * value];
172 ((ArrayImpl)this).array = new0 byte[value * type.typeSize];
181 memcpy(((ArrayImpl)this).array, value, type.typeSize * size);
188 public class UndoAction : struct
191 subclass(UndoAction) type;
194 virtual void Undo(void * data) { type.Undo(this, data); }
195 virtual void Redo(void * data) { type.Redo(this, data); }
197 virtual void Print(void * data) { type.Print(this, data); }
201 if(((Class)type).Destructor)
202 ((void (*)(void *))((Class)type).Destructor)(this);
206 public class UndoBuffer
208 Array<UndoAction> actions { size = 8 };
227 bool continued = true;
228 while(curAction > 0 && continued)
230 UndoAction action = actions[--curAction];
234 /*Print("Undoing: ");
235 action.Print(data);*/
240 continued = curAction > 0 && actions[curAction-1].continued;
246 bool continued = true;
247 while(curAction < count && continued)
249 UndoAction action = actions[curAction];
250 continued = action.continued;
256 /*Print("Redoing: ");
257 action.Print(data);*/
266 void Record(UndoAction action)
268 if(!dontRecord && !insideRedo)
270 if(curAction < count)
273 for(c = curAction; c < count; c++)
279 if(count >= actions.size)
280 actions.size += actions.size / 2;
283 /*Print("Recording: ");
284 action.Print(data);*/
288 if(!firstEvent && count > 0)
289 actions[count-1].continued = true;
292 actions[count++] = action;
295 if(actions.size > count + count / 2 && count + count / 2 >= 8)
296 actions.size = count + count / 2;
312 static class AddCharAction : UndoAction
316 int addedSpaces, addedTabs, xAdjustment;
317 type = class(AddCharAction);
319 void Undo(EditBox editBox)
321 editBox.GoToPosition(null, (ch == '\n') ? (y + 1) : y, (ch == '\n') ? 0 : (x + 1) - xAdjustment);
323 if(addedTabs || addedSpaces)
325 editBox.DelCh(editBox.line, y, x - xAdjustment - (addedSpaces + addedTabs), editBox.line, y, x - xAdjustment, false);
326 editBox.GoToPosition(editBox.line, y, x);
328 editBox.UpdateDirty();
331 void Redo(EditBox editBox)
333 editBox.GoToPosition(null, y, x);
335 editBox.UpdateDirty();
338 void Print(EditBox editBox)
340 PrintLn("AddChar: y = ", y, "x = ", x, ", ch = ", ch, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
345 static class AddTextAction : UndoAction
349 int addedSpaces, addedTabs, xAdjustment;
350 type = class(AddTextAction);
353 void Print(EditBox editBox)
355 PrintLn("AddText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
363 void Undo(EditBox editBox)
368 editBox.GoToPosition(null, y1, x1);
370 l2 = editBox.lines.first;
371 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
373 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
374 if(addedTabs || addedSpaces)
375 editBox.DelCh(editBox.line, y1, x1 - (addedSpaces + addedTabs), editBox.line, y1, x1, false);
377 editBox.SetViewToCursor(true);
381 void Redo(EditBox editBox)
383 editBox.GoToPosition(null, y1, x1);
384 editBox.PutS(string);
388 static class DelTextAction : UndoAction
392 bool placeAfter, noHighlight;
394 type = class(DelTextAction);
397 void Print(EditBox editBox)
399 PrintLn("DelText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", placeAfter = ", placeAfter);
402 void Undo(EditBox editBox)
404 editBox.GoToPosition(null, y1, x1);
405 editBox.PutS(string);
409 editBox.GoToPosition(null, y1, x1);
415 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
416 //editBox.SetViewToCursor(true);
419 editBox.DelCh(editBox.line, y1, x1 - addedSpaces, editBox.line, y1, x1, false);
428 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
429 //editBox.SetViewToCursor(true);
432 editBox.DelCh(editBox.selLine, y1, x1 - addedSpaces, editBox.selLine, y1, x1, false);
436 void Redo(EditBox editBox)
441 editBox.GoToPosition(null, y1, x1);
444 l2 = editBox.lines.first;
445 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
447 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
448 editBox.SetViewToCursor(true);
458 static class ReplaceTextAction : UndoAction
460 int y1, x1, y2, x2, y3, x3;
464 int addedSpaces, addedTabs, xAdjustment;
466 type = class(ReplaceTextAction);
469 void Print(EditBox editBox)
471 PrintLn("ReplaceText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", y3 = ", y3, ", x3 = ", x3, ", oldString = ", oldString, ", newString = ", newString, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs, ", placeAfter = ", placeAfter);
474 void Undo(EditBox editBox)
479 editBox.GoToPosition(null, y1, x1);
481 l3 = editBox.lines.first;
482 for(c = 0; c < y3 && l3; c++, l3 = l3.next);
484 editBox.DelCh(l1, y1, x1, l3, y3, x3, true);
486 editBox.PutS(oldString);
487 if(addedSpaces || addedTabs)
488 editBox.DelCh(l1, y1, x1 - (addedSpaces + addedTabs), l1, y1, x1, false);
490 //editBox.PutS(oldString);
493 editBox.GoToPosition(null, y1, x1);
496 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
502 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
506 void Redo(EditBox editBox)
511 editBox.GoToPosition(null, y1, x1);
513 l2 = editBox.lines.first;
514 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
516 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
518 editBox.PutS(newString);
528 static class MoveTextAction : UndoAction
530 int fy1, fx1, fy2, fx2;
532 type = class(MoveTextAction);
534 void Undo(EditBox editBox)
539 void Redo(EditBox editBox)
545 public class EditLine : struct
553 property const char * text
557 // Only works for single line edit for now...
558 EditBox editBox = this.editBox;
562 get { return this ? buffer : null; }
564 property EditLine prev { get { return this ? prev : null; } };
565 property EditLine next { get { return this ? next : null; } };
566 property int count { get { return this ? count : 0; } };
573 // This makes sure the buffer always contains at least count characters
574 // Keeps a count/2 pad for avoiding always reallocating memory.
575 bool AdjustBuffer(int count)
583 newSize = (count + (count >> 1));
585 // Shrink down the buffer
588 buffer = new char[newSize];
589 if(!buffer) return false;
593 CopyBytes(buffer, this.buffer, count);
596 this.buffer = buffer;
600 // Increase the buffer
601 else if(size < count)
603 buffer = new char[newSize];
604 if(!buffer) return false;
608 CopyBytes(buffer, this.buffer, this.count + 1); // size);
611 this.buffer = buffer;
620 public struct BufferLocation
625 void AdjustDelete(BufferLocation start, BufferLocation end)
627 // Location is before, nothing to do
628 if(y < start.y || (y == start.y && x < start.x))
630 // Location is inside deleted bytes, point to the start
631 if((y >= start.y && (y > start.y || x >= start.x)) &&
632 (y >= end.y && (y > end.y || x >= end.x)))
637 // Location is on another line
639 y -= end.y - start.y;
640 // Location is the last touched line
647 //if(start.line == end.line)
648 x -= end.x - start.x;
657 // Assuming no carriage return before first character ???? fixed?
658 void AdjustAdd(BufferLocation start, BufferLocation end)
660 int numLines = end.y - start.y;
667 if(x > start.x || (x == start.x /*&& (numLines ? true : false)*/))
670 for(c = 0, line = start.line; c<numLines; c++)
673 //x += numLines ? end.x : (end.x - start.x);
674 x += end.x - start.x;
681 public enum EditBoxFindResult { notFound, found, wrapped };
683 static const char * keyWords1[] =
686 "return","break","continue","default","switch","case","if","else","for","while", "do","long","short",
687 "void", "char","int","float","double","signed","unsigned","static", "extern", "struct", "union", "typedef","enum",
689 "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line",
690 "__attribute__", "__stdcall", "_stdcall",
691 "__declspec", "goto",
692 "inline", "__inline__", "_inline", "__inline", "__typeof","__extension__",
693 "asm", "__asm", "_asm", "volatile", "#cpu", "__stdcall__",
694 "__restrict__", "__restrict", "restrict",
697 "class", "private", "public",
699 "delete", "new", "new0", "renew", "renew0", "define",
702 "dllexport", "dllimport", "stdcall",
703 "subclass", "__on_register_module", "namespace", "using",
704 "typed_object", "any_object", "incref", "register", "watch", "stopwatching", "firewatchers", "watchable", "class_designer",
705 "class_fixed", "class_no_expansion", "isset", "class_default_property", "property_category", "class_data", "class_property",
706 "virtual", "thisclass","unichar", "dbtable", "dbindex", "database_open", "dbfield",
709 "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64", "uintptr", "intptr", "intsize", "uintsize",
712 "this", "true", "false", "null", "value",
721 static const char * keyWords2[] =
723 "defined", "warning",
724 "include", "pragma", "elif", "ifdef", "ifndef", "endif", "undef", "line",
728 static const char ** keyWords[] = { keyWords1, keyWords2 };
729 #define NUM_KEYWORD_GROUPS (sizeof(keyWords) / sizeof(char **))
730 //static int * keyLen[NUM_KEYWORD_GROUPS];
731 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
733 static char searchString[1025], replaceString[1025];
734 static bool matchCase = false, wholeWord = false, searchUp = false;
736 static GoToDialog goToDialog
738 autoCreate = false, isModal = true, text = $"Go To"
741 public class EditBox : CommonControl
743 class_property(icon) = "<:ecere>controls/editBox.png";
747 virtual bool Window::NotifyModified(EditBox editBox);
748 virtual void Window::NotifyCaretMove(EditBox editBox, int line, int charPos);
749 virtual void Window::NotifyUpdate(EditBox editBox);
750 virtual bool Window::NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods);
751 virtual void Window::NotifyOvrToggle(EditBox editBox, bool overwrite);
752 virtual bool Window::NotifyKeyDown(EditBox editBox, Key key, unichar ch);
754 virtual bool Window::NotifyCharsAdded(EditBox editBox, BufferLocation before, BufferLocation after, bool pasteOperation);
755 virtual bool Window::NotifyCharsDeleted(EditBox editBox, BufferLocation beforeLoc, BufferLocation after, bool pasteOperation);
756 virtual bool Window::NotifyDropped(EditBox editBox, int x, int y);
758 virtual bool Window::NotifyUnsetModified(EditBox editBox);
760 // Why was this commented out?
761 // It is required otherwise updating font property from property sheet doesn't immediately reflect in form designer,
762 // and the scrollArea property isn't compared properly either.
766 if(font) ComputeFont();
767 SetInitSize(initSize);
773 style.vScroll = true;
779 style.hScroll = true;
783 property bool textHorzScroll { property_category $"Behavior" set { style.hScroll = value; } get { return style.hScroll; } }; // Should cut the text on set to false
784 property bool textVertScroll { property_category $"Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
785 property bool readOnly
787 property_category $"Behavior"
790 style.readOnly = value;
791 itemEditCut.disabled = value || !selection;
792 itemEditDelete.disabled = value || !selection;
793 itemEditPaste.disabled = value;
795 get { return style.readOnly; }
797 property bool multiLine { property_category $"Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
798 property bool freeCaret { property_category $"Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
799 property bool tabKey { property_category $"Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
800 property int tabSize { property_category $"Behavior" set { tabSize = value; } get { return tabSize; } };
801 property bool tabSelection { property_category $"Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
802 property bool smartHome { property_category $"Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
803 property bool autoEmpty { property_category $"Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
804 property bool noCaret { property_category $"Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
805 property int maxLineSize { property_category $"Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
806 property int maxNumLines { property_category $"Behavior" set { maxLines = value; } get { return maxLines; } };
807 property bool useTab { property_category $"Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
808 property bool syntaxHighlighting { property_category $"Appearance" set { style.syntax = value; } get { return style.syntax; } };
809 property bool noSelect { property_category $"Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
810 property bool allCaps { property_category $"Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
811 property bool autoSize { property_category $"Behavior" set { style.autoSize = value; } get { return style.autoSize; } };
812 property bool wrap { set { style.wrap = value; Update(null); } get { return style.wrap; } };
813 //property bool lineNumbers { set { style.lineNumbers = value; } get { return style.lineNumbers; } };
814 property int numLines { get { return this ? lineCount : 0; } };
815 property int lineNumber { get { return y; } }; // TODO: Change to property of EditLine this.line.number
816 property int column { get { return col; } }; // TODO: Add Set
817 property int charPos { get { return x; } }; // TODO: Add Set
818 property EditLine firstLine { get { return lines.first; } }; // Change these to a List<EditLine>... (this.lines[10].text)
819 property EditLine lastLine { get { return lines.last; } };
820 property EditLine line { get { return this.line; } }; // TODO: Add Set this.line = this.lines[10]
821 property const char * contents
823 property_category $"Data"
830 undoBuffer.dontRecord++;
832 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
835 //SetViewToCursor(true);
838 undoBuffer.dontRecord--;
844 char * buffer = null;
847 /* Can't implement this right now because of memory leak... Need string reference counting...
854 for(line = lines.first; line; line = line.next)
855 len += strlen(line.buffer);
857 buffer = new char[len+1];
859 for(line = lines.first; line; line = line.next)
861 int lineLen = strlen(line.buffer);
862 memcpy(buffer + len, line.buffer, lineLen);
868 buffer = this.line ? this.line.buffer : null;
873 property bool overwrite { get { return overwrite; } };
874 property bool caretFollowsScrolling { get { return style.cursorFollowsView; } set { style.cursorFollowsView = value; } }
876 property char * multiLineContents
880 char * buffer = null;
887 for(line = lines.first; line; line = line.next)
888 len += strlen(line.buffer)+1;
890 buffer = new char[len+1];
892 for(line = lines.first; line; line = line.next)
894 int lineLen = strlen(line.buffer);
895 memcpy(buffer + len, line.buffer, lineLen);
897 if(line.next) buffer[len++] = '\n';
910 return this.line.buffer;
915 void SetLineText(const char * text)
919 EditLine_SetText(this.line, text);
923 property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
924 property Color selectionText { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
925 property SyntaxColorScheme syntaxColorScheme { set { delete colorScheme; colorScheme = value; incref colorScheme; } }
926 property bool recordUndoEvent { set { undoBuffer.recordAsOne = value; undoBuffer.firstEvent = true; } get { return undoBuffer.recordAsOne; } };
928 // selectionStart.line, selectionStart.column (With Set)
929 // selection.line1, selection.line2, selection.column1, selection.column2 (Read only)
943 // Position of Caret (Not necessarily displayed position)
946 // Position of beginning of block (Block ends at (x,y))
948 // line is line at carret, selLine is line at beginning of block
949 EditLine line, selLine, dropLine;
950 // Mouse selection Moving virtual caret
955 // ViewX is x offset in pixels, ViewY is y offset in lines
957 // viewLine is first displayed line
960 // start and end of area to redraw
963 // MaxLine is the longest line
965 // MaxLength is the longest line's length
968 // MouseSelect is true once button is pressed, overwrite is true if mode is on
969 bool mouseSelect, mouseMove, overwrite, wordSelect;
970 // Timer is used for mouse selection scrolling
973 window = this, delay = 0.1;
982 OnMouseMove(mouseX, mouseY, -1);
987 // (mouseX,mouseY) is the position of the mouse in the edit box
992 void (* FontExtent)(Display display, Font font, const char * text, int len, int * width, int * height, int prevGlyph, int * rPrevGlyph, int * overHang);
995 bool rightButtonDown;
998 UndoBuffer undoBuffer { data = this };
1000 ColorAlpha selectionColor, selectionText;
1001 SyntaxColorScheme colorScheme { };
1006 Menu editMenu { menu, $"Edit", e };
1007 MenuItem itemEditCut
1009 editMenu, $"Cut\tCtrl+X", t, disabled = true;
1011 bool NotifySelect(MenuItem item, Modifiers mods)
1013 if(!(style.readOnly))
1018 MenuItem itemEditCopy
1020 editMenu, $"Copy\tCtrl+C", c, disabled = true;
1022 bool NotifySelect(MenuItem item, Modifiers mods)
1028 MenuItem itemEditPaste
1030 editMenu, $"Paste\tCtrl+V", p;
1032 bool NotifySelect(MenuItem item, Modifiers mods)
1034 if(!(style.readOnly))
1039 MenuItem itemEditDelete
1041 editMenu, $"Delete\tDel", d, disabled = true;
1043 bool NotifySelect(MenuItem item, Modifiers mods)
1045 if(!(style.readOnly))
1050 MenuDivider { editMenu };
1051 MenuItem itemEditSelectAll
1053 editMenu, $"Select All\tCtrl+A", a;
1055 bool NotifySelect(MenuItem item, Modifiers mods)
1061 MenuDivider { editMenu };
1062 MenuItem itemEditUndo
1064 editMenu, $"Undo\tCtrl+Z", u;
1067 bool NotifySelect(MenuItem item, Modifiers mods)
1073 MenuItem itemEditRedo
1075 editMenu, $"Redo\tCtrl+Y", o;
1078 bool NotifySelect(MenuItem item, Modifiers mods)
1084 MenuDivider { editMenu };
1087 editMenu, $"Find Previous\tShift-F3", e, shiftF3;
1089 bool NotifySelect(MenuItem item, Modifiers mods)
1092 Find(searchString, wholeWord, matchCase, false);
1094 itemEditFind.NotifySelect(this, item, mods);
1100 editMenu, $"Find Next\tF3", n, f3;
1102 bool NotifySelect(MenuItem item, Modifiers mods)
1105 Find(searchString, wholeWord, matchCase, true);
1107 itemEditFind.NotifySelect(this, item, mods);
1111 MenuItem itemEditFind
1113 editMenu, $"Find...\tCtrl+F", f, ctrlF;
1115 bool NotifySelect(MenuItem item, Modifiers mods)
1119 editBox = this, master = master, isModal = true, searchString = searchString, matchCase = matchCase, wholeWord = wholeWord,
1120 searchUp = searchUp;
1123 // Fix dialog from above shouldn't be visible
1124 // void NotifyDestroyed(FindDialog dialog, DialogResult result)
1125 void NotifyDestroyed(Window window, DialogResult result)
1127 FindDialog dialog = (FindDialog) window;
1128 searchUp = dialog.searchUp;
1129 strcpy(searchString, dialog.searchString);
1130 matchCase = dialog.matchCase;
1131 wholeWord = dialog.wholeWord;
1140 editMenu, $"Replace...\tCtrl+R", r, ctrlR;
1142 bool NotifySelect(MenuItem item, Modifiers mods)
1144 ReplaceDialog dialog
1148 searchString = searchString,
1149 replaceString = replaceString,
1150 matchCase = matchCase,
1151 wholeWord = wholeWord,
1155 // void NotifyDestroyed(ReplaceDialog dialog, DialogResult result)
1156 void NotifyDestroyed(Window window, DialogResult result)
1158 ReplaceDialog dialog = (ReplaceDialog)window;
1159 const char * replace = dialog.replaceString;
1161 strcpy(replaceString, replace);
1162 strcpy(searchString, dialog.searchString);
1163 matchCase = dialog.matchCase;
1164 wholeWord = dialog.wholeWord;
1171 MenuDivider { editMenu };
1174 editMenu, $"Go To...\tCtrl+G", g, ctrlG;
1176 bool NotifySelect(MenuItem item, Modifiers mods)
1178 goToDialog.editBox = this;
1179 goToDialog.master = master;
1180 goToDialog.Create();
1184 MenuDivider { editMenu };
1185 MenuItem itemEditInsertTab
1187 editMenu, $"Insert Tabs", i, checkable = true;
1189 bool NotifySelect(MenuItem item, Modifiers mods)
1191 style.useTab = item.checked;
1197 snapVertScroll = true;
1198 snapHorzScroll = true;
1202 static bool syntaxInit = false;
1207 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
1209 for(c = 0; keyWords[g][c]; c++);
1210 //keyLen[g] = new int[c];
1211 for(c = 0; keyWords[g][c]; c++)
1213 keyLen[g][c] = strlen(keyWords[g][c]);
1217 colorScheme.commentColor = dimGray;
1218 colorScheme.charLiteralColor = crimson;
1219 colorScheme.stringLiteralColor = crimson;
1220 colorScheme.preprocessorColor = green;
1221 colorScheme.numberColor = teal;
1222 colorScheme.keywordColors = [ blue, blue ];
1225 FontExtent = Display::FontExtent2;
1227 lines.offset = (uint)(uintptr)&((EditLine)0).prev;
1229 style = EditBoxBits { hScroll = true };
1231 // cursor = guiApp.GetCursor(IBeam);
1233 // Default Properties
1235 maxLineSize = 1024;*/
1238 maxLineSize = MAXINT;
1243 mouseSelect = this.mouseMove = false;
1246 x = selX = selY = 0;
1249 line = selLine = null;
1255 endY = clientSize.h;
1258 // Default space to 8 in case window is not constructed
1261 undoBuffer.dontRecord++;
1263 undoBuffer.dontRecord--;
1265 viewLine = lines.first;
1266 style.recomputeSyntax = true;
1272 UpdateCaretPosition(true);
1278 lines.Free(EditLine::Free);
1281 void FlushBuffer(Surface surface, EditLine line, int wc, int * renderStart, int * x, int y, int * previousGlyph, int * oh, int numSpaces, bool drawSpaces, Box box)
1283 int count = wc - *renderStart;
1286 if(y + space.h >= box.top && y <= box.bottom)
1293 //FontExtent(display, font, line.buffer + *renderStart, count, &w, null);
1294 surface.TextFont(font);
1295 surface.TextExtent2(line.buffer + *renderStart, count, &w, null, *previousGlyph, null, &coh);
1297 if(*x + w + coh + XOFFSET > 0)
1299 surface.WriteText2(XOFFSET + *x,y, line.buffer + *renderStart, count, *previousGlyph, previousGlyph);
1301 //surface.WriteText(XOFFSET + *x,y, line.buffer + *renderStart, count);
1307 w = numSpaces; // * space.w;
1308 if(*x + w + XOFFSET > 0 && (drawSpaces))
1309 surface.Area(XOFFSET + *x /*- 1*/ + *oh, y, XOFFSET + *x + w, y + space.h-1);
1310 // WHATS UP WITH THIS... surface.Area(XOFFSET + *x, y, XOFFSET + *x + w, y + space.h-1);
1318 bool CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected,
1319 Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, int *overwrite)
1325 if((wc == selX && line == selLine) || (wc == editX && line == this.line))
1329 *foreground = (*selected) ? selectionForeground : textColor;
1330 *background = selectionBackground;
1331 *opacity = *selected;
1338 if(this.overwrite && active)
1340 if((style.stuckCaret && wc == line.count && !line.next) ||
1341 (!mouseMove && line == this.line && wc == editX))
1350 void FigureStartSyntaxStates(EditLine firstLine, bool reset)
1354 bool inMultiLineComment = reset ? false : style.inMultiLineComment;
1355 bool wasInMultiLine = reset ? false : style.wasInMultiLine;
1356 bool inString = false;
1357 bool inQuotes = false;
1358 bool inPrep = reset ? false : style.inPrep;
1359 bool inSingleLineComment = false;
1360 bool escaped = reset ? false : style.escaped;
1361 bool continuedSingleLineComment = reset ? false : style.continuedSingleLineComment;
1362 bool continuedString = reset ? false : style.continuedString;
1363 bool continuedQuotes = reset ? false : style.continuedQuotes;
1365 EditLine line = reset ? lines.first : firstLine;
1366 // int maxBackUp = 1000, c;
1367 // for(c = 0, line = viewLine; c < maxBackUp && line && line.prev; line = line.prev);
1368 for(; line != viewLine; line = line.next)
1370 char * text = line.buffer;
1373 bool lastWasStar = false;
1374 bool firstWord = true;
1375 if(!escaped) inPrep = false;
1376 inSingleLineComment = continuedSingleLineComment;
1378 inString = continuedString;
1379 inQuotes = continuedQuotes;
1382 for(c = 0; (ch = text[c]); c++)
1384 bool wasEscaped = escaped;
1385 bool backLastWasStar = lastWasStar;
1386 bool backWasInMultiLine = wasInMultiLine;
1388 lastWasStar = false;
1389 wasInMultiLine = inMultiLineComment;
1392 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1394 if(text[c+1] == '/')
1396 inSingleLineComment = true;
1398 else if(text[c+1] == '*')
1400 inMultiLineComment = true;
1403 else if(backLastWasStar)
1404 inMultiLineComment = false;
1408 if(backWasInMultiLine) lastWasStar = true;
1410 else if(ch == '\"' && !inSingleLineComment && !inMultiLineComment && !inQuotes)
1412 if(inString && !wasEscaped)
1421 else if(ch == '\'' && !inSingleLineComment && !inMultiLineComment && !inString)
1423 if(inQuotes && !wasEscaped)
1435 else if(ch == '#' && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1442 else if(ch != ' ' && ch != '\t')
1445 if(line.count && line.text[line.count - 1] == '\\')
1447 continuedSingleLineComment = inSingleLineComment;
1448 continuedString = inString;
1449 continuedQuotes = inQuotes;
1453 continuedSingleLineComment = false;
1454 continuedString = false;
1455 continuedQuotes = false;
1459 style.continuedSingleLineComment = continuedSingleLineComment;
1460 style.continuedString = continuedString;
1461 style.continuedQuotes = continuedQuotes;
1462 style.inMultiLineComment = inMultiLineComment;
1463 style.wasInMultiLine = wasInMultiLine;
1464 style.inPrep = inPrep;
1465 style.escaped = escaped;
1469 /*void OnDrawOverChildren(Surface surface)
1471 if(style.lineNumbers)
1473 int currentLineNumber = this.viewY + 1;
1476 for(i = 0; i * space.h < box.height; i++)
1478 // ********* LINE NUMBERING *********
1479 surface.SetForeground(Color{60, 60, 60});
1480 //Highlight current line
1481 if(this.caretY / space.h == currentLineNumber - 1)
1482 surface.SetBackground(Color{220, 220, 220});
1484 surface.SetBackground(Color{230, 230, 230});
1485 surface.textOpacity = true;
1487 sprintf(lineText,"%5u ", currentLineNumber % 100000);
1488 if(currentLineNumber > this.lineCount)
1489 surface.WriteText(0,i*space.h+1," ",6);
1491 surface.WriteText(0,i*space.h+1,lineText,6);
1493 currentLineNumber++;
1498 static inline int countTabsExtraSpaces(char * buffer, int tabSize, int start, int end)
1500 // TODO: Handle tabs position properly with UTF-8 (and everywhere else)
1501 int p = 0, i, extra = 0;
1502 for(i = 0; i < end; i++)
1504 if(buffer[i] == '\t')
1506 int t = tabSize - (p % tabSize);
1517 void OnRedraw(Surface surface)
1521 bool selected = false, selection = true;
1523 Color selectionBackground = selectionColor ? selectionColor : SELECTION_COLOR;
1524 Color selectionForeground = selectionText ? selectionText : SELECTION_TEXT;
1525 Color defaultTextColor = property::foreground;
1528 int maxW = clientSize.w;
1530 Color foreground, background;
1533 // Overwrite Caret Stuff
1535 int overWriteX = 0, overWriteY = 0;
1538 // ****** SYNTAX STATES ******
1539 bool inMultiLineComment = style.inMultiLineComment;
1540 bool inString = false;
1541 bool inQuotes = false;
1542 bool inPrep = style.inPrep;
1543 bool inSingleLineComment = false;
1544 bool escaped = style.escaped;
1545 bool continuedSingleLineComment = style.continuedSingleLineComment;
1546 bool continuedString = style.continuedString;
1547 bool continuedQuotes = style.continuedQuotes;
1548 bool wasInMultiLine = style.wasInMultiLine;
1549 // ****** ************* ******
1551 // For drawing selection background
1552 EditLine selStartLine = this.y < this.selY ? this.line : this.selLine;
1553 EditLine selEndLine = this.y < this.selY ? this.selLine : this.line;
1554 int selStartX = this.y < this.selY || (this.y == this.selY && this.x < this.selX) ? this.x : this.selX;
1555 int selEndX = this.y > this.selY || (this.y == this.selY && this.x > this.selX) ? this.x : this.selX;
1556 ///////////////////////////////////
1559 defaultTextColor = Color { 85, 85, 85 };
1560 textColor = defaultTextColor;
1563 Abs(selectionBackground.r - property::background.r) +
1564 Abs(selectionBackground.g - property::background.g) +
1565 Abs(selectionBackground.b - property::background.b) < 92)
1567 selectionBackground = formColor;
1568 selectionForeground = selectionColor ? selectionColor : SELECTION_COLOR;
1571 surface.TextFont(this.font);
1573 surface.SetBackground(GDefaultPalette()[RND_Get(1, 15)]);
1574 surface.Area(0,0,MAXINT,MAXINT);
1579 surface.SetBackground(formColor);
1580 surface.Area(0,0,clientSize.w, clientSize.h);
1583 if(this.selX == this.x && this.selY == this.y)
1593 editX = Min(this.x,this.line.count);
1594 selX = Min(this.selX,this.selLine.count);
1597 selected = (this.selY < this.viewY) ^ (this.y < this.viewY);
1599 foreground = selected ? selectionForeground : textColor;
1600 background = selectionBackground;
1606 surface.SetForeground(foreground);
1607 surface.SetBackground(background);
1608 surface.TextOpacity(false);
1610 surface.GetBox(box);
1612 for(line = this.viewLine; line; line = line.next)
1614 int x = -this.viewX;
1618 Color newTextColor = textColor = defaultTextColor;
1619 bool lineComplete = false;
1622 // ****** SYNTAX HIGHLIGHTING ******
1623 bool lastWasStar = false;
1624 bool trailingSpace = false;
1625 bool firstWord = true;
1626 if(!escaped) inPrep = false;
1627 inSingleLineComment = continuedSingleLineComment;
1629 inString = continuedString;
1630 inQuotes = continuedQuotes;
1631 // *********************************
1633 /* === DEBUGGING TOOL FOR MAXLINE ===
1635 if(line == this.maxLine)
1637 surface.SetBackground(GREEN|0xFF000000);
1638 surface.TextOpacity(true);
1642 surface.TextOpacity(selected ? true : false);
1643 surface.SetBackground(selected ? SELECTION_COLOR|0xFF000000 : BLACK|0xFF000000);
1647 // Draw Selection Background all at once here
1648 if(selected || line == this.line || line == this.selLine)
1650 int sx = XOFFSET + x, sy = y;
1653 char * buffer = line.buffer;
1654 if(line != this.line && line != this.selLine)
1658 tw = clientSize.w - sx;
1663 surface.TextExtent2(buffer, line.count, &tw, &th, 0, null, &oh);
1664 tw += countTabsExtraSpaces(buffer, tabSize, 0, line.count) * space.w;
1667 else if(line == selStartLine)
1670 int start = Min(line.count, selStartX);
1671 int end = Min(line.count, selEndX);
1672 surface.TextExtent2(buffer, start, &tw, &th, 0, &prevGlyph, null);
1674 sx += countTabsExtraSpaces(buffer, tabSize, 0, start) * space.w;
1675 if(selStartX > start) sx += space.w * (selStartX - start);
1676 if(style.freeCaret && line != selEndLine)
1678 tw = clientSize.w - sx;
1681 else if(line != selEndLine)
1683 surface.TextExtent2(buffer + start, line.count - start, &tw, &th, prevGlyph, null, &oh);
1684 tw += countTabsExtraSpaces(buffer, tabSize, start, line.count) * space.w;
1688 surface.TextExtent2(buffer + start, end - start, &tw, &th, prevGlyph, null, &oh);
1689 tw += countTabsExtraSpaces(buffer, tabSize, start, end) * space.w;
1690 end = Max(end, selStartX);
1691 if(selEndX > end) { tw += space.w * (selEndX - end); th = space.h; }
1694 else if(line == selEndLine)
1696 int end = Min(line.count, selEndX);
1697 surface.TextExtent2(buffer, end, &tw, &th, 0, null, &oh);
1698 tw += countTabsExtraSpaces(buffer, tabSize, 0, end) * space.w;
1699 if(selEndX - end) { tw += space.w * (selEndX - end); th = space.h; }
1702 surface.Area(sx, sy, sx + tw-1, sy + th-1);
1705 if(line == this.selLine && line == this.line)
1707 end = Max(line.count, this.x);
1708 end = Max(end, this.selX);
1710 else if(line == this.selLine)
1711 end = Max(line.count, this.selX);
1712 else if(line == this.line)
1713 end = Max(line.count, this.x);
1721 bool spacing = true;
1722 bool cantHaveWords = false;
1729 lineComplete = false;
1732 textColor = newTextColor;
1735 foreground = textColor;
1736 surface.SetForeground(textColor);
1740 for(; c<end && !cantHaveWords;)
1744 bufferLen += wordLen;
1748 /*if(line.count > 4000)
1755 for(; c<line.count; c++)
1757 unichar ch = line.buffer[c];
1758 unichar bf = (wordLen == 1) ? line.buffer[c-1] : 0;
1759 //if(ch == ' ' || ch == '\t' || (wordLen && (ch == '(' || ch == ')' || ch == ';' || ch == ':')) || (wordLen == 1 && line.buffer[c-1] == '('))
1760 if(CharMatchCategories(ch, separators) || /*ch == ' ' ||*/ ch == '\t' ||
1761 (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && ch != '#' /*&& ch != '_'*/) ||
1762 (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && bf != '#' && bf != '\t' /*&& bf != '_' && bf != ' '*/))
1765 trailingSpace = false;
1771 for(; c<line.count; c++)
1773 unichar ch = line.buffer[c];
1774 if(ch == '\t' || ch == ' ')
1776 cantHaveWords = true;
1777 if(ch == ' ' && c == line.count-1)
1778 trailingSpace = true;
1782 if(ch != ' ' && ch != '\t')
1787 if(c == line.count && c < end)
1796 cantHaveWords = true;
1801 lastWasStar = false;
1807 bool backEscaped = escaped;
1808 bool backLastWasStar = lastWasStar;
1809 bool backInMultiLineComment = inMultiLineComment;
1810 bool backInString = inString;
1811 bool backInQuotes = inQuotes;
1812 bool backInPrep = inPrep;
1813 bool backInSingleLineComment = inSingleLineComment;
1814 bool backWasInMultiLine = wasInMultiLine;
1816 char * word = line.buffer + c - wordLen;
1818 bool wasEscaped = escaped;
1820 lastWasStar = false;
1822 wasInMultiLine = inMultiLineComment;
1824 // Determine Syntax Highlighting
1825 newTextColor = defaultTextColor;
1828 if(inSingleLineComment || inMultiLineComment)
1830 newTextColor = colorScheme.commentColor;
1834 newTextColor = colorScheme.charLiteralColor;
1838 newTextColor = colorScheme.stringLiteralColor;
1842 newTextColor = colorScheme.preprocessorColor;
1844 if(wordLen == 1 && word[0] == '/')
1846 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1850 inSingleLineComment = true;
1851 newTextColor = colorScheme.commentColor;
1853 else if(word[1] == '*')
1855 inMultiLineComment = true;
1856 newTextColor = colorScheme.commentColor;
1859 else if(backLastWasStar)
1860 inMultiLineComment = false;
1862 else if(wordLen == 1 && word[0] == '*')
1864 if(backWasInMultiLine)
1867 else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && wordLen == 1 && word[0] == '\"')
1869 if(inString && !wasEscaped)
1876 newTextColor = colorScheme.stringLiteralColor;
1879 else if(!inSingleLineComment && !inMultiLineComment && !inString && wordLen == 1 && word[0] == '\'')
1881 if(inQuotes && !wasEscaped)
1886 newTextColor = colorScheme.charLiteralColor;
1889 else if(wordLen == 1 && word[0] == '\\')
1894 else if(x < box.right && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && (isdigit(word[0]) || (word[0] == '.' && isdigit(word[1]))))
1896 char * dot = word[wordLen] == '.' ? word + wordLen : (word[0] == '.' && (word == line.buffer || word[-1] == '-' || isspace(word[-1])) ? word : null);
1897 bool isReal = dot != null;
1904 bool isHex = (word[0] == '0' && (word[1] == 'x' || word[1] == 'X'));
1907 exponent = strchrmax(word, 'p', wordLen);
1908 if(!exponent) exponent = strchrmax(word, 'P', wordLen);
1912 exponent = strchrmax(word, 'e', wordLen);
1913 if(!exponent) exponent = strchrmax(word, 'E', wordLen);
1915 isReal = exponent != null;
1918 strtod(word, &s); // strtod() seems to break on hex floats (e.g. 0x23e3p12, 0x1.fp3)
1920 strtol(word, &s, 0);
1926 int gotF = 0, gotL = 0, gotU = 0, gotI = 0;
1929 for(i = 0; valid && i < 5 && (ch = s[i]) && (isalnum(ch) || ch == '_'); i++)
1933 case 'f': case 'F': gotF++; if(gotF > 1 || !isReal) valid = false; break;
1936 if(gotL > 2 || (isReal && (gotL == 2 || gotF)) || (gotL == 2 && (s[i-1] != ch)))
1939 case 'u': case 'U': gotU++; if(gotU > 1 || isReal) valid = false; break;
1940 case 'i': case 'I': case 'j': case 'J': gotI++; if(gotI > 1) valid = false; break;
1941 default: valid = false;
1945 // Don't highlight numbers with too many decimal points
1946 if(s[0] == '.' && isdigit(s[1]))
1949 while(s[0] == '.' && isdigit(s[1]))
1951 int newWordLen = s - word;
1952 c += newWordLen - wordLen;
1953 wordLen = newWordLen;
1956 newWordLen = s - word;
1957 c += newWordLen - wordLen;
1958 wordLen = newWordLen;
1962 int newWordLen = s + i - word;
1963 newTextColor = colorScheme.numberColor;
1964 c += newWordLen - wordLen;
1965 wordLen = newWordLen;
1967 else if(dot && dot > word && dot < s)
1968 newTextColor = colorScheme.numberColor;
1973 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && word[0] == '#')
1978 newTextColor = wordLen == 1 ? colorScheme.keywordColors[1] : colorScheme.preprocessorColor;
1981 if(x < box.right && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1983 for(g = 0; g < ((inPrep && word[0] != '#') ? 2 : 1); g++)
1985 const char ** keys = keyWords[g];
1986 int * len = keyLen[g];
1987 for(ccc = 0; keys[ccc]; ccc++)
1989 if(len[ccc] == wordLen && !strncmp(keys[ccc], word, wordLen))
1991 newTextColor = colorScheme.keywordColors[g];
2000 // If highlighting for this word is different, break
2001 if(newTextColor != textColor)
2005 // Better solution than going back?
2008 // Reset syntax flags
2009 escaped = backEscaped;
2010 lastWasStar = backLastWasStar;
2011 inMultiLineComment = backInMultiLineComment;
2012 inString = backInString;
2013 inQuotes = backInQuotes;
2014 inPrep = backInPrep;
2015 inSingleLineComment = backInSingleLineComment;
2016 wasInMultiLine = backWasInMultiLine;
2021 textColor = newTextColor;
2024 foreground = textColor;
2025 surface.SetForeground(textColor);
2032 // If we're not breaking, this can't be rendered as spacing anymore
2035 // Do word wrapping here
2042 FontExtent(display, font, line.buffer + start, bufferLen + wordLen, &tw, null, 0, null, &oh);
2047 w += numSpaces * space.w;
2049 if(x + viewX + w > maxW)
2051 // Avoid an endless loop until we fix wrapping
2052 if(c - wordLen > start)
2055 lineComplete = true;
2061 bufferLen += wordLen;
2066 int renderStart = start;
2068 bool flagTrailingSpace = false;
2071 int previousGlyph = 0;
2073 // Render checking if we need to split because of selection or to find where to draw insert caret
2074 for(wc = start; wc < start + bufferLen; wc++)
2076 flush = CheckColors(line, wc, selection, selX, editX, &selected, selectionForeground,
2077 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite);
2080 overWriteCh = (wc < line.count) ? line.buffer[wc] : ' ';
2081 if(overWriteCh == '\t') overWriteCh = ' ';
2086 flagTrailingSpace = numSpaces && trailingSpace && style.syntax && start + bufferLen == line.count && line != this.line;
2087 if(flagTrailingSpace) surface.SetBackground(red);
2088 FlushBuffer(surface, line, wc, &renderStart, &x, y, &previousGlyph, &overHang, numSpaces, flagTrailingSpace, box);
2089 if(flagTrailingSpace) surface.SetBackground(background);
2098 surface.SetForeground(foreground);
2105 if(wc < line.count && line.buffer[wc] == '\t')
2107 numSpaces += (tabSize * space.w) - ((x + numSpaces + viewX) % (tabSize * space.w));
2111 numSpaces += space.w;
2115 flagTrailingSpace = numSpaces && trailingSpace && style.syntax && start + bufferLen == line.count && line != this.line;
2116 if(flagTrailingSpace) surface.SetBackground(red);
2117 FlushBuffer(surface, line, wc, &renderStart, &x, y, &previousGlyph, &overHang, numSpaces, flagTrailingSpace, box);
2118 if(flagTrailingSpace) surface.SetBackground(background);
2123 if(CheckColors(line, c, selection, selX, editX, &selected, selectionForeground,
2124 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite))
2133 surface.SetBackground(background);
2134 surface.SetForeground(foreground);
2137 if(line.count && line.text[line.count - 1] == '\\')
2139 continuedSingleLineComment = inSingleLineComment;
2140 continuedString = inString;
2141 continuedQuotes = inQuotes;
2145 continuedSingleLineComment = false;
2146 continuedString = false;
2147 continuedQuotes = false;
2151 if(y > box.bottom) // >=clientSize.h)
2157 surface.TextOpacity(true);
2158 surface.SetForeground(black);
2159 surface.SetBackground(Color {255,255,85});
2160 surface.WriteText(XOFFSET + overWriteX,overWriteY, &overWriteCh, 1);
2164 void FixScrollArea()
2166 if(style.hScroll || style.vScroll)
2168 int width = maxLength + XOFFSET;
2169 int height = lineCount * space.h;
2170 if(style.freeCaret && line)
2175 width = Max(line.length + (x - line.count) * space.w, maxLength + XOFFSET);
2179 if(selX > selLine.count)
2180 width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength + XOFFSET);
2185 SetScrollLineStep(8, space.h);
2186 SetScrollArea(width, height, true);
2190 void ComputeLength(EditLine line)
2195 for(c = 0; c < line.count; )
2204 for(len = 0; c < line.count; c += numBytes)
2206 ch = line.buffer[c];
2207 numBytes = UTF8_NUM_BYTES(ch);
2209 if(ch == ' ' || ch == '\t')
2216 if(!len && ch == ' ')
2221 else if(!len && ch == '\t')
2223 w = (tabSize * space.w) - (x % (tabSize * space.w));
2229 FontExtent(display, font, line.buffer + start, len, &w, null, 0, null, &oh);
2241 if(line.length > this.maxLength)
2243 this.maxLine = line;
2244 this.maxLength = line.length;
2246 if(style.autoSize) AutoSize();
2255 this.maxLine = null;
2257 for(line = lines.first; line; line = line.next)
2259 if(line.length > this.maxLength)
2261 this.maxLength = line.length;
2262 this.maxLine = line;
2266 if(style.autoSize) AutoSize();
2271 if(this.selY != this.y)
2273 else if(this.selX != this.x) // commented out to erase caret: if(this.selX != this.x)
2277 void ComputeColumn()
2279 // Adjust new edit X position according to tabs
2280 int c, position = 0;
2283 for(c = 0; c<this.line.count && c<this.x && (ch = UTF8_GET_CHAR(this.line.buffer + c, nb)); c+= nb)
2285 // TODO: MIGHT WANT TO RETHINK WHAT COLUMN SHOULD BE REGARDING TABS
2287 position += this.tabSize - (position % this.tabSize);
2291 position += this.x - c;
2292 this.col = position;
2295 void DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
2297 _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, true, null);
2300 bool HasCommentOrEscape(EditLine line)
2302 bool hadComment = strstr(line.buffer, "/*") || strstr(line.buffer, "*/");
2307 for(c = line.count-1; c >= 0; c--)
2309 char ch = line.buffer[c];
2315 else //if(ch != ' ' && ch != '\t')
2322 int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, bool highlight, int * addedSpacesPtr)
2324 EditLine line = l1, next;
2326 int oldCount1, oldCount2;
2327 int y, firstViewY, firstY, firstDropY, firstSelY;
2330 DelTextAction action = null;
2331 bool hadComment = false;
2335 hadComment = HasCommentOrEscape(line);
2337 if(y2 > y1 || c2 > c1)
2339 if(start < l1.count)
2341 while(!UTF8_IS_FIRST(l1.buffer[start]) && start)
2345 oldCount1 = l1.count;
2347 while(c1 < oldCount1)
2349 byte ch = buffer[c1];
2350 if(UTF8_IS_FIRST(ch)) break;
2354 oldCount2 = l2.count;
2356 while(c2 < oldCount2)
2358 byte ch = buffer[c2];
2359 if(UTF8_IS_FIRST(ch)) break;
2364 if(!undoBuffer.dontRecord && (y2 > y1 || c2 > c1))
2369 len = GetText(null, l1, y1, start, l2, y2, c2, false, false);
2370 string = new char[len];
2371 action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter, noHighlight = !highlight };
2372 GetText(string, l1, y1, start, l2, y2, c2, false, false);
2376 //oldCount1 = l1.count;
2377 //oldCount2 = l2.count;
2380 BufferLocation before = { l1,y1,c1}, after = { l2,y2,c2 };
2381 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
2384 if(c2 > oldCount2) c2 = oldCount2;
2385 if(!(style.freeCaret))
2386 if(c1 > oldCount1) c1 = oldCount1;
2387 newLineCount = c1 + l2.count-c2;
2393 buffer = new char[line.size ? line.size : 1];
2395 buffer = new char[line.size];
2396 // TODO: Better handling of these allocation failures
2397 if(!buffer) return extras;
2398 CopyBytes(buffer,l2.buffer,oldCount1 + 1/*line.count + 1*//*line.size*/);
2403 // TODO: Better handling of these allocation failures
2404 if(!line.AdjustBuffer(newLineCount))
2408 /*if(newLineCount > 4000 || newLineCount < 0)
2409 printf("Warning");*/
2411 line.count = newLineCount;
2413 memmove(l1.buffer+c1, buffer+c2,line.count-c1);
2418 action.addedSpaces = c1-oldCount1;
2420 if(action.addedSpaces > action.x1)
2426 if(addedSpacesPtr) *addedSpacesPtr = c1-oldCount1;
2427 FillBytes(l1.buffer+oldCount1,' ',c1-oldCount1);
2429 line.buffer[line.count] = '\0';
2436 this.dropX -= c2-c1;
2437 this.dropX = Max(this.dropX,0);
2439 this.x = Max(this.x,0);
2446 firstViewY = this.viewY;
2448 firstDropY = this.dropY;
2449 firstSelY = this.selY;
2450 for(line = l1;line;line = next, y++)
2458 if(line == this.viewLine)
2460 if(this.viewLine.next)
2462 this.viewLine = this.viewLine.next;
2464 style.recomputeSyntax = true;
2468 this.viewLine = this.viewLine.prev;
2470 style.recomputeSyntax = true;
2473 else if(y < firstViewY)
2475 if(line == this.line)
2479 this.line = this.line.next;
2480 this.x = this.line.count;
2485 this.line = this.line.prev;
2486 this.x = this.line.count;
2493 if(line == this.dropLine)
2495 if(this.dropLine.next)
2497 this.dropLine = this.dropLine.next;
2498 this.dropX = this.dropLine.count;
2502 this.dropLine = this.dropLine.prev;
2503 this.dropX = this.dropLine.count;
2507 else if(y < firstDropY)
2509 if(line == this.selLine)
2511 if(this.selLine.next)
2513 this.selLine = this.selLine.next;
2514 this.selX = this.selLine.count;
2518 this.selLine = this.selLine.prev;
2519 this.selX = this.selLine.count;
2523 else if(y < firstSelY)
2527 if(line == l2) break;
2531 if(style.syntax && (hadComment || HasCommentOrEscape(this.line)))
2534 style.recomputeSyntax = true;
2539 bool DelSel(int * addedSpacesPtr)
2541 if(this.line != this.selLine || this.x != this.selX)
2543 if(this.selY < this.y)
2545 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, true, addedSpacesPtr);
2548 this.line = this.selLine;
2550 else if(this.selY > this.y)
2552 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, true, addedSpacesPtr);
2555 this.selLine = this.line;
2557 else if(this.selX < this.x)
2559 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, true, addedSpacesPtr);
2562 this.line = this.selLine;
2566 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, true, addedSpacesPtr);
2569 this.selLine = this.line;
2577 bool AddToLine(const char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr, int * xAdjustmentPtr)
2579 bool hadComment = false;
2580 // Add the line here
2581 EditLine line = this.line;
2583 // The purpose of this is solely to lock a max number of characters if no HSCROLLING is present
2584 if(!style.hScroll && created)
2586 int endX = (style.freeCaret) ? this.x : Min(this.x, line.count);
2591 // Lock if no place to display.
2594 else if(endX > this.x)
2595 max = Max(this.x, line.count);
2599 for(x = 0, c = 0; c < max+count; )
2603 const char * string;
2604 if(c < Min(this.x, line.count))
2605 string = line.buffer + c;
2608 else if(c < endX + count)
2609 string = stringLine + c - endX;
2611 string = line.buffer + c - endX - count;
2615 w = (tabSize * space.w) - (x % (tabSize * space.w));
2620 numBytes = UTF8_NUM_BYTES(*string);
2621 FontExtent(display, this.font, string, numBytes, &w, null, 0, null, &oh);
2625 if(x >= clientSize.w)
2630 c += numBytes; // - 1;
2636 int addedSpaces = 0;
2638 int xAdjustment = 0;
2640 // Add blank spaces if EES_FREECARET
2641 if(this.x > line.count)
2651 for(c = 0; c<line.count; c++)
2653 if(this.line.buffer[c] == '\t')
2654 position += this.tabSize - (position % this.tabSize);
2658 wantedPosition = position + (this.x - line.count);
2660 // A tab is too much...
2661 if(position + (this.tabSize - (position % this.tabSize)) > wantedPosition)
2662 addedSpaces = wantedPosition - position;
2665 xAdjustment = wantedPosition - position;
2669 position += this.tabSize - (position % this.tabSize);
2670 // Add as many tabs as needed
2671 addedTabs += (wantedPosition - position) / this.tabSize;
2672 position += (addedTabs-1) * this.tabSize;
2673 // Finish off with spaces
2674 addedSpaces = wantedPosition - position;
2676 xAdjustment -= addedSpaces + addedTabs;
2680 addedSpaces = this.x - line.count;
2683 this.x = Min(this.x, line.count);
2685 if(line.count + count + addedSpaces + addedTabs > this.maxLineSize)
2691 hadComment = HasCommentOrEscape(line);
2693 // Adjust the size of the line
2694 if(!line.AdjustBuffer(line.count+count+addedTabs+addedSpaces))
2698 BufferLocation before = { this.line, this.y, this.x }, after = { this.line, this.y, this.x };
2701 memmove(line.buffer+this.x+count, line.buffer+this.x,line.count-this.x);
2702 CopyBytes(line.buffer + this.x + addedTabs + addedSpaces, stringLine, count);
2705 FillBytes(line.buffer+line.count,'\t',addedTabs);
2707 if(addedTabs > 4000 || addedTabs < 0)
2710 line.count += addedTabs;
2715 FillBytes(line.buffer+line.count,' ',addedSpaces);
2717 if(addedSpaces > 4000 || addedSpaces < 0)
2720 line.count += addedSpaces;
2723 if(addedTabsPtr) *addedTabsPtr = addedTabs;
2724 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
2725 if(xAdjustmentPtr) *xAdjustmentPtr = xAdjustment;
2727 if(count > 4000 || count < 0)
2730 line.count += count;
2731 this.x += count + addedTabs + addedSpaces;
2734 this.selLine = this.line;
2736 line.buffer[line.count] = '\0';
2738 ComputeLength(line);
2743 hasComment = HasCommentOrEscape(line);
2744 if(!undoBuffer.insideRedo)
2746 int backDontRecord = undoBuffer.dontRecord;
2747 undoBuffer.dontRecord = 0;
2748 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
2749 undoBuffer.dontRecord = backDontRecord;
2751 if(style.syntax && (hadComment || hasComment || line != this.line))
2753 style.recomputeSyntax = true;
2761 void Emptyline(EditLine line, int y)
2764 DelCh(line, y, 0, line.next, y+1, 0, true);
2766 DelCh(line, y, 0, line, y, line.count, true);
2769 void GoToEnd(bool deselect)
2773 this.line = this.lines.last;
2774 if(this.y != this.lineCount-1)
2776 else if (this.x != this.line.count)
2777 DirtyLine(this.lineCount-1);
2778 this.y = this.lineCount-1;
2779 this.x = this.line.count;
2786 void GoToHome(bool deselect)
2790 this.line = this.lines.first;
2793 else if (this.x !=0)
2794 DirtyLine(this.lineCount-1);
2803 // Returns true if it needs scrolling
2804 bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
2809 bool needHScroll = false;
2816 line = this.viewLine ? (void *)this.viewLine.prev : null;
2821 line = (void *)this.lines.first;
2826 py = Min(py, clientSize.h);
2828 py = Min(py, this.lineCount);
2830 for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
2836 if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
2839 px = Min(px,clientSize.w+this.space.w);
2843 *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
2846 if(tline) *tline = line;
2849 // Prevent divide by 0 from non valid this.font
2851 return (y < this.viewY) || needHScroll;
2853 return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
2857 // Minimal Update Management Functions
2861 this.endY = clientSize.h-1;
2862 // ErrorLog("DirtyAll\n");
2865 void DirtyEnd(int y)
2867 if((y - this.viewY)*this.space.h < this.startY)
2868 this.startY = (y - this.viewY)*this.space.h+ YOFFSET;
2869 this.endY = clientSize.h-1;
2870 //ErrorLog("DirtyEnd %d\n", y);
2873 void DirtyLine(int y)
2877 if((y - this.viewY)*this.space.h < this.startY)
2878 this.startY = (y - this.viewY)*this.space.h + YOFFSET;
2879 if((y - this.viewY+1)*this.space.h > this.endY)
2880 this.endY = (y - this.viewY+1)*this.space.h-1 + YOFFSET;
2882 //ErrorLog("DirtyLine %d\n", y);
2889 if(style.recomputeSyntax)
2891 FigureStartSyntaxStates(lines.first, true);
2892 style.recomputeSyntax = false;
2895 if(this.startY > this.endY) return;
2896 if(this.startY <= 0 && this.endY >= clientSize.h-1)
2902 box.right = clientSize.w-1;
2903 box.top = this.startY;
2904 box.bottom = this.endY;
2907 this.startY = clientSize.h;
2911 bool IsMouseOnSelection()
2913 bool mouseOnSelection = false;
2916 int minY = Min(this.selY, this.y);
2917 int maxY = Max(this.selY, this.y);
2918 int minX = Min(this.selX, this.x);
2919 int maxX = Max(this.selX, this.x);
2921 FindMouse(this.mouseX - this.space.w / 2, this.mouseY, &x, &y, null, false);
2923 if(maxX != minX || maxY != minY)
2925 if(y > minY && y < maxY)
2926 mouseOnSelection = true;
2927 else if(y == minY && y == maxY)
2928 mouseOnSelection = (x < maxX && x >= minX);
2932 mouseOnSelection = (x >= this.selX);
2933 else if(y == this.y)
2934 mouseOnSelection = (x >= this.x);
2939 mouseOnSelection = (x < this.selX);
2940 else if(y == this.y)
2941 mouseOnSelection = (x < this.x);
2944 return mouseOnSelection;
2947 void UpdateCaretPosition(bool setCaret)
2951 if(mouseMove || !style.noCaret)
2953 int max = this.mouseMove ? this.dropX : this.x;
2954 int y = this.mouseMove ? this.dropY : this.y;
2955 EditLine line = this.mouseMove ? this.dropLine : this.line;
2957 if(!(style.freeCaret))
2958 max = Min(max, line.count);
2960 if(FontExtent && display)
2962 for(c = 0; c < max; )
2971 for(len = 0; c < Min(max, line.count); c += numBytes)
2973 ch = line.buffer[c];
2974 numBytes = UTF8_NUM_BYTES(ch);
2976 if(ch == ' ' || ch == '\t')
2983 if(!len && ch == ' ')
2988 else if(!len && ch == '\t')
2990 w = (tabSize * space.w) - (x % (tabSize * space.w));
2996 FontExtent(display, this.font, line.buffer + start, len, &w, null, 0, null, &oh);
3009 caretY = y * this.space.h;
3011 SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
3018 // TOFIX: Mismatch between NotifyCaretMove() and NotifyDropped() / GoToPosition()
3019 NotifyCaretMove(master, this, y + 1, x + 1);
3025 void SelectionEnables()
3027 if((x != selX || y != selY) && !selection)
3031 itemEditCut.disabled = false;
3032 itemEditDelete.disabled = false;
3034 itemEditCopy.disabled = false;
3036 this.selection = true;
3038 else if((x == selX && y == selY) && selection)
3040 itemEditCut.disabled = true;
3041 itemEditCopy.disabled = true;
3042 itemEditDelete.disabled = true;
3044 this.selection = false;
3048 void SetSelectCursor()
3050 if(!inactive || !style.noSelect)
3053 cursor = guiApp.GetCursor(arrow);
3054 else if(this.mouseSelect)
3055 cursor = guiApp.GetCursor(iBeam);
3058 if(IsMouseOnSelection())
3059 cursor = guiApp.GetCursor(arrow);
3061 cursor = guiApp.GetCursor(iBeam);
3066 int AdjustXPosition(EditLine line, int position, bool half, int * px, int max, int sc)
3069 int x = px ? *px : 0;
3075 if(c < Min(max, line.count))
3079 for(len = 0; c < Min(max, line.count); c += numBytes)
3081 ch = line.buffer[c];
3082 numBytes = UTF8_NUM_BYTES(ch);
3084 if(ch == ' ' || ch == '\t')
3091 if(!len && ch == ' ')
3096 else if(!len && ch == '\t')
3098 w = (tabSize * space.w) - (x % (tabSize * space.w));
3104 FontExtent(display, font, line.buffer + start, len, &w, null, 0, null, &oh);
3109 if(style.freeCaret && c < max)
3118 if(x + (((half && len == 1) ? (w / 2) : w)) >= position)
3123 int a = start + len;
3126 while(a > 0 && !UTF8_IS_FIRST(line.buffer[--a]));
3132 FontExtent(display, font, line.buffer + start, a - start, &w, null, 0, null, &oh);
3136 if(position > x + (half ? ((w + lastW) / 2) : lastW)) break;
3140 return Min(this.maxLineSize - 1, start + len);
3146 void SetCursorToViewX()
3148 bool selecting = this.x != selX || y != selY;
3150 // Horizontal Adjustment
3152 int c = AdjustXPosition(line, viewX, false, &x, MAXINT, 0);
3157 c = AdjustXPosition(line, viewX + clientSize.w - 1, false, &x, MAXINT, c);
3169 UpdateCaretPosition(false);
3175 void SetCursorToViewY()
3178 EditLine oldLine = this.line;
3180 bool selecting = this.x != this.selX || this.y != this.selY;
3182 numLines = clientSize.h / this.space.h;
3184 // Vertical Adjustment
3185 if(this.viewY > this.y)
3187 this.y = this.viewY;
3188 this.line = this.viewLine;
3191 if(this.viewY + numLines <= this.y)
3195 this.y = this.viewY-1;
3196 for(c = 0, line = this.viewLine; line && c<numLines; line = line.next, c++)
3203 if(this.line != oldLine)
3205 this.x = AdjustXPosition(this.line, caretX, true, null, MAXINT, 0);
3213 this.selLine = this.line;
3216 UpdateCaretPosition(false);
3223 bool SaveFile(const char * fileName)
3225 File f = eFile_Open(fileName, FO_WRITE);
3229 eWindow_SetModified(false);
3230 eInstance_Delete(f);
3237 bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
3241 if(!style.multiLine)
3243 x = (active && this.active && !style.readOnly) ? line.count : 0;
3246 SetViewToCursor(true);
3251 if(!active && modified)
3254 if(!NotifyModified(master, this))
3257 *goOnWithActivation = false;
3264 if(timer) timer.Stop();
3266 mouseSelect = false;
3274 int aw = maxLength + 12, ah = Max(lineCount, 1) * space.h + 2;
3275 int nw = minClientSize.w, nh = minClientSize.h, xw = maxClientSize.w, xh = maxClientSize.h;
3276 clientSize = { nw && aw < nw ? nw : xw && aw > xw ? xw : aw, nh && ah < nh ? nh : xh && ah > xh ? xh : ah };
3279 bool OnResizing(int *w, int *h)
3285 *w = space.h * 80 / 14;
3289 void OnResize(int w, int h)
3292 if(!hasHorzScroll && !hasVertScroll && viewLine)
3293 SetViewToCursor(true);
3295 //if(!hasHorzScroll && !hasVertScroll && viewLine)
3297 SetViewToCursor(true);
3302 bool OnMiddleButtonDown(int x, int y, Modifiers mods)
3304 if(style.readOnly) return true;
3305 // We really shouldn't be pasting here, Unix middle button paste is for the selection (not the clipboard), good for the terminal
3306 // Middle button already serves as a dragger as well.
3311 bool OnRightButtonDown(int x, int y, Modifiers mods)
3313 this.rightButtonDown = true;
3318 bool OnRightButtonUp(int x, int y, Modifiers mods)
3321 if(!parent.inactive && rightButtonDown)
3324 Menu contextMenu { };
3326 MenuItem { contextMenu, $"Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
3327 MenuItem { contextMenu, $"Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
3328 MenuItem { contextMenu, $"Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
3329 MenuItem { contextMenu, $"Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
3330 MenuDivider { contextMenu };
3331 MenuItem { contextMenu, $"Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
3333 popup = PopupMenu { master = this, menu = contextMenu,
3335 nonClient = true, interim = false, parent = parent,
3336 position = { x + clientStart.x + parent.clientStart.x + position.x, y + cientStart.y + parent.sy + clientStart.y + position.y };
3338 position = { x + clientStart.x + absPosition.x - guiApp.desktop.position.x, y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
3342 rightButtonDown = false;
3346 bool OnLeftButtonDown(int mx, int my, Modifiers mods)
3351 if(style.noSelect) return true;
3353 // Should we have a separate 'selectOnActivate' style?
3354 if(!mods.isActivate || (style.readOnly && style.multiLine))
3360 mouseX = mx - XOFFSET;
3363 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3365 //PrintLn("OnLeftButtonDown: ", x, ", ", y);
3371 else if(IsMouseOnSelection() && !mods.isActivate)
3381 if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
3383 if(mods.shift && !mods.isActivate)
3404 UpdateCaretPosition(true);
3405 // Return false because DataBoxes automatically set EditBox editor's clickThrough to true for MouseMove events
3406 // ( for tool tips -- see 95ee4962c4c7bc3fe0a04aa6a4f98cacada40884)
3410 bool OnLeftButtonUp(int x, int y, Modifiers mods)
3414 mouseSelect = false;
3425 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3427 //PrintLn("MouseMove: ", x, ", ", y);
3433 mouseMove = IsMouseOnSelection();
3437 int size = SelSize();
3440 char * text = new char[size+1];
3444 GetSel(text, false);
3446 if(Max(selY, this.y) == dropY)
3450 if(this.dropX > this.selX)
3451 moveX = this.x - this.selX;
3455 if(this.dropX > this.x)
3456 moveX = this.selX - this.x;
3460 recordUndoEvent = true;
3462 this.dropX -= moveX;
3463 this.selX = this.x = this.dropX;
3464 this.selY = this.y = this.dropY;
3465 this.selLine = this.line = this.dropLine;
3467 recordUndoEvent = false;
3469 SetViewToCursor(true);
3474 byte * c = ((EditBox)this).multiLineContents, * c2;
3475 int l1 = c ? strlen(c) : 0, l2;
3478 c2 = ((EditBox)this).multiLineContents;
3479 l2 = c2 ? strlen(c2) : 0;
3480 if(l1 != l2 || (l1 && strcmp(c, c2)))
3510 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3512 //PrintLn("Dropped: ", x, ", ", y);
3514 NotifyDropped(master, this, x, y);
3518 // Return false because DataBoxes automatically set EditBox editor's clickThrough to true for MouseMove events
3519 // ( for tool tips -- see 95ee4962c4c7bc3fe0a04aa6a4f98cacada40884)
3523 bool OnMouseMove(int mx, int my, Modifiers mods)
3529 if(mods != -1 && mods.isSideEffect)
3534 if(style.noSelect) return true;
3535 if(wordSelect) return true;
3536 mouseX = mx - XOFFSET;
3539 needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
3541 if(this.mouseMove || this.mouseSelect)
3550 ((style.hScroll) || (style.vScroll)))
3557 DirtyLine(this.dropY);
3560 DirtyLine(this.dropY);
3561 this.dropLine = line;
3562 SetViewToCursor(true);
3564 //PrintLn("MouseMove: ", "dropX = ", x, ", dropY = ", y);
3567 else if(this.mouseSelect)
3569 DirtyLine(this.selY);
3576 SetViewToCursor(true);
3579 //PrintLn("MouseSelect: ", "x = ", x, ", y = ", y);
3586 bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
3591 //PrintLn("OnLeftDoubleClick: ", mx, ", ", my, ", mods = ", mods);
3595 if(style.noSelect) return true;
3596 FindMouse(mx, my, &x, &y, &line, false);
3597 if(!NotifyDoubleClick(master, this, line, mods))
3604 for(c = x; c >= 0; c--)
3607 while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
3608 ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3615 for(c = start; c<line.count; c += numBytes)
3617 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3618 if(!ch || !IS_ALUNDER(ch))
3628 this.line = this.selLine = line;
3629 this.wordSelect = (c != start);
3640 Menu fileMenu { menu, "File", F };
3641 saveDialog = fileDialog;
3642 MenuItem { fileMenu, $"Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
3643 MenuItem { fileMenu, $"Save As...", A, NotifySelect = MenuFileSaveAs };
3645 if(style.autoSize) AutoSize();
3654 FontExtent(display, font, " ", 1, (int *)&space.w, (int *)&space.h, 0, null, &oh);
3655 FontExtent(display, font, "W", 1, (int *)&large.w, (int *)&large.h, 0, null, &oh);
3657 space.w = Max(space.w, 1);
3658 large.w = Max(large.w, 1);
3659 space.h = Max(space.h, 1);
3660 large.h = Max(large.h, 1);
3664 for(line = lines.first; line; line = line.next)
3665 ComputeLength(line);
3670 SetViewToCursor(true);
3678 bool OnLoadGraphics()
3680 FontExtent = Display::FontExtent2;
3683 // UpdateCaretPosition(true);
3687 void OnUnloadGraphics()
3692 bool OnKeyHit(Key key, unichar ch)
3694 bool shift = (key.shift) ? true : false;
3696 //PrintLn("OnKeyHit: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
3698 if(!ch && !key.alt && !key.ctrl)
3700 key.code = (SmartKey)key.code;
3702 else if(!ch && key.alt)
3705 switch(key.code) //(ch || key.alt || key.ctrl) ? key.code : (Key)(SmartKey)key.code)
3708 if(style.readOnly) break;
3709 if(style.stuckCaret) GoToEnd(true);
3710 if(!(style.freeCaret))
3712 this.x = Min(this.x, this.line.count);
3718 EditLine line = this.line;
3720 for(y = this.y; y>= 0; y--)
3722 c = (y == this.y) ? (Min(this.x-1, line.count-1)) : line.count-1;
3724 // Slow down when going on lines...
3725 if(y != this.y) break;
3729 //if(this.line.buffer[c] != '\t' && this.line.buffer[c] != ' ')
3730 if(IS_ALUNDER(line.buffer[c]))
3735 if(!IS_ALUNDER(line.buffer[c]))
3737 //if(this.line.buffer[c] == '\t' || this.line.buffer[c] == ' ')
3752 _DelCh(line, y, c+1, this.line, this.y, this.x, true, false, null);
3753 this.x = this.selX = Min(c+1, line.count);
3754 this.y = this.selY = y;
3755 this.line = this.selLine = line;
3756 SetViewToCursor(true);
3767 if(style.readOnly) break;
3768 if(style.stuckCaret) break;
3777 if(this.line != this.selLine || this.x != this.selX)
3780 SetViewToCursor(true);
3785 EditLine line1 = this.line, line2 = this.line;
3786 int x1, y1 = this.y, x2, y2 = this.y;
3787 if(!style.freeCaret)
3789 this.selX = this.x = Min(this.x, this.line.count);
3794 if(x1 < line1.count)
3800 char * buffer = line1.buffer;
3801 for(i = x1; i < line1.count; i++)
3803 if(!IS_ALUNDER(buffer[i]))
3807 for(; i < line1.count; i++)
3809 // Delete trailing whitespace
3810 if(IS_ALUNDER(buffer[i]))
3820 // Avoid creating trailing spaces if there is no content on next line
3823 if(line2 && !line2.count)
3828 _DelCh(line1, y1, x1, line2, y2, x2, false, false, null);
3829 SetViewToCursor(true);
3838 if(!key.alt && !key.ctrl)
3842 bool stuffAfter = false;
3845 /*bool resetX = false;
3848 if(style.stuckCaret) GoToEnd(true);
3849 if(style.readOnly) break;
3850 if(!(style.multiLine)) break;
3852 for(c = 0; c<this.line.count && c<this.x; c++)
3854 if(this.line.buffer[c] == '\t')
3855 position += this.tabSize - (position % this.tabSize);
3856 else if(this.line.buffer[c] == ' ')
3862 // Prevent adding trailing spaces if at the head of a line
3863 /*if(c && c == this.x && c < this.line.count && this.x == this.selX && this.y == this.selY)
3875 if(this.x < this.line.count)
3878 //If last character is a { indent one tab
3879 c = Min(x, line.count);
3880 if(c > 0 && line.buffer[c - 1] == '{')
3882 //Except if the next non space character is a }
3883 bool indent = false;
3885 for(i = c; i <= this.line.count; i++) // indent will be set to true on nul terminating char
3886 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
3888 if(this.line.buffer[i] != '}')
3893 position += this.tabSize;
3896 addString = new char[position + 2];
3897 addString[len++] = '\n';
3898 addString[len] = '\0';
3900 if(stuffAfter || !style.freeCaret)
3902 for(c = 0; c<position; )
3904 if(style.useTab && c + this.tabSize <= position)
3906 addString[len++] = '\t';
3911 addString[len++] = ' ';
3915 addString[len] = '\0';
3917 recordUndoEvent = true;
3920 EditLine prevLine = this.line.prev;
3923 // Nuke spaces if that is all that is left on previous line
3925 char * buffer = prevLine.buffer;
3926 for(i = 0; i < prevLine.count; i++)
3927 if(buffer[i] != ' ' && buffer[i] != '\t')
3929 if(i == prevLine.count)
3930 DelCh(prevLine, this.y - 1, 0, prevLine, this.y - 1, prevLine.count, false);
3934 this.x = this.selX = backX;
3937 else */if(!stuffAfter && style.freeCaret)
3939 this.x = this.selX = position;
3943 SetViewToCursor(true);
3946 recordUndoEvent = false;
3954 if(style.stuckCaret) break;
3955 if(!(style.freeCaret))
3957 this.x = Min(this.x, this.line.count);
3958 this.selX = Min(this.selX, this.selLine.count);
3961 if(!shift) SelDirty();
3964 bool foundAlpha = false;
3967 EditLine line, lastLine = null;
3968 int lastC = 0, lastY = 0;
3970 for(line = this.line; (line && !found); line = line.prev, y--)
3975 if(this.x == 0 && line != this.line)
3984 if(line == this.line) start = this.x -1; else start = line.count-1;
3985 start = Min(start, line.count-1);
3987 for(c = start; c >= 0;)
3990 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
4008 byte ch = line.buffer[c];
4009 if(UTF8_IS_FIRST(ch)) break;
4013 // No next word found,
4014 if(!found && ( this.x > 0 || (!line.count && this.x)))
4028 this.line = lastLine;
4039 byte * buffer = (byte *)line.buffer;
4042 byte ch = buffer[x];
4043 if(UTF8_IS_FIRST(ch)) break;
4060 if(!shift) _Deselect();
4061 SetViewToCursor(true);
4067 if(style.stuckCaret) break;
4068 if(!(style.freeCaret))
4070 this.x = Min(this.x, this.line.count);
4071 this.selX = Min(this.selX, this.selLine.count);
4074 if(!shift) SelDirty();
4075 if(!shift && (this.x != this.selX || this.y != this.selY));
4078 bool onAChar = false;
4079 if(this.selX != this.x || this.selY != this.y)
4081 if(this.x<this.line.count)
4082 if(this.line.buffer[this.x] != '\t' && this.line.buffer[this.x] !=' ')
4084 if(key.shift && onAChar &&
4085 ((this.y > this.selY)||((this.selY == this.y)&&(this.x >= this.selX))))
4087 bool foundAlpha = false;
4089 EditLine line = null, lastLine = null;
4091 int lastC = 0, lastY = 0, lastNumBytes = 0;
4093 for(line = this.line; (line && !found); line = line.next, y++)
4095 int start = (line == this.line) ? this.x : 0;
4099 for(c = start; c < line.count && (ch = UTF8_GET_CHAR(line.buffer + c, numBytes)); c += numBytes)
4105 lastNumBytes = numBytes;
4115 if(!found && (c != this.x || line != this.line))
4128 this.x = lastC + lastNumBytes;
4130 this.line = lastLine;
4137 bool foundAlpha = false;
4142 for(line = this.line; (line && !found); line = line.next, y++)
4144 int start = (line == this.line) ? this.x : 0;
4148 for(c = start; c < line.count && (ch = UTF8_GET_CHAR(line.buffer + c, numBytes)); c += numBytes)
4164 // No next word found,
4165 if(!found && (c != this.x || line != this.line))
4169 this.x = line.count;
4181 if(x < line.count || (style.freeCaret && line.count < maxLineSize))
4185 byte * buffer = (byte *)line.buffer;
4188 byte ch = buffer[x];
4189 if(UTF8_IS_FIRST(ch)) break;
4210 if(!shift) _Deselect();
4211 SetViewToCursor(true);
4218 if(!style.vScroll || hasVertScroll) break;
4224 if(style.stuckCaret) break;
4226 if(!shift) SelDirty();
4236 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
4240 if(!shift) _Deselect();
4242 SetViewToCursor(false);
4245 if(caretY == this.y * space.h)
4251 if(!style.freeCaret)
4252 this.x = Min(this.x, line.count);
4262 int sx = 0, sy = this.y * space.h;
4263 char * text = line.text;
4264 int maxW = clientSize.w - sx;
4265 display.FontExtent(font, " ", 1, null, &th);
4269 int startPos = textPos;
4272 bool lineComplete = false;
4274 if(!style.wrap && caretY == MAXINT)
4277 //textPos = line.count;
4278 //lineComplete = true;
4281 for(; (style.freeCaret || textPos < line.count) && !lineComplete;)
4285 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4288 len = (nextSpace - (text + textPos));
4290 len = line.count - textPos;
4292 if(textPos < line.count)
4294 display.FontExtent(font, text + textPos, len, &w, null);
4296 if(nextSpace) { w += space.w; len++; }
4298 if(style.wrap && x + width + w > maxW && x > 0)
4300 lineComplete = true;
4310 if((!style.freeCaret && textPos >= line.count) || (sy == caretY - th && caretX <= x + width + sx))
4314 while(this.x > 0 && x + sx > caretX && this.x > startPos)
4317 if(this.x > line.count)
4320 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4321 len = this.x - startPos;
4322 display.FontExtent(font, text + startPos, len, &x, null);
4326 if(!shift) _Deselect();
4328 SetViewToCursor(false);
4332 if(sy == caretY - th || textPos >= line.count)
4334 if(textPos >= line.count)
4336 int c = textPos - 1;
4337 while(c > 0 && text[c] == ' ') c--;
4341 this.x = line.count;
4344 if(!shift) _Deselect();
4346 SetViewToCursor(false);
4351 } while(textPos < line.count);
4354 if(!shift) _Deselect();
4356 SetViewToCursor(false);
4365 int x = AdjustXPosition(this.line, this.line.prev, true, null, MAXINT, 0);
4366 if(!shift) SelDirty();
4367 this.line = this.line.prev;
4373 if(!shift) _Deselect();
4377 SetViewToCursor(false);
4383 return style.multiLine ? false : true;
4387 if(!style.vScroll || hasVertScroll)
4394 if(style.stuckCaret) break;
4399 int sx = 0, sy = this.y * this.space.h;
4400 int maxW = clientSize.w - sx;
4401 char * text = line.buffer;
4404 if(!shift) SelDirty();
4410 if(AdjustXPosition(line, maxW, this.x, line.count, true, null, MAXINT, 0) <= line.count)
4420 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
4424 if(!shift) _Deselect();
4426 SetViewToCursor(false);
4429 while(!textPos || (style.freeCaret || textPos<line.count))
4431 int startPos = textPos;
4434 bool lineComplete = false;
4435 if(!style.wrap && sy <= caretY)
4437 textPos = line.count;
4438 lineComplete = true;
4440 for(; (style.freeCaret || textPos<line.count) && !lineComplete;)
4444 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4447 len = (nextSpace - (text + textPos));
4449 len = line.count - textPos;
4451 if(textPos < line.count)
4453 display.FontExtent(font, text + textPos, len, &w, &th);
4455 if(nextSpace) { w += space.w; len++; }
4456 if(style.wrap && x + width + w > maxW && x > 0)
4458 lineComplete = true;
4468 if(sy > caretY && ((!style.freeCaret && textPos >= line.count) || caretX <= x + width + sx))
4472 while(this.x > 0 && x + sx > caretX && textPos > startPos)
4475 if(this.x > line.count)
4478 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4480 len = this.x - startPos;
4481 display.FontExtent(font, text + startPos, len, &x, null);
4484 if(!shift) _Deselect();
4487 SetViewToCursor(false);
4493 this.x = line.count;
4496 if(!shift) _Deselect();
4499 SetViewToCursor(false);
4502 else if(textPos >= line.count && line.next)
4508 sy = this.y * this.space.h;
4509 sx = 0; //textBlock.startX;
4515 sx = 0; //textBlock.startX;
4523 this.x = Min(this.x, line.count);
4524 //int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4526 if(!shift) _Deselect();
4529 if(this.selX != this.x || this.selY != this.y)
4532 SetViewToCursor(false);
4539 int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4540 if(!shift) SelDirty();
4541 this.line = this.line.next;
4545 if(!shift) _Deselect();
4548 if(this.selX != this.x || this.selY != this.y)
4556 return style.multiLine ? false : true;
4559 if(style.stuckCaret) break;
4560 if(!style.multiLine && key.ctrl) break;
4561 if(!(style.freeCaret))
4562 this.selX = Min(this.selX, this.selLine.count);
4564 if(!shift) SelDirty();
4567 this.line = this.lines.first;
4568 if(this.y != 0 || this.x != 0)
4578 EditLine line = this.line;
4580 for(c=0; line.buffer[c]; c++)
4581 if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
4583 if(overwrite || (shift && (c != 0 || this.x)))
4592 if(overwrite || (shift && this.x != 0))
4598 if(!shift) _Deselect();
4599 SetViewToCursor(true);
4605 if(style.stuckCaret) break;
4606 if(!style.multiLine && key.ctrl) break;
4607 if(!style.freeCaret)
4608 this.selX = Min(this.selX, this.selLine.count);
4610 if(!shift) SelDirty();
4615 else if(this.x != this.line.count)
4617 this.x = this.line.count;
4618 if(overwrite || shift)
4622 if(!shift) _Deselect();
4623 SetViewToCursor(true);
4628 if(style.tabKey && !key.ctrl)
4630 if(this.selY != this.y && style.tabSel)
4632 EditLine firstLine, lastLine;
4636 // Do multi line selection tabbing here
4637 if(this.selY < this.y)
4639 firstLine = this.selLine;
4640 lastLine = this.line;
4646 // Selecting going up
4647 firstLine = this.line;
4648 lastLine = this.selLine;
4655 for(line = firstLine; line; line = line.next, y++)
4657 if(line != lastLine || x)
4661 BufferLocation before = { line, y, 0 }, after = { line, y, 0 };
4663 for(c=0; c<line.count && lastC < this.tabSize; c++, lastC++)
4665 if(line.buffer[c] == '\t')
4670 else if(line.buffer[c] != ' ')
4675 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
4678 int len = GetText(null, line, y, 0, line, y, lastC, false, false);
4679 char * string = new char[len];
4680 DelTextAction action { y1 = y, x1 = 0, y2 = y, x2 = lastC, string = string, placeAfter = true };
4681 GetText(string, line, y, 0, line, y, lastC, false, false);
4684 memmove(line.buffer,line.buffer+lastC,line.size-lastC);
4685 if(!line.AdjustBuffer(line.count-lastC))
4691 if(line == lastLine) break;
4696 for(line = firstLine; line; line = line.next, y++)
4700 if(line != lastLine || x)
4704 if(line.count + 1 <= this.maxLineSize)
4706 BufferLocation before = { line, y, 0 }, after = { line, y, 1 };
4708 if(!line.AdjustBuffer(line.count+1))
4711 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4713 AddCharAction action { ch = '\t', x = 0, y = y };
4717 memmove(line.buffer+1,line.buffer,line.size-1);
4719 line.buffer[0] = '\t';
4724 if(line.count + this.tabSize <= this.maxLineSize)
4727 BufferLocation before = { line, y, 0 }, after = { line, y, this.tabSize };
4728 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4730 if(!line.AdjustBuffer(line.count+this.tabSize))
4734 char * string = new char[this.tabSize + 1];
4735 memset(string, ' ', this.tabSize);
4736 string[this.tabSize] = '\0';
4737 Record(AddTextAction { string = string, x1 = 0, y1 = y, x2 = this.tabSize, y2 = y });
4740 memmove(line.buffer+this.tabSize,line.buffer,line.size-(this.tabSize));
4741 line.count+=this.tabSize;
4742 for(c=0; c<this.tabSize; c++)
4743 line.buffer[c] = ' ';
4749 if(line == lastLine) break;
4752 ComputeLength(maxLine);
4764 char * addString = new char[this.tabSize + 1];
4766 if(!(style.freeCaret))
4768 this.x = Min(this.x, this.line.count);
4772 start = Min(this.x, this.selX);
4773 for(c=start; ((c == start) || ((c) % this.tabSize)); c++)
4775 addString[len++] = ' ';
4783 SetViewToCursor(true);
4792 if(!(style.hScroll) || hasHorzScroll) break;
4793 if(this.viewX < this.maxLength)
4795 //this.viewX+=this.space.w*this.tabSize;
4797 SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
4804 if(!shift) _Deselect();
4816 if(!(style.hScroll) || hasHorzScroll) break;
4819 //this.viewX-=this.space.w*this.tabSize;
4820 //this.viewX = Max(this.viewX,0);
4822 SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
4823 // SetCursorToView();
4830 if(!shift) _Deselect();
4843 else if(!style.readOnly)
4847 if(!(style.readOnly))
4853 this.overwrite ^= 1;
4854 UpdateCaretPosition(true);
4859 NotifyOvrToggle(master, this, this.overwrite);
4871 if(style.noSelect) break;
4874 //Save current view position
4875 int tempX = this.viewX;
4876 int tempY = this.viewY;
4880 this.selLine = this.lines.first;
4881 this.y = this.lineCount-1;
4882 this.line = this.lines.last;
4883 this.x = this.line.count;
4886 SetViewToCursor(true);
4888 //Restore previous view position
4889 SetScrollPosition(tempX, tempY * this.space.h);
4895 // TOCHECK: Was there any good reason why we weren't returning false here?
4896 return false; // break;
4901 if(!(style.readOnly))
4909 if(style.readOnly) break;
4915 if(style.readOnly) break;
4920 if(style.readOnly) break;
4924 if(style.readOnly) break;
4928 if(style.readOnly) break;
4929 if(key.shift && key.code == rightBracket)
4931 //Only indent back if you are exactly at one tab.
4933 //bool whitespace = true;
4938 int indentwidth = 0;
4939 EditLine line = this.line;
4941 //Only remove one tab if there is nothing else on the line.
4942 for(i = 0; i < this.line.count; i++)
4944 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
4958 for(pos = line.count - 1; pos >= 0; pos--)
4960 char c = line.buffer[pos];
4964 indentwidth += this.tabSize;
4966 //Counting backwards, so when you find a character, indentation is reset
4976 //Place the } to get an undo:
4979 this.x = this.line.count;
4983 putsize = indentwidth;
4985 putsize = indentwidth / this.tabSize + indentwidth % this.tabSize;
4987 newline = new char[putsize+2];
4988 newline[putsize] = '}';
4989 newline[putsize+1] = '\0';
4993 for(; i < indentwidth / this.tabSize; i++)
4995 for(;i < putsize; i++)
5004 else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
5016 void OnHScroll(ScrollBarAction action, int position, Key key)
5019 //PrintLn("OnHScroll: ", action, ", pos = ", position, ", key = ", key);
5021 this.viewX = position;
5022 if(action != setRange)
5024 if(!this.mouseMove && style.cursorFollowsView)
5031 void OnVScroll(ScrollBarAction action, int position, Key key)
5033 int oldViewY = this.viewY;
5036 //PrintLn("OnVScroll: ", action, ", pos = ", position, ", key = ", key);
5038 position /= this.space.h;
5040 if(position < this.viewY)
5042 for(; position < this.viewY && this.viewLine.prev; this.viewLine = this.viewLine.prev, this.viewY--);
5043 style.recomputeSyntax = true;
5045 else if(position > this.viewY)
5047 EditLine oldViewLine = viewLine;
5048 for(; position > this.viewY && this.viewLine.next; this.viewLine = this.viewLine.next, this.viewY++);
5049 FigureStartSyntaxStates(oldViewLine, false);
5052 if(action != setRange)
5054 if(!this.mouseMove && style.cursorFollowsView && !SelSize()) SetCursorToViewY();
5057 if(this.x != this.selX || this.y != this.selY)
5061 Scroll(0, (this.viewY - oldViewY) * this.space.h);
5064 int numLines = clientSize.h / this.space.h;
5066 if(Abs(this.viewY - oldViewY) < numLines)
5069 if(this.viewY > oldViewY)
5071 for(y = oldViewY; y <this.viewY; y++)
5072 DirtyLine(y + numLines);
5076 for(y = this.viewY; y <oldViewY; y++)
5077 DirtyLine(y + numLines);
5082 // Fix dirt of stuff before first line...
5083 if(this.viewY - oldViewY > 0)
5085 Box box { 0,0, clientSize.w-1, YOFFSET-1 };
5092 bool _AddCh(unichar ch, int * addedSpacesPtr, int * addedTabsPtr, int * xAdjustmentPtr)
5095 int length, endX = 0;
5097 ReplaceTextAction replaceAction = null;
5098 AddCharAction addCharAction = null;
5099 int addedSpaces = 0, addedTabs = 0, xAdjustment = 0;
5101 if(ch == '\r') return true;
5102 if(style.stuckCaret /*|EES_READONLY)*/ )
5105 if(ch == '\n' && !(style.multiLine) && this.line) return false;
5107 if(!undoBuffer.dontRecord)
5109 if(selX != x || selY != y)
5114 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
5115 oldString = new char[len];
5116 UTF32toUTF8Len(&ch, 1, buffer, 4);
5117 newString = CopyString(buffer);
5118 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
5120 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5121 if(selY < y || (selY == y && selX < x))
5123 replaceAction.x1 = selX;
5124 replaceAction.y1 = selY;
5125 replaceAction.x2 = x;
5126 replaceAction.y2 = y;
5130 replaceAction.x1 = x;
5131 replaceAction.y1 = y;
5132 replaceAction.x2 = selX;
5133 replaceAction.y2 = selY;
5135 Record(replaceAction);
5136 undoBuffer.dontRecord++;
5140 addCharAction = AddCharAction { y = y, x = x, ch = ch };
5141 Record(addCharAction);
5147 DelSel(&addedSpaces);
5148 if(this.lineCount+1 > this.maxLines)
5151 Emptyline(this.lines.first,0);
5155 if(!(style.autoSize && (!maxClientSize.h || maxClientSize.h > clientSize.h + this.space.h)) && !(style.vScroll))
5157 // Make sure it fits, but we need a default line is this.font is too big for window
5158 if(this.space.h * (this.lineCount+1) > clientSize.h && this.line)
5161 if((this.y >= 0) && this.y < this.viewY)
5166 line = EditLine { };
5169 lines.Insert(this.line, line);
5170 line.editBox = this;
5176 // If we're displacing the lines from a current line ...
5179 if(this.line.buffer)
5182 if(this.line.count < endX) endX = this.line.count;
5183 length = this.line.count - endX;
5186 if(!line.AdjustBuffer(length))
5190 if(this.line.buffer)
5192 CopyBytes(line.buffer,this.line.buffer+endX, length+1);
5194 if(endX > 4000 || endX < 0)
5197 this.line.count = endX;
5198 this.line.buffer[this.line.count] = '\0';
5199 this.line.AdjustBuffer(this.line.count);
5200 ComputeLength(this.line);
5204 BufferLocation before = { this.line, this.y, this.x }, after;
5209 line.count = length;
5212 if(length > 4000 || length < 0)
5215 ComputeLength(this.line);
5220 line.buffer[line.count] = '\0';
5223 after.line = this.line, after.y = this.y, after.x = this.x;
5225 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
5231 int count = UTF32toUTF8Len(&ch, 1, string, 5);
5232 DelSel(&addedSpaces);
5233 result = AddToLine(string, count, false, addedSpaces ? null : &addedSpaces, &addedTabs, &xAdjustment);
5234 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
5235 if(addedTabsPtr) *addedTabsPtr = addedTabs;
5236 if(xAdjustmentPtr) *xAdjustmentPtr = xAdjustment;
5240 this.selLine = this.line;
5244 replaceAction.x3 = x;
5245 replaceAction.y3 = y;
5246 replaceAction.addedSpaces = addedSpaces;
5247 replaceAction.addedTabs = addedTabs;
5248 undoBuffer.dontRecord--;
5252 addCharAction.x -= addedTabs * (tabSize-1);
5253 addCharAction.addedSpaces = addedSpaces;
5254 addCharAction.addedTabs = addedTabs;
5261 /****************************************************************************
5263 ****************************************************************************/
5266 bool AddCh(unichar ch)
5268 return _AddCh(ch, null, null, null);
5273 this.modified = true;
5274 NotifyUpdate(master, this);
5275 modifiedDocument = true;
5278 void Delete(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
5281 _DelCh(line1, y1, x1, line2, y2, x2, false, false, null);
5282 SetViewToCursor(true);
5290 itemEditUndo.disabled = undoBuffer.curAction == 0;
5291 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5297 if(savedAction == undoBuffer.curAction)
5299 modifiedDocument = false;
5301 NotifyUnsetModified(master, this);
5308 itemEditUndo.disabled = undoBuffer.curAction == 0;
5309 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5315 if(savedAction == undoBuffer.curAction)
5317 modifiedDocument = false;
5319 NotifyUnsetModified(master, this);
5323 void Record(UndoAction action)
5325 if(!undoBuffer.dontRecord)
5327 undoBuffer.Record(action);
5328 itemEditUndo.disabled = undoBuffer.curAction == 0;
5329 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5336 this.lines.first, 0,0,
5337 this.lines.last, this.lines.count-1, strlen(((EditLine)this.lines.last).buffer));
5340 void Select(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
5345 this.selLine = line1 ? (EditLine)line1 : this.lines.first;
5346 this.x = line2 ? x2 : ((EditLine)this.lines.last).count;
5347 this.y = line2 ? y2 : (this.lineCount-1);
5348 this.line = line2 ? (EditLine)line2 : this.lines.last;
5351 SetViewToCursor(true);
5355 // TODO: Fix this vs modifiedDocument window property
5356 void SetModified(bool flag)
5360 this.modified = false;
5361 if(flag && !NotifyModified(master, this))
5362 this.modified = true;
5367 bool AddS(const char * string)
5374 int addedSpaces = 0, addedTabs = 0, xAdjustment = 0;
5375 AddTextAction action = null;
5376 ReplaceTextAction replaceAction = null;
5378 this.pasteOperation = true;
5380 if(style.stuckCaret /*|EES_READONLY)*/ )
5383 if(!undoBuffer.dontRecord)
5385 char * placeString = CopyString(string);
5386 if(!style.multiLine)
5390 for(i = 0; (ch = placeString[i]); i++)
5393 placeString[i] = '\0';
5394 placeString = renew placeString byte[i+1];
5399 if(selX != x || selY != y)
5403 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
5404 oldString = new char[len];
5405 newString = placeString;
5406 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
5408 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5409 if(selY < y || (selY == y && selX < x))
5411 replaceAction.x1 = selX;
5412 replaceAction.y1 = selY;
5413 replaceAction.x2 = x;
5414 replaceAction.y2 = y;
5418 replaceAction.x1 = x;
5419 replaceAction.y1 = y;
5420 replaceAction.x2 = selX;
5421 replaceAction.y2 = selY;
5423 Record(replaceAction);
5427 if(string[0] == '\n')
5428 action = AddTextAction { y1 = y, x1 = Min(this.line.count, x), string = placeString };
5430 action = AddTextAction { y1 = y, x1 = x, string = placeString };
5438 undoBuffer.dontRecord++;
5439 DelSel(&addedSpaces);
5443 for(c = 0; string[c]; c++)
5445 if(string[c] == '\n' || string[c] == '\r')
5447 if(!AddToLine(line, count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs, xAdjustment ? null : &xAdjustment))
5452 if(string[c] == '\n')
5461 // Reset for next line
5465 if(string[c] == '\r' && *line == '\n')
5477 // Why was this here?
5480 // Add the line here
5482 if(!AddToLine(line,count,false, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs, xAdjustment ? null : &xAdjustment))
5488 undoBuffer.dontRecord--;
5493 action.addedSpaces = addedSpaces;
5494 action.addedTabs = addedTabs;
5495 action.xAdjustment = xAdjustment;
5497 else if(replaceAction)
5499 replaceAction.y3 = y;
5500 replaceAction.x3 = x;
5501 replaceAction.addedSpaces = addedSpaces;
5502 replaceAction.addedTabs = addedTabs;
5503 replaceAction.xAdjustment = xAdjustment;
5506 UpdateCaretPosition(true);
5508 this.pasteOperation = false;
5521 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5522 SetViewToCursor(true);
5528 void PutCh(unichar ch)
5532 if((ch >= 32 /*&& ch <=126*/) || ch == '\n' || ch == '\t')
5533 //if((ch >= 32) || ch == '\n')
5535 int addedSpaces = 0, addedTabs = 0, xAdjustment = 0;
5536 ReplaceTextAction replaceAction = null;
5537 AddCharAction addCharAction = null;
5540 ch = (ch < 128) ? toupper(ch) : ch; // TODO: UNICODE TO UPPER
5542 if(this.overwrite && selX == x && selY == y && this.x < this.line.count)
5547 int len = GetText(null, line, y, x, line, y, x+1, false, false);
5548 oldString = new char[len];
5549 UTF32toUTF8Len(&ch, 1, buffer, 4);
5550 newString = CopyString(buffer);
5551 GetText(oldString, line, y, x, line, y, x+1, false, false);
5552 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5553 replaceAction.x1 = x;
5554 replaceAction.y1 = y;
5555 replaceAction.x2 = x+1;
5556 replaceAction.y2 = y;
5557 Record(replaceAction);
5559 undoBuffer.dontRecord++;
5560 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, true);
5561 undoBuffer.dontRecord--;
5563 else if(!undoBuffer.dontRecord)
5565 if(selX != x || selY != y)
5570 int len = GetText(null, line, y, x, selLine, selY, selX, false, false);
5571 oldString = new char[len];
5572 UTF32toUTF8Len(&ch, 1, buffer, 4);
5573 newString = CopyString(buffer);
5574 GetText(oldString, line, y, x, selLine, selY, selX, false, false);
5575 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = true };
5576 if(selY < y || (selY == y && selX < x))
5578 replaceAction.x1 = selX;
5579 replaceAction.y1 = selY;
5580 replaceAction.x2 = x;
5581 replaceAction.y2 = y;
5585 replaceAction.x1 = x;
5586 replaceAction.y1 = y;
5587 replaceAction.x2 = selX;
5588 replaceAction.y2 = selY;
5590 Record(replaceAction);
5594 addCharAction = AddCharAction { y = y, x = x, ch = ch };
5595 Record(addCharAction);
5598 undoBuffer.dontRecord++;
5599 result = _AddCh(ch, &addedSpaces, &addedTabs, &xAdjustment);
5602 replaceAction.x3 = x;
5603 replaceAction.y3 = y;
5604 replaceAction.addedSpaces = addedSpaces;
5605 replaceAction.addedTabs = addedTabs;
5606 replaceAction.addedTabs = xAdjustment;
5610 addCharAction.addedSpaces = addedSpaces;
5611 addCharAction.addedTabs = addedTabs;
5612 addCharAction.xAdjustment = xAdjustment;
5614 undoBuffer.dontRecord--;
5618 if(result) SetViewToCursor(true);
5622 void PutS(const char * string)
5627 SetViewToCursor(true);
5632 void Printf(const char * format, ...)
5636 char temp[MAX_F_STRING];
5638 va_start(args, format);
5639 vsnprintf(temp, sizeof(temp), format, args);
5640 temp[sizeof(temp)-1] = 0;
5646 void SetContents(const char * format, ...)
5650 undoBuffer.dontRecord++;
5652 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5655 char temp[MAX_F_STRING];
5657 va_start(args, format);
5658 vsnprintf(temp, sizeof(temp), format, args);
5659 temp[sizeof(temp)-1] = 0;
5666 undoBuffer.dontRecord--;
5679 x -= 1 + _DelCh(line, y, x-1, line, y, x, true, false, null);
5682 else if(this.line.prev)
5684 EditLine line = this.line.prev;
5688 _DelCh(line, this.y-1, x, this.line, this.y, this.x, true, false, null);
5696 this.selLine = this.line;
5701 SetViewToCursor(true);
5706 Emptyline(this.line,this.y);
5707 this.selX = this.x = 0;
5710 this.selLine = this.line;
5712 SetViewToCursor(true);
5722 SetViewToCursor(true);
5730 SetViewToCursor(true);
5734 bool GoToLineNum(int lineNum)
5739 EditLine line = this.lines.first;
5740 for(c = 0; c < lineNum && line; c++, line = line.next);
5750 SetViewToCursor(true);
5757 // NOTE: Mismatch with NotifyCaretMove() for x/y + 1
5758 bool GoToPosition(EditLine line, int y, int x)
5770 for(line = this.lines.first, c = 0; c<y && line; c++, line = line.next);
5784 SetViewToCursor(true);
5791 void SetViewToCursor(bool setCaret)
5800 bool dontScroll = false;
5806 selected = selX != this.x || selY != y;
5813 checkLine = dropLine;
5819 checkLine = this.line;
5824 numLines = clientSize.h / space.h;
5826 // This is broken. The EditBox now doesn't do anything different when adding to it,
5827 // regardless of the previous scrolling position. It should be read and then set again
5828 // if one wishes to preserve it.
5829 /* // Don't scroll view to cursor if we're in a EES_NOCARET box
5830 if(style.noCaret && this.viewY < lineCount - numLines - 1)
5834 // Horizontal Adjustment
5835 if(!dontScroll && checkLine)
5839 dropX = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5842 this.x = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5848 if(x + space.w >= this.viewX + clientSize.w && clientSize.w >= space.w)
5849 viewX = x - clientSize.w+space.w;
5850 if(x < this.viewX + clientSize.w/2 - space.w)
5851 viewX = Max(0, x - clientSize.w/2 + space.w);
5859 // Vertical Adjustment
5860 if(viewY > checkY) viewY = checkY;
5861 if(viewY + numLines <= checkY)
5863 if(clientSize.h >= space.h)
5865 for(line = viewLine; line && (viewY + numLines <= checkY); line = line.next)
5875 for(;dropLine && dropLine.prev && dropY >= numLines;)
5877 dropLine = dropLine.prev;
5881 for(;this.line && this.line.prev && this.y >= numLines;)
5883 this.line = this.line.prev;
5888 SetScrollPosition(viewX, viewY * this.space.h);
5890 UpdateCaretPosition(setCaret);
5896 selLine = this.line;
5906 void CenterOnCursor()
5908 int numLines = clientSize.h / this.space.h;
5909 int y = this.y - numLines / 2;
5911 bool figureSyntax = false;
5912 EditLine oldViewLine = viewLine;
5913 if(y > this.lineCount - numLines) y = this.lineCount-numLines;
5918 for(;y < this.viewY; y++)
5920 this.viewLine = this.viewLine.prev;
5921 style.recomputeSyntax = true;
5923 for(;y > this.viewY; y--)
5925 this.viewLine = this.viewLine.next;
5926 figureSyntax = true;
5929 FigureStartSyntaxStates(oldViewLine, false);
5933 SetScrollPosition(this.viewX, viewY * this.space.h);
5934 UpdateCaretPosition(true);
5938 void SetCursorToView()
5949 numLines = clientSize.h / this.space.h;
5953 for(c=0, line = this.viewLine.next; line && c<numLines && (this.viewY < this.lineCount - numLines); line = line.next, c++);
5954 SetScrollPosition(this.viewX, (this.viewY + c) * this.space.h);
5958 //EditLine oldLine = this.line;
5959 bool lastOne = false;
5960 EditLine oldViewLine = this.viewLine;
5961 bool figureSyntax = false;
5963 if(this.y >= this.lineCount-1) return;
5965 for(c=0, line = this.line.next; line && c<numLines; line = line.next, c++)
5967 if(this.viewY + numLines < this.lines.count)
5969 this.viewLine = this.viewLine.next;
5971 figureSyntax = true;
5973 else if(c && !lastOne)
5982 FigureStartSyntaxStates(oldViewLine, false);
5983 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5986 SetViewToCursor(false);
5995 if(this.y == 0) return;
5997 numLines = clientSize.h / this.space.h;
6001 for(c=0, line = this.viewLine.prev; line && c<numLines; line = line.prev, c++);
6002 SetScrollPosition(this.viewX, (this.viewY - c) * this.space.h);
6006 for(c=0, line = this.line.prev; line && c<numLines; line = line.prev, c++)
6011 if(this.viewLine.prev)
6013 this.viewLine = this.viewLine.prev;
6015 style.recomputeSyntax = true;
6019 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
6022 SetViewToCursor(false);
6029 if(this.viewLine.prev)
6032 // this.viewLine = this.viewLine.prev;
6035 SetScrollPosition(this.viewX, (this.viewY - 1) * this.space.h);
6042 if(this.viewLine.next)
6045 // this.viewLine = this.viewLine.next;
6048 SetScrollPosition(this.viewX, (this.viewY + 1) * this.space.h);
6055 EditLine l1, l2, line;
6056 int x1, x2, nx1, nx2;
6061 if(!this.selLine) return 0;
6062 if(this.selLine == this.line && this.selX == this.x) return 0;
6063 if(this.selY < this.y)
6070 else if(this.selY > this.y)
6077 else if(this.selX < this.x)
6079 l1 = l2 = this.line;
6085 l1 = l2 = this.line;
6089 nx1 = Min(x1,l1.count);
6090 nx2 = Min(x2,l2.count);
6092 // Find Number of Bytes Needed
6094 for(line = l1; line; line = line.next)
6096 if(line == l1) start = nx1; else start = 0;
6097 if(line == l2) end = nx2; else end = line.count;
6099 if(style.freeCaret && line == l2)
6102 count = Max(x2-Max(x1,l1.count),0);
6104 count = Max(x2-l2.count,0);
6108 if(line == l2) break;
6109 // Add Carriage Return / line Feed
6116 void GetSelPos(EditLine * l1, int *y1, int *x1, EditLine * l2, int * y2, int * x2, bool reorder)
6120 if(!reorder || this.selY < this.y)
6122 if(l1) *l1 = this.selLine;
6123 if(y1) *y1 = this.selY;
6124 if(x1) *x1 = this.selX;
6125 if(l2) *l2 = this.line;
6126 if(y2) *y2 = this.y;
6127 if(x2) *x2 = this.x;
6129 else if(this.selY > this.y)
6131 if(l1) *l1 = this.line;
6132 if(y1) *y1 = this.y;
6133 if(x1) *x1 = this.x;
6134 if(l2) *l2 = this.selLine;
6135 if(y2) *y2 = this.selY;
6136 if(x2) *x2 = this.selX;
6138 else if(this.selX < this.x)
6140 if(l1) *l1 = this.line;
6141 if(y1) *y1 = this.selY;
6142 if(x1) *x1 = this.selX;
6143 if(l2) *l2 = this.line;
6144 if(y2) *y2 = this.y;
6145 if(x2) *x2 = this.x;
6149 if(l1) *l1 = this.line;
6150 if(y1) *y1 = this.y;
6151 if(x1) *x1 = this.x;
6152 if(l2) *l2 = this.line;
6153 if(y2) *y2 = this.selY;
6154 if(x2) *x2 = this.selX;
6159 void SetSelPos(EditLine l1, int y1, int x1, EditLine l2, int y2, int x2)
6161 if(this && (this.selY != y1 || this.selX != x1 || this.y != y2 || this.x != x2))
6163 this.selLine = (EditLine)l1;
6166 this.line = (EditLine)l2;
6170 SetViewToCursor(true);
6174 int GetText(char * text, EditLine _l1, int _y1, int _x1, EditLine _l2, int _y2, int _x2, bool addCr, bool addSpaces)
6176 EditLine l1, l2, line;
6177 int x1, x2, nx1, nx2;
6208 nx1 = Min(x1,l1.count);
6209 nx2 = Min(x2,l2.count);
6212 for(line = l1; line; line = line.next)
6214 if(line == l1) start = nx1; else start = 0;
6215 if(line == l2) end = nx2; else end = line.count;
6218 CopyBytes(text, line.buffer + start, end - start);
6221 numChars += end-start;
6223 if(style.freeCaret && line == l2 && addSpaces)
6226 count = Max(x2-Max(x1,l1.count),0);
6228 count = Max(x2-l2.count,0);
6231 FillBytes(text,' ',count);
6237 if(line == l2) break;
6249 // '\0' terminate Terminate
6256 void GetSel(char * text, bool addCr)
6258 GetText(text, line, y, x, selLine, selY, selX, addCr, true);
6261 private void _Deselect() // This assumes marking lines as dirty is handled by the caller
6279 int size = SelSize();
6282 // Try to allocate memory
6283 ClipBoard clipBoard { };
6284 if(clipBoard.Allocate(size+1))
6286 GetSel(clipBoard.memory, true);
6299 ClipBoard clipBoard { };
6300 if(clipBoard.Load())
6301 PutS(clipBoard.memory);
6313 SetViewToCursor(true);
6319 void DeleteSelection()
6324 SetViewToCursor(true);
6330 void Save(File f, bool cr)
6333 savedAction = undoBuffer.curAction;
6335 for(line = this.lines.first; line; line = line.next)
6337 f.Write(line.buffer, line.count, 1);
6340 if(cr) f.Putc('\r');
6346 #define BUFFER_SIZE 16384
6349 undoBuffer.dontRecord++;
6352 char buffer[BUFFER_SIZE];
6356 int count = f.Read(buffer, 1, BUFFER_SIZE-1);
6357 buffer[count] = '\0';
6363 undoBuffer.dontRecord--;
6364 undoBuffer.count = 0;
6365 undoBuffer.curAction = 0;
6366 itemEditUndo.disabled = undoBuffer.curAction == 0;
6367 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
6370 EditBoxFindResult Find(const char * text, bool matchWord, bool matchCase, bool isSearchDown)
6374 bool firstPass = true;
6375 EditBoxFindResult result = found;
6377 if(!this.line) return notFound;
6380 for(line = this.line;;)
6388 line = this.lines.first;
6394 line = this.lines.last;
6395 num = this.lineCount - 1;
6401 string = SearchString(line.buffer, firstPass ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6403 string = RSearchString(line.buffer,text,firstPass ? (Min(this.x,line.count)-1) : line.count,matchCase,matchWord);
6407 Select((void *)line,num,string - line.buffer,(void *)line,num,string - line.buffer + strlen(text));
6410 if(line == this.line && !firstPass) break;
6428 EditBoxFindResult FindInSelection(const char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
6432 int searchLen = strlen(text);
6433 for(line = (EditLine)this.line, y = this.y; y <= y2; line = line.next, y++)
6435 char * string = SearchString(line.buffer, (y == this.y) ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6436 if(string && (y < y2 || (string - line.buffer) + searchLen <= x2))
6438 Select((void *)line,y,string - line.buffer,(void *)line,y,string - line.buffer + strlen(text));
6445 bool OnKeyDown(Key key, unichar ch)
6448 //PrintLn("OnKeyDown: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
6450 if(!NotifyKeyDown(master, this, key, ch))
6460 this.mouseMove = false;
6461 OnLeftButtonUp(0,0,0);
6462 SetViewToCursor(true);
6472 public class EditBoxStream : File
6475 BufferLocation start, sel;
6482 EditBox editBox = this.editBox;
6484 editBox.x = start.x;
6485 editBox.y = start.y;
6486 editBox.line = start.line;
6488 editBox.selX = sel.x;
6489 editBox.selY = sel.y;
6490 editBox.selLine = sel.line;
6492 editBox.SetViewToCursor(true);
6493 //editBox.ComputeColumn();
6497 property EditBox editBox
6505 start.line = value.line;
6509 sel.line = value.selLine;
6512 value.GoToHome(true);
6515 get { return editBox; }
6518 uint Read(byte * buffer, uint size, uint count)
6521 EditBox editBox = this.editBox;
6522 EditLine line = editBox.line;
6528 for(;read < count && line; line = (*&line.next))
6530 int numBytes = Min(count - read, (*&line.count) - x);
6533 memcpy(buffer + read, (*&line.buffer) + x, numBytes);
6538 for(;read < count && x < (*&line.count);)
6540 buffer[read++] = (*&line.buffer)[x++];
6547 buffer[read++] = '\n';
6552 if(x == (*&line.count) && (*&line.next))
6561 editBox.line = editBox.selLine = line;
6562 editBox.x = editBox.selX = x;
6563 editBox.y = editBox.selY = y;
6568 bool Seek(int pos, FileSeekMode mode)
6571 EditBox editBox = this.editBox;
6572 EditLine line = editBox.line;
6574 if(mode == FileSeekMode::start)
6576 pos = pos - this.pos;
6586 for(;read < pos && line; line = (*&line.next))
6588 int numBytes = Min(pos - read, (*&line.count) - x);
6591 read += numBytes; x += numBytes;
6594 /*for(;read < pos && x < (*&line.count);)
6622 for(;read < pos && line; line = (*&line.prev))
6624 int numBytes = Min(pos - read, x);
6630 /*for(;read < pos && x > 0;)
6640 x = (*&(*&line.prev).count);
6656 editBox.line = editBox.selLine = line;
6657 editBox.x = editBox.selX = x;
6658 editBox.y = editBox.selY = y;
6663 bool Puts(const char * string)
6665 EditBox editBox = this.editBox;
6666 BufferLocation start { editBox.line, editBox.y, editBox.x };
6670 editBox.AddS(string);
6672 pos.line = editBox.line;
6676 this.start.AdjustAdd(start, pos);
6677 sel.AdjustAdd(start, pos);
6682 // NOTE: BYTE, NOT UNICODE CHARACTER!
6685 EditBox editBox = this.editBox;
6686 BufferLocation start = { editBox.line, editBox.y, editBox.x };
6691 utf8Bytes[numBytes++] = ch;
6692 utf8Bytes[numBytes] = 0;
6693 if(UTF8Validate((char *)utf8Bytes))
6695 editBox.AddCh(UTF8_GET_CHAR(utf8Bytes, numBytes));
6697 pos.line = editBox.line;
6700 this.start.AdjustAdd(start, pos);
6701 sel.AdjustAdd(start, pos);
6708 bool Getc(char * ch)
6710 return Read(ch, 1, 1) ? true : false;
6713 void DeleteBytes(uint count)
6715 EditBox editBox = this.editBox;
6718 BufferLocation pos { editBox.line, editBox.y, editBox.x };
6719 BufferLocation end = pos;
6724 for(;c < count && end.line && end.x < end.line.count;)
6729 if(c < count && end.line && end.line.next)
6731 end.line = end.line.next;
6740 start.AdjustDelete(pos, end);
6741 sel.AdjustDelete(pos, end);
6743 editBox._DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true, false, null);