1 namespace gui::controls;
4 selectionForeground = white;
5 disabled: defaultTextColor = Color { 85, 85, 85 };
14 public class SyntaxColorScheme
18 Color charLiteralColor;
19 Color stringLiteralColor;
20 Color preprocessorColor;
22 private Array<Color> keywordColors { };
24 public property Container<Color> keywordColors
26 set { keywordColors.Copy((void *)value); }
27 get { return keywordColors; }
35 #define XOFFSET (3 + (/*style.lineNumbers?6 * this.space.w:*/0))
37 #define YOFFSET (style.multiLine ? 1 : ((clientSize.h + 1 - space.h) / 2))
39 #define IS_ALUNDER(ch) (/*(ch) == '_' || */CharMatchCategories((ch), letters|numbers|marks|connector))
41 #define UTF8_IS_FIRST(x) (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || ((b) & 0x40)); }))
42 #define UTF8_NUM_BYTES(x) (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
43 #define UTF8_GET_CHAR(string, numBytes) \
47 byte b = (string)[0]; \
50 numBytes = b ? 1 : 0; \
64 if(b & 0x08) { numBytes = 0; } \
73 for(i = 0; i<numBytes; i++) \
76 ch |= (b = (string)[i]) & mask; \
78 if(i > 1 && (!(b & 0x80) || (b & 0x40))) \
85 ch > 0x10FFFF || (ch >= 0xD800 && ch <= 0xDFFF) || \
86 (ch < 0x80 && numBytes > 1) || \
87 (ch < 0x800 && numBytes > 2) || \
88 (ch < 0x10000 && numBytes > 3))\
98 bool autoEmpty:1, readOnly:1, multiLine:1, stuckCaret:1, freeCaret:1, select:1, hScroll:1, vScroll:1, smartHome:1;
99 bool noCaret:1, noSelect:1, tabKey:1, useTab:1, tabSel:1, allCaps:1, syntax:1, wrap:1;
102 bool inMultiLineComment:1, inPrep:1, escaped:1, continuedSingleLineComment:1, wasInMultiLine:1;
104 bool recomputeSyntax:1;
105 bool cursorFollowsView:1;
107 // bool lineNumbers:1;
112 void UnregisterClass_EditBox()
115 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
124 extern int __ecereVMethodID_class_OnFree;
127 static class ArrayImpl
134 public class OldArray
139 void ** array = (void **)((ArrayImpl)this).array;
140 if(type.type == normalClass || type.type == noHeadClass)
142 for(c = 0; c<size; c++)
143 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, array[c]);
145 // TODO: Call OnFree for structClass
146 delete ((ArrayImpl)this).array;
155 if(((ArrayImpl)this).array)
159 ((ArrayImpl)this).array = renew0 ((ArrayImpl)this).array byte[type.typeSize * value];
162 ((ArrayImpl)this).array = new0 byte[value * type.typeSize];
171 memcpy(((ArrayImpl)this).array, value, type.typeSize * size);
178 public class UndoAction : struct
181 subclass(UndoAction) type;
182 virtual void Undo(void * data) { type.Undo(this, data); }
183 virtual void Redo(void * data) { type.Redo(this, data); }
185 virtual void Print(void * data) { type.Print(this, data); }
189 if(((Class)type).Destructor)
190 ((void (*)(void *))((Class)type).Destructor)(this);
194 public class UndoBuffer
196 Array<UndoAction> actions { size = 8 };
216 UndoAction action = actions[--curAction];
218 /*Print("Undoing: ");
219 action.Print(data);*/
230 if(curAction < count)
232 UndoAction action = actions[curAction];
235 /*Print("Redoing: ");
236 action.Print(data);*/
244 void Record(UndoAction action)
246 if(!dontRecord && !insideRedo)
248 if(curAction < count)
251 for(c = curAction; c < count; c++)
257 if(count >= actions.size)
258 actions.size += actions.size / 2;
261 /*Print("Recording: ");
262 action.Print(data);*/
264 actions[count++] = action;
267 if(actions.size > count + count / 2 && count + count / 2 >= 8)
268 actions.size = count + count / 2;
275 static class AddCharAction : UndoAction
279 int addedSpaces, addedTabs;
280 type = class(AddCharAction);
282 void Undo(EditBox editBox)
284 editBox.GoToPosition(null, (ch == '\n') ? (y + 1) : y, (ch == '\n') ? 0 : (x + 1));
286 if(addedTabs || addedSpaces)
287 editBox.DelCh(editBox.line, y, x - (addedSpaces + addedTabs), editBox.line, y, x, false);
288 editBox.UpdateDirty();
291 void Redo(EditBox editBox)
293 editBox.GoToPosition(null, y, x);
295 editBox.UpdateDirty();
298 void Print(EditBox editBox)
300 PrintLn("AddChar: y = ", y, "x = ", x, ", ch = ", ch, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
305 static class AddTextAction : UndoAction
309 int addedSpaces, addedTabs;
310 type = class(AddTextAction);
313 void Print(EditBox editBox)
315 PrintLn("AddText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
323 void Undo(EditBox editBox)
328 editBox.GoToPosition(null, y1, x1);
330 l2 = editBox.lines.first;
331 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
333 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
334 if(addedTabs || addedSpaces)
335 editBox.DelCh(editBox.line, y1, x1 - (addedSpaces + addedTabs), editBox.line, y1, x1, false);
337 editBox.SetViewToCursor(true);
341 void Redo(EditBox editBox)
343 editBox.GoToPosition(null, y1, x1);
344 editBox.PutS(string);
348 static class DelTextAction : UndoAction
354 type = class(DelTextAction);
357 void Print(EditBox editBox)
359 PrintLn("DelText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", placeAfter = ", placeAfter);
362 void Undo(EditBox editBox)
364 editBox.GoToPosition(null, y1, x1);
365 editBox.PutS(string);
369 editBox.GoToPosition(null, y1, x1);
372 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
373 //editBox.SetViewToCursor(true);
376 editBox.DelCh(editBox.line, y1, x1 - addedSpaces, editBox.line, y1, x1, false);
382 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
383 //editBox.SetViewToCursor(true);
386 editBox.DelCh(editBox.selLine, y1, x1 - addedSpaces, editBox.selLine, y1, x1, false);
390 void Redo(EditBox editBox)
395 editBox.GoToPosition(null, y1, x1);
398 l2 = editBox.lines.first;
399 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
401 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
402 editBox.SetViewToCursor(true);
412 static class ReplaceTextAction : UndoAction
414 int y1, x1, y2, x2, y3, x3;
418 int addedSpaces, addedTabs;
420 type = class(ReplaceTextAction);
423 void Print(EditBox editBox)
425 PrintLn("ReplaceText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", y3 = ", y3, ", x3 = ", x3, ", oldString = ", oldString, ", newString = ", newString, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs, ", placeAfter = ", placeAfter);
428 void Undo(EditBox editBox)
433 editBox.GoToPosition(null, y1, x1);
435 l3 = editBox.lines.first;
436 for(c = 0; c < y3 && l3; c++, l3 = l3.next);
438 editBox.DelCh(l1, y1, x1, l3, y3, x3, true);
440 editBox.PutS(oldString);
441 if(addedSpaces || addedTabs)
442 editBox.DelCh(l1, y1, x1 - (addedSpaces + addedTabs), l1, y1, x1, false);
444 //editBox.PutS(oldString);
447 editBox.GoToPosition(null, y1, x1);
450 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
456 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
460 void Redo(EditBox editBox)
465 editBox.GoToPosition(null, y1, x1);
467 l2 = editBox.lines.first;
468 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
470 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
472 editBox.PutS(newString);
482 static class MoveTextAction : UndoAction
484 int fy1, fx1, fy2, fx2;
486 type = class(MoveTextAction);
488 void Undo(EditBox editBox)
493 void Redo(EditBox editBox)
499 public class EditLine : struct
511 // Only works for single line edit for now...
512 EditBox editBox = this.editBox;
516 get { return this ? buffer : null; }
518 property EditLine prev { get { return this ? prev : null; } };
519 property EditLine next { get { return this ? next : null; } };
520 property int count { get { return this ? count : 0; } };
527 // This makes sure the buffer always contains at least count characters
528 // Keeps a count/2 pad for avoiding always reallocating memory.
529 bool AdjustBuffer(int count)
537 newSize = (count + (count >> 1));
539 // Shrink down the buffer
542 buffer = new char[newSize];
543 if(!buffer) return false;
547 CopyBytes(buffer, this.buffer, count);
550 this.buffer = buffer;
554 // Increase the buffer
555 else if(size < count)
557 buffer = new char[newSize];
558 if(!buffer) return false;
562 CopyBytes(buffer, this.buffer, this.count + 1); // size);
565 this.buffer = buffer;
574 public struct BufferLocation
579 void AdjustDelete(BufferLocation start, BufferLocation end)
581 // Location is before, nothing to do
582 if(y < start.y || (y == start.y && x < start.x))
584 // Location is inside deleted bytes, point to the start
585 if((y >= start.y && (y > start.y || x >= start.x)) &&
586 (y >= end.y && (y > end.y || x >= end.x)))
591 // Location is on another line
593 y -= end.y - start.y;
594 // Location is the last touched line
601 //if(start.line == end.line)
602 x -= end.x - start.x;
611 // Assuming no carriage return before first character ???? fixed?
612 void AdjustAdd(BufferLocation start, BufferLocation end)
614 int numLines = end.y - start.y;
621 if(x > start.x || (x == start.x /*&& (numLines ? true : false)*/))
624 for(c = 0, line = start.line; c<numLines; c++)
627 //x += numLines ? end.x : (end.x - start.x);
628 x += end.x - start.x;
635 public enum EditBoxFindResult { notFound, found, wrapped };
637 static char * keyWords1[] =
640 "return","break","continue","default","switch","case","if","else","for","while", "do","long","short",
641 "void", "char","int","float","double","unsigned","static", "extern", "struct", "union", "typedef","enum",
643 "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line",
644 "__attribute__", "__stdcall", "_stdcall",
645 "__declspec", "goto",
646 "inline", "__inline__", "_inline", "__inline", "__typeof","__extension__",
647 "asm", "__asm", "_asm", "volatile", "#cpu", "__stdcall__",
650 "class", "private", "public",
652 "delete", "new", "new0", "renew", "renew0", "define",
655 "dllexport", "dllimport", "stdcall",
656 "subclass", "__on_register_module", "namespace", "using",
657 "typed_object", "any_object", "incref", "register", "watch", "stopwatching", "firewatchers", "watchable", "class_designer",
658 "class_fixed", "class_no_expansion", "isset", "class_default_property", "property_category", "class_data", "class_property",
659 "virtual", "thisclass","unichar", "dbtable", "dbindex", "database_open", "dbfield",
662 "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64", "uintptr", "intptr", "intsize", "uintsize",
665 "this", "true", "false", "null", "value",
674 static char * keyWords2[] =
676 "defined", "warning", null
679 static char ** keyWords[] = { keyWords1, keyWords2 };
680 #define NUM_KEYWORD_GROUPS (sizeof(keyWords) / sizeof(char **))
681 //static int * keyLen[NUM_KEYWORD_GROUPS];
682 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
684 static char searchString[1025], replaceString[1025];
685 static bool matchCase = false, wholeWord = false, searchUp = false;
687 static GoToDialog goToDialog
689 autoCreate = false, isModal = true, text = $"Go To"
692 public class EditBox : CommonControl
694 class_property(icon) = "<:ecere>controls/editBox.png";
698 virtual bool Window::NotifyModified(EditBox editBox);
699 virtual void Window::NotifyCaretMove(EditBox editBox, int line, int charPos);
700 virtual void Window::NotifyUpdate(EditBox editBox);
701 virtual bool Window::NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods);
702 virtual void Window::NotifyOvrToggle(EditBox editBox, bool overwrite);
703 virtual bool Window::NotifyKeyDown(EditBox editBox, Key key, unichar ch);
705 virtual bool Window::NotifyCharsAdded(EditBox editBox, BufferLocation before, BufferLocation after, bool pasteOperation);
706 virtual bool Window::NotifyCharsDeleted(EditBox editBox, BufferLocation beforeLoc, BufferLocation after, bool pasteOperation);
707 virtual bool Window::NotifyDropped(EditBox editBox, int x, int y);
709 virtual bool Window::NotifyUnsetModified(EditBox editBox);
711 // Why was this commented out?
712 // It is required otherwise updating font property from property sheet doesn't immediately reflect in form designer,
713 // and the scrollArea property isn't compared properly either.
717 if(font) ComputeFont();
718 SetInitSize(initSize);
724 style.vScroll = true;
730 style.hScroll = true;
734 property bool textHorzScroll { property_category $"Behavior" set { style.hScroll = value; } get { return style.hScroll; } }; // Should cut the text on set to false
735 property bool textVertScroll { property_category $"Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
736 property bool readOnly
738 property_category $"Behavior"
741 style.readOnly = value;
742 itemEditCut.disabled = value || !selection;
743 itemEditDelete.disabled = value || !selection;
744 itemEditPaste.disabled = value;
746 get { return style.readOnly; }
748 property bool multiLine { property_category $"Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
749 property bool freeCaret { property_category $"Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
750 property bool tabKey { property_category $"Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
751 property int tabSize { property_category $"Behavior" set { tabSize = value; } get { return tabSize; } };
752 property bool tabSelection { property_category $"Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
753 property bool smartHome { property_category $"Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
754 property bool autoEmpty { property_category $"Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
755 property bool noCaret { property_category $"Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
756 property int maxLineSize { property_category $"Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
757 property int maxNumLines { property_category $"Behavior" set { maxLines = value; } get { return maxLines; } };
758 property bool useTab { property_category $"Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
759 property bool syntaxHighlighting { property_category $"Appearance" set { style.syntax = value; } get { return style.syntax; } };
760 property bool noSelect { property_category $"Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
761 property bool allCaps { property_category $"Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
762 property bool autoSize { property_category $"Behavior" set { style.autoSize = value; } get { return style.autoSize; } };
763 property bool wrap { set { style.wrap = value; Update(null); } get { return style.wrap; } };
764 //property bool lineNumbers { set { style.lineNumbers = value; } get { return style.lineNumbers; } };
765 property int numLines { get { return this ? lineCount : 0; } };
766 property int lineNumber { get { return y; } }; // TODO: Change to property of EditLine this.line.number
767 property int column { get { return col; } }; // TODO: Add Set
768 property int charPos { get { return x; } }; // TODO: Add Set
769 property EditLine firstLine { get { return lines.first; } }; // Change these to a List<EditLine>... (this.lines[10].text)
770 property EditLine lastLine { get { return lines.last; } };
771 property EditLine line { get { return this.line; } }; // TODO: Add Set this.line = this.lines[10]
772 property char * contents
774 property_category $"Data"
779 undoBuffer.dontRecord++;
781 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
784 //SetViewToCursor(true);
787 undoBuffer.dontRecord--;
793 char * buffer = null;
796 /* Can't implement this right now because of memory leak... Need string reference counting...
803 for(line = lines.first; line; line = line.next)
804 len += strlen(line.buffer);
806 buffer = new char[len+1];
808 for(line = lines.first; line; line = line.next)
810 int lineLen = strlen(line.buffer);
811 memcpy(buffer + len, line.buffer, lineLen);
817 buffer = this.line ? this.line.buffer : null;
822 property bool overwrite { get { return overwrite; } };
823 property bool caretFollowsScrolling { get { return style.cursorFollowsView; } set { style.cursorFollowsView = value; } }
825 property char * multiLineContents
829 char * buffer = null;
836 for(line = lines.first; line; line = line.next)
837 len += strlen(line.buffer)+1;
839 buffer = new char[len+1];
841 for(line = lines.first; line; line = line.next)
843 int lineLen = strlen(line.buffer);
844 memcpy(buffer + len, line.buffer, lineLen);
846 if(line.next) buffer[len++] = '\n';
859 return this.line.buffer;
864 void SetLineText(char * text)
868 EditLine_SetText(this.line, text);
872 property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
873 property Color selectionText { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
874 property SyntaxColorScheme syntaxColorScheme { set { delete colorScheme; colorScheme = value; incref colorScheme; } }
876 // selectionStart.line, selectionStart.column (With Set)
877 // selection.line1, selection.line2, selection.column1, selection.column2 (Read only)
891 // Position of Caret (Not necessarily displayed position)
894 // Position of beginning of block (Block ends at (x,y))
896 // line is line at carret, selLine is line at beginning of block
897 EditLine line, selLine, dropLine;
898 // Mouse selection Moving virtual caret
903 // ViewX is x offset in pixels, ViewY is y offset in lines
905 // viewLine is first displayed line
908 // start and end of area to redraw
911 // MaxLine is the longest line
913 // MaxLength is the longest line's length
916 // MouseSelect is true once button is pressed, overwrite is true if mode is on
917 bool mouseSelect, mouseMove, overwrite, wordSelect;
918 // Timer is used for mouse selection scrolling
921 window = this, delay = 0.1;
930 OnMouseMove(mouseX, mouseY, -1);
935 // (mouseX,mouseY) is the position of the mouse in the edit box
940 void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
943 bool rightButtonDown;
946 UndoBuffer undoBuffer { data = this };
948 ColorAlpha selectionColor, selectionText;
949 SyntaxColorScheme colorScheme { };
954 Menu editMenu { menu, $"Edit", e };
957 editMenu, $"Cut\tCtrl+X", t, disabled = true;
959 bool NotifySelect(MenuItem item, Modifiers mods)
961 if(!(style.readOnly))
966 MenuItem itemEditCopy
968 editMenu, $"Copy\tCtrl+C", c, disabled = true;
970 bool NotifySelect(MenuItem item, Modifiers mods)
976 MenuItem itemEditPaste
978 editMenu, $"Paste\tCtrl+V", p;
980 bool NotifySelect(MenuItem item, Modifiers mods)
982 if(!(style.readOnly))
987 MenuItem itemEditDelete
989 editMenu, $"Delete\tDel", d, disabled = true;
991 bool NotifySelect(MenuItem item, Modifiers mods)
993 if(!(style.readOnly))
998 MenuDivider { editMenu };
999 MenuItem itemEditSelectAll
1001 editMenu, $"Select All\tCtrl+A", a;
1003 bool NotifySelect(MenuItem item, Modifiers mods)
1009 MenuDivider { editMenu };
1010 MenuItem itemEditUndo
1012 editMenu, $"Undo\tCtrl+Z", u;
1015 bool NotifySelect(MenuItem item, Modifiers mods)
1021 MenuItem itemEditRedo
1023 editMenu, $"Redo\tCtrl+Y", o;
1026 bool NotifySelect(MenuItem item, Modifiers mods)
1032 MenuDivider { editMenu };
1035 editMenu, $"Find Previous\tShift-F3", e, shiftF3;
1037 bool NotifySelect(MenuItem item, Modifiers mods)
1040 Find(searchString, wholeWord, matchCase, false);
1042 itemEditFind.NotifySelect(this, item, mods);
1048 editMenu, $"Find Next\tF3", n, f3;
1050 bool NotifySelect(MenuItem item, Modifiers mods)
1053 Find(searchString, wholeWord, matchCase, true);
1055 itemEditFind.NotifySelect(this, item, mods);
1059 MenuItem itemEditFind
1061 editMenu, $"Find...\tCtrl+F", f, ctrlF;
1063 bool NotifySelect(MenuItem item, Modifiers mods)
1067 editBox = this, master = master, isModal = true, searchString = searchString, matchCase = matchCase, wholeWord = wholeWord,
1068 searchUp = searchUp;
1071 // Fix dialog from above shouldn't be visible
1072 // void NotifyDestroyed(FindDialog dialog, DialogResult result)
1073 void NotifyDestroyed(Window window, DialogResult result)
1075 FindDialog dialog = (FindDialog) window;
1076 searchUp = dialog.searchUp;
1077 strcpy(searchString, dialog.searchString);
1078 matchCase = dialog.matchCase;
1079 wholeWord = dialog.wholeWord;
1088 editMenu, $"Replace...\tCtrl+R", r, ctrlR;
1090 bool NotifySelect(MenuItem item, Modifiers mods)
1092 ReplaceDialog dialog
1096 searchString = searchString,
1097 replaceString = replaceString,
1098 matchCase = matchCase,
1099 wholeWord = wholeWord,
1103 // void NotifyDestroyed(ReplaceDialog dialog, DialogResult result)
1104 void NotifyDestroyed(Window window, DialogResult result)
1106 ReplaceDialog dialog = (ReplaceDialog)window;
1107 char * replace = dialog.replaceString;
1109 strcpy(replaceString, replace);
1110 strcpy(searchString, dialog.searchString);
1111 matchCase = dialog.matchCase;
1112 wholeWord = dialog.wholeWord;
1119 MenuDivider { editMenu };
1122 editMenu, $"Go To...\tCtrl+G", g, ctrlG;
1124 bool NotifySelect(MenuItem item, Modifiers mods)
1126 goToDialog.editBox = this;
1127 goToDialog.master = master;
1128 goToDialog.Create();
1132 MenuDivider { editMenu };
1133 MenuItem itemEditInsertTab
1135 editMenu, $"Insert Tabs", i, checkable = true;
1137 bool NotifySelect(MenuItem item, Modifiers mods)
1139 style.useTab = item.checked;
1145 snapVertScroll = true;
1146 snapHorzScroll = true;
1150 static bool syntaxInit = false;
1155 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
1157 for(c = 0; keyWords[g][c]; c++);
1158 //keyLen[g] = new int[c];
1159 for(c = 0; keyWords[g][c]; c++)
1161 keyLen[g][c] = strlen(keyWords[g][c]);
1165 colorScheme.commentColor = dimGray;
1166 colorScheme.charLiteralColor = crimson;
1167 colorScheme.stringLiteralColor = crimson;
1168 colorScheme.preprocessorColor = green;
1169 colorScheme.numberColor = teal;
1170 colorScheme.keywordColors = [ blue, blue ];
1173 FontExtent = Display::FontExtent;
1175 lines.offset = (uint)&((EditLine)0).prev;
1177 style = EditBoxBits { hScroll = true };
1179 // cursor = guiApp.GetCursor(IBeam);
1181 // Default Properties
1183 maxLineSize = 1024;*/
1186 maxLineSize = MAXINT;
1191 mouseSelect = this.mouseMove = false;
1194 x = selX = selY = 0;
1197 line = selLine = null;
1203 endY = clientSize.h;
1206 // Default space to 8 in case window is not constructed
1209 undoBuffer.dontRecord++;
1211 undoBuffer.dontRecord--;
1213 viewLine = lines.first;
1214 style.recomputeSyntax = true;
1220 UpdateCaretPosition(true);
1226 lines.Free(EditLine::Free);
1229 void FlushBuffer(Surface surface, EditLine line, int wc, int * renderStart, int * x, int y, int numSpaces, Box box)
1231 int count = wc - *renderStart;
1234 if(y + space.h >= box.top && y <= box.bottom)
1240 //FontExtent(display, font, line.buffer + *renderStart, count, &w, null);
1241 surface.TextFont(font);
1242 surface.TextExtent(line.buffer + *renderStart, count, &w, null);
1243 if(*x + w + XOFFSET > 0)
1244 surface.WriteText(XOFFSET + *x,y, line.buffer + *renderStart, count);
1249 w = numSpaces; // * space.w;
1250 if(*x + w + XOFFSET > 0 && surface.GetTextOpacity())
1251 surface.Area(XOFFSET + *x - 1, y, XOFFSET + *x + w, y + space.h-1);
1252 // WHATS UP WITH THIS... surface.Area(XOFFSET + *x, y, XOFFSET + *x + w, y + space.h-1);
1260 int CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected,
1261 Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, bool *overwrite)
1267 if((wc == selX && line == selLine) || (wc == editX && line == this.line))
1271 *foreground = (*selected) ? selectionForeground : textColor;
1272 *background = selectionBackground;
1273 *opacity = *selected;
1280 if(this.overwrite && active)
1282 if((style.stuckCaret && wc == line.count && !line.next) ||
1283 (!mouseMove && line == this.line && wc == editX))
1292 void FigureStartSyntaxStates(EditLine firstLine, bool reset)
1296 bool inMultiLineComment = reset ? false : style.inMultiLineComment;
1297 bool wasInMultiLine = reset ? false : style.wasInMultiLine;
1298 bool inString = false;
1299 bool inQuotes = false;
1300 bool inPrep = reset ? false : style.inPrep;
1301 bool inSingleLineComment = false;
1302 bool escaped = reset ? false : style.escaped;
1303 bool continuedSingleLineComment = reset ? false : style.continuedSingleLineComment;
1305 EditLine line = reset ? lines.first : firstLine;
1306 // int maxBackUp = 1000, c;
1307 // for(c = 0, line = viewLine; c < maxBackUp && line && line.prev; line = line.prev);
1308 for(; line != viewLine; line = line.next)
1310 char * text = line.buffer;
1313 bool lastWasStar = false;
1314 bool firstWord = true;
1315 if(!escaped) inPrep = false;
1316 inSingleLineComment = continuedSingleLineComment;
1322 for(c = 0; (ch = text[c]); c++)
1324 bool wasEscaped = escaped;
1325 bool backLastWasStar = lastWasStar;
1326 bool backWasInMultiLine = wasInMultiLine;
1328 lastWasStar = false;
1329 wasInMultiLine = inMultiLineComment;
1332 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1334 if(text[c+1] == '/')
1336 inSingleLineComment = true;
1338 else if(text[c+1] == '*')
1340 inMultiLineComment = true;
1343 else if(backLastWasStar)
1344 inMultiLineComment = false;
1348 if(backWasInMultiLine) lastWasStar = true;
1350 else if(ch == '\"' && !inSingleLineComment && !inMultiLineComment && !inQuotes)
1352 if(inString && !wasEscaped)
1361 else if(ch == '\'' && !inSingleLineComment && !inMultiLineComment && !inString)
1363 if(inQuotes && !wasEscaped)
1375 else if(ch == '#' && !inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1382 else if(ch != ' ' && ch != '\t')
1385 continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
1388 style.continuedSingleLineComment = continuedSingleLineComment;
1389 style.inMultiLineComment = inMultiLineComment;
1390 style.wasInMultiLine = wasInMultiLine;
1391 style.inPrep = inPrep;
1392 style.escaped = escaped;
1396 /*void OnDrawOverChildren(Surface surface)
1398 if(style.lineNumbers)
1400 int currentLineNumber = this.viewY + 1;
1403 for(i = 0; i * space.h < box.height; i++)
1405 // ********* LINE NUMBERING *********
1406 surface.SetForeground(Color{60, 60, 60});
1407 //Highlight current line
1408 if(this.caretY / space.h == currentLineNumber - 1)
1409 surface.SetBackground(Color{220, 220, 220});
1411 surface.SetBackground(Color{230, 230, 230});
1412 surface.textOpacity = true;
1414 sprintf(lineText,"%5u ", currentLineNumber % 100000);
1415 if(currentLineNumber > this.lineCount)
1416 surface.WriteText(0,i*space.h+1," ",6);
1418 surface.WriteText(0,i*space.h+1,lineText,6);
1420 currentLineNumber++;
1425 void OnRedraw(Surface surface)
1429 bool selected = false, selection = true;
1431 Color selectionBackground = selectionColor ? selectionColor : SELECTION_COLOR;
1432 Color selectionForeground = selectionText ? selectionText : SELECTION_TEXT;
1433 Color defaultTextColor = property::foreground;
1436 int maxW = clientSize.w;
1438 Color foreground, background;
1441 // Overwrite Caret Stuff
1442 bool overWrite = false;
1443 int overWriteX, overWriteY;
1446 // ****** SYNTAX STATES ******
1447 bool inMultiLineComment = style.inMultiLineComment;
1448 bool inString = false;
1449 bool inQuotes = false;
1450 bool inPrep = style.inPrep;
1451 bool inSingleLineComment = false;
1452 bool escaped = style.escaped;
1453 bool continuedSingleLineComment = style.continuedSingleLineComment;
1454 bool wasInMultiLine = style.wasInMultiLine;
1455 // ****** ************* ******
1458 defaultTextColor = Color { 85, 85, 85 };
1459 textColor = defaultTextColor;
1462 Abs(selectionBackground.r - property::background.r) +
1463 Abs(selectionBackground.g - property::background.g) +
1464 Abs(selectionBackground.b - property::background.b) < 92)
1466 selectionBackground = formColor;
1467 selectionForeground = selectionColor ? selectionColor : SELECTION_COLOR;
1470 surface.TextFont(this.font);
1472 surface.SetBackground(GDefaultPalette()[RND_Get(1, 15)]);
1473 surface.Area(0,0,MAXINT,MAXINT);
1478 surface.SetBackground(formColor);
1479 surface.Area(0,0,clientSize.w, clientSize.h);
1482 if(this.selX == this.x && this.selY == this.y)
1492 editX = Min(this.x,this.line.count);
1493 selX = Min(this.selX,this.selLine.count);
1496 selected = (this.selY < this.viewY) ^ (this.y < this.viewY);
1498 foreground = selected ? selectionForeground : textColor;
1499 background = selectionBackground;
1505 surface.SetForeground(foreground);
1506 surface.SetBackground(background);
1507 surface.TextOpacity(opacity);
1509 surface.GetBox(box);
1511 for(line = this.viewLine; line; line = line.next)
1513 int x = -this.viewX;
1517 Color newTextColor = textColor = defaultTextColor;
1518 bool lineComplete = false;
1521 // ****** SYNTAX HIGHLIGHTING ******
1522 bool lastWasStar = false;
1523 bool firstWord = true;
1524 if(!escaped) inPrep = false;
1525 inSingleLineComment = continuedSingleLineComment;
1529 // *********************************
1531 /* === DEBUGGING TOOL FOR MAXLINE ===
1533 if(line == this.maxLine)
1535 surface.SetBackground(GREEN|0xFF000000);
1536 surface.TextOpacity(true);
1540 surface.TextOpacity(selected ? true : false);
1541 surface.SetBackground(selected ? SELECTION_COLOR|0xFF000000 : BLACK|0xFF000000);
1545 if(line == this.selLine && line == this.line)
1547 end = Max(line.count, this.x);
1548 end = Max(end, this.selX);
1550 else if(line == this.selLine)
1551 end = Max(line.count, this.selX);
1552 else if(line == this.line)
1553 end = Max(line.count, this.x);
1561 bool spacing = true;
1562 bool cantHaveWords = false;
1569 lineComplete = false;
1572 textColor = newTextColor;
1575 foreground = textColor;
1576 surface.SetForeground(textColor);
1580 for(; c<end && !cantHaveWords;)
1584 bufferLen += wordLen;
1588 /*if(line.count > 4000)
1595 for(; c<line.count; c++)
1597 unichar ch = line.buffer[c];
1598 unichar bf = (wordLen == 1) ? line.buffer[c-1] : 0;
1599 //if(ch == ' ' || ch == '\t' || (wordLen && (ch == '(' || ch == ')' || ch == ';' || ch == ':')) || (wordLen == 1 && line.buffer[c-1] == '('))
1600 if(CharMatchCategories(ch, separators) || /*ch == ' ' ||*/ ch == '\t' ||
1601 (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && ch != '#' /*&& ch != '_'*/) ||
1602 (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && bf != '#' && bf != '\t' /*&& bf != '_' && bf != ' '*/))
1610 for(; c<line.count; c++)
1612 unichar ch = line.buffer[c];
1613 if(ch == '\t' || ch == ' ')
1615 cantHaveWords = true;
1619 if(ch != ' ' && ch != '\t')
1624 if(c == line.count && c < end)
1633 cantHaveWords = true;
1638 lastWasStar = false;
1644 bool backEscaped = escaped;
1645 bool backLastWasStar = lastWasStar;
1646 bool backInMultiLineComment = inMultiLineComment;
1647 bool backInString = inString;
1648 bool backInQuotes = inQuotes;
1649 bool backInPrep = inPrep;
1650 bool backInSingleLineComment = inSingleLineComment;
1651 bool backWasInMultiLine = wasInMultiLine;
1653 char * word = line.buffer + c - wordLen;
1655 bool wasEscaped = escaped;
1657 lastWasStar = false;
1659 wasInMultiLine = inMultiLineComment;
1661 // Determine Syntax Highlighting
1662 newTextColor = defaultTextColor;
1665 if(inSingleLineComment || inMultiLineComment)
1667 newTextColor = colorScheme.commentColor;
1671 newTextColor = colorScheme.charLiteralColor;
1675 newTextColor = colorScheme.stringLiteralColor;
1679 newTextColor = colorScheme.preprocessorColor;
1681 if(wordLen == 1 && word[0] == '/')
1683 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1687 inSingleLineComment = true;
1688 newTextColor = colorScheme.commentColor;
1690 else if(word[1] == '*')
1692 inMultiLineComment = true;
1693 newTextColor = colorScheme.commentColor;
1696 else if(backLastWasStar)
1697 inMultiLineComment = false;
1699 else if(wordLen == 1 && word[0] == '*')
1701 if(backWasInMultiLine)
1704 else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && wordLen == 1 && word[0] == '\"')
1706 if(inString && !wasEscaped)
1713 newTextColor = colorScheme.stringLiteralColor;
1716 else if(!inSingleLineComment && !inMultiLineComment && !inString && wordLen == 1 && word[0] == '\'')
1718 if(inQuotes && !wasEscaped)
1723 newTextColor = colorScheme.charLiteralColor;
1726 else if(wordLen == 1 && word[0] == '\\')
1731 else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && (isdigit(word[0]) || (word[0] == '.' && isdigit(word[1]))))
1733 char * dot = strchr(word, '.');
1735 if(dot && dot > word + wordLen) dot = null;
1737 strtod((dot == word + wordLen) ? (dot+1) : word, &s);
1739 strtol(word, &s, 0);
1742 if((dot && *s == 'f' && !isalnum(s[1]) && s[1] != '_') || (!isalpha(*s) && *s != '_'))
1744 int newWordLen = s + ((*s == 'f') ? 1 : 0) - word;
1745 newTextColor = colorScheme.numberColor;
1746 c += newWordLen - wordLen;
1747 wordLen = newWordLen;
1749 else if(dot && dot > word && (isalpha(dot[1]) || dot[1] == '_'))
1750 newTextColor = colorScheme.numberColor;
1755 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && word[0] == '#')
1760 newTextColor = colorScheme.preprocessorColor;
1763 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1765 for(g = 0; g < ((inPrep && word[0] != '#') ? 2 : 1); g++)
1767 char ** keys = keyWords[g];
1768 int * len = keyLen[g];
1769 for(ccc = 0; keys[ccc]; ccc++)
1771 if(len[ccc] == wordLen && !strncmp(keys[ccc], word, wordLen))
1773 newTextColor = colorScheme.keywordColors[g];
1782 // If highlighting for this word is different, break
1783 if(newTextColor != textColor)
1787 // Better solution than going back?
1790 // Reset syntax flags
1791 escaped = backEscaped;
1792 lastWasStar = backLastWasStar;
1793 inMultiLineComment = backInMultiLineComment;
1794 inString = backInString;
1795 inQuotes = backInQuotes;
1796 inPrep = backInPrep;
1797 inSingleLineComment = backInSingleLineComment;
1798 wasInMultiLine = backWasInMultiLine;
1803 textColor = newTextColor;
1806 foreground = textColor;
1807 surface.SetForeground(textColor);
1814 // If we're not breaking, this can't be rendered as spacing anymore
1817 // Do word wrapping here
1823 FontExtent(display, font, line.buffer + start, bufferLen + wordLen, &tw, null);
1828 w += numSpaces * space.w;
1830 if(x + viewX + w > maxW)
1832 // Avoid an endless loop until we fix wrapping
1833 if(c - wordLen > start)
1836 lineComplete = true;
1842 bufferLen += wordLen;
1847 int renderStart = start;
1852 // Render checking if we need to split because of selection or to find where to draw insert caret
1853 for(wc = start; wc < start + bufferLen; wc++)
1855 flush = CheckColors(line, wc, selection, selX, editX, &selected, selectionForeground,
1856 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite);
1857 if(overWrite == true)
1859 overWriteCh = (wc < line.count) ? line.buffer[wc] : ' ';
1860 if(overWriteCh == '\t') overWriteCh = ' ';
1865 FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1866 if(overWrite == true)
1874 surface.TextOpacity(opacity);
1875 surface.SetBackground(background);
1876 surface.SetForeground(foreground);
1883 if(wc < line.count && line.buffer[wc] == '\t')
1885 numSpaces += (tabSize * space.w) - ((x + numSpaces + viewX) % (tabSize * space.w));
1889 numSpaces += space.w;
1893 FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1898 if(CheckColors(line, c, selection, selX, editX, &selected, selectionForeground,
1899 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite))
1901 if(overWrite == true)
1908 surface.TextOpacity(opacity);
1909 surface.SetBackground(background);
1910 surface.SetForeground(foreground);
1913 if(style.freeCaret && selected)
1915 surface.SetBackground(selectionBackground);
1916 surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1917 // TEST: surface.Area(x + XOFFSET,y,clientSize.w-1,y+this.space.h-1);
1922 if(style.freeCaret && selected)
1924 surface.SetBackground(selectionBackground);
1925 surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1929 continuedSingleLineComment = inSingleLineComment && (line.count && line.text[line.count - 1] == '\\');
1932 if(y > box.bottom) // >=clientSize.h)
1938 surface.TextOpacity(true);
1939 surface.SetForeground(black);
1940 surface.SetBackground(Color {255,255,85});
1941 surface.WriteText(XOFFSET + overWriteX,overWriteY, &overWriteCh, 1);
1945 void FixScrollArea()
1947 if(style.hScroll || style.vScroll)
1949 int width = maxLength + XOFFSET;
1950 int height = lineCount * space.h;
1951 if(style.freeCaret && line)
1956 width = Max(line.length + (x - line.count) * space.w, maxLength + XOFFSET);
1960 if(selX > selLine.count)
1961 width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength + XOFFSET);
1966 SetScrollLineStep(8, space.h);
1967 SetScrollArea(width, height, true);
1971 void ComputeLength(EditLine line)
1978 for(c = 0; c < line.count; )
1987 for(len = 0; c < line.count; c += numBytes)
1989 ch = line.buffer[c];
1990 numBytes = UTF8_NUM_BYTES(ch);
1992 if(ch == ' ' || ch == '\t')
1999 if(!len && ch == ' ')
2004 else if(!len && ch == '\t')
2006 w = (tabSize * space.w) - (x % (tabSize * space.w));
2010 FontExtent(display, font, line.buffer + start, len, &w, null);
2021 if(line.length > this.maxLength)
2023 this.maxLine = line;
2024 this.maxLength = line.length;
2026 if(style.autoSize) AutoSize();
2035 this.maxLine = null;
2037 for(line = lines.first; line; line = line.next)
2039 if(line.length > this.maxLength)
2041 this.maxLength = line.length;
2042 this.maxLine = line;
2046 if(style.autoSize) AutoSize();
2051 if(this.selY != this.y)
2053 else if(this.selX != this.x) // commented out to erase caret: if(this.selX != this.x)
2057 void ComputeColumn()
2059 // Adjust new edit X position according to tabs
2060 int c, position = 0;
2063 for(c = 0; c<this.line.count && c<this.x && (ch = UTF8_GET_CHAR(this.line.buffer + c, nb)); c+= nb)
2065 // TODO: MIGHT WANT TO RETHINK WHAT COLUMN SHOULD BE REGARDING TABS
2067 position += this.tabSize - (position % this.tabSize);
2071 position += this.x - c;
2072 this.col = position;
2075 int DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
2077 return _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, null);
2080 bool HasCommentOrEscape(EditLine line)
2082 bool hadComment = strstr(line.buffer, "/*") || strstr(line.buffer, "*/");
2087 for(c = line.count-1; c >= 0; c--)
2089 char ch = line.buffer[c];
2095 else //if(ch != ' ' && ch != '\t')
2102 int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, int * addedSpacesPtr)
2104 EditLine line = l1, next;
2106 int oldCount1, oldCount2;
2107 int y, firstViewY, firstY, firstDropY, firstSelY;
2110 DelTextAction action = null;
2111 bool hadComment = false;
2115 hadComment = HasCommentOrEscape(line);
2117 if(y2 > y1 || c2 > c1)
2119 if(start < l1.count)
2121 while(!UTF8_IS_FIRST(l1.buffer[start]) && start)
2125 oldCount1 = l1.count;
2127 while(c1 < oldCount1)
2129 byte ch = buffer[c1];
2130 if(UTF8_IS_FIRST(ch)) break;
2134 oldCount2 = l2.count;
2136 while(c2 < oldCount2)
2138 byte ch = buffer[c2];
2139 if(UTF8_IS_FIRST(ch)) break;
2144 if(!undoBuffer.dontRecord && (y2 > y1 || c2 > c1))
2149 len = GetText(null, l1, y1, start, l2, y2, c2, false, false);
2150 string = new char[len];
2151 action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter };
2152 GetText(string, l1, y1, start, l2, y2, c2, false, false);
2156 //oldCount1 = l1.count;
2157 //oldCount2 = l2.count;
2160 BufferLocation before = { l1,y1,c1}, after = { l2,y2,c2 };
2161 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
2164 if(c2 > oldCount2) c2 = oldCount2;
2165 if(!(style.freeCaret))
2166 if(c1 > oldCount1) c1 = oldCount1;
2167 newLineCount = c1 + l2.count-c2;
2173 buffer = new char[line.size ? line.size : 1];
2175 buffer = new char[line.size];
2177 CopyBytes(buffer,l2.buffer,oldCount1 + 1/*line.count + 1*//*line.size*/);
2182 if(!line.AdjustBuffer(newLineCount))
2186 /*if(newLineCount > 4000 || newLineCount < 0)
2187 printf("Warning");*/
2189 line.count = newLineCount;
2191 memmove(l1.buffer+c1, buffer+c2,line.count-c1);
2196 action.addedSpaces = c1-oldCount1;
2198 if(action.addedSpaces > action.x1)
2204 if(addedSpacesPtr) *addedSpacesPtr = c1-oldCount1;
2205 FillBytes(l1.buffer+oldCount1,' ',c1-oldCount1);
2207 line.buffer[line.count] = '\0';
2214 this.dropX -= c2-c1;
2215 this.dropX = Max(this.dropX,0);
2217 this.x = Max(this.x,0);
2224 firstViewY = this.viewY;
2226 firstDropY = this.dropY;
2227 firstSelY = this.selY;
2228 for(line = l1;line;line = next, y++)
2236 if(line == this.viewLine)
2238 if(this.viewLine.next)
2240 this.viewLine = this.viewLine.next;
2242 style.recomputeSyntax = true;
2246 this.viewLine = this.viewLine.prev;
2248 style.recomputeSyntax = true;
2251 else if(y < firstViewY)
2253 if(line == this.line)
2257 this.line = this.line.next;
2258 this.x = this.line.count;
2263 this.line = this.line.prev;
2264 this.x = this.line.count;
2271 if(line == this.dropLine)
2273 if(this.dropLine.next)
2275 this.dropLine = this.dropLine.next;
2276 this.dropX = this.dropLine.count;
2280 this.dropLine = this.dropLine.prev;
2281 this.dropX = this.dropLine.count;
2285 else if(y < firstDropY)
2287 if(line == this.selLine)
2289 if(this.selLine.next)
2291 this.selLine = this.selLine.next;
2292 this.selX = this.selLine.count;
2296 this.selLine = this.selLine.prev;
2297 this.selX = this.selLine.count;
2301 else if(y < firstSelY)
2305 if(line == l2) break;
2309 if(style.syntax && (hadComment || HasCommentOrEscape(this.line)))
2312 style.recomputeSyntax = true;
2317 bool DelSel(int * addedSpacesPtr)
2319 if(this.line != this.selLine || this.x != this.selX)
2321 if(this.selY < this.y)
2323 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2326 this.line = this.selLine;
2328 else if(this.selY > this.y)
2330 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2333 this.selLine = this.line;
2335 else if(this.selX < this.x)
2337 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2340 this.line = this.selLine;
2344 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2347 this.selLine = this.line;
2355 bool AddToLine(char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr)
2357 bool hadComment = false;
2358 // Add the line here
2359 EditLine line = this.line;
2361 // The purpose of this is solely to lock a max number of characters if no HSCROLLING is present
2362 if(!style.hScroll && created)
2364 int endX = (style.freeCaret) ? this.x : Min(this.x, line.count);
2369 // Lock if no place to display.
2372 else if(endX > this.x)
2373 max = Max(this.x, line.count);
2377 for(x = 0, c = 0; c < max+count; )
2382 if(c < Min(this.x, line.count))
2383 string = line.buffer + c;
2386 else if(c < endX + count)
2387 string = stringLine + c - endX;
2389 string = line.buffer + c - endX - count;
2393 w = (tabSize * space.w) - (x % (tabSize * space.w));
2397 numBytes = UTF8_NUM_BYTES(*string);
2398 FontExtent(display, this.font, string, numBytes, &w, null);
2402 if(x >= clientSize.w)
2407 c += numBytes; // - 1;
2413 int addedSpaces = 0;
2416 // Add blank spaces if EES_FREECARET
2417 if(this.x > line.count)
2427 for(c = 0; c<line.count; c++)
2429 if(this.line.buffer[c] == '\t')
2430 position += this.tabSize - (position % this.tabSize);
2434 wantedPosition = position + (this.x - line.count);
2436 // A tab is too much...
2437 if(position + (this.tabSize - (position % this.tabSize)) > wantedPosition)
2438 addedSpaces = wantedPosition - position;
2443 position += this.tabSize - (position % this.tabSize);
2444 // Add as many tabs as needed
2445 addedTabs += (wantedPosition - position) / this.tabSize;
2446 position += (addedTabs-1) * this.tabSize;
2447 // Finish off with spaces
2448 addedSpaces = wantedPosition - position;
2452 addedSpaces = this.x - line.count;
2455 this.x = Min(this.x, line.count);
2457 if(line.count + count + addedSpaces + addedTabs > this.maxLineSize)
2463 hadComment = HasCommentOrEscape(line);
2465 // Adjust the size of the line
2466 if(!line.AdjustBuffer(line.count+count+addedTabs+addedSpaces))
2470 BufferLocation before = { this.line, this.y, this.x }, after = { this.line, this.y, this.x };
2473 memmove(line.buffer+this.x+count, line.buffer+this.x,line.count-this.x);
2474 CopyBytes(line.buffer + this.x + addedTabs + addedSpaces, stringLine, count);
2477 *addedTabsPtr = addedTabs;
2478 FillBytes(line.buffer+line.count,'\t',addedTabs);
2480 if(addedTabs > 4000 || addedTabs < 0)
2483 line.count += addedTabs;
2489 FillBytes(line.buffer+line.count,' ',addedSpaces);
2491 if(addedSpaces > 4000 || addedSpaces < 0)
2494 line.count += addedSpaces;
2495 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
2497 else if(addedSpacesPtr)
2498 *addedSpacesPtr = 0;
2500 if(count > 4000 || count < 0)
2503 line.count += count;
2504 this.x += count + addedTabs + addedSpaces;
2507 this.selLine = this.line;
2509 line.buffer[line.count] = '\0';
2511 ComputeLength(line);
2516 hasComment = HasCommentOrEscape(line);
2517 if(!undoBuffer.insideRedo)
2519 int backDontRecord = undoBuffer.dontRecord;
2520 undoBuffer.dontRecord = 0;
2521 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
2522 undoBuffer.dontRecord = backDontRecord;
2524 if(style.syntax && (hadComment || hasComment || line != this.line))
2526 style.recomputeSyntax = true;
2534 void Emptyline(EditLine line, int y)
2537 DelCh(line, y, 0, line.next, y+1, 0, true);
2539 DelCh(line, y, 0, line, y, line.count, true);
2542 void GoToEnd(bool deselect)
2546 this.line = this.lines.last;
2547 if(this.y != this.lineCount-1)
2549 else if (this.x != this.line.count)
2550 DirtyLine(this.lineCount-1);
2551 this.y = this.lineCount-1;
2552 this.x = this.line.count;
2559 void GoToHome(bool deselect)
2563 this.line = this.lines.first;
2566 else if (this.x !=0)
2567 DirtyLine(this.lineCount-1);
2576 // Returns true if it needs scrolling
2577 bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
2583 bool needHScroll = false;
2590 line = this.viewLine ? (void *)this.viewLine.prev : null;
2595 line = (void *)this.lines.first;
2600 py = Min(py, clientSize.h);
2602 py = Min(py, this.lineCount);
2604 for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
2610 if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
2613 px = Min(px,clientSize.w+this.space.w);
2617 *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
2620 if(tline) *tline = line;
2623 // Prevent divide by 0 from non valid this.font
2625 return (y < this.viewY) || needHScroll;
2627 return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
2631 // Minimal Update Management Functions
2635 this.endY = clientSize.h-1;
2636 // ErrorLog("DirtyAll\n");
2639 void DirtyEnd(int y)
2641 if((y - this.viewY)*this.space.h < this.startY)
2642 this.startY = (y - this.viewY)*this.space.h+ YOFFSET;
2643 this.endY = clientSize.h-1;
2644 //ErrorLog("DirtyEnd %d\n", y);
2647 void DirtyLine(int y)
2651 if((y - this.viewY)*this.space.h < this.startY)
2652 this.startY = (y - this.viewY)*this.space.h + YOFFSET;
2653 if((y - this.viewY+1)*this.space.h > this.endY)
2654 this.endY = (y - this.viewY+1)*this.space.h-1 + YOFFSET;
2656 //ErrorLog("DirtyLine %d\n", y);
2663 if(style.recomputeSyntax)
2665 FigureStartSyntaxStates(lines.first, true);
2666 style.recomputeSyntax = false;
2669 if(this.startY > this.endY) return;
2670 if(this.startY <= 0 && this.endY >= clientSize.h-1)
2676 box.right = clientSize.w-1;
2677 box.top = this.startY;
2678 box.bottom = this.endY;
2681 this.startY = clientSize.h;
2685 bool IsMouseOnSelection()
2687 bool mouseOnSelection = false;
2690 int minY = Min(this.selY, this.y);
2691 int maxY = Max(this.selY, this.y);
2692 int minX = Min(this.selX, this.x);
2693 int maxX = Max(this.selX, this.x);
2695 FindMouse(this.mouseX - this.space.w / 2, this.mouseY, &x, &y, null, false);
2697 if(maxX != minX || maxY != minY)
2699 if(y > minY && y < maxY)
2700 mouseOnSelection = true;
2701 else if(y == minY && y == maxY)
2702 mouseOnSelection = (x < maxX && x >= minX);
2706 mouseOnSelection = (x >= this.selX);
2707 else if(y == this.y)
2708 mouseOnSelection = (x >= this.x);
2713 mouseOnSelection = (x < this.selX);
2714 else if(y == this.y)
2715 mouseOnSelection = (x < this.x);
2718 return mouseOnSelection;
2721 void UpdateCaretPosition(bool setCaret)
2725 if(mouseMove || (!overwrite && !style.noCaret))
2727 int max = this.mouseMove ? this.dropX : this.x;
2728 int y = this.mouseMove ? this.dropY : this.y;
2729 EditLine line = this.mouseMove ? this.dropLine : this.line;
2731 if(!(style.freeCaret))
2732 max = Min(max, line.count);
2734 if(FontExtent && display)
2736 for(c = 0; c < max; )
2745 for(len = 0; c < Min(max, line.count); c += numBytes)
2747 ch = line.buffer[c];
2748 numBytes = UTF8_NUM_BYTES(ch);
2750 if(ch == ' ' || ch == '\t')
2757 if(!len && ch == ' ')
2762 else if(!len && ch == '\t')
2764 w = (tabSize * space.w) - (x % (tabSize * space.w));
2768 FontExtent(display, this.font, line.buffer + start, len, &w, null);
2780 caretY = y * this.space.h;
2781 SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
2786 NotifyCaretMove(master, this, y + 1, x + 1);
2792 void SelectionEnables()
2794 if((x != selX || y != selY) && !selection)
2798 itemEditCut.disabled = false;
2799 itemEditDelete.disabled = false;
2801 itemEditCopy.disabled = false;
2803 this.selection = true;
2805 else if((x == selX && y == selY) && selection)
2807 itemEditCut.disabled = true;
2808 itemEditCopy.disabled = true;
2809 itemEditDelete.disabled = true;
2811 this.selection = false;
2815 void SetSelectCursor()
2817 if(!inactive || !style.noSelect)
2820 cursor = guiApp.GetCursor(arrow);
2821 else if(this.mouseSelect)
2822 cursor = guiApp.GetCursor(iBeam);
2825 if(IsMouseOnSelection())
2826 cursor = guiApp.GetCursor(arrow);
2828 cursor = guiApp.GetCursor(iBeam);
2833 int AdjustXPosition(EditLine line, int position, bool half, int * px, int max, int sc)
2836 int x = px ? *px : 0;
2843 if(c < Min(max, line.count))
2847 for(len = 0; c < Min(max, line.count); c += numBytes)
2849 ch = line.buffer[c];
2850 numBytes = UTF8_NUM_BYTES(ch);
2852 if(ch == ' ' || ch == '\t')
2859 if(!len && ch == ' ')
2864 else if(!len && ch == '\t')
2866 w = (tabSize * space.w) - (x % (tabSize * space.w));
2870 FontExtent(display, font, line.buffer + start, len, &w, null);
2874 if(style.freeCaret && c < max)
2883 if(x + (((half && len == 1) ? (w / 2) : w)) >= position)
2888 int a = start + len;
2891 while(a > 0 && !UTF8_IS_FIRST(line.buffer[--a]));
2895 FontExtent(display, font, line.buffer + start, a - start, &w, null);
2898 if(position > x + (half ? ((w + lastW) / 2) : lastW)) break;
2902 return Min(this.maxLineSize - 1, start + len);
2908 void SetCursorToViewX()
2910 bool selecting = this.x != selX || y != selY;
2912 // Horizontal Adjustment
2914 int c = AdjustXPosition(line, viewX, false, &x, MAXINT, 0);
2919 c = AdjustXPosition(line, viewX + clientSize.w - 1, false, &x, MAXINT, c);
2931 UpdateCaretPosition(false);
2937 void SetCursorToViewY()
2940 EditLine oldLine = this.line;
2942 bool selecting = this.x != this.selX || this.y != this.selY;
2944 numLines = clientSize.h / this.space.h;
2946 // Vertical Adjustment
2947 if(this.viewY > this.y)
2949 this.y = this.viewY;
2950 this.line = this.viewLine;
2953 if(this.viewY + numLines <= this.y)
2957 this.y = this.viewY-1;
2958 for(c = 0, line = this.viewLine; line && c<numLines; line = line.next, c++)
2965 if(this.line != oldLine)
2967 this.x = AdjustXPosition(this.line, caretX, true, null, MAXINT, 0);
2975 this.selLine = this.line;
2978 UpdateCaretPosition(false);
2985 bool SaveFile(char * fileName)
2987 File f = eFile_Open(fileName, FO_WRITE);
2991 eWindow_SetModified(false);
2992 eInstance_Delete(f);
2999 bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
3003 if(!style.multiLine)
3005 x = (active && this.active && !style.readOnly) ? line.count : 0;
3008 SetViewToCursor(true);
3013 if(!active && modified)
3016 if(!NotifyModified(master, this))
3019 *goOnWithActivation = false;
3026 if(timer) timer.Stop();
3028 mouseSelect = false;
3036 int aw = maxLength + 12, ah = Max(lineCount, 1) * space.h + 2;
3037 int nw = minClientSize.w, nh = minClientSize.h, xw = maxClientSize.w, xh = maxClientSize.h;
3038 clientSize = { nw && aw < nw ? nw : xw && aw > xw ? xw : aw, nh && ah < nh ? nh : xh && ah > xh ? xh : ah };
3041 bool OnResizing(int *w, int *h)
3047 *w = space.h * 80 / 14;
3051 void OnResize(int w, int h)
3054 if(!hasHorzScroll && !hasVertScroll && viewLine)
3055 SetViewToCursor(true);
3057 //if(!hasHorzScroll && !hasVertScroll && viewLine)
3059 SetViewToCursor(true);
3064 bool OnMiddleButtonDown(int x, int y, Modifiers mods)
3066 if(style.readOnly) return true;
3067 // We really shouldn't be pasting here, Unix middle button paste is for the selection (not the clipboard), good for the terminal
3068 // Middle button already serves as a dragger as well.
3073 bool OnRightButtonDown(int x, int y, Modifiers mods)
3075 this.rightButtonDown = true;
3080 bool OnRightButtonUp(int x, int y, Modifiers mods)
3083 if(!parent.inactive && rightButtonDown)
3086 Menu contextMenu { };
3088 MenuItem { contextMenu, $"Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
3089 MenuItem { contextMenu, $"Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
3090 MenuItem { contextMenu, $"Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
3091 MenuItem { contextMenu, $"Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
3092 MenuDivider { contextMenu };
3093 MenuItem { contextMenu, $"Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
3095 popup = PopupMenu { master = this, menu = contextMenu,
3097 nonClient = true, interim = false, parent = parent,
3098 position = { x + clientStart.x + parent.clientStart.x + position.x, y + cientStart.y + parent.sy + clientStart.y + position.y };
3100 position = { x + clientStart.x + absPosition.x - guiApp.desktop.position.x, y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
3104 rightButtonDown = false;
3108 bool OnLeftButtonDown(int mx, int my, Modifiers mods)
3113 if(style.noSelect) return true;
3115 // Should we have a separate 'selectOnActivate' style?
3116 if(!mods.isActivate || (style.readOnly && style.multiLine))
3122 mouseX = mx - XOFFSET;
3125 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3127 //PrintLn("OnLeftButtonDown: ", x, ", ", y);
3133 else if(IsMouseOnSelection() && !mods.isActivate)
3143 if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
3145 if(mods.shift && !mods.isActivate)
3166 UpdateCaretPosition(true);
3170 bool OnLeftButtonUp(int x, int y, Modifiers mods)
3174 mouseSelect = false;
3185 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3187 //PrintLn("MouseMove: ", x, ", ", y);
3193 mouseMove = IsMouseOnSelection();
3197 int size = SelSize();
3200 char * text = new char[size+1];
3204 GetSel(text, false);
3206 if(Max(selY, this.y) == dropY)
3210 if(this.dropX > this.selX)
3211 moveX = this.x - this.selX;
3215 if(this.dropX > this.x)
3216 moveX = this.selX - this.x;
3220 this.dropX -= moveX;
3221 this.selX = this.x = this.dropX;
3222 this.selY = this.y = this.dropY;
3223 this.selLine = this.line = this.dropLine;
3225 SetViewToCursor(true);
3230 byte * c = ((EditBox)this).multiLineContents, * c2;
3231 int l1 = c ? strlen(c) : 0, l2;
3234 c2 = ((EditBox)this).multiLineContents;
3235 l2 = c2 ? strlen(c2) : 0;
3236 if(l1 != l2 || (l1 && strcmp(c, c2)))
3266 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3268 //PrintLn("Dropped: ", x, ", ", y);
3270 NotifyDropped(master, this, x, y);
3277 bool OnMouseMove(int mx, int my, Modifiers mods)
3283 if(mods != -1 && mods.isSideEffect)
3288 if(style.noSelect) return true;
3289 if(wordSelect) return true;
3290 mouseX = mx - XOFFSET;
3293 needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
3295 if(this.mouseMove || this.mouseSelect)
3304 ((style.hScroll) || (style.vScroll)))
3311 DirtyLine(this.dropY);
3314 DirtyLine(this.dropY);
3315 this.dropLine = line;
3316 SetViewToCursor(true);
3318 //PrintLn("MouseMove: ", "dropX = ", x, ", dropY = ", y);
3321 else if(this.mouseSelect)
3323 DirtyLine(this.selY);
3330 SetViewToCursor(true);
3333 //PrintLn("MouseSelect: ", "x = ", x, ", y = ", y);
3340 bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
3345 //PrintLn("OnLeftDoubleClick: ", mx, ", ", my, ", mods = ", mods);
3349 if(style.noSelect) return true;
3350 FindMouse(mx, my, &x, &y, &line, false);
3351 if(!NotifyDoubleClick(master, this, line, mods))
3358 for(c = x; c >= 0; c--)
3361 while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
3362 ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3369 for(c = start; c<line.count; c += numBytes)
3371 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3372 if(!ch || !IS_ALUNDER(ch))
3382 this.line = this.selLine = line;
3383 this.wordSelect = (c != start);
3394 Menu fileMenu { menu, "File", F };
3395 saveDialog = fileDialog;
3396 MenuItem { fileMenu, $"Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
3397 MenuItem { fileMenu, $"Save As...", A, NotifySelect = MenuFileSaveAs };
3399 if(style.autoSize) AutoSize();
3407 FontExtent(display, font, " ", 1, (int *)&space.w, (int *)&space.h);
3408 FontExtent(display, font, "W", 1, (int *)&large.w, (int *)&large.h);
3410 space.w = Max(space.w, 1);
3411 large.w = Max(large.w, 1);
3412 space.h = Max(space.h, 1);
3413 large.h = Max(large.h, 1);
3417 for(line = lines.first; line; line = line.next)
3418 ComputeLength(line);
3423 SetViewToCursor(true);
3431 bool OnLoadGraphics()
3433 FontExtent = Display::FontExtent;
3436 // UpdateCaretPosition(true);
3440 void OnUnloadGraphics()
3445 bool OnKeyHit(Key key, unichar ch)
3447 bool shift = (key.shift) ? true : false;
3449 //PrintLn("OnKeyHit: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
3451 if(!ch && !key.alt && !key.ctrl)
3453 key.code = (SmartKey)key.code;
3455 else if(!ch && key.alt)
3458 switch(key.code) //(ch || key.alt || key.ctrl) ? key.code : (Key)(SmartKey)key.code)
3461 if(style.readOnly) break;
3462 if(style.stuckCaret) GoToEnd(true);
3463 if(!(style.freeCaret))
3465 this.x = Min(this.x, this.line.count);
3471 EditLine line = this.line;
3473 for(y = this.y; y>= 0; y--)
3475 c = (y == this.y) ? (this.x-1) : line.count-1;
3477 // Slow down when going on lines...
3478 if(y != this.y) break;
3482 //if(this.line.buffer[c] != '\t' && this.line.buffer[c] != ' ')
3483 if(IS_ALUNDER(line.buffer[c]))
3488 if(!IS_ALUNDER(line.buffer[c]))
3490 //if(this.line.buffer[c] == '\t' || this.line.buffer[c] == ' ')
3504 DelCh(line,y,c+1,this.line,this.y,this.x, true);
3505 this.x = this.selX = Min(c+1, line.count);
3506 this.y = this.selY = y;
3507 this.line = this.selLine = line;
3508 SetViewToCursor(true);
3519 if(style.readOnly) break;
3520 if(style.stuckCaret) break;
3529 if(this.line != this.selLine || this.x != this.selX)
3532 SetViewToCursor(true);
3538 if(this.x < this.line.count)
3542 for(i = this.x; i < this.line.count; i++)
3544 if(!IS_ALUNDER(this.line.buffer[i]))
3548 for(; i < this.line.count; i++)
3550 //Delete trailing whitespace
3551 if(IS_ALUNDER(this.line.buffer[i]))
3554 DelCh(this.line, this.y, this.x, this.line, this.y, i, false);
3555 SetViewToCursor(true);
3558 else if(this.line.next)
3560 DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3561 SetViewToCursor(true);
3567 if(!(style.freeCaret))
3569 this.selX = this.x = Min(this.x, this.line.count);
3572 if(this.x < this.line.count)
3574 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3576 else if(this.line.next)
3578 DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3580 SetViewToCursor(true);
3589 if(!key.alt && !key.ctrl)
3593 bool stuffAfter = false;
3597 if(style.stuckCaret) GoToEnd(true);
3598 if(style.readOnly) break;
3599 if(!(style.multiLine)) break;
3601 for(c = 0; c<this.line.count && c<this.x; c++)
3603 if(this.line.buffer[c] == '\t')
3604 position += this.tabSize - (position % this.tabSize);
3605 else if(this.line.buffer[c] == ' ')
3613 if(this.x < this.line.count)
3616 //If last character is a { indent one tab
3617 if(this.line.buffer[this.x - 1] == '{')
3619 //Except if the next non space character is a }
3620 bool indent = false;
3622 for(i = this.x; i < this.line.size; i++)
3623 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
3625 if(this.line.buffer[i] != '}')
3630 position += this.tabSize;
3633 addString = new char[position + 2];
3634 addString[len++] = '\n';
3635 addString[len] = '\0';
3637 if(stuffAfter || !style.freeCaret)
3639 for(c = 0; c<position; )
3641 if(style.useTab && c + this.tabSize <= position)
3643 addString[len++] = '\t';
3648 addString[len++] = ' ';
3652 addString[len] = '\0';
3656 if(!stuffAfter && style.freeCaret)
3658 this.x = this.selX = position;
3662 SetViewToCursor(true);
3672 if(style.stuckCaret) break;
3673 if(!(style.freeCaret))
3675 this.x = Min(this.x, this.line.count);
3676 this.selX = Min(this.selX, this.selLine.count);
3679 if(!shift) SelDirty();
3682 bool foundAlpha = false;
3685 EditLine line, lastLine;
3688 for(line = this.line; (line && !found); line = line.prev, y--)
3693 if(this.x == 0 && line != this.line)
3702 if(line == this.line) start = this.x -1; else start = line.count-1;
3703 start = Min(start, line.count-1);
3705 for(c = start; c >= 0;)
3708 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3726 byte ch = line.buffer[c];
3727 if(UTF8_IS_FIRST(ch)) break;
3731 // No next word found,
3732 if(!found && ( this.x > 0 || (!line.count && this.x)))
3746 this.line = lastLine;
3757 byte * buffer = line.buffer;
3760 byte ch = buffer[x];
3761 if(UTF8_IS_FIRST(ch)) break;
3778 if(!shift) _Deselect();
3779 SetViewToCursor(true);
3785 if(style.stuckCaret) break;
3786 if(!(style.freeCaret))
3788 this.x = Min(this.x, this.line.count);
3789 this.selX = Min(this.selX, this.selLine.count);
3792 if(!shift) SelDirty();
3793 if(!shift && (this.x != this.selX || this.y != this.selY));
3796 bool onAChar = false;
3797 if(this.selX != this.x || this.selY != this.y)
3799 if(this.x<this.line.count)
3800 if(this.line.buffer[this.x] != '\t' && this.line.buffer[this.x] !=' ')
3802 if(key.shift && onAChar &&
3803 ((this.y > this.selY)||((this.selY == this.y)&&(this.x >= this.selX))))
3805 bool foundAlpha = false;
3807 EditLine line, lastLine;
3809 int lastC, lastY, lastNumBytes;
3811 for(line = this.line; (line && !found); line = line.next, y++)
3813 int start = (line == this.line) ? this.x : 0;
3817 for(c = start; c < line.count && (ch = UTF8_GET_CHAR(line.buffer + c, numBytes)); c += numBytes)
3823 lastNumBytes = numBytes;
3833 if(!found && (c != this.x || line != this.line))
3846 this.x = lastC + lastNumBytes;
3848 this.line = lastLine;
3855 bool foundAlpha = false;
3860 for(line = this.line; (line && !found); line = line.next, y++)
3862 int start = (line == this.line) ? this.x : 0;
3866 for(c = start; c < line.count && (ch = UTF8_GET_CHAR(line.buffer + c, numBytes)); c += numBytes)
3882 // No next word found,
3883 if(!found && (c != this.x || line != this.line))
3887 this.x = line.count;
3899 if(x < line.count || (style.freeCaret && line.count < maxLineSize))
3903 byte * buffer = line.buffer;
3906 byte ch = buffer[x];
3907 if(UTF8_IS_FIRST(ch)) break;
3928 if(!shift) _Deselect();
3929 SetViewToCursor(true);
3936 if(!style.vScroll || hasVertScroll) break;
3942 if(style.stuckCaret) break;
3944 if(!shift) SelDirty();
3954 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
3958 if(!shift) _Deselect();
3960 SetViewToCursor(false);
3963 if(caretY == this.y * space.h)
3969 if(!style.freeCaret)
3970 this.x = Min(this.x, line.count);
3980 int sx = 0, sy = this.y * space.h;
3981 char * text = line.text;
3982 int maxW = clientSize.w - sx;
3983 display.FontExtent(font, " ", 1, null, &th);
3987 int startPos = textPos;
3990 bool lineComplete = false;
3992 if(!style.wrap && caretY == MAXINT)
3995 //textPos = line.count;
3996 //lineComplete = true;
3999 for(; (style.freeCaret || textPos < line.count) && !lineComplete;)
4003 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4006 len = (nextSpace - (text + textPos));
4008 len = line.count - textPos;
4010 if(textPos < line.count)
4012 display.FontExtent(font, text + textPos, len, &w, null);
4014 if(nextSpace) { w += space.w; len++; }
4016 if(style.wrap && x + width + w > maxW && x > 0)
4018 lineComplete = true;
4028 if((!style.freeCaret && textPos >= line.count) || (sy == caretY - th && caretX <= x + width + sx))
4032 while(this.x > 0 && x + sx > caretX && this.x > startPos)
4035 if(this.x > line.count)
4038 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4039 len = this.x - startPos;
4040 display.FontExtent(font, text + startPos, len, &x, null);
4044 if(!shift) _Deselect();
4046 SetViewToCursor(false);
4050 if(sy == caretY - th || textPos >= line.count)
4052 if(textPos >= line.count)
4054 int c = textPos - 1;
4055 while(c > 0 && text[c] == ' ') c--;
4059 this.x = line.count;
4062 if(!shift) _Deselect();
4064 SetViewToCursor(false);
4069 } while(textPos < line.count);
4072 if(!shift) _Deselect();
4074 SetViewToCursor(false);
4083 int x = AdjustXPosition(this.line, this.line.prev, true, null, MAXINT, 0);
4084 if(!shift) SelDirty();
4085 this.line = this.line.prev;
4091 if(!shift) _Deselect();
4095 SetViewToCursor(false);
4101 return style.multiLine ? false : true;
4105 if(!style.vScroll || hasVertScroll)
4112 if(style.stuckCaret) break;
4116 int sx = 0, sy = this.y * this.space.h;
4117 int maxW = clientSize.w - sx;
4118 char * text = line.buffer;
4120 if(!shift) SelDirty();
4126 if(AdjustXPosition(line, maxW, this.x, line.count, true, null, MAXINT, 0) <= line.count)
4136 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
4140 if(!shift) _Deselect();
4142 SetViewToCursor(false);
4145 while(!textPos || (style.freeCaret || textPos<line.count))
4147 int startPos = textPos;
4150 bool lineComplete = false;
4151 if(!style.wrap && sy <= caretY)
4153 textPos = line.count;
4154 lineComplete = true;
4156 for(; (style.freeCaret || textPos<line.count) && !lineComplete;)
4160 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4163 len = (nextSpace - (text + textPos));
4165 len = line.count - textPos;
4167 if(textPos < line.count)
4169 display.FontExtent(font, text + textPos, len, &w, &th);
4171 if(nextSpace) { w += space.w; len++; }
4172 if(style.wrap && x + width + w > maxW && x > 0)
4174 lineComplete = true;
4184 if(sy > caretY && ((!style.freeCaret && textPos >= line.count) || caretX <= x + width + sx))
4188 while(this.x > 0 && x + sx > caretX && textPos > startPos)
4191 if(this.x > line.count)
4194 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4196 len = this.x - startPos;
4197 display.FontExtent(font, text + startPos, len, &x, null);
4200 if(!shift) _Deselect();
4203 SetViewToCursor(false);
4209 this.x = line.count;
4212 if(!shift) _Deselect();
4215 SetViewToCursor(false);
4218 else if(textPos >= line.count && line.next)
4224 sy = this.y * this.space.h;
4225 sx = 0; //textBlock.startX;
4231 sx = 0; //textBlock.startX;
4239 this.x = Min(this.x, line.count);
4240 //int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4242 if(!shift) _Deselect();
4245 if(this.selX != this.x || this.selY != this.y)
4248 SetViewToCursor(false);
4255 int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4256 if(!shift) SelDirty();
4257 this.line = this.line.next;
4261 if(!shift) _Deselect();
4264 if(this.selX != this.x || this.selY != this.y)
4272 return style.multiLine ? false : true;
4275 if(style.stuckCaret) break;
4276 if(!style.multiLine && key.ctrl) break;
4277 if(!(style.freeCaret))
4278 this.selX = Min(this.selX, this.selLine.count);
4280 if(!shift) SelDirty();
4283 this.line = this.lines.first;
4284 if(this.y != 0 || this.x != 0)
4294 EditLine line = this.line;
4296 for(c=0; line.buffer[c]; c++)
4297 if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
4299 if(shift && (c != 0 || this.x))
4308 if(shift && this.x != 0)
4314 if(!shift) _Deselect();
4315 SetViewToCursor(true);
4321 if(style.stuckCaret) break;
4322 if(!style.multiLine && key.ctrl) break;
4323 if(!style.freeCaret)
4324 this.selX = Min(this.selX, this.selLine.count);
4326 if(!shift) SelDirty();
4331 else if(this.x != this.line.count)
4333 this.x = this.line.count;
4338 if(!shift) _Deselect();
4339 SetViewToCursor(true);
4344 if(style.tabKey && !key.ctrl)
4346 if(this.selY != this.y && style.tabSel)
4348 EditLine firstLine, lastLine;
4352 // Do multi line selection tabbing here
4353 if(this.selY < this.y)
4355 firstLine = this.selLine;
4356 lastLine = this.line;
4362 // Selecting going up
4363 firstLine = this.line;
4364 lastLine = this.selLine;
4371 for(line = firstLine; line; line = line.next, y++)
4373 if(line != lastLine || x)
4377 BufferLocation before = { line, y, 0 }, after = { line, y, 0 };
4379 for(c=0; c<line.count && lastC < this.tabSize; c++, lastC++)
4381 if(line.buffer[c] == '\t')
4386 else if(line.buffer[c] != ' ')
4391 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
4394 int len = GetText(null, line, y, 0, line, y, lastC, false, false);
4395 char * string = new char[len];
4396 DelTextAction action { y1 = y, x1 = 0, y2 = y, x2 = lastC, string = string, placeAfter = true };
4397 GetText(string, line, y, 0, line, y, lastC, false, false);
4400 memmove(line.buffer,line.buffer+lastC,line.size-lastC);
4401 if(!line.AdjustBuffer(line.count-lastC))
4407 if(line == lastLine) break;
4412 for(line = firstLine; line; line = line.next, y++)
4416 if(line != lastLine || x)
4420 if(line.count + 1 <= this.maxLineSize)
4422 BufferLocation before = { line, y, 0 }, after = { line, y, 1 };
4424 if(!line.AdjustBuffer(line.count+1))
4427 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4429 AddCharAction action { ch = '\t', x = 0, y = y };
4433 memmove(line.buffer+1,line.buffer,line.size-1);
4435 line.buffer[0] = '\t';
4440 if(line.count + this.tabSize <= this.maxLineSize)
4443 BufferLocation before = { line, y, 0 }, after = { line, y, this.tabSize };
4444 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4446 if(!line.AdjustBuffer(line.count+this.tabSize))
4450 char * string = new char[this.tabSize + 1];
4451 memset(string, ' ', this.tabSize);
4452 string[this.tabSize] = '\0';
4453 Record(AddTextAction { string = string, x1 = 0, y1 = y, x2 = this.tabSize, y2 = y });
4456 memmove(line.buffer+this.tabSize,line.buffer,line.size-(this.tabSize));
4457 line.count+=this.tabSize;
4458 for(c=0; c<this.tabSize; c++)
4459 line.buffer[c] = ' ';
4465 if(line == lastLine) break;
4468 ComputeLength(maxLine);
4480 char * addString = new char[this.tabSize + 1];
4482 if(!(style.freeCaret))
4484 this.x = Min(this.x, this.line.count);
4488 start = Min(this.x, this.selX);
4489 for(c=start; ((c == start) || ((c) % this.tabSize)); c++)
4491 addString[len++] = ' ';
4499 SetViewToCursor(true);
4508 if(!(style.hScroll) || hasHorzScroll) break;
4509 if(this.viewX < this.maxLength)
4511 //this.viewX+=this.space.w*this.tabSize;
4513 SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
4520 if(!shift) _Deselect();
4532 if(!(style.hScroll) || hasHorzScroll) break;
4535 //this.viewX-=this.space.w*this.tabSize;
4536 //this.viewX = Max(this.viewX,0);
4538 SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
4539 // SetCursorToView();
4546 if(!shift) _Deselect();
4561 if(!(style.readOnly))
4567 this.overwrite ^= 1;
4568 UpdateCaretPosition(true);
4573 NotifyOvrToggle(master, this, this.overwrite);
4584 if(style.noSelect) break;
4587 //Save current view position
4588 int tempX = this.viewX;
4589 int tempY = this.viewY;
4593 this.selLine = this.lines.first;
4594 this.y = this.lineCount-1;
4595 this.line = this.lines.last;
4596 this.x = this.line.count;
4599 SetViewToCursor(true);
4601 //Restore previous view position
4602 SetScrollPosition(tempX, tempY * this.space.h);
4608 // TOCHECK: Was there any good reason why we weren't returning false here?
4609 return false; // break;
4614 if(!(style.readOnly))
4622 if(style.readOnly) break;
4628 if(style.readOnly) break;
4633 if(style.readOnly) break;
4637 if(style.readOnly) break;
4641 if(style.readOnly) break;
4642 if(key.shift && key.code == rightBracket)
4644 //Only indent back if you are exactly at one tab.
4646 bool whitespace = true;
4652 EditLine line = this.line;
4654 //Only remove one tab if there is nothing else on the line.
4655 for(i = 0; i < this.line.count; i++)
4657 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
4671 for(pos = line.count - 1; pos >= 0; pos--)
4673 char c = line.buffer[pos];
4677 indentwidth += this.tabSize;
4679 //Counting backwards, so when you find a character, indentation is reset
4689 //Place the } to get an undo:
4692 this.x = this.line.count;
4696 putsize = indentwidth;
4698 putsize = indentwidth / this.tabSize + indentwidth % this.tabSize;
4700 newline = new char[putsize+2];
4701 newline[putsize] = '}';
4702 newline[putsize+1] = '\0';
4706 for(; i < indentwidth / this.tabSize; i++)
4708 for(;i < putsize; i++)
4717 else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
4729 void OnHScroll(ScrollBarAction action, int position, Key key)
4732 //PrintLn("OnHScroll: ", action, ", pos = ", position, ", key = ", key);
4734 this.viewX = position;
4735 if(action != setRange)
4737 if(!this.mouseMove && style.cursorFollowsView)
4744 void OnVScroll(ScrollBarAction action, int position, Key key)
4746 int oldViewY = this.viewY;
4749 //PrintLn("OnVScroll: ", action, ", pos = ", position, ", key = ", key);
4751 position /= this.space.h;
4753 if(position < this.viewY)
4755 for(; position < this.viewY && this.viewLine.prev; this.viewLine = this.viewLine.prev, this.viewY--);
4756 style.recomputeSyntax = true;
4758 else if(position > this.viewY)
4760 EditLine oldViewLine = viewLine;
4761 for(; position > this.viewY && this.viewLine.next; this.viewLine = this.viewLine.next, this.viewY++);
4762 FigureStartSyntaxStates(oldViewLine, false);
4765 if(action != setRange)
4767 if(!this.mouseMove && style.cursorFollowsView && !SelSize()) SetCursorToViewY();
4770 if(this.x != this.selX || this.y != this.selY)
4774 Scroll(0, (this.viewY - oldViewY) * this.space.h);
4777 int numLines = clientSize.h / this.space.h;
4779 if(Abs(this.viewY - oldViewY) < numLines)
4782 if(this.viewY > oldViewY)
4784 for(y = oldViewY; y <this.viewY; y++)
4785 DirtyLine(y + numLines);
4789 for(y = this.viewY; y <oldViewY; y++)
4790 DirtyLine(y + numLines);
4795 // Fix dirt of stuff before first line...
4796 if(this.viewY - oldViewY > 0)
4798 Box box { 0,0, clientSize.w-1, YOFFSET-1 };
4805 bool _AddCh(unichar ch, int * addedSpacesPtr, int * addedTabsPtr)
4810 ReplaceTextAction replaceAction = null;
4811 AddCharAction addCharAction = null;
4812 int addedSpaces = 0, addedTabs = 0;
4814 if(ch == '\r') return true;
4815 if(style.stuckCaret /*|EES_READONLY)*/ )
4818 if(ch == '\n' && !(style.multiLine) && this.line) return false;
4820 if(!undoBuffer.dontRecord)
4822 if(selX != x || selY != y)
4827 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
4828 oldString = new char[len];
4829 UTF32toUTF8Len(&ch, 1, buffer, 4);
4830 newString = CopyString(buffer);
4831 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
4833 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
4834 if(selY < y || (selY == y && selX < x))
4836 replaceAction.x1 = selX;
4837 replaceAction.y1 = selY;
4838 replaceAction.x2 = x;
4839 replaceAction.y2 = y;
4843 replaceAction.x1 = x;
4844 replaceAction.y1 = y;
4845 replaceAction.x2 = selX;
4846 replaceAction.y2 = selY;
4848 Record(replaceAction);
4849 undoBuffer.dontRecord++;
4853 addCharAction = AddCharAction { y = y, x = x, ch = ch };
4854 Record(addCharAction);
4860 DelSel(&addedSpaces);
4861 if(this.lineCount+1 > this.maxLines)
4864 Emptyline(this.lines.first,0);
4868 if(!(style.autoSize && (!maxClientSize.h || maxClientSize.h > clientSize.h + this.space.h)) && !(style.vScroll))
4870 // Make sure it fits, but we need a default line is this.font is too big for window
4871 if(this.space.h * (this.lineCount+1) > clientSize.h && this.line)
4874 if((this.y >= 0) && this.y < this.viewY)
4879 line = EditLine { };
4882 lines.Insert(this.line, line);
4883 line.editBox = this;
4889 // If we're displacing the lines from a current line ...
4892 if(this.line.buffer)
4895 if(this.line.count < endX) endX = this.line.count;
4896 length = this.line.count - endX;
4899 if(!line.AdjustBuffer(length))
4903 if(this.line.buffer)
4905 CopyBytes(line.buffer,this.line.buffer+endX, length+1);
4907 if(endX > 4000 || endX < 0)
4910 this.line.count = endX;
4911 this.line.buffer[this.line.count] = '\0';
4912 this.line.AdjustBuffer(this.line.count);
4913 ComputeLength(this.line);
4917 BufferLocation before = { this.line, this.y, this.x }, after;
4922 line.count = length;
4925 if(length > 4000 || length < 0)
4928 ComputeLength(this.line);
4933 line.buffer[line.count] = '\0';
4936 after.line = this.line, after.y = this.y, after.x = this.x;
4938 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4944 int count = UTF32toUTF8Len(&ch, 1, string, 5);
4945 DelSel(&addedSpaces);
4946 result = AddToLine(string, count, false, addedSpaces ? null : &addedSpaces, &addedTabs);
4947 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
4948 if(addedTabsPtr) *addedTabsPtr = addedTabs;
4952 this.selLine = this.line;
4956 replaceAction.x3 = x;
4957 replaceAction.y3 = y;
4958 replaceAction.addedSpaces = addedSpaces;
4959 replaceAction.addedTabs = addedTabs;
4960 undoBuffer.dontRecord--;
4964 addCharAction.x -= addedTabs * (tabSize-1);
4965 addCharAction.addedSpaces = addedSpaces;
4966 addCharAction.addedTabs = addedTabs;
4973 /****************************************************************************
4975 ****************************************************************************/
4978 bool AddCh(unichar ch)
4980 return _AddCh(ch, null, null);
4985 this.modified = true;
4986 NotifyUpdate(master, this);
4987 modifiedDocument = true;
4990 void Delete(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
4993 DelCh(line1, y1, x1, line2, y2, x2, false);
4994 SetViewToCursor(true);
5002 itemEditUndo.disabled = undoBuffer.curAction == 0;
5003 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5004 if(savedAction == undoBuffer.curAction)
5006 modifiedDocument = false;
5008 NotifyUnsetModified(master, this);
5015 itemEditUndo.disabled = undoBuffer.curAction == 0;
5016 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5017 if(savedAction == undoBuffer.curAction)
5019 modifiedDocument = false;
5021 NotifyUnsetModified(master, this);
5025 void Record(UndoAction action)
5027 if(!undoBuffer.dontRecord)
5029 undoBuffer.Record(action);
5030 itemEditUndo.disabled = undoBuffer.curAction == 0;
5031 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5038 this.lines.first, 0,0,
5039 this.lines.last, this.lines.count-1, strlen(((EditLine)this.lines.last).buffer));
5042 void Select(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
5047 this.selLine = line1 ? (EditLine)line1 : this.lines.first;
5048 this.x = line2 ? x2 : ((EditLine)this.lines.last).count;
5049 this.y = line2 ? y2 : (this.lineCount-1);
5050 this.line = line2 ? (EditLine)line2 : this.lines.last;
5053 SetViewToCursor(true);
5057 // TODO: Fix this vs modifiedDocument window property
5058 void SetModified(bool flag)
5062 this.modified = false;
5063 if(flag && !NotifyModified(master, this))
5064 this.modified = true;
5069 bool AddS(char * string)
5076 int addedSpaces = 0, addedTabs = 0;
5077 AddTextAction action = null;
5078 ReplaceTextAction replaceAction = null;
5080 this.pasteOperation = true;
5082 if(style.stuckCaret /*|EES_READONLY)*/ )
5085 if(!undoBuffer.dontRecord)
5087 char * placeString = CopyString(string);
5088 if(!style.multiLine)
5092 for(i = 0; (ch = placeString[i]); i++)
5095 placeString[i] = '\0';
5096 placeString = renew placeString byte[i+1];
5101 if(selX != x || selY != y)
5105 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
5106 oldString = new char[len];
5107 newString = placeString;
5108 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
5110 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5111 if(selY < y || (selY == y && selX < x))
5113 replaceAction.x1 = selX;
5114 replaceAction.y1 = selY;
5115 replaceAction.x2 = x;
5116 replaceAction.y2 = y;
5120 replaceAction.x1 = x;
5121 replaceAction.y1 = y;
5122 replaceAction.x2 = selX;
5123 replaceAction.y2 = selY;
5125 Record(replaceAction);
5129 if(string[0] == '\n')
5130 action = AddTextAction { y1 = y, x1 = Min(this.line.count, x), string = placeString };
5132 action = AddTextAction { y1 = y, x1 = x, string = placeString };
5140 undoBuffer.dontRecord++;
5141 DelSel(&addedSpaces);
5145 for(c = 0; string[c]; c++)
5147 if(string[c] == '\n' || string[c] == '\r')
5149 if(!AddToLine(line,count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5154 if(string[c] == '\n')
5163 // Reset for next line
5167 if(string[c] == '\r' && *line == '\n')
5179 // Why was this here?
5182 // Add the line here
5184 if(!AddToLine(line,count,false, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5190 undoBuffer.dontRecord--;
5195 action.addedSpaces = addedSpaces;
5196 action.addedTabs = addedTabs;
5198 else if(replaceAction)
5200 replaceAction.y3 = y;
5201 replaceAction.x3 = x;
5202 replaceAction.addedSpaces = addedSpaces;
5203 replaceAction.addedTabs = addedTabs;
5206 UpdateCaretPosition(true);
5208 this.pasteOperation = false;
5221 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5222 SetViewToCursor(true);
5228 void PutCh(unichar ch)
5232 if((ch >= 32 /*&& ch <=126*/) || ch == '\n')
5233 //if((ch >= 32) || ch == '\n')
5235 int addedSpaces = 0, addedTabs = 0;
5236 ReplaceTextAction replaceAction = null;
5237 AddCharAction addCharAction = null;
5240 ch = (ch < 128) ? toupper(ch) : ch; // TODO: UNICODE TO UPPER
5242 if(this.overwrite && selX == x && selY == y && this.x < this.line.count)
5247 int len = GetText(null, line, y, x, line, y, x+1, false, false);
5248 oldString = new char[len];
5249 UTF32toUTF8Len(&ch, 1, buffer, 4);
5250 newString = CopyString(buffer);
5251 GetText(oldString, line, y, x, line, y, x+1, false, false);
5252 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5253 replaceAction.x1 = x;
5254 replaceAction.y1 = y;
5255 replaceAction.x2 = x+1;
5256 replaceAction.y2 = y;
5257 Record(replaceAction);
5259 undoBuffer.dontRecord++;
5260 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, true);
5261 undoBuffer.dontRecord--;
5263 else if(!undoBuffer.dontRecord)
5265 if(selX != x || selY != y)
5270 int len = GetText(null, line, y, x, selLine, selY, selX, false, false);
5271 oldString = new char[len];
5272 UTF32toUTF8Len(&ch, 1, buffer, 4);
5273 newString = CopyString(buffer);
5274 GetText(oldString, line, y, x, selLine, selY, selX, false, false);
5275 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = true };
5276 if(selY < y || (selY == y && selX < x))
5278 replaceAction.x1 = selX;
5279 replaceAction.y1 = selY;
5280 replaceAction.x2 = x;
5281 replaceAction.y2 = y;
5285 replaceAction.x1 = x;
5286 replaceAction.y1 = y;
5287 replaceAction.x2 = selX;
5288 replaceAction.y2 = selY;
5290 Record(replaceAction);
5294 addCharAction = AddCharAction { y = y, x = x, ch = ch };
5295 Record(addCharAction);
5298 undoBuffer.dontRecord++;
5299 result = _AddCh(ch, &addedSpaces, &addedTabs);
5302 replaceAction.x3 = x;
5303 replaceAction.y3 = y;
5304 replaceAction.addedSpaces = addedSpaces;
5305 replaceAction.addedTabs = addedTabs;
5309 addCharAction.x -= addedTabs * (tabSize-1);
5310 addCharAction.addedSpaces = addedSpaces;
5311 addCharAction.addedTabs = addedTabs;
5313 undoBuffer.dontRecord--;
5317 if(result) SetViewToCursor(true);
5321 void PutS(char * string)
5326 SetViewToCursor(true);
5331 void Printf(char * format, ...)
5335 char temp[MAX_F_STRING];
5337 va_start(args, format);
5338 vsnprintf(temp, sizeof(temp), format, args);
5339 temp[sizeof(temp)-1] = 0;
5345 void SetContents(char * format, ...)
5349 undoBuffer.dontRecord++;
5351 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5354 char temp[MAX_F_STRING];
5356 va_start(args, format);
5357 vsnprintf(temp, sizeof(temp), format, args);
5358 temp[sizeof(temp)-1] = 0;
5365 undoBuffer.dontRecord--;
5375 x -= 1 + DelCh(line, y, x-1, line, y, x, true);
5378 else if(this.line.prev)
5380 EditLine line = this.line.prev;
5384 DelCh(line, this.y-1, x, this.line, this.y, this.x, true);
5392 this.selLine = this.line;
5397 SetViewToCursor(true);
5402 Emptyline(this.line,this.y);
5403 this.selX = this.x = 0;
5406 this.selLine = this.line;
5408 SetViewToCursor(true);
5418 SetViewToCursor(true);
5426 SetViewToCursor(true);
5430 bool GoToLineNum(int lineNum)
5435 EditLine line = this.lines.first;
5436 for(c = 0; c < lineNum && line; c++, line = line.next);
5446 SetViewToCursor(true);
5453 bool GoToPosition(EditLine line, int y, int x)
5465 for(line = this.lines.first, c = 0; c<y && line; c++, line = line.next);
5479 SetViewToCursor(true);
5486 void SetViewToCursor(bool setCaret)
5496 bool dontScroll = false;
5502 selected = selX != this.x || selY != y;
5509 checkLine = dropLine;
5515 checkLine = this.line;
5520 numLines = clientSize.h / space.h;
5522 // This is broken. The EditBox now doesn't do anything different when adding to it,
5523 // regardless of the previous scrolling position. It should be read and then set again
5524 // if one wishes to preserve it.
5525 /* // Don't scroll view to cursor if we're in a EES_NOCARET box
5526 if(style.noCaret && this.viewY < lineCount - numLines - 1)
5530 // Horizontal Adjustment
5531 if(!dontScroll && checkLine)
5535 dropX = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5538 this.x = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5544 if(x + space.w >= this.viewX + clientSize.w && clientSize.w >= space.w)
5545 viewX = x - clientSize.w+space.w;
5546 if(x < this.viewX + clientSize.w/2 - space.w)
5547 viewX = Max(0, x - clientSize.w/2 + space.w);
5555 // Vertical Adjustment
5556 if(viewY > checkY) viewY = checkY;
5557 if(viewY + numLines <= checkY)
5559 if(clientSize.h >= space.h)
5561 for(line = viewLine; line && (viewY + numLines <= checkY); line = line.next)
5571 for(;dropLine && dropLine.prev && dropY >= numLines;)
5573 dropLine = dropLine.prev;
5577 for(;this.line && this.line.prev && this.y >= numLines;)
5579 this.line = this.line.prev;
5584 SetScrollPosition(viewX, viewY * this.space.h);
5586 UpdateCaretPosition(setCaret);
5592 selLine = this.line;
5602 void CenterOnCursor()
5604 int numLines = clientSize.h / this.space.h;
5605 int y = this.y - numLines / 2;
5607 bool figureSyntax = false;
5608 EditLine oldViewLine = viewLine;
5609 if(y > this.lineCount - numLines) y = this.lineCount-numLines;
5614 for(;y < this.viewY; y++)
5616 this.viewLine = this.viewLine.prev;
5617 style.recomputeSyntax = true;
5619 for(;y > this.viewY; y--)
5621 this.viewLine = this.viewLine.next;
5622 figureSyntax = true;
5625 FigureStartSyntaxStates(oldViewLine, false);
5629 SetScrollPosition(this.viewX, viewY * this.space.h);
5630 UpdateCaretPosition(true);
5634 void SetCursorToView()
5645 numLines = clientSize.h / this.space.h;
5649 for(c=0, line = this.viewLine.next; line && c<numLines && (this.viewY < this.lineCount - numLines); line = line.next, c++);
5650 SetScrollPosition(this.viewX, (this.viewY + c) * this.space.h);
5654 EditLine oldLine = this.line;
5655 bool lastOne = false;
5656 EditLine oldViewLine = this.viewLine;
5657 bool figureSyntax = false;
5659 if(this.y >= this.lineCount-1) return;
5661 for(c=0, line = this.line.next; line && c<numLines; line = line.next, c++)
5663 if(this.viewY + numLines < this.lines.count)
5665 this.viewLine = this.viewLine.next;
5667 figureSyntax = true;
5669 else if(c && !lastOne)
5678 FigureStartSyntaxStates(oldViewLine, false);
5679 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5682 SetViewToCursor(false);
5691 if(this.y == 0) return;
5693 numLines = clientSize.h / this.space.h;
5697 for(c=0, line = this.viewLine.prev; line && c<numLines; line = line.prev, c++);
5698 SetScrollPosition(this.viewX, (this.viewY - c) * this.space.h);
5702 EditLine oldLine = this.line;
5704 for(c=0, line = this.line.prev; line && c<numLines; line = line.prev, c++)
5709 if(this.viewLine.prev)
5711 this.viewLine = this.viewLine.prev;
5713 style.recomputeSyntax = true;
5717 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5720 SetViewToCursor(false);
5727 if(this.viewLine.prev)
5730 // this.viewLine = this.viewLine.prev;
5733 SetScrollPosition(this.viewX, (this.viewY - 1) * this.space.h);
5740 if(this.viewLine.next)
5743 // this.viewLine = this.viewLine.next;
5746 SetScrollPosition(this.viewX, (this.viewY + 1) * this.space.h);
5753 EditLine l1, l2, line;
5754 int x1, x2, nx1, nx2;
5759 if(!this.selLine) return 0;
5760 if(this.selLine == this.line && this.selX == this.x) return 0;
5761 if(this.selY < this.y)
5768 else if(this.selY > this.y)
5775 else if(this.selX < this.x)
5777 l1 = l2 = this.line;
5783 l1 = l2 = this.line;
5787 nx1 = Min(x1,l1.count);
5788 nx2 = Min(x2,l2.count);
5790 // Find Number of Bytes Needed
5792 for(line = l1; line; line = line.next)
5794 if(line == l1) start = nx1; else start = 0;
5795 if(line == l2) end = nx2; else end = line.count;
5797 if(style.freeCaret && line == l2)
5800 count = Max(x2-Max(x1,l1.count),0);
5802 count = Max(x2-l2.count,0);
5806 if(line == l2) break;
5807 // Add Carriage Return / line Feed
5814 void GetSelPos(EditLine * l1, int *y1, int *x1, EditLine * l2, int * y2, int * x2, bool reorder)
5818 if(!reorder || this.selY < this.y)
5820 if(l1) *l1 = this.selLine;
5821 if(y1) *y1 = this.selY;
5822 if(x1) *x1 = this.selX;
5823 if(l2) *l2 = this.line;
5824 if(y2) *y2 = this.y;
5825 if(x2) *x2 = this.x;
5827 else if(this.selY > this.y)
5829 if(l1) *l1 = this.line;
5830 if(y1) *y1 = this.y;
5831 if(x1) *x1 = this.x;
5832 if(l2) *l2 = this.selLine;
5833 if(y2) *y2 = this.selY;
5834 if(x2) *x2 = this.selX;
5836 else if(this.selX < this.x)
5838 if(l1) *l1 = this.line;
5839 if(y1) *y1 = this.selY;
5840 if(x1) *x1 = this.selX;
5841 if(l2) *l2 = this.line;
5842 if(y2) *y2 = this.y;
5843 if(x2) *x2 = this.x;
5847 if(l1) *l1 = this.line;
5848 if(y1) *y1 = this.y;
5849 if(x1) *x1 = this.x;
5850 if(l2) *l2 = this.line;
5851 if(y2) *y2 = this.selY;
5852 if(x2) *x2 = this.selX;
5857 void SetSelPos(EditLine l1, int y1, int x1, EditLine l2, int y2, int x2)
5859 if(this && (this.selY != y1 || this.selX != x1 || this.y != y2 || this.x != x2))
5861 this.selLine = (EditLine)l1;
5864 this.line = (EditLine)l2;
5868 SetViewToCursor(true);
5872 int GetText(char * text, EditLine _l1, int _y1, int _x1, EditLine _l2, int _y2, int _x2, bool addCr, bool addSpaces)
5874 EditLine l1, l2, line;
5875 int x1, x2, nx1, nx2;
5906 nx1 = Min(x1,l1.count);
5907 nx2 = Min(x2,l2.count);
5910 for(line = l1; line; line = line.next)
5912 if(line == l1) start = nx1; else start = 0;
5913 if(line == l2) end = nx2; else end = line.count;
5916 CopyBytes(text, line.buffer + start, end - start);
5919 numChars += end-start;
5921 if(style.freeCaret && line == l2 && addSpaces)
5924 count = Max(x2-Max(x1,l1.count),0);
5926 count = Max(x2-l2.count,0);
5929 FillBytes(text,' ',count);
5935 if(line == l2) break;
5947 // '\0' terminate Terminate
5954 void GetSel(char * text, bool addCr)
5956 GetText(text, line, y, x, selLine, selY, selX, addCr, true);
5959 private void _Deselect() // This assumes marking lines as dirty is handled by the caller
5977 int size = SelSize();
5980 // Try to allocate memory
5981 ClipBoard clipBoard { };
5982 if(clipBoard.Allocate(size+1))
5984 GetSel(clipBoard.memory, true);
5997 ClipBoard clipBoard { };
5998 if(clipBoard.Load())
5999 PutS(clipBoard.memory);
6011 SetViewToCursor(true);
6017 void DeleteSelection()
6022 SetViewToCursor(true);
6028 void Save(File f, bool cr)
6031 savedAction = undoBuffer.curAction;
6033 for(line = this.lines.first; line; line = line.next)
6035 f.Write(line.buffer, line.count,1);
6038 if(cr) f.Putc('\r');
6044 #define BUFFER_SIZE 16384
6047 undoBuffer.dontRecord++;
6050 char buffer[BUFFER_SIZE];
6054 int count = f.Read(buffer, 1, BUFFER_SIZE-1);
6055 buffer[count] = '\0';
6061 undoBuffer.dontRecord--;
6062 undoBuffer.count = 0;
6063 undoBuffer.curAction = 0;
6064 itemEditUndo.disabled = undoBuffer.curAction == 0;
6065 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
6068 EditBoxFindResult Find(char * text, bool matchWord, bool matchCase, bool isSearchDown)
6072 bool firstPass = true;
6073 EditBoxFindResult result = found;
6075 if(!this.line) return notFound;
6078 for(line = this.line;;)
6086 line = this.lines.first;
6092 line = this.lines.last;
6093 num = this.lineCount - 1;
6099 string = SearchString(line.buffer, firstPass ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6101 string = RSearchString(line.buffer,text,firstPass ? (Min(this.x,line.count)-1) : line.count,matchCase,matchWord);
6105 Select((void *)line,num,string - line.buffer,(void *)line,num,string - line.buffer + strlen(text));
6108 if(line == this.line && !firstPass) break;
6126 EditBoxFindResult FindInSelection(char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
6130 int searchLen = strlen(text);
6131 for(line = (EditLine)this.line, y = this.y; y <= y2; line = line.next, y++)
6133 char * string = SearchString(line.buffer, (y == this.y) ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6134 if(string && (y < y2 || (string - line.buffer) + searchLen <= x2))
6136 Select((void *)line,y,string - line.buffer,(void *)line,y,string - line.buffer + strlen(text));
6143 bool OnKeyDown(Key key, unichar ch)
6146 //PrintLn("OnKeyDown: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
6148 if(!NotifyKeyDown(master, this, key, ch))
6158 this.mouseMove = false;
6159 OnLeftButtonUp(0,0,0);
6160 SetViewToCursor(true);
6170 public class EditBoxStream : File
6173 BufferLocation start, sel;
6180 EditBox editBox = this.editBox;
6182 editBox.x = start.x;
6183 editBox.y = start.y;
6184 editBox.line = start.line;
6186 editBox.selX = sel.x;
6187 editBox.selY = sel.y;
6188 editBox.selLine = sel.line;
6190 editBox.SetViewToCursor(true);
6191 //editBox.ComputeColumn();
6195 property EditBox editBox
6203 start.line = value.line;
6207 sel.line = value.selLine;
6210 value.GoToHome(true);
6213 get { return editBox; }
6216 uint Read(byte * buffer, uint size, uint count)
6219 EditBox editBox = this.editBox;
6220 EditLine line = editBox.line;
6226 for(;read < count && line; line = (*&line.next))
6228 int numBytes = Min(count - read, (*&line.count) - x);
6231 memcpy(buffer + read, (*&line.buffer) + x, numBytes);
6236 for(;read < count && x < (*&line.count);)
6238 buffer[read++] = (*&line.buffer)[x++];
6245 buffer[read++] = '\n';
6250 if(x == (*&line.count) && (*&line.next))
6259 editBox.line = editBox.selLine = line;
6260 editBox.x = editBox.selX = x;
6261 editBox.y = editBox.selY = y;
6266 bool Seek(int pos, FileSeekMode mode)
6269 EditBox editBox = this.editBox;
6270 EditLine line = editBox.line;
6272 if(mode == FileSeekMode::start)
6274 pos = pos - this.pos;
6284 for(;read < pos && line; line = (*&line.next))
6286 int numBytes = Min(pos - read, (*&line.count) - x);
6289 read += numBytes; x += numBytes;
6292 /*for(;read < pos && x < (*&line.count);)
6320 for(;read < pos && line; line = (*&line.prev))
6322 int numBytes = Min(pos - read, x);
6328 /*for(;read < pos && x > 0;)
6338 x = (*&(*&line.prev).count);
6354 editBox.line = editBox.selLine = line;
6355 editBox.x = editBox.selX = x;
6356 editBox.y = editBox.selY = y;
6361 bool Puts(char * string)
6363 EditBox editBox = this.editBox;
6364 BufferLocation start { editBox.line, editBox.y, editBox.x };
6368 editBox.AddS(string);
6370 pos.line = editBox.line;
6374 this.start.AdjustAdd(start, pos);
6375 sel.AdjustAdd(start, pos);
6380 // NOTE: BYTE, NOT UNICODE CHARACTER!
6383 EditBox editBox = this.editBox;
6384 BufferLocation start = { editBox.line, editBox.y, editBox.x };
6389 utf8Bytes[numBytes++] = ch;
6390 utf8Bytes[numBytes] = 0;
6391 if(UTF8Validate(utf8Bytes))
6393 editBox.AddCh(UTF8_GET_CHAR(utf8Bytes, numBytes));
6395 pos.line = editBox.line;
6398 this.start.AdjustAdd(start, pos);
6399 sel.AdjustAdd(start, pos);
6406 bool Getc(char * ch)
6408 return Read(ch, 1, 1) ? true : false;
6411 void DeleteBytes(uint count)
6413 EditBox editBox = this.editBox;
6416 BufferLocation pos { editBox.line, editBox.y, editBox.x };
6417 BufferLocation end = pos;
6422 for(;c < count && end.line && end.x < end.line.count;)
6427 if(c < count && end.line && end.line.next)
6429 end.line = end.line.next;
6438 start.AdjustDelete(pos, end);
6439 sel.AdjustDelete(pos, end);
6441 editBox.DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true);