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]; \
52 if(b & 0x80 && b & 0x40) \
67 for(i = 0; i<numBytes; i++) \
70 ch |= (string)[i] & mask; \
78 bool autoEmpty:1, readOnly:1, multiLine:1, stuckCaret:1, freeCaret:1, select:1, hScroll:1, vScroll:1, smartHome:1;
79 bool noCaret:1, noSelect:1, tabKey:1, useTab:1, tabSel:1, allCaps:1, syntax:1, wrap:1;
82 bool inMultiLineComment:1, inPrep:1, escaped:1;
84 bool recomputeSyntax:1;
85 bool cursorFollowsView:1;
87 // bool lineNumbers:1;
91 void UnregisterClass_EditBox()
94 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
103 extern int __ecereVMethodID_class_OnFree;
106 static class ArrayImpl
113 public class OldArray
120 void ** array = (void **)((ArrayImpl)this).array;
121 if(type.type == normalClass || type.type == noHeadClass)
123 for(c = 0; c<size; c++)
124 type._vTbl[__ecereVMethodID_class_OnFree](type, array[c]);
126 // TODO: Call OnFree for structClass
127 delete ((ArrayImpl)this).array;
136 if(((ArrayImpl)this).array)
140 ((ArrayImpl)this).array = renew0 ((ArrayImpl)this).array byte[type.typeSize * value];
143 ((ArrayImpl)this).array = new0 byte[value * type.typeSize];
152 memcpy(((ArrayImpl)this).array, value, type.typeSize * size);
157 public class UndoAction : struct
160 subclass(UndoAction) type;
161 virtual void Undo(void * data) { type.Undo(this, data); }
162 virtual void Redo(void * data) { type.Redo(this, data); }
164 virtual void Print(void * data) { type.Print(this, data); }
168 if(((Class)type).Destructor)
169 ((void (*)(void *))((Class)type).Destructor)(this);
174 class ArrayUndoActions : OldArray
176 type = class(UndoAction);
180 public class UndoBuffer
182 ArrayUndoActions actions { size = 8 };
197 UndoAction action = actions._[--curAction];
199 /*Print("Undoing: ");
200 action.Print(data);*/
211 if(curAction < count)
213 UndoAction action = actions._[curAction];
216 /*Print("Redoing: ");
217 action.Print(data);*/
225 void Record(UndoAction action)
227 if(!dontRecord && !insideRedo)
229 if(curAction < count)
232 for(c = curAction; c < count; c++)
238 if(count >= actions.size)
239 actions.size += actions.size / 2;
242 /*Print("Recording: ");
243 action.Print(data);*/
245 actions._[count++] = action;
248 if(actions.size > count + count / 2 && count + count / 2 >= 8)
249 actions.size = count + count / 2;
256 static class AddCharAction : UndoAction
260 int addedSpaces, addedTabs;
261 type = class(AddCharAction);
263 void Undo(EditBox editBox)
265 editBox.GoToPosition(null, (ch == '\n') ? (y + 1) : y, (ch == '\n') ? 0 : (x + 1));
267 if(addedTabs || addedSpaces)
268 editBox.DelCh(editBox.line, y, x - (addedSpaces + addedTabs), editBox.line, y, x, false);
269 editBox.UpdateDirty();
272 void Redo(EditBox editBox)
274 editBox.GoToPosition(null, y, x);
276 editBox.UpdateDirty();
279 void Print(EditBox editBox)
281 PrintLn("AddChar: y = ", y, "x = ", x, ", ch = ", ch, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
286 static class AddTextAction : UndoAction
290 int addedSpaces, addedTabs;
291 type = class(AddTextAction);
294 void Print(EditBox editBox)
296 PrintLn("AddText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
304 void Undo(EditBox editBox)
309 editBox.GoToPosition(null, y1, x1);
311 l2 = editBox.lines.first;
312 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
314 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
315 if(addedTabs || addedSpaces)
316 editBox.DelCh(editBox.line, y1, x1 - (addedSpaces + addedTabs), editBox.line, y1, x1, false);
318 editBox.SetViewToCursor(true);
322 void Redo(EditBox editBox)
324 editBox.GoToPosition(null, y1, x1);
325 editBox.PutS(string);
329 static class DelTextAction : UndoAction
335 type = class(DelTextAction);
338 void Print(EditBox editBox)
340 PrintLn("DelText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", placeAfter = ", placeAfter);
343 void Undo(EditBox editBox)
345 editBox.GoToPosition(null, y1, x1);
346 editBox.PutS(string);
350 editBox.GoToPosition(null, y1, x1);
353 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
354 //editBox.SetViewToCursor(true);
357 editBox.DelCh(editBox.line, y1, x1 - addedSpaces, editBox.line, y1, x1, false);
363 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
364 //editBox.SetViewToCursor(true);
367 editBox.DelCh(editBox.selLine, y1, x1 - addedSpaces, editBox.selLine, y1, x1, false);
371 void Redo(EditBox editBox)
376 editBox.GoToPosition(null, y1, x1);
379 l2 = editBox.lines.first;
380 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
382 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
383 editBox.SetViewToCursor(true);
393 static class ReplaceTextAction : UndoAction
395 int y1, x1, y2, x2, y3, x3;
399 int addedSpaces, addedTabs;
401 type = class(ReplaceTextAction);
404 void Print(EditBox editBox)
406 PrintLn("ReplaceText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", y3 = ", y3, ", x3 = ", x3, ", oldString = ", oldString, ", newString = ", newString, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs, ", placeAfter = ", placeAfter);
409 void Undo(EditBox editBox)
414 editBox.GoToPosition(null, y1, x1);
416 l3 = editBox.lines.first;
417 for(c = 0; c < y3 && l3; c++, l3 = l3.next);
419 editBox.DelCh(l1, y1, x1, l3, y3, x3, true);
421 editBox.PutS(oldString);
422 if(addedSpaces || addedTabs)
423 editBox.DelCh(l1, y1, x1 - (addedSpaces + addedTabs), l1, y1, x1, false);
425 //editBox.PutS(oldString);
428 editBox.GoToPosition(null, y1, x1);
431 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
437 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
441 void Redo(EditBox editBox)
446 editBox.GoToPosition(null, y1, x1);
448 l2 = editBox.lines.first;
449 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
451 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
453 editBox.PutS(newString);
463 static class MoveTextAction : UndoAction
465 int fy1, fx1, fy2, fx2;
467 type = class(MoveTextAction);
469 void Undo(EditBox editBox)
474 void Redo(EditBox editBox)
480 public class EditLine : struct
492 // Only works for single line edit for now...
493 EditBox editBox = this.editBox;
497 get { return this ? buffer : null; }
499 property EditLine prev { get { return this ? prev : null; } };
500 property EditLine next { get { return this ? next : null; } };
501 property int count { get { return this ? count : 0; } };
508 // This makes sure the buffer always contains at least count characters
509 // Keeps a count/2 pad for avoiding always reallocating memory.
510 bool AdjustBuffer(int count)
518 newSize = (count + (count >> 1));
520 // Shrink down the buffer
523 buffer = new char[newSize];
524 if(!buffer) return false;
528 CopyBytes(buffer, this.buffer, count);
531 this.buffer = buffer;
535 // Increase the buffer
536 else if(size < count)
538 buffer = new char[newSize];
539 if(!buffer) return false;
543 CopyBytes(buffer, this.buffer, this.count + 1); // size);
546 this.buffer = buffer;
555 public struct BufferLocation
560 void AdjustDelete(BufferLocation start, BufferLocation end)
562 // Location is before, nothing to do
563 if(y < start.y || (y == start.y && x < start.x))
565 // Location is inside deleted bytes, point to the start
566 if((y >= start.y && (y > start.y || x >= start.x)) &&
567 (y >= end.y && (y > end.y || x >= end.x)))
572 // Location is on another line
574 y -= end.y - start.y;
575 // Location is the last touched line
582 //if(start.line == end.line)
583 x -= end.x - start.x;
592 // Assuming no carriage return before first character ???? fixed?
593 void AdjustAdd(BufferLocation start, BufferLocation end)
595 int numLines = end.y - start.y;
602 if(x > start.x || (x == start.x /*&& (numLines ? true : false)*/))
605 for(c = 0, line = start.line; c<numLines; c++)
608 //x += numLines ? end.x : (end.x - start.x);
609 x += end.x - start.x;
616 public enum EditBoxFindResult { notFound, found, wrapped };
618 static char * keyWords1[] =
621 "return","break","continue","default","switch","case","if","else","for","while", "do","long","short",
622 "void", "char","int","float","double","unsigned","static", "extern", "struct", "union", "typedef","enum",
624 "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line",
625 "__attribute__", "__stdcall", "_stdcall",
626 "__declspec", "goto",
627 "inline", "__inline__", "_inline", "__inline", "__typeof","__extension__",
628 "asm", "__asm", "_asm", "volatile", "#cpu", "__stdcall__",
631 "class", "private", "public",
633 "delete", "new", "new0", "renew", "renew0", "define",
636 "dllexport", "dllimport", "stdcall",
637 "subclass", "__on_register_module", "namespace", "using",
638 "typed_object", "any_object", "incref", "register", "watch", "stopwatching", "firewatchers", "watchable", "class_designer",
639 "class_fixed", "class_no_expansion", "isset", "class_default_property", "property_category", "class_data", "class_property",
640 "virtual", "thisclass","unichar", "dbtable", "dbindex", "database_open", "dbfield",
643 "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64",
646 "this", "true", "false", "null", "value",
655 static char * keyWords2[] =
657 "defined", "warning", null
660 static char ** keyWords[] = { keyWords1, keyWords2 };
661 #define NUM_KEYWORD_GROUPS (sizeof(keyWords) / sizeof(char **))
662 //static int * keyLen[NUM_KEYWORD_GROUPS];
663 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
666 static FileFilter filters[] =
668 { "All files", null },
670 "Text Files (*.txt)",
674 static FileListConfig fileListConfig = { "", "", filters, sizeof(filters), null, 0 };
676 static char searchString[1025], replaceString[1025];
677 static bool matchCase = false, wholeWord = false, searchUp = false;
679 static GoToDialog goToDialog
681 autoCreate = false, isModal = true, text = "Go To"
684 public class EditBox : CommonControl
686 class_property(icon) = "<:ecere>controls/editBox.png";
690 virtual bool Window::NotifyModified(EditBox editBox);
691 virtual void Window::NotifyCaretMove(EditBox editBox, int line, int charPos);
692 virtual void Window::NotifyUpdate(EditBox editBox);
693 virtual bool Window::NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods);
694 virtual void Window::NotifyOvrToggle(EditBox editBox, bool overwrite);
695 virtual bool Window::NotifyKeyDown(EditBox editBox, Key key, unichar ch);
697 virtual bool Window::NotifyCharsAdded(EditBox editBox, BufferLocation before, BufferLocation after, bool pasteOperation);
698 virtual bool Window::NotifyCharsDeleted(EditBox editBox, BufferLocation beforeLoc, BufferLocation after, bool pasteOperation);
699 virtual bool Window::NotifyDropped(EditBox editBox, int x, int y);
701 virtual bool Window::NotifyUnsetModified(EditBox editBox);
703 // Why was this commented out?
704 // It is required otherwise updating font property from property sheet doesn't immediately reflect in form designer,
705 // and the scrollArea property isn't compared properly either.
709 if(font) ComputeFont();
710 SetInitSize(initSize);
716 style.vScroll = true;
722 style.hScroll = true;
726 property bool textHorzScroll { property_category "Behavior" set { style.hScroll = value; } get { return style.hScroll; } }; // Should cut the text on set to false
727 property bool textVertScroll { property_category "Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
728 property bool readOnly
730 property_category "Behavior"
733 style.readOnly = value;
734 itemEditCut.disabled = value || !selection;
735 itemEditDelete.disabled = value || !selection;
736 itemEditPaste.disabled = value;
738 get { return style.readOnly; }
740 property bool multiLine { property_category "Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
741 property bool freeCaret { property_category "Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
742 property bool tabKey { property_category "Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
743 property int tabSize { property_category "Behavior" set { tabSize = value; } get { return tabSize; } };
744 property bool tabSelection { property_category "Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
745 property bool smartHome { property_category "Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
746 property bool autoEmpty { property_category "Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
747 property bool noCaret { property_category "Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
748 property int maxLineSize { property_category "Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
749 property int maxNumLines { property_category "Behavior" set { maxLines = value; } get { return maxLines; } };
750 property bool useTab { property_category "Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
751 property bool syntaxHighlighting { property_category "Appearance" set { style.syntax = value; } get { return style.syntax; } };
752 property bool noSelect { property_category "Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
753 property bool allCaps { property_category "Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
754 property bool wrap { set { style.wrap = value; Update(null); } get { return style.wrap; } };
755 //property bool lineNumbers { set { style.lineNumbers = value; } get { return style.lineNumbers; } };
756 property int numLines { get { return this ? lineCount : 0; } };
757 property int lineNumber { get { return y; } }; // TODO: Change to property of EditLine this.line.number
758 property int column { get { return col; } }; // TODO: Add Set
759 property int charPos { get { return x; } }; // TODO: Add Set
760 property EditLine firstLine { get { return lines.first; } }; // Change these to a List<EditLine>... (this.lines[10].text)
761 property EditLine lastLine { get { return lines.last; } };
762 property EditLine line { get { return this.line; } }; // TODO: Add Set this.line = this.lines[10]
763 property char * contents
765 property_category "Data"
771 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
774 //SetViewToCursor(true);
782 char * buffer = null;
785 /* Can't implement this right now because of memory leak... Need string reference counting...
792 for(line = lines.first; line; line = line.next)
793 len += strlen(line.buffer);
795 buffer = new char[len+1];
797 for(line = lines.first; line; line = line.next)
799 int lineLen = strlen(line.buffer);
800 memcpy(buffer + len, line.buffer, lineLen);
806 buffer = this.line ? this.line.buffer : null;
811 property bool overwrite { get { return overwrite; } };
812 property bool caretFollowsScrolling { get { return style.cursorFollowsView; } set { style.cursorFollowsView = value; } }
814 property char * multiLineContents
818 char * buffer = null;
825 for(line = lines.first; line; line = line.next)
826 len += strlen(line.buffer)+1;
828 buffer = new char[len+1];
830 for(line = lines.first; line; line = line.next)
832 int lineLen = strlen(line.buffer);
833 memcpy(buffer + len, line.buffer, lineLen);
835 if(line.next) buffer[len++] = '\n';
848 return this.line.buffer;
853 void SetLineText(char * text)
857 EditLine_SetText(this.line, text);
861 property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
862 property Color selectionText { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
863 property SyntaxColorScheme syntaxColorScheme { set { delete colorScheme; colorScheme = value; incref colorScheme; } }
865 // selectionStart.line, selectionStart.column (With Set)
866 // selection.line1, selection.line2, selection.column1, selection.column2 (Read only)
880 // Position of Caret (Not necessarily displayed position)
883 // Position of beginning of block (Block ends at (x,y))
885 // line is line at carret, selLine is line at beginning of block
886 EditLine line, selLine, dropLine;
887 // Mouse selection Moving virtual caret
892 // ViewX is x offset in pixels, ViewY is y offset in lines
894 // viewLine is first displayed line
897 // start and end of area to redraw
900 // MaxLine is the longest line
902 // MaxLength is the longest line's length
905 // MouseSelect is true once button is pressed, overwrite is true if mode is on
906 bool mouseSelect, mouseMove, overwrite, wordSelect;
907 // Timer is used for mouse selection scrolling
910 window = this, delay = 0.1;
919 OnMouseMove(mouseX, mouseY, -1);
924 // (mouseX,mouseY) is the position of the mouse in the edit box
929 void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
932 bool rightButtonDown;
935 UndoBuffer undoBuffer { data = this };
937 Color selectionColor, selectionText;
938 SyntaxColorScheme colorScheme { };
943 Menu editMenu { menu, "Edit", e };
946 editMenu, "Cut\tCtrl+X", t, disabled = true;
948 bool NotifySelect(MenuItem item, Modifiers mods)
950 if(!(style.readOnly))
955 MenuItem itemEditCopy
957 editMenu, "Copy\tCtrl+C", c, disabled = true;
959 bool NotifySelect(MenuItem item, Modifiers mods)
965 MenuItem itemEditPaste
967 editMenu, "Paste\tCtrl+V", p;
969 bool NotifySelect(MenuItem item, Modifiers mods)
971 if(!(style.readOnly))
976 MenuItem itemEditDelete
978 editMenu, "Delete\tDel", d, disabled = true;
980 bool NotifySelect(MenuItem item, Modifiers mods)
982 if(!(style.readOnly))
987 MenuDivider { editMenu };
988 MenuItem itemEditSelectAll
990 editMenu, "Select All\tCtrl+A", a;
992 bool NotifySelect(MenuItem item, Modifiers mods)
998 MenuDivider { editMenu };
999 MenuItem itemEditUndo
1001 editMenu, "Undo\tCtrl+Z", u;
1004 bool NotifySelect(MenuItem item, Modifiers mods)
1010 MenuItem itemEditRedo
1012 editMenu, "Redo\tCtrl+Y", o;
1015 bool NotifySelect(MenuItem item, Modifiers mods)
1021 MenuDivider { editMenu };
1024 editMenu, "Find Previous\tShift-F3", e, shiftF3;
1026 bool NotifySelect(MenuItem item, Modifiers mods)
1029 Find(searchString, wholeWord, matchCase, false);
1031 itemEditFind.NotifySelect(this, item, mods);
1037 editMenu, "Find Next\tF3", n, f3;
1039 bool NotifySelect(MenuItem item, Modifiers mods)
1042 Find(searchString, wholeWord, matchCase, true);
1044 itemEditFind.NotifySelect(this, item, mods);
1048 MenuItem itemEditFind
1050 editMenu, "Find...\tCtrl+F", f, ctrlF;
1052 bool NotifySelect(MenuItem item, Modifiers mods)
1056 editBox = this, master = master, isModal = true, searchString = searchString, matchCase = matchCase, wholeWord = wholeWord,
1057 searchUp = searchUp;
1060 // Fix dialog from above shouldn't be visible
1061 // void NotifyDestroyed(FindDialog dialog, DialogResult result)
1062 void NotifyDestroyed(Window window, DialogResult result)
1064 FindDialog dialog = (FindDialog) window;
1065 searchUp = dialog.searchUp;
1066 strcpy(searchString, dialog.searchString);
1067 matchCase = dialog.matchCase;
1068 wholeWord = dialog.wholeWord;
1077 editMenu, "Replace...\tCtrl+R", r, ctrlR;
1079 bool NotifySelect(MenuItem item, Modifiers mods)
1081 ReplaceDialog dialog
1085 searchString = searchString,
1086 replaceString = replaceString,
1087 matchCase = matchCase,
1088 wholeWord = wholeWord,
1092 // void NotifyDestroyed(ReplaceDialog dialog, DialogResult result)
1093 void NotifyDestroyed(Window window, DialogResult result)
1095 ReplaceDialog dialog = (ReplaceDialog)window;
1096 char * replace = dialog.replaceString;
1098 strcpy(replaceString, replace);
1099 strcpy(searchString, dialog.searchString);
1100 matchCase = dialog.matchCase;
1101 wholeWord = dialog.wholeWord;
1108 MenuDivider { editMenu };
1111 editMenu, "Go To...\tCtrl+G", g, ctrlG;
1113 bool NotifySelect(MenuItem item, Modifiers mods)
1115 goToDialog.editBox = this;
1116 goToDialog.master = master;
1117 goToDialog.Create();
1121 MenuDivider { editMenu };
1122 MenuItem itemEditInsertTab
1124 editMenu, "Insert Tabs", i, checkable = true;
1126 bool NotifySelect(MenuItem item, Modifiers mods)
1128 style.useTab = item.checked;
1134 snapVertScroll = true;
1135 snapHorzScroll = true;
1139 static bool syntaxInit = false;
1144 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
1146 for(c = 0; keyWords[g][c]; c++);
1147 //keyLen[g] = new int[c];
1148 for(c = 0; keyWords[g][c]; c++)
1150 keyLen[g][c] = strlen(keyWords[g][c]);
1154 colorScheme.commentColor = dimGray;
1155 colorScheme.charLiteralColor = crimson;
1156 colorScheme.stringLiteralColor = crimson;
1157 colorScheme.preprocessorColor = green;
1158 colorScheme.numberColor = teal;
1159 colorScheme.keywordColors = [ blue, blue ];
1162 FontExtent = Display::FontExtent;
1164 lines.offset = (uint)&((EditLine)0).prev;
1166 style = EditBoxBits { hScroll = true };
1168 // cursor = guiApp.GetCursor(IBeam);
1170 // Default Properties
1172 maxLineSize = 1024;*/
1175 maxLineSize = MAXINT;
1180 mouseSelect = this.mouseMove = false;
1183 x = selX = selY = 0;
1186 line = selLine = null;
1192 endY = clientSize.h;
1195 // Default space to 8 in case window is not constructed
1198 undoBuffer.dontRecord++;
1200 undoBuffer.dontRecord--;
1202 viewLine = lines.first;
1203 style.recomputeSyntax = true;
1209 UpdateCaretPosition(true);
1215 lines.Free(EditLine::Free);
1218 void FlushBuffer(Surface surface, EditLine line, int wc, int * renderStart, int * x, int y, int numSpaces, Box box)
1220 int count = wc - *renderStart;
1223 if(y + space.h >= box.top && y <= box.bottom)
1229 //FontExtent(display, font, line.buffer + *renderStart, count, &w, null);
1230 surface.TextFont(font);
1231 surface.TextExtent(line.buffer + *renderStart, count, &w, null);
1232 if(*x + w + XOFFSET > 0)
1233 surface.WriteText(XOFFSET + *x,y, line.buffer + *renderStart, count);
1238 w = numSpaces; // * space.w;
1239 if(*x + w + XOFFSET > 0 && surface.GetTextOpacity())
1240 surface.Area(XOFFSET + *x - 1, y, XOFFSET + *x + w, y + space.h-1);
1241 // WHATS UP WITH THIS... surface.Area(XOFFSET + *x, y, XOFFSET + *x + w, y + space.h-1);
1249 int CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected,
1250 Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, bool *overwrite)
1256 if((wc == selX && line == selLine) || (wc == editX && line == this.line))
1260 *foreground = (*selected) ? selectionForeground : textColor;
1261 *background = selectionBackground;
1262 *opacity = *selected;
1269 if(this.overwrite && active)
1271 if((style.stuckCaret && wc == line.count && !line.next) ||
1272 (!mouseMove && line == this.line && wc == editX))
1281 void FigureStartSyntaxStates(EditLine firstLine, bool reset)
1285 bool inMultiLineComment = reset ? false : style.inMultiLineComment;
1286 bool inString = false;
1287 bool inQuotes = false;
1288 bool inPrep = reset ? false : style.inPrep;
1289 bool inSingleLineComment = false;
1290 bool escaped = reset ? false : style.escaped;
1291 bool continuedSingleLineComment = false;
1293 EditLine line = reset ? lines.first : firstLine;
1294 // int maxBackUp = 1000, c;
1295 // for(c = 0, line = viewLine; c < maxBackUp && line && line.prev; line = line.prev);
1296 for(; line != viewLine; line = line.next)
1298 char * text = line.buffer;
1301 bool lastWasStar = false;
1302 bool firstWord = true;
1303 if(!escaped) inPrep = false;
1304 inSingleLineComment = continuedSingleLineComment;
1310 for(c = 0; (ch = text[c]); c++)
1312 bool wasEscaped = escaped;
1313 bool backLastWasStar = lastWasStar;
1315 lastWasStar = false;
1318 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1320 if(text[c+1] == '/')
1322 inSingleLineComment = true;
1324 else if(text[c+1] == '*')
1326 inMultiLineComment = true;
1329 else if(backLastWasStar)
1330 inMultiLineComment = false;
1334 if(!c || text[c-1] != '/') lastWasStar = true;
1336 else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && ch == '\"')
1338 if(inString && !wasEscaped)
1347 else if(!inSingleLineComment && !inMultiLineComment && !inString && ch == '\'')
1349 if(inQuotes && !wasEscaped)
1363 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && ch == '#')
1375 continuedSingleLineComment = false;
1376 if(line.count && line.text[line.count - 1] == '\\')
1377 continuedSingleLineComment = true;
1379 style.inMultiLineComment = inMultiLineComment;
1380 style.inPrep = inPrep;
1381 style.escaped = escaped;
1385 /*void OnDrawOverChildren(Surface surface)
1387 if(style.lineNumbers)
1389 int currentLineNumber = this.viewY + 1;
1392 for(i = 0; i * space.h < box.height; i++)
1394 // ********* LINE NUMBERING *********
1395 surface.SetForeground(Color{60, 60, 60});
1396 //Highlight current line
1397 if(this.caretY / space.h == currentLineNumber - 1)
1398 surface.SetBackground(Color{220, 220, 220});
1400 surface.SetBackground(Color{230, 230, 230});
1401 surface.textOpacity = true;
1403 sprintf(lineText,"%5u ", currentLineNumber % 100000);
1404 if(currentLineNumber > this.lineCount)
1405 surface.WriteText(0,i*space.h+1," ",6);
1407 surface.WriteText(0,i*space.h+1,lineText,6);
1409 currentLineNumber++;
1414 void OnRedraw(Surface surface)
1418 bool selected = false, selection = true;
1420 Color selectionBackground = selectionColor ? selectionColor : SELECTION_COLOR;
1421 Color selectionForeground = selectionText ? selectionText : SELECTION_TEXT;
1422 Color defaultTextColor = property::foreground;
1425 int maxW = clientSize.w;
1427 Color foreground, background;
1430 // Overwrite Caret Stuff
1431 bool overWrite = false;
1432 int overWriteX, overWriteY;
1435 // ****** SYNTAX STATES ******
1436 bool inMultiLineComment = style.inMultiLineComment;
1437 bool inString = false;
1438 bool inQuotes = false;
1439 bool inPrep = style.inPrep;
1440 bool inSingleLineComment = false;
1441 bool escaped = style.escaped;
1442 bool continuedSingleLineComment = false;
1443 // ****** ************* ******
1446 defaultTextColor = Color { 85, 85, 85 };
1447 textColor = defaultTextColor;
1450 Abs(selectionBackground.r - property::background.r) +
1451 Abs(selectionBackground.g - property::background.g) +
1452 Abs(selectionBackground.b - property::background.b) < 92)
1454 selectionBackground = activeBorder;
1455 selectionForeground = selectionColor ? selectionColor : SELECTION_COLOR;
1458 surface.TextFont(this.font);
1460 surface.SetBackground(GDefaultPalette()[RND_Get(1, 15)]);
1461 surface.Area(0,0,MAXINT,MAXINT);
1466 surface.SetBackground(activeBorder);
1467 surface.Area(0,0,clientSize.w, clientSize.h);
1470 if(this.selX == this.x && this.selY == this.y)
1480 editX = Min(this.x,this.line.count);
1481 selX = Min(this.selX,this.selLine.count);
1484 selected = (this.selY < this.viewY) ^ (this.y < this.viewY);
1486 foreground = selected ? selectionForeground : textColor;
1487 background = selectionBackground;
1493 surface.SetForeground(foreground);
1494 surface.SetBackground(background);
1495 surface.TextOpacity(opacity);
1497 surface.GetBox(box);
1499 for(line = this.viewLine; line; line = line.next)
1501 int x = -this.viewX;
1505 Color newTextColor = textColor = defaultTextColor;
1506 bool lineComplete = false;
1509 // ****** SYNTAX HIGHLIGHTING ******
1510 bool lastWasStar = false;
1511 bool firstWord = true;
1512 if(!escaped) inPrep = false;
1513 inSingleLineComment = continuedSingleLineComment;
1517 // *********************************
1519 /* === DEBUGGING TOOL FOR MAXLINE ===
1521 if(line == this.maxLine)
1523 surface.SetBackground(GREEN|0xFF000000);
1524 surface.TextOpacity(true);
1528 surface.TextOpacity(selected ? true : false);
1529 surface.SetBackground(selected ? SELECTION_COLOR|0xFF000000 : BLACK|0xFF000000);
1533 if(line == this.selLine && line == this.line)
1535 end = Max(line.count, this.x);
1536 end = Max(end, this.selX);
1538 else if(line == this.selLine)
1539 end = Max(line.count, this.selX);
1540 else if(line == this.line)
1541 end = Max(line.count, this.x);
1549 bool spacing = true;
1550 bool cantHaveWords = false;
1557 lineComplete = false;
1560 textColor = newTextColor;
1562 surface.SetForeground(textColor);
1565 for(; c<end && !cantHaveWords;)
1569 bufferLen += wordLen;
1573 /*if(line.count > 4000)
1580 for(; c<line.count; c++)
1582 unichar ch = line.buffer[c];
1583 unichar bf = (wordLen == 1) ? line.buffer[c-1] : 0;
1584 //if(ch == ' ' || ch == '\t' || (wordLen && (ch == '(' || ch == ')' || ch == ';' || ch == ':')) || (wordLen == 1 && line.buffer[c-1] == '('))
1585 if(CharMatchCategories(ch, separators) || /*ch == ' ' ||*/ ch == '\t' ||
1586 (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && ch != '#' /*&& ch != '_'*/) ||
1587 (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && bf != '#' && bf != '\t' /*&& bf != '_' && bf != ' '*/))
1595 for(; c<line.count; c++)
1597 unichar ch = line.buffer[c];
1598 if(ch == '\t' || ch == ' ')
1600 cantHaveWords = true;
1604 if(ch != ' ' && ch != '\t')
1609 if(c == line.count && c < end)
1618 cantHaveWords = true;
1623 lastWasStar = false;
1629 bool backEscaped = escaped;
1630 bool backLastWasStar = lastWasStar;
1631 bool backInMultiLineComment = inMultiLineComment;
1632 bool backInString = inString;
1633 bool backInQuotes = inQuotes;
1634 bool backInPrep = inPrep;
1635 bool backInSingleLineComment = inSingleLineComment;
1637 char * word = line.buffer + c - wordLen;
1639 bool wasEscaped = escaped;
1641 lastWasStar = false;
1643 // Determine Syntax Highlighting
1644 newTextColor = defaultTextColor;
1647 if(inSingleLineComment || inMultiLineComment)
1649 newTextColor = colorScheme.commentColor;
1653 newTextColor = colorScheme.charLiteralColor;
1657 newTextColor = colorScheme.stringLiteralColor;
1661 newTextColor = colorScheme.preprocessorColor;
1663 if(wordLen == 1 && word[0] == '/')
1665 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1669 inSingleLineComment = true;
1670 newTextColor = colorScheme.commentColor;
1672 else if(word[1] == '*')
1674 inMultiLineComment = true;
1675 newTextColor = colorScheme.commentColor;
1678 else if(backLastWasStar)
1679 inMultiLineComment = false;
1681 else if(wordLen == 1 && word[0] == '*')
1683 if(!c || word[-1] != '/')
1686 else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && wordLen == 1 && word[0] == '\"')
1688 if(inString && !wasEscaped)
1695 newTextColor = colorScheme.stringLiteralColor;
1698 else if(!inSingleLineComment && !inMultiLineComment && !inString && wordLen == 1 && word[0] == '\'')
1700 if(inQuotes && !wasEscaped)
1705 newTextColor = colorScheme.charLiteralColor;
1708 else if(wordLen == 1 && word[0] == '\\')
1713 else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment &&
1714 ( ( isdigit(word[0]) /*&& (!c || word[-1] == ' ' || word[-1] == '\t')*/ ) || (word[0] == '.' && isdigit(word[1]))))
1716 newTextColor = colorScheme.numberColor;
1720 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && word[0] == '#')
1725 newTextColor = colorScheme.preprocessorColor;
1728 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1730 for(g = 0; g < ((inPrep && word[0] != '#') ? 2 : 1); g++)
1732 char ** keys = keyWords[g];
1733 int * len = keyLen[g];
1734 for(ccc = 0; keys[ccc]; ccc++)
1736 if(len[ccc] == wordLen && !strncmp(keys[ccc], word, wordLen))
1738 newTextColor = colorScheme.keywordColors[g];
1747 // If highlighting for this word is different, break
1748 if(newTextColor != textColor)
1752 // Better solution than going back?
1755 // Reset syntax flags
1756 escaped = backEscaped;
1757 lastWasStar = backLastWasStar;
1758 inMultiLineComment = backInMultiLineComment;
1759 inString = backInString;
1760 inQuotes = backInQuotes;
1761 inPrep = backInPrep;
1762 inSingleLineComment = backInSingleLineComment;
1767 textColor = newTextColor;
1769 surface.SetForeground(textColor);
1775 // If we're not breaking, this can't be rendered as spacing anymore
1778 // Do word wrapping here
1784 FontExtent(display, font, line.buffer + start, bufferLen + wordLen, &tw, null);
1789 w += numSpaces * space.w;
1791 if(x + viewX + w > maxW)
1794 lineComplete = true;
1799 bufferLen += wordLen;
1804 int renderStart = start;
1809 // Render checking if we need to split because of selection or to find where to draw insert caret
1810 for(wc = start; wc < start + bufferLen; wc++)
1812 flush = CheckColors(line, wc, selection, selX, editX, &selected, selectionForeground,
1813 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite);
1814 if(overWrite == true)
1816 overWriteCh = (wc < line.count) ? line.buffer[wc] : ' ';
1817 if(overWriteCh == '\t') overWriteCh = ' ';
1822 FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1823 if(overWrite == true)
1831 surface.TextOpacity(opacity);
1832 surface.SetBackground(background);
1833 surface.SetForeground(foreground);
1840 if(wc < line.count && line.buffer[wc] == '\t')
1842 numSpaces += (tabSize * space.w) - ((x + numSpaces + viewX) % (tabSize * space.w));
1846 numSpaces += space.w;
1850 FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1855 if(CheckColors(line, c, selection, selX, editX, &selected, selectionForeground,
1856 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite))
1858 if(overWrite == true)
1865 surface.TextOpacity(opacity);
1866 surface.SetBackground(background);
1867 surface.SetForeground(foreground);
1870 if(style.freeCaret && selected)
1872 surface.SetBackground(selectionBackground);
1873 surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1874 // TEST: surface.Area(x + XOFFSET,y,clientSize.w-1,y+this.space.h-1);
1879 if(style.freeCaret && selected)
1881 surface.SetBackground(selectionBackground);
1882 surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1886 continuedSingleLineComment = false;
1887 if(line.count && line.text[line.count - 1] == '\\')
1888 continuedSingleLineComment = true;
1891 if(y > box.bottom) // >=clientSize.h)
1897 surface.TextOpacity(true);
1898 surface.SetForeground(black);
1899 surface.SetBackground(Color {255,255,85});
1900 surface.WriteText(XOFFSET + overWriteX,overWriteY, &overWriteCh, 1);
1904 void FixScrollArea()
1906 if(style.hScroll || style.vScroll)
1908 int width = maxLength + XOFFSET;
1909 int height = lineCount * space.h;
1910 if(style.freeCaret && line)
1915 width = Max(line.length + (x - line.count) * space.w, maxLength);
1919 if(selX > selLine.count)
1920 width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength);
1925 SetScrollLineStep(8, space.h);
1926 SetScrollArea(width, height, true);
1930 void ComputeLength(EditLine line)
1937 for(c = 0; c < line.count; )
1946 for(len = 0; c < line.count; c += numBytes)
1948 ch = line.buffer[c];
1949 numBytes = UTF8_NUM_BYTES(ch);
1951 if(ch == ' ' || ch == '\t')
1958 if(!len && ch == ' ')
1963 else if(!len && ch == '\t')
1965 w = (tabSize * space.w) - (x % (tabSize * space.w));
1969 FontExtent(display, font, line.buffer + start, len, &w, null);
1980 if(line.length > this.maxLength)
1982 this.maxLine = line;
1983 this.maxLength = line.length;
1992 this.maxLine = null;
1994 for(line = lines.first; line; line = line.next)
1996 if(line.length > this.maxLength)
1998 this.maxLength = line.length;
1999 this.maxLine = line;
2006 if(this.selY != this.y)
2008 else if(this.selX != this.x) // commented out to erase caret: if(this.selX != this.x)
2012 void ComputeColumn()
2014 // Adjust new edit X position according to tabs
2015 int c, position = 0;
2018 for(c = 0; c<this.line.count && c<this.x; c+= nb)
2020 ch = UTF8_GET_CHAR(this.line.buffer + c, nb);
2021 // TODO: MIGHT WANT TO RETHINK WHAT COLUMN SHOULD BE REGARDING TABS
2023 position += this.tabSize - (position % this.tabSize);
2027 position += this.x - c;
2028 this.col = position;
2031 int DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
2033 return _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, null);
2036 bool HasCommentOrEscape(EditLine line)
2038 bool hadComment = strstr(line.buffer, "/*") || strstr(line.buffer, "*/");
2043 for(c = line.count-1; c >= 0; c--)
2045 char ch = line.buffer[c];
2051 else //if(ch != ' ' && ch != '\t')
2058 int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, int * addedSpacesPtr)
2060 EditLine line = l1, next;
2062 int oldCount1, oldCount2;
2063 int y, firstViewY, firstY, firstDropY, firstSelY;
2066 DelTextAction action = null;
2067 bool hadComment = false;
2071 hadComment = HasCommentOrEscape(line);
2073 if(y2 > y1 || c2 > c1)
2075 if(start < l1.count)
2077 while(!UTF8_IS_FIRST(l1.buffer[start]) && start)
2081 oldCount1 = l1.count;
2083 while(c1 < oldCount1)
2085 byte ch = buffer[c1];
2086 if(UTF8_IS_FIRST(ch)) break;
2090 oldCount2 = l2.count;
2092 while(c2 < oldCount2)
2094 byte ch = buffer[c2];
2095 if(UTF8_IS_FIRST(ch)) break;
2100 if(!undoBuffer.dontRecord && (y2 > y1 || c2 > c1))
2105 len = GetText(null, l1, y1, start, l2, y2, c2, false, false);
2106 string = new char[len];
2107 action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter };
2108 GetText(string, l1, y1, start, l2, y2, c2, false, false);
2112 //oldCount1 = l1.count;
2113 //oldCount2 = l2.count;
2116 BufferLocation before = { l1,y1,c1}, after = { l2,y2,c2 };
2117 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
2120 if(c2 > oldCount2) c2 = oldCount2;
2121 if(!(style.freeCaret))
2122 if(c1 > oldCount1) c1 = oldCount1;
2123 newLineCount = c1 + l2.count-c2;
2129 buffer = new char[line.size ? line.size : 1];
2131 buffer = new char[line.size];
2133 CopyBytes(buffer,l2.buffer,oldCount1 + 1/*line.count + 1*//*line.size*/);
2138 if(!line.AdjustBuffer(newLineCount))
2142 /*if(newLineCount > 4000 || newLineCount < 0)
2143 printf("Warning");*/
2145 line.count = newLineCount;
2147 memmove(l1.buffer+c1, buffer+c2,line.count-c1);
2152 action.addedSpaces = c1-oldCount1;
2154 if(action.addedSpaces > action.x1)
2160 if(addedSpacesPtr) *addedSpacesPtr = c1-oldCount1;
2161 FillBytes(l1.buffer+oldCount1,' ',c1-oldCount1);
2163 line.buffer[line.count] = '\0';
2170 this.dropX -= c2-c1;
2171 this.dropX = Max(this.dropX,0);
2173 this.x = Max(this.x,0);
2180 firstViewY = this.viewY;
2182 firstDropY = this.dropY;
2183 firstSelY = this.selY;
2184 for(line = l1;line;line = next, y++)
2192 if(line == this.viewLine)
2194 if(this.viewLine.next)
2196 this.viewLine = this.viewLine.next;
2198 style.recomputeSyntax = true;
2202 this.viewLine = this.viewLine.prev;
2204 style.recomputeSyntax = true;
2207 else if(y < firstViewY)
2209 if(line == this.line)
2213 this.line = this.line.next;
2214 this.x = this.line.count;
2219 this.line = this.line.prev;
2220 this.x = this.line.count;
2227 if(line == this.dropLine)
2229 if(this.dropLine.next)
2231 this.dropLine = this.dropLine.next;
2232 this.dropX = this.dropLine.count;
2236 this.dropLine = this.dropLine.prev;
2237 this.dropX = this.dropLine.count;
2241 else if(y < firstDropY)
2243 if(line == this.selLine)
2245 if(this.selLine.next)
2247 this.selLine = this.selLine.next;
2248 this.selX = this.selLine.count;
2252 this.selLine = this.selLine.prev;
2253 this.selX = this.selLine.count;
2257 else if(y < firstSelY)
2261 if(line == l2) break;
2265 if(style.syntax && (hadComment || HasCommentOrEscape(this.line)))
2268 style.recomputeSyntax = true;
2273 bool DelSel(int * addedSpacesPtr)
2275 if(this.line != this.selLine || this.x != this.selX)
2277 if(this.selY < this.y)
2279 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2282 this.line = this.selLine;
2284 else if(this.selY > this.y)
2286 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2289 this.selLine = this.line;
2291 else if(this.selX < this.x)
2293 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2296 this.line = this.selLine;
2300 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2303 this.selLine = this.line;
2311 bool AddToLine(char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr)
2313 bool hadComment = false;
2314 // Add the line here
2315 EditLine line = this.line;
2317 // The purpose of this is solely to lock a max number of characters if no HSCROLLING is present
2318 if(!style.hScroll && created)
2320 int endX = (style.freeCaret) ? this.x : Min(this.x, line.count);
2325 // Lock if no place to display.
2328 else if(endX > this.x)
2329 max = Max(this.x, line.count);
2333 for(x = 0, c = 0; c < max+count; )
2338 if(c < Min(this.x, line.count))
2339 string = line.buffer + c;
2342 else if(c < endX + count)
2343 string = stringLine + c - endX;
2345 string = line.buffer + c - endX - count;
2349 w = (tabSize * space.w) - (x % (tabSize * space.w));
2353 numBytes = UTF8_NUM_BYTES(*string);
2354 FontExtent(display, this.font, string, numBytes, &w, null);
2358 if(x >= clientSize.w)
2363 c += numBytes; // - 1;
2369 int addedSpaces = 0;
2372 // Add blank spaces if EES_FREECARET
2373 if(this.x > line.count)
2383 for(c = 0; c<line.count; c++)
2385 if(this.line.buffer[c] == '\t')
2386 position += this.tabSize - (position % this.tabSize);
2390 wantedPosition = position + (this.x - line.count);
2392 // A tab is too much...
2393 if(position + (this.tabSize - (position % this.tabSize)) > wantedPosition)
2394 addedSpaces = wantedPosition - position;
2399 position += this.tabSize - (position % this.tabSize);
2400 // Add as many tabs as needed
2401 addedTabs += (wantedPosition - position) / this.tabSize;
2402 position += (addedTabs-1) * this.tabSize;
2403 // Finish off with spaces
2404 addedSpaces = wantedPosition - position;
2408 addedSpaces = this.x - line.count;
2411 this.x = Min(this.x, line.count);
2413 if(line.count + count + addedSpaces + addedTabs > this.maxLineSize)
2419 hadComment = HasCommentOrEscape(line);
2421 // Adjust the size of the line
2422 if(!line.AdjustBuffer(line.count+count+addedTabs+addedSpaces))
2426 BufferLocation before = { this.line, this.y, this.x }, after = { this.line, this.y, this.x };
2429 memmove(line.buffer+this.x+count, line.buffer+this.x,line.count-this.x);
2430 CopyBytes(line.buffer + this.x + addedTabs + addedSpaces, stringLine, count);
2433 *addedTabsPtr = addedTabs;
2434 FillBytes(line.buffer+line.count,'\t',addedTabs);
2436 if(addedTabs > 4000 || addedTabs < 0)
2439 line.count += addedTabs;
2445 FillBytes(line.buffer+line.count,' ',addedSpaces);
2447 if(addedSpaces > 4000 || addedSpaces < 0)
2450 line.count += addedSpaces;
2451 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
2453 else if(addedSpacesPtr)
2454 *addedSpacesPtr = 0;
2456 if(count > 4000 || count < 0)
2459 line.count += count;
2460 this.x += count + addedTabs + addedSpaces;
2463 this.selLine = this.line;
2465 line.buffer[line.count] = '\0';
2467 ComputeLength(line);
2472 hasComment = HasCommentOrEscape(line);
2473 if(!undoBuffer.insideRedo)
2475 int backDontRecord = undoBuffer.dontRecord;
2476 undoBuffer.dontRecord = 0;
2477 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
2478 undoBuffer.dontRecord = backDontRecord;
2480 if(style.syntax && (hadComment || hasComment || line != this.line))
2482 style.recomputeSyntax = true;
2490 void Emptyline(EditLine line, int y)
2493 DelCh(line, y, 0, line.next, y+1, 0, true);
2495 DelCh(line, y, 0, line, y, line.count, true);
2498 void GoToEnd(bool deselect)
2502 this.line = this.lines.last;
2503 if(this.y != this.lineCount-1)
2505 else if (this.x != this.line.count)
2506 DirtyLine(this.lineCount-1);
2507 this.y = this.lineCount-1;
2508 this.x = this.line.count;
2515 void GoToHome(bool deselect)
2519 this.line = this.lines.first;
2522 else if (this.x !=0)
2523 DirtyLine(this.lineCount-1);
2532 // Returns true if it needs scrolling
2533 bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
2539 bool needHScroll = false;
2546 line = this.viewLine ? (void *)this.viewLine.prev : null;
2551 line = (void *)this.lines.first;
2556 py = Min(py, clientSize.h);
2558 py = Min(py, this.lineCount);
2560 for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
2566 if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
2569 px = Min(px,clientSize.w+this.space.w);
2573 *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
2576 if(tline) *tline = line;
2579 // Prevent divide by 0 from non valid this.font
2581 return (y < this.viewY) || needHScroll;
2583 return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
2587 // Minimal Update Management Functions
2591 this.endY = clientSize.h-1;
2592 // ErrorLog("DirtyAll\n");
2595 void DirtyEnd(int y)
2597 if((y - this.viewY)*this.space.h < this.startY)
2598 this.startY = (y - this.viewY)*this.space.h+ YOFFSET;
2599 this.endY = clientSize.h-1;
2600 //ErrorLog("DirtyEnd %d\n", y);
2603 void DirtyLine(int y)
2607 if((y - this.viewY)*this.space.h < this.startY)
2608 this.startY = (y - this.viewY)*this.space.h + YOFFSET;
2609 if((y - this.viewY+1)*this.space.h > this.endY)
2610 this.endY = (y - this.viewY+1)*this.space.h-1 + YOFFSET;
2612 //ErrorLog("DirtyLine %d\n", y);
2619 if(style.recomputeSyntax)
2621 FigureStartSyntaxStates(lines.first, true);
2622 style.recomputeSyntax = false;
2625 if(this.startY > this.endY) return;
2626 if(this.startY <= 0 && this.endY >= clientSize.h-1)
2632 box.right = clientSize.w-1;
2633 box.top = this.startY;
2634 box.bottom = this.endY;
2637 this.startY = clientSize.h;
2641 bool IsMouseOnSelection()
2643 bool mouseOnSelection = false;
2646 int minY = Min(this.selY, this.y);
2647 int maxY = Max(this.selY, this.y);
2648 int minX = Min(this.selX, this.x);
2649 int maxX = Max(this.selX, this.x);
2651 FindMouse(this.mouseX - this.space.w / 2, this.mouseY, &x, &y, null, false);
2653 if(maxX != minX || maxY != minY)
2655 if(y > minY && y < maxY)
2656 mouseOnSelection = true;
2657 else if(y == minY && y == maxY)
2658 mouseOnSelection = (x < maxX && x >= minX);
2662 mouseOnSelection = (x >= this.selX);
2663 else if(y == this.y)
2664 mouseOnSelection = (x >= this.x);
2669 mouseOnSelection = (x < this.selX);
2670 else if(y == this.y)
2671 mouseOnSelection = (x < this.x);
2674 return mouseOnSelection;
2677 void UpdateCaretPosition(bool setCaret)
2681 if(mouseMove || (!overwrite && !style.noCaret))
2683 int max = this.mouseMove ? this.dropX : this.x;
2684 int y = this.mouseMove ? this.dropY : this.y;
2685 EditLine line = this.mouseMove ? this.dropLine : this.line;
2687 if(!(style.freeCaret))
2688 max = Min(max, line.count);
2690 if(FontExtent && display)
2692 for(c = 0; c < max; )
2701 for(len = 0; c < Min(max, line.count); c += numBytes)
2703 ch = line.buffer[c];
2704 numBytes = UTF8_NUM_BYTES(ch);
2706 if(ch == ' ' || ch == '\t')
2713 if(!len && ch == ' ')
2718 else if(!len && ch == '\t')
2720 w = (tabSize * space.w) - (x % (tabSize * space.w));
2724 FontExtent(display, this.font, line.buffer + start, len, &w, null);
2736 caretY = y * this.space.h;
2737 SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
2742 NotifyCaretMove(master, this, y + 1, x + 1);
2748 void SelectionEnables()
2750 if((x != selX || y != selY) && !selection)
2754 itemEditCut.disabled = false;
2755 itemEditDelete.disabled = false;
2757 itemEditCopy.disabled = false;
2759 this.selection = true;
2761 else if((x == selX && y == selY) && selection)
2763 itemEditCut.disabled = true;
2764 itemEditCopy.disabled = true;
2765 itemEditDelete.disabled = true;
2767 this.selection = false;
2771 void SetSelectCursor()
2773 if(!inactive || !style.noSelect)
2776 cursor = guiApp.GetCursor(arrow);
2777 else if(this.mouseSelect)
2778 cursor = guiApp.GetCursor(iBeam);
2781 if(IsMouseOnSelection())
2782 cursor = guiApp.GetCursor(arrow);
2784 cursor = guiApp.GetCursor(iBeam);
2789 int AdjustXPosition(EditLine line, int position, bool half, int * px, int max, int sc)
2792 int x = px ? *px : 0;
2799 if(c < Min(max, line.count))
2803 for(len = 0; c < Min(max, line.count); c += numBytes)
2805 ch = line.buffer[c];
2806 numBytes = UTF8_NUM_BYTES(ch);
2808 if(ch == ' ' || ch == '\t')
2815 if(!len && ch == ' ')
2820 else if(!len && ch == '\t')
2822 w = (tabSize * space.w) - (x % (tabSize * space.w));
2826 FontExtent(display, font, line.buffer + start, len, &w, null);
2830 if(style.freeCaret && c < max)
2839 if(x + (((half && len == 1) ? (w / 2) : w)) >= position)
2844 int a = start + len;
2847 while(a > 0 && !UTF8_IS_FIRST(line.buffer[--a]));
2851 FontExtent(display, font, line.buffer + start, a - start, &w, null);
2854 if(position > x + (half ? ((w + lastW) / 2) : lastW)) break;
2858 return Min(this.maxLineSize - 1, start + len);
2864 void SetCursorToViewX()
2866 bool selecting = this.x != selX || y != selY;
2868 // Horizontal Adjustment
2870 int c = AdjustXPosition(line, viewX, false, &x, MAXINT, 0);
2875 c = AdjustXPosition(line, viewX + clientSize.w - 1, false, &x, MAXINT, c);
2887 UpdateCaretPosition(false);
2893 void SetCursorToViewY()
2896 EditLine oldLine = this.line;
2898 bool selecting = this.x != this.selX || this.y != this.selY;
2900 numLines = clientSize.h / this.space.h;
2902 // Vertical Adjustment
2903 if(this.viewY > this.y)
2905 this.y = this.viewY;
2906 this.line = this.viewLine;
2909 if(this.viewY + numLines <= this.y)
2913 this.y = this.viewY-1;
2914 for(c = 0, line = this.viewLine; line && c<numLines; line = line.next, c++)
2921 if(this.line != oldLine)
2923 this.x = AdjustXPosition(this.line, caretX, true, null, MAXINT, 0);
2931 this.selLine = this.line;
2934 UpdateCaretPosition(false);
2941 bool SaveFile(char * fileName)
2943 File f = eFile_Open(fileName, FO_WRITE);
2947 eWindow_SetModified(false);
2948 eInstance_Delete(f);
2955 bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
2959 if(!style.multiLine)
2961 x = (active && this.active && !style.readOnly) ? line.count : 0;
2964 SetViewToCursor(true);
2969 if(!active && modified)
2972 if(!NotifyModified(master, this))
2975 *goOnWithActivation = false;
2982 if(timer) timer.Stop();
2984 mouseSelect = false;
2990 bool OnResizing(int *w, int *h)
2996 *w = space.h * 80 / 14;
3000 void OnResize(int w, int h)
3003 if(!hasHorzScroll && !hasVertScroll && viewLine)
3004 SetViewToCursor(true);
3006 //if(!hasHorzScroll && !hasVertScroll && viewLine)
3008 SetViewToCursor(true);
3013 bool OnMiddleButtonDown(int x, int y, Modifiers mods)
3015 if(style.readOnly) return true;
3016 // We really shouldn't be pasting here, Unix middle button paste is for the selection (not the clipboard), good for the terminal
3017 // Middle button already serves as a dragger as well.
3022 bool OnRightButtonDown(int x, int y, Modifiers mods)
3024 this.rightButtonDown = true;
3029 bool OnRightButtonUp(int x, int y, Modifiers mods)
3032 if(!parent.inactive && rightButtonDown)
3035 Menu contextMenu { };
3037 MenuItem { contextMenu, "Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
3038 MenuItem { contextMenu, "Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
3039 MenuItem { contextMenu, "Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
3040 MenuItem { contextMenu, "Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
3041 MenuDivider { contextMenu };
3042 MenuItem { contextMenu, "Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
3044 popup = PopupMenu { master = this, menu = contextMenu,
3046 nonClient = true, interim = false, parent = parent,
3047 position = { x + clientStart.x + parent.clientStart.x + position.x, y + cientStart.y + parent.sy + clientStart.y + position.y };
3049 position = { x + clientStart.x + absPosition.x - guiApp.desktop.position.x, y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
3053 rightButtonDown = false;
3057 bool OnLeftButtonDown(int mx, int my, Modifiers mods)
3062 if(style.noSelect) return true;
3064 // Should we have a separate 'selectOnActivate' style?
3065 if(!mods.isActivate || (style.readOnly && style.multiLine))
3071 mouseX = mx - XOFFSET;
3074 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3076 //PrintLn("OnLeftButtonDown: ", x, ", ", y);
3082 else if(IsMouseOnSelection() && !mods.isActivate)
3092 if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
3094 if(mods.shift && !mods.isActivate)
3109 this.selLine = this.line;
3118 UpdateCaretPosition(true);
3122 bool OnLeftButtonUp(int x, int y, Modifiers mods)
3126 mouseSelect = false;
3137 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3139 //PrintLn("MouseMove: ", x, ", ", y);
3145 mouseMove = IsMouseOnSelection();
3149 int size = SelSize();
3152 char * text = new char[size+1];
3156 GetSel(text, false);
3158 if(Max(selY, this.y) == dropY)
3162 if(this.dropX > this.selX)
3163 moveX = this.x - this.selX;
3167 if(this.dropX > this.x)
3168 moveX = this.selX - this.x;
3172 this.dropX -= moveX;
3173 this.selX = this.x = this.dropX;
3174 this.selY = this.y = this.dropY;
3175 this.selLine = this.line = this.dropLine;
3177 SetViewToCursor(true);
3182 byte * c = ((EditBox)this).multiLineContents, * c2;
3183 int l1 = c ? strlen(c) : 0, l2;
3186 c2 = ((EditBox)this).multiLineContents;
3187 l2 = c2 ? strlen(c2) : 0;
3188 if(l1 != l2 || (l1 && strcmp(c, c2)))
3218 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3220 //PrintLn("Dropped: ", x, ", ", y);
3222 NotifyDropped(master, this, x, y);
3229 bool OnMouseMove(int mx, int my, Modifiers mods)
3235 if(mods != -1 && mods.isSideEffect)
3240 if(style.noSelect) return true;
3241 if(wordSelect) return true;
3242 mouseX = mx - XOFFSET;
3245 needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
3247 if(this.mouseMove || this.mouseSelect)
3256 ((style.hScroll) || (style.vScroll)))
3263 DirtyLine(this.dropY);
3266 DirtyLine(this.dropY);
3267 this.dropLine = line;
3268 SetViewToCursor(true);
3270 //PrintLn("MouseMove: ", "dropX = ", x, ", dropY = ", y);
3273 else if(this.mouseSelect)
3275 DirtyLine(this.selY);
3282 SetViewToCursor(true);
3285 //PrintLn("MouseSelect: ", "x = ", x, ", y = ", y);
3292 bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
3297 //PrintLn("OnLeftDoubleClick: ", mx, ", ", my, ", mods = ", mods);
3301 if(style.noSelect) return true;
3302 FindMouse(mx, my, &x, &y, &line, false);
3303 if(!NotifyDoubleClick(master, this, line, mods))
3310 for(c = x; c >= 0; c--)
3313 while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
3314 ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3321 for(c = start; c<line.count; c += numBytes)
3323 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3334 this.line = this.selLine = line;
3335 this.wordSelect = (c != start);
3347 Menu fileMenu { menu, "File", F };
3348 saveDialog = fileDialog;
3349 MenuItem { fileMenu, "Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
3350 MenuItem { fileMenu, "Save As...", A, NotifySelect = MenuFileSaveAs };
3360 FontExtent(display, font, " ", 1, (int *)&space.w, (int *)&space.h);
3361 FontExtent(display, font, "W", 1, (int *)&large.w, (int *)&large.h);
3363 space.w = Max(space.w, 1);
3364 large.w = Max(large.w, 1);
3365 space.h = Max(space.h, 1);
3366 large.h = Max(large.h, 1);
3370 for(line = lines.first; line; line = line.next)
3371 ComputeLength(line);
3376 SetViewToCursor(true);
3384 bool OnLoadGraphics()
3386 FontExtent = Display::FontExtent;
3389 // UpdateCaretPosition(true);
3393 void OnUnloadGraphics()
3398 bool OnKeyHit(Key key, unichar ch)
3400 bool shift = (key.shift) ? true : false;
3402 //PrintLn("OnKeyHit: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
3404 if(!ch && !key.alt && !key.ctrl)
3406 key.code = (SmartKey)key.code;
3409 switch(key.code) //(ch || key.alt || key.ctrl) ? key.code : (Key)(SmartKey)key.code)
3412 if(style.readOnly) break;
3413 if(style.stuckCaret) GoToEnd(true);
3414 if(!(style.freeCaret))
3416 this.x = Min(this.x, this.line.count);
3422 EditLine line = this.line;
3424 for(y = this.y; y>= 0; y--)
3426 c = (y == this.y) ? (this.x-1) : line.count-1;
3428 // Slow down when going on lines...
3429 if(y != this.y) break;
3433 //if(this.line.buffer[c] != '\t' && this.line.buffer[c] != ' ')
3434 if(IS_ALUNDER(line.buffer[c]))
3439 if(!IS_ALUNDER(line.buffer[c]))
3441 //if(this.line.buffer[c] == '\t' || this.line.buffer[c] == ' ')
3455 DelCh(line,y,c+1,this.line,this.y,this.x, true);
3456 this.x = this.selX = Min(c+1, line.count);
3457 this.y = this.selY = y;
3458 this.line = this.selLine = line;
3459 SetViewToCursor(true);
3470 if(style.readOnly) break;
3471 if(style.stuckCaret) break;
3480 if(this.line != this.selLine || this.x != this.selX)
3483 SetViewToCursor(true);
3489 if(this.x < this.line.count)
3493 //Can't delete right away as that would change the line.count
3494 for(i = this.x; i < this.line.count; i++)
3496 if(!IS_ALUNDER(this.line.buffer[i]))
3498 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3502 for(; i < this.line.count; i++)
3504 //Delete trailing whitespace
3505 if(IS_ALUNDER(this.line.buffer[i]))
3507 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3510 SetViewToCursor(true);
3513 else if(this.line.next)
3515 DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3516 SetViewToCursor(true);
3522 if(!(style.freeCaret))
3524 this.selX = this.x = Min(this.x, this.line.count);
3527 if(this.x < this.line.count)
3529 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3531 else if(this.line.next)
3533 DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3535 SetViewToCursor(true);
3544 if(!key.alt && !key.ctrl)
3548 bool stuffAfter = false;
3552 if(style.stuckCaret) GoToEnd(true);
3553 if(style.readOnly) break;
3554 if(!(style.multiLine)) break;
3556 for(c = 0; c<this.line.count && c<this.x; c++)
3558 if(this.line.buffer[c] == '\t')
3559 position += this.tabSize - (position % this.tabSize);
3560 else if(this.line.buffer[c] == ' ')
3568 if(this.x < this.line.count)
3571 //If last character is a { indent one tab
3572 if(this.line.buffer[this.x - 1] == '{')
3574 //Except if the next non space character is a }
3575 bool indent = false;
3577 for(i = this.x; i < this.line.size; i++)
3578 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
3580 if(this.line.buffer[i] != '}')
3585 position += this.tabSize;
3588 addString = new char[position + 2];
3589 addString[len++] = '\n';
3590 addString[len] = '\0';
3592 if(stuffAfter || !style.freeCaret)
3594 for(c = 0; c<position; )
3596 if(style.useTab && c + this.tabSize <= position)
3598 addString[len++] = '\t';
3603 addString[len++] = ' ';
3607 addString[len] = '\0';
3611 if(!stuffAfter && style.freeCaret)
3613 this.x = this.selX = position;
3617 SetViewToCursor(true);
3627 if(style.stuckCaret) break;
3628 if(!(style.freeCaret))
3630 this.x = Min(this.x, this.line.count);
3631 this.selX = Min(this.selX, this.selLine.count);
3634 if(!shift) SelDirty();
3637 bool foundAlpha = false;
3640 EditLine line, lastLine;
3643 for(line = this.line; (line && !found); line = line.prev, y--)
3648 if(this.x == 0 && line != this.line)
3657 if(line == this.line) start = this.x -1; else start = line.count-1;
3658 start = Min(start, line.count-1);
3660 for(c = start; c >= 0;)
3663 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3681 byte ch = line.buffer[c];
3682 if(UTF8_IS_FIRST(ch)) break;
3686 // No next word found,
3687 if(!found && ( this.x > 0 || (!line.count && this.x)))
3701 this.line = lastLine;
3712 byte * buffer = line.buffer;
3715 byte ch = buffer[x];
3716 if(UTF8_IS_FIRST(ch)) break;
3733 if(!shift) Deselect();
3734 SetViewToCursor(true);
3740 if(style.stuckCaret) break;
3741 if(!(style.freeCaret))
3743 this.x = Min(this.x, this.line.count);
3744 this.selX = Min(this.selX, this.selLine.count);
3747 if(!shift) SelDirty();
3748 if(!shift && (this.x != this.selX || this.y != this.selY));
3751 bool onAChar = false;
3752 if(this.selX != this.x || this.selY != this.y)
3754 if(this.x<this.line.count)
3755 if(this.line.buffer[this.x] != '\t' && this.line.buffer[this.x] !=' ')
3757 if(key.shift && onAChar &&
3758 ((this.y > this.selY)||((this.selY == this.y)&&(this.x >= this.selX))))
3760 bool foundAlpha = false;
3762 EditLine line, lastLine;
3764 int lastC, lastY, lastNumBytes;
3766 for(line = this.line; (line && !found); line = line.next, y++)
3768 int start = (line == this.line) ? this.x : 0;
3771 for(c = start; c < line.count; c += numBytes)
3773 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3778 lastNumBytes = numBytes;
3788 if(!found && (c != this.x || line != this.line))
3801 this.x = lastC + lastNumBytes;
3803 this.line = lastLine;
3810 bool foundAlpha = false;
3815 for(line = this.line; (line && !found); line = line.next, y++)
3817 int start = (line == this.line) ? this.x : 0;
3820 for(c = start; c < line.count; c += numBytes)
3822 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3837 // No next word found,
3838 if(!found && (c != this.x || line != this.line))
3842 this.x = line.count;
3854 if(x < line.count || (style.freeCaret && line.count < maxLineSize))
3858 byte * buffer = line.buffer;
3861 byte ch = buffer[x];
3862 if(UTF8_IS_FIRST(ch)) break;
3883 if(!shift) Deselect();
3884 SetViewToCursor(true);
3891 if(!style.vScroll || hasVertScroll) break;
3897 if(style.stuckCaret) break;
3899 if(!shift) SelDirty();
3909 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
3913 if(!shift) Deselect();
3915 SetViewToCursor(false);
3918 if(caretY == this.y * space.h)
3924 if(!style.freeCaret)
3925 this.x = Min(this.x, line.count);
3935 int sx = 0, sy = this.y * space.h;
3936 char * text = line.text;
3937 int maxW = clientSize.w - sx;
3938 display.FontExtent(font, " ", 1, null, &th);
3942 int startPos = textPos;
3945 bool lineComplete = false;
3947 if(!style.wrap && caretY == MAXINT)
3950 //textPos = line.count;
3951 //lineComplete = true;
3954 for(; (style.freeCaret || textPos < line.count) && !lineComplete;)
3958 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
3961 len = (nextSpace - (text + textPos));
3963 len = line.count - textPos;
3965 if(textPos < line.count)
3967 display.FontExtent(font, text + textPos, len, &w, null);
3969 if(nextSpace) { w += space.w; len++; }
3971 if(style.wrap && x + width + w > maxW && x > 0)
3973 lineComplete = true;
3983 if((!style.freeCaret && textPos >= line.count) || (sy == caretY - th && caretX <= x + width + sx))
3987 while(this.x > 0 && x + sx > caretX && this.x > startPos)
3990 if(this.x > line.count)
3993 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
3994 len = this.x - startPos;
3995 display.FontExtent(font, text + startPos, len, &x, null);
3999 if(!shift) Deselect();
4001 SetViewToCursor(false);
4005 if(sy == caretY - th || textPos >= line.count)
4007 if(textPos >= line.count)
4009 int c = textPos - 1;
4010 while(c > 0 && text[c] == ' ') c--;
4014 this.x = line.count;
4017 if(!shift) Deselect();
4019 SetViewToCursor(false);
4024 } while(textPos < line.count);
4027 if(!shift) Deselect();
4029 SetViewToCursor(false);
4038 int x = AdjustXPosition(this.line, this.line.prev, true, null, MAXINT, 0);
4039 if(!shift) SelDirty();
4040 this.line = this.line.prev;
4046 if(!shift) Deselect();
4050 SetViewToCursor(false);
4056 return style.multiLine ? false : true;
4060 if(!style.vScroll || hasVertScroll)
4067 if(style.stuckCaret) break;
4071 int sx = 0, sy = this.y * this.space.h;
4072 int maxW = clientSize.w - sx;
4073 char * text = line.buffer;
4075 if(!shift) SelDirty();
4081 if(AdjustXPosition(line, maxW, this.x, line.count, true, null, MAXINT, 0) <= line.count)
4091 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
4094 if(!shift) Deselect();
4096 if(this.selX != this.x || this.selY != this.y)
4098 SetViewToCursor(false);
4101 while(!textPos || (style.freeCaret || textPos<line.count))
4103 int startPos = textPos;
4106 bool lineComplete = false;
4107 if(!style.wrap && sy <= caretY)
4109 textPos = line.count;
4110 lineComplete = true;
4112 for(; (style.freeCaret || textPos<line.count) && !lineComplete;)
4116 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4119 len = (nextSpace - (text + textPos));
4121 len = line.count - textPos;
4123 if(textPos < line.count)
4125 display.FontExtent(font, text + textPos, len, &w, &th);
4127 if(nextSpace) { w += space.w; len++; }
4128 if(style.wrap && x + width + w > maxW && x > 0)
4130 lineComplete = true;
4140 if(sy > caretY && ((!style.freeCaret && textPos >= line.count) || caretX <= x + width + sx))
4144 while(this.x > 0 && x + sx > caretX && textPos > startPos)
4147 if(this.x > line.count)
4150 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4152 len = this.x - startPos;
4153 display.FontExtent(font, text + startPos, len, &x, null);
4156 if(!shift) Deselect();
4159 SetViewToCursor(false);
4165 this.x = line.count;
4168 if(!shift) Deselect();
4171 SetViewToCursor(false);
4174 else if(textPos >= line.count && line.next)
4180 sy = this.y * this.space.h;
4181 sx = 0; //textBlock.startX;
4187 sx = 0; //textBlock.startX;
4195 this.x = Min(this.x, line.count);
4196 //int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4198 if(!shift) Deselect();
4201 if(this.selX != this.x || this.selY != this.y)
4204 SetViewToCursor(false);
4211 int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4212 if(!shift) SelDirty();
4213 this.line = this.line.next;
4217 if(!shift) Deselect();
4220 if(this.selX != this.x || this.selY != this.y)
4228 return style.multiLine ? false : true;
4231 if(style.stuckCaret) break;
4232 if(!(style.freeCaret))
4233 this.selX = Min(this.selX, this.selLine.count);
4235 if(!shift) SelDirty();
4238 this.line = this.lines.first;
4239 if(this.y != 0 || this.x != 0)
4249 EditLine line = this.line;
4251 for(c=0; line.buffer[c]; c++)
4252 if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
4254 if(c != 0 || this.x)
4264 DirtyLine(this.y);*/
4269 if(!shift) Deselect();
4270 SetViewToCursor(true);
4276 if(style.stuckCaret) break;
4277 if(!(style.freeCaret))
4278 this.selX = Min(this.selX, this.selLine.count);
4280 if(!shift) SelDirty();
4285 else if(this.x != this.line.count)
4287 this.x = this.line.count;
4288 //DirtyLine(this.y);
4291 if(!shift) Deselect();
4292 SetViewToCursor(true);
4297 if(style.tabKey && !key.ctrl)
4299 if(this.selY != this.y && style.tabSel)
4301 EditLine firstLine, lastLine;
4305 // Do multi line selection tabbing here
4306 if(this.selY < this.y)
4308 firstLine = this.selLine;
4309 lastLine = this.line;
4315 // Selecting going up
4316 firstLine = this.line;
4317 lastLine = this.selLine;
4324 for(line = firstLine; line; line = line.next, y++)
4326 if(line != lastLine || x)
4330 BufferLocation before = { line, y, 0 }, after = { line, y, 0 };
4332 for(c=0; c<line.count && lastC < this.tabSize; c++, lastC++)
4334 if(line.buffer[c] == '\t')
4339 else if(line.buffer[c] != ' ')
4344 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
4347 int len = GetText(null, line, y, 0, line, y, lastC, false, false);
4348 char * string = new char[len];
4349 DelTextAction action { y1 = y, x1 = 0, y2 = y, x2 = lastC, string = string, placeAfter = true };
4350 GetText(string, line, y, 0, line, y, lastC, false, false);
4353 memmove(line.buffer,line.buffer+lastC,line.size-lastC);
4354 if(!line.AdjustBuffer(line.count-lastC))
4360 if(line == lastLine) break;
4365 for(line = firstLine; line; line = line.next, y++)
4369 if(line != lastLine || x)
4373 if(line.count + 1 <= this.maxLineSize)
4375 BufferLocation before = { line, y, 0 }, after = { line, y, 1 };
4377 if(!line.AdjustBuffer(line.count+1))
4380 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4382 AddCharAction action { ch = '\t', x = 0, y = y };
4386 memmove(line.buffer+1,line.buffer,line.size-1);
4388 line.buffer[0] = '\t';
4393 if(line.count + this.tabSize <= this.maxLineSize)
4396 BufferLocation before = { line, y, 0 }, after = { line, y, this.tabSize };
4397 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4399 if(!line.AdjustBuffer(line.count+this.tabSize))
4403 char * string = new char[this.tabSize + 1];
4404 memset(string, ' ', this.tabSize);
4405 string[this.tabSize] = '\0';
4406 Record(AddTextAction { string = string, x1 = 0, y1 = y, x2 = this.tabSize, y2 = y });
4409 memmove(line.buffer+this.tabSize,line.buffer,line.size-(this.tabSize));
4410 line.count+=this.tabSize;
4411 for(c=0; c<this.tabSize; c++)
4412 line.buffer[c] = ' ';
4418 if(line == lastLine) break;
4432 char * addString = new char[this.tabSize + 1];
4434 if(!(style.freeCaret))
4436 this.x = Min(this.x, this.line.count);
4441 for(c=start; ((c == start) || ((c) % this.tabSize)); c++)
4443 addString[len++] = ' ';
4451 SetViewToCursor(true);
4458 if(!(style.hScroll) || hasHorzScroll) break;
4459 if(this.viewX < this.maxLength)
4461 //this.viewX+=this.space.w*this.tabSize;
4463 SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
4470 if(!shift) Deselect();
4479 if(!(style.hScroll) || hasHorzScroll) break;
4482 //this.viewX-=this.space.w*this.tabSize;
4483 //this.viewX = Max(this.viewX,0);
4485 SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
4486 // SetCursorToView();
4493 if(!shift) Deselect();
4507 if(!(style.readOnly))
4513 this.overwrite ^= 1;
4514 UpdateCaretPosition(true);
4519 NotifyOvrToggle(master, this, this.overwrite);
4530 if(style.noSelect) break;
4533 //Save current view position
4534 int tempX = this.viewX;
4535 int tempY = this.viewY;
4539 this.selLine = this.lines.first;
4540 this.y = this.lineCount-1;
4541 this.line = this.lines.last;
4542 this.x = this.line.count;
4545 SetViewToCursor(true);
4547 //Restore previous view position
4548 SetScrollPosition(tempX, tempY * this.space.h);
4554 // TOCHECK: Was there any good reason why we weren't returning false here?
4555 return false; // break;
4560 if(!(style.readOnly))
4568 if(style.readOnly) break;
4574 if(style.readOnly) break;
4579 if(style.readOnly) break;
4583 if(style.readOnly) break;
4587 if(style.readOnly) break;
4588 if(key.shift && key.code == rightBracket)
4590 //Only indent back if you are exactly at one tab.
4592 bool whitespace = true;
4598 EditLine line = this.line;
4600 //Only remove one tab if there is nothing else on the line.
4601 for(i = 0; i < this.line.count; i++)
4603 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
4617 for(pos = line.count - 1; pos >= 0; pos--)
4619 char c = line.buffer[pos];
4623 indentwidth += this.tabSize;
4625 //Counting backwards, so when you find a character, indentation is reset
4635 //Place the } to get an undo:
4638 this.x = this.line.count;
4642 putsize = indentwidth;
4644 putsize = indentwidth / this.tabSize + indentwidth % this.tabSize;
4646 newline = new char[putsize+2];
4647 newline[putsize] = '}';
4648 newline[putsize+1] = '\0';
4652 for(; i < indentwidth / this.tabSize; i++)
4654 for(;i < putsize; i++)
4662 } else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
4674 void OnHScroll(ScrollBarAction action, int position, Key key)
4677 //PrintLn("OnHScroll: ", action, ", pos = ", position, ", key = ", key);
4679 this.viewX = position;
4680 if(action != setRange)
4682 if(!this.mouseMove && style.cursorFollowsView)
4689 void OnVScroll(ScrollBarAction action, int position, Key key)
4691 int oldViewY = this.viewY;
4694 //PrintLn("OnVScroll: ", action, ", pos = ", position, ", key = ", key);
4696 position /= this.space.h;
4698 if(position < this.viewY)
4700 for(; position < this.viewY && this.viewLine.prev; this.viewLine = this.viewLine.prev, this.viewY--);
4701 style.recomputeSyntax = true;
4703 else if(position > this.viewY)
4705 EditLine oldViewLine = viewLine;
4706 for(; position > this.viewY && this.viewLine.next; this.viewLine = this.viewLine.next, this.viewY++);
4707 FigureStartSyntaxStates(oldViewLine, false);
4710 if(action != setRange)
4712 if(!this.mouseMove && style.cursorFollowsView && !SelSize()) SetCursorToViewY();
4715 if(this.x != this.selX || this.y != this.selY)
4719 Scroll(0, (this.viewY - oldViewY) * this.space.h);
4722 int numLines = clientSize.h / this.space.h;
4724 if(Abs(this.viewY - oldViewY) < numLines)
4727 if(this.viewY > oldViewY)
4729 for(y = oldViewY; y <this.viewY; y++)
4730 DirtyLine(y + numLines);
4734 for(y = this.viewY; y <oldViewY; y++)
4735 DirtyLine(y + numLines);
4740 // Fix dirt of stuff before first line...
4741 if(this.viewY - oldViewY > 0)
4743 Box box { 0,0, clientSize.w-1, YOFFSET-1 };
4750 bool _AddCh(unichar ch, int * addedSpacesPtr, int * addedTabsPtr)
4755 ReplaceTextAction replaceAction = null;
4756 AddCharAction addCharAction = null;
4757 int addedSpaces = 0, addedTabs = 0;
4759 if(ch == '\r') return true;
4760 if(style.stuckCaret /*|EES_READONLY)*/ )
4763 if(ch == '\n' && !(style.multiLine) && this.line) return false;
4765 if(!undoBuffer.dontRecord)
4767 if(selX != x || selY != y)
4772 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
4773 oldString = new char[len];
4774 UTF32toUTF8Len(&ch, 1, buffer, 4);
4775 newString = CopyString(buffer);
4776 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
4778 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
4779 if(selY < y || (selY == y && selX < x))
4781 replaceAction.x1 = selX;
4782 replaceAction.y1 = selY;
4783 replaceAction.x2 = x;
4784 replaceAction.y2 = y;
4788 replaceAction.x1 = x;
4789 replaceAction.y1 = y;
4790 replaceAction.x2 = selX;
4791 replaceAction.y2 = selY;
4793 Record(replaceAction);
4794 undoBuffer.dontRecord++;
4798 addCharAction = AddCharAction { y = y, x = x, ch = ch };
4799 Record(addCharAction);
4805 DelSel(&addedSpaces);
4806 if(this.lineCount+1 > this.maxLines)
4809 Emptyline(this.lines.first,0);
4813 if(!(style.vScroll))
4815 // Make sure it fits, but we need a default line is this.font is too big for window
4816 if(this.space.h * (this.lineCount+1) > clientSize.h && this.line)
4819 if((this.y >= 0) && this.y < this.viewY)
4824 line = EditLine { };
4827 lines.Insert(this.line, line);
4828 line.editBox = this;
4834 // If we're displacing the lines from a current line ...
4837 if(this.line.buffer)
4840 if(this.line.count < endX) endX = this.line.count;
4841 length = this.line.count - endX;
4844 if(!line.AdjustBuffer(length))
4848 if(this.line.buffer)
4850 CopyBytes(line.buffer,this.line.buffer+endX, length+1);
4852 if(endX > 4000 || endX < 0)
4855 this.line.count = endX;
4856 this.line.buffer[this.line.count] = '\0';
4857 this.line.AdjustBuffer(this.line.count);
4858 ComputeLength(this.line);
4862 BufferLocation before = { this.line, this.y, this.x }, after;
4868 ComputeLength(this.line);
4871 if(length > 4000 || length < 0)
4874 line.count = length;
4878 line.buffer[line.count] = '\0';
4881 after.line = this.line, after.y = this.y, after.x = this.x;
4883 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4889 int count = UTF32toUTF8Len(&ch, 1, string, 5);
4890 DelSel(&addedSpaces);
4891 result = AddToLine(string, count, false, addedSpaces ? null : &addedSpaces, &addedTabs);
4892 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
4893 if(addedTabsPtr) *addedTabsPtr = addedTabs;
4897 this.selLine = this.line;
4901 replaceAction.x3 = x;
4902 replaceAction.y3 = y;
4903 replaceAction.addedSpaces = addedSpaces;
4904 replaceAction.addedTabs = addedTabs;
4905 undoBuffer.dontRecord--;
4909 addCharAction.addedSpaces = addedSpaces;
4910 addCharAction.addedTabs = addedTabs;
4917 /****************************************************************************
4919 ****************************************************************************/
4922 bool AddCh(unichar ch)
4924 return _AddCh(ch, null, null);
4929 this.modified = true;
4930 NotifyUpdate(master, this);
4931 modifiedDocument = true;
4934 void Delete(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
4937 DelCh(line1, y1, x1, line2, y2, x2, false);
4938 SetViewToCursor(true);
4946 itemEditUndo.disabled = undoBuffer.curAction == 0;
4947 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4948 if(savedAction == undoBuffer.curAction)
4950 modifiedDocument = false;
4952 NotifyUnsetModified(master, this);
4959 itemEditUndo.disabled = undoBuffer.curAction == 0;
4960 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4961 if(savedAction == undoBuffer.curAction)
4963 modifiedDocument = false;
4965 NotifyUnsetModified(master, this);
4969 void Record(UndoAction action)
4971 if(!undoBuffer.dontRecord)
4973 undoBuffer.Record(action);
4974 itemEditUndo.disabled = undoBuffer.curAction == 0;
4975 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4982 this.lines.first, 0,0,
4983 this.lines.last, this.lines.count-1, strlen(((EditLine)this.lines.last).buffer));
4986 void Select(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
4991 this.selLine = line1 ? (EditLine)line1 : this.lines.first;
4992 this.x = line2 ? x2 : ((EditLine)this.lines.last).count;
4993 this.y = line2 ? y2 : (this.lineCount-1);
4994 this.line = line2 ? (EditLine)line2 : this.lines.last;
4997 SetViewToCursor(true);
5001 // TODO: Fix this vs modifiedDocument window property
5002 void SetModified(bool flag)
5006 this.modified = false;
5007 if(flag && !NotifyModified(master, this))
5008 this.modified = true;
5013 bool AddS(char * string)
5020 int addedSpaces = 0, addedTabs = 0;
5021 AddTextAction action = null;
5022 ReplaceTextAction replaceAction = null;
5024 this.pasteOperation = true;
5026 if(style.stuckCaret /*|EES_READONLY)*/ )
5029 if(!undoBuffer.dontRecord)
5031 char * placeString = CopyString(string);
5032 if(!style.multiLine)
5036 for(i = 0; (ch = placeString[i]); i++)
5039 placeString[i] = '\0';
5040 placeString = renew placeString byte[i+1];
5045 if(selX != x || selY != y)
5049 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
5050 oldString = new char[len];
5051 newString = placeString;
5052 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
5054 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5055 if(selY < y || (selY == y && selX < x))
5057 replaceAction.x1 = selX;
5058 replaceAction.y1 = selY;
5059 replaceAction.x2 = x;
5060 replaceAction.y2 = y;
5064 replaceAction.x1 = x;
5065 replaceAction.y1 = y;
5066 replaceAction.x2 = selX;
5067 replaceAction.y2 = selY;
5069 Record(replaceAction);
5073 if(string[0] == '\n')
5074 action = AddTextAction { y1 = y, x1 = Min(this.line.count, x), string = placeString };
5076 action = AddTextAction { y1 = y, x1 = x, string = placeString };
5084 undoBuffer.dontRecord++;
5085 DelSel(&addedSpaces);
5089 for(c = 0; string[c]; c++)
5091 if(string[c] == '\n' || string[c] == '\r')
5093 if(!AddToLine(line,count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5098 if(string[c] == '\n')
5106 // Reset for next line
5110 if(string[c] == '\r' && *line == '\n')
5122 // Why was this here?
5125 // Add the line here
5127 if(!AddToLine(line,count,false, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5133 undoBuffer.dontRecord--;
5138 action.addedSpaces = addedSpaces;
5139 action.addedTabs = addedTabs;
5141 else if(replaceAction)
5143 replaceAction.y3 = y;
5144 replaceAction.x3 = x;
5145 replaceAction.addedSpaces = addedSpaces;
5146 replaceAction.addedTabs = addedTabs;
5149 UpdateCaretPosition(true);
5151 this.pasteOperation = false;
5164 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5165 SetViewToCursor(true);
5171 void PutCh(unichar ch)
5175 if((ch >= 32 /*&& ch <=126*/) || ch == '\n')
5176 //if((ch >= 32) || ch == '\n')
5178 int addedSpaces = 0, addedTabs = 0;
5179 ReplaceTextAction replaceAction = null;
5180 AddCharAction addCharAction = null;
5183 ch = (ch < 128) ? toupper(ch) : ch; // TODO: UNICODE TO UPPER
5185 if(this.x < this.line.count && this.overwrite)
5190 int len = GetText(null, line, y, x, line, y, x+1, false, false);
5191 oldString = new char[len];
5192 UTF32toUTF8Len(&ch, 1, buffer, 4);
5193 newString = CopyString(buffer);
5194 GetText(oldString, line, y, x, line, y, x+1, false, false);
5195 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5196 replaceAction.x1 = x;
5197 replaceAction.y1 = y;
5198 replaceAction.x2 = x+1;
5199 replaceAction.y2 = y;
5200 Record(replaceAction);
5202 undoBuffer.dontRecord++;
5203 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, true);
5204 undoBuffer.dontRecord--;
5206 else if(!undoBuffer.dontRecord)
5208 if(selX != x || selY != y)
5213 int len = GetText(null, line, y, x, selLine, selY, selX, false, false);
5214 oldString = new char[len];
5215 UTF32toUTF8Len(&ch, 1, buffer, 4);
5216 newString = CopyString(buffer);
5217 GetText(oldString, line, y, x, selLine, selY, selX, false, false);
5218 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = true };
5219 if(selY < y || (selY == y && selX < x))
5221 replaceAction.x1 = selX;
5222 replaceAction.y1 = selY;
5223 replaceAction.x2 = x;
5224 replaceAction.y2 = y;
5228 replaceAction.x1 = x;
5229 replaceAction.y1 = y;
5230 replaceAction.x2 = selX;
5231 replaceAction.y2 = selY;
5233 Record(replaceAction);
5237 addCharAction = AddCharAction { y = y, x = x, ch = ch };
5238 Record(addCharAction);
5241 undoBuffer.dontRecord++;
5242 result = _AddCh(ch, &addedSpaces, &addedTabs);
5245 replaceAction.x3 = x;
5246 replaceAction.y3 = y;
5247 replaceAction.addedSpaces = addedSpaces;
5248 replaceAction.addedTabs = addedTabs;
5252 addCharAction.addedSpaces = addedSpaces;
5253 addCharAction.addedTabs = addedTabs;
5255 undoBuffer.dontRecord--;
5259 if(result) SetViewToCursor(true);
5263 void PutS(char * string)
5268 SetViewToCursor(true);
5273 void Printf(char * format, ...)
5277 char temp[MAX_F_STRING];
5279 va_start(args, format);
5280 vsprintf(temp, format, args);
5286 void SetContents(char * format, ...)
5291 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5294 char temp[MAX_F_STRING];
5296 va_start(args, format);
5297 vsprintf(temp, format, args);
5313 x -= 1 + DelCh(line, y, x-1, line, y, x, true);
5316 else if(this.line.prev)
5318 EditLine line = this.line.prev;
5322 DelCh(line, this.y-1, x, this.line, this.y, this.x, true);
5330 this.selLine = this.line;
5335 SetViewToCursor(true);
5340 Emptyline(this.line,this.y);
5341 this.selX = this.x = 0;
5344 this.selLine = this.line;
5346 SetViewToCursor(true);
5356 SetViewToCursor(true);
5364 SetViewToCursor(true);
5368 bool GoToLineNum(int lineNum)
5373 EditLine line = this.lines.first;
5374 for(c = 0; c < lineNum && line; c++, line = line.next);
5384 SetViewToCursor(true);
5391 bool GoToPosition(EditLine line, int y, int x)
5403 for(line = this.lines.first, c = 0; c<y && line; c++, line = line.next);
5417 SetViewToCursor(true);
5424 void SetViewToCursor(bool setCaret)
5434 bool dontScroll = false;
5440 selected = selX != this.x || selY != y;
5447 checkLine = dropLine;
5453 checkLine = this.line;
5458 numLines = clientSize.h / space.h;
5460 // This is broken. The EditBox now doesn't do anything different when adding to it,
5461 // regardless of the previous scrolling position. It should be read and then set again
5462 // if one wishes to preserve it.
5463 /* // Don't scroll view to cursor if we're in a EES_NOCARET box
5464 if(style.noCaret && this.viewY < lineCount - numLines - 1)
5468 // Horizontal Adjustment
5469 if(!dontScroll && checkLine)
5473 dropX = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5476 this.x = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5482 if(x + space.w >= this.viewX + clientSize.w && clientSize.w >= space.w)
5483 viewX = x - clientSize.w+space.w;
5484 if(x < this.viewX + clientSize.w/2 - space.w)
5485 viewX = Max(0, x - clientSize.w/2 + space.w);
5493 // Vertical Adjustment
5494 if(viewY > checkY) viewY = checkY;
5495 if(viewY + numLines <= checkY)
5497 if(clientSize.h >= space.h)
5499 for(line = viewLine; line && (viewY + numLines <= checkY); line = line.next)
5509 for(;dropLine && dropLine.prev && dropY >= numLines;)
5511 dropLine = dropLine.prev;
5515 for(;this.line && this.line.prev && this.y >= numLines;)
5517 this.line = this.line.prev;
5522 SetScrollPosition(viewX, viewY * this.space.h);
5524 UpdateCaretPosition(setCaret);
5530 selLine = this.line;
5540 void CenterOnCursor()
5542 int numLines = clientSize.h / this.space.h;
5543 int y = this.y - numLines / 2;
5545 bool figureSyntax = false;
5546 EditLine oldViewLine = viewLine;
5547 if(y > this.lineCount - numLines) y = this.lineCount-numLines;
5552 for(;y < this.viewY; y++)
5554 this.viewLine = this.viewLine.prev;
5555 style.recomputeSyntax = true;
5557 for(;y > this.viewY; y--)
5559 this.viewLine = this.viewLine.next;
5560 figureSyntax = true;
5563 FigureStartSyntaxStates(oldViewLine, false);
5567 SetScrollPosition(this.viewX, viewY * this.space.h);
5568 UpdateCaretPosition(true);
5572 void SetCursorToView()
5583 numLines = clientSize.h / this.space.h;
5587 for(c=0, line = this.viewLine.next; line && c<numLines && (this.viewY < this.lineCount - numLines); line = line.next, c++);
5588 SetScrollPosition(this.viewX, (this.viewY + c) * this.space.h);
5592 EditLine oldLine = this.line;
5593 bool lastOne = false;
5594 EditLine oldViewLine = this.viewLine;
5595 bool figureSyntax = false;
5597 if(this.y >= this.lineCount-1) return;
5599 for(c=0, line = this.line.next; line && c<numLines; line = line.next, c++)
5601 if(this.viewY + numLines < this.lines.count)
5603 this.viewLine = this.viewLine.next;
5605 figureSyntax = true;
5607 else if(c && !lastOne)
5616 FigureStartSyntaxStates(oldViewLine, false);
5617 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5620 SetViewToCursor(false);
5629 if(this.y == 0) return;
5631 numLines = clientSize.h / this.space.h;
5635 for(c=0, line = this.viewLine.prev; line && c<numLines; line = line.prev, c++);
5636 SetScrollPosition(this.viewX, (this.viewY - c) * this.space.h);
5640 EditLine oldLine = this.line;
5642 for(c=0, line = this.line.prev; line && c<numLines; line = line.prev, c++)
5647 if(this.viewLine.prev)
5649 this.viewLine = this.viewLine.prev;
5651 style.recomputeSyntax = true;
5655 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5658 SetViewToCursor(false);
5665 if(this.viewLine.prev)
5668 // this.viewLine = this.viewLine.prev;
5671 SetScrollPosition(this.viewX, (this.viewY - 1) * this.space.h);
5678 if(this.viewLine.next)
5681 // this.viewLine = this.viewLine.next;
5684 SetScrollPosition(this.viewX, (this.viewY + 1) * this.space.h);
5691 EditLine l1, l2, line;
5692 int x1, x2, nx1, nx2;
5697 if(!this.selLine) return 0;
5698 if(this.selLine == this.line && this.selX == this.x) return 0;
5699 if(this.selY < this.y)
5706 else if(this.selY > this.y)
5713 else if(this.selX < this.x)
5715 l1 = l2 = this.line;
5721 l1 = l2 = this.line;
5725 nx1 = Min(x1,l1.count);
5726 nx2 = Min(x2,l2.count);
5728 // Find Number of Bytes Needed
5730 for(line = l1; line; line = line.next)
5732 if(line == l1) start = nx1; else start = 0;
5733 if(line == l2) end = nx2; else end = line.count;
5735 if(style.freeCaret && line == l2)
5738 count = Max(x2-Max(x1,l1.count),0);
5740 count = Max(x2-l2.count,0);
5744 if(line == l2) break;
5745 // Add Carriage Return / line Feed
5752 void GetSelPos(EditLine * l1, int *y1, int *x1, EditLine * l2, int * y2, int * x2, bool reorder)
5756 if(!reorder || this.selY < this.y)
5758 if(l1) *l1 = this.selLine;
5759 if(y1) *y1 = this.selY;
5760 if(x1) *x1 = this.selX;
5761 if(l2) *l2 = this.line;
5762 if(y2) *y2 = this.y;
5763 if(x2) *x2 = this.x;
5765 else if(this.selY > this.y)
5767 if(l1) *l1 = this.line;
5768 if(y1) *y1 = this.y;
5769 if(x1) *x1 = this.x;
5770 if(l2) *l2 = this.selLine;
5771 if(y2) *y2 = this.selY;
5772 if(x2) *x2 = this.selX;
5774 else if(this.selX < this.x)
5776 if(l1) *l1 = this.line;
5777 if(y1) *y1 = this.selY;
5778 if(x1) *x1 = this.selX;
5779 if(l2) *l2 = this.line;
5780 if(y2) *y2 = this.y;
5781 if(x2) *x2 = this.x;
5785 if(l1) *l1 = this.line;
5786 if(y1) *y1 = this.y;
5787 if(x1) *x1 = this.x;
5788 if(l2) *l2 = this.line;
5789 if(y2) *y2 = this.selY;
5790 if(x2) *x2 = this.selX;
5795 void SetSelPos(EditLine l1, int y1, int x1, EditLine l2, int y2, int x2)
5797 if(this && (this.selY != y1 || this.selX != x1 || this.y != y2 || this.x != x2))
5799 this.selLine = (EditLine)l1;
5802 this.line = (EditLine)l2;
5806 SetViewToCursor(true);
5810 int GetText(char * text, EditLine _l1, int _y1, int _x1, EditLine _l2, int _y2, int _x2, bool addCr, bool addSpaces)
5812 EditLine l1, l2, line;
5813 int x1, x2, nx1, nx2;
5844 nx1 = Min(x1,l1.count);
5845 nx2 = Min(x2,l2.count);
5848 for(line = l1; line; line = line.next)
5850 if(line == l1) start = nx1; else start = 0;
5851 if(line == l2) end = nx2; else end = line.count;
5854 CopyBytes(text, line.buffer + start, end - start);
5857 numChars += end-start;
5859 if(style.freeCaret && line == l2 && addSpaces)
5862 count = Max(x2-Max(x1,l1.count),0);
5864 count = Max(x2-l2.count,0);
5867 FillBytes(text,' ',count);
5873 if(line == l2) break;
5885 // '\0' terminate Terminate
5892 void GetSel(char * text, bool addCr)
5894 GetText(text, line, y, x, selLine, selY, selX, addCr, true);
5900 this.selLine = this.line;
5910 int size = SelSize();
5913 // Try to allocate memory
5914 ClipBoard clipBoard { };
5915 if(clipBoard.Allocate(size+1))
5917 GetSel(clipBoard.memory, true);
5930 ClipBoard clipBoard { };
5931 if(clipBoard.Load())
5932 PutS(clipBoard.memory);
5943 SetViewToCursor(true);
5948 void DeleteSelection()
5953 SetViewToCursor(true);
5959 void Save(File f, bool lf)
5962 savedAction = undoBuffer.curAction;
5964 for(line = this.lines.first; line; line = line.next)
5966 f.Write(line.buffer, line.count,1);
5969 if(lf) f.Putc('\r');
5975 #define BUFFER_SIZE 16384
5978 undoBuffer.dontRecord++;
5981 char buffer[BUFFER_SIZE];
5985 int count = f.Read(buffer, 1, BUFFER_SIZE-1);
5986 buffer[count] = '\0';
5992 undoBuffer.dontRecord--;
5993 undoBuffer.count = 0;
5994 undoBuffer.curAction = 0;
5995 itemEditUndo.disabled = undoBuffer.curAction == 0;
5996 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5999 EditBoxFindResult Find(char * text, bool matchWord, bool matchCase, bool isSearchDown)
6003 bool firstPass = true;
6004 EditBoxFindResult result = found;
6006 if(!this.line) return notFound;
6009 for(line = this.line;;)
6017 line = this.lines.first;
6023 line = this.lines.last;
6024 num = this.lineCount - 1;
6030 string = SearchString(line.buffer, firstPass ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6032 string = RSearchString(line.buffer,text,firstPass ? (Min(this.x,line.count)-1) : line.count,matchCase,matchWord);
6036 Select((void *)line,num,string - line.buffer,(void *)line,num,string - line.buffer + strlen(text));
6039 if(line == this.line && !firstPass) break;
6057 EditBoxFindResult FindInSelection(char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
6061 int searchLen = strlen(text);
6062 for(line = (EditLine)this.line, y = this.y; y <= y2; line = line.next, y++)
6064 char * string = SearchString(line.buffer, (y == this.y) ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6065 if(string && (y < y2 || (string - line.buffer) + searchLen <= x2))
6067 Select((void *)line,y,string - line.buffer,(void *)line,y,string - line.buffer + strlen(text));
6074 bool OnKeyDown(Key key, unichar ch)
6077 //PrintLn("OnKeyDown: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
6079 if(!NotifyKeyDown(master, this, key, ch))
6089 this.mouseMove = false;
6090 OnLeftButtonUp(0,0,0);
6091 SetViewToCursor(true);
6101 public class EditBoxStream : File
6104 BufferLocation start, sel;
6111 EditBox editBox = this.editBox;
6113 editBox.x = start.x;
6114 editBox.y = start.y;
6115 editBox.line = start.line;
6117 editBox.selX = sel.x;
6118 editBox.selY = sel.y;
6119 editBox.selLine = sel.line;
6121 editBox.SetViewToCursor(true);
6122 //editBox.ComputeColumn();
6126 property EditBox editBox
6134 start.line = value.line;
6138 sel.line = value.selLine;
6141 value.GoToHome(true);
6144 get { return editBox; }
6147 uint Read(byte * buffer, uint size, uint count)
6150 EditBox editBox = this.editBox;
6151 EditLine line = editBox.line;
6157 for(;read < count && line; line = (*&line.next))
6159 int numBytes = Min(count - read, (*&line.count) - x);
6162 memcpy(buffer + read, (*&line.buffer) + x, numBytes);
6167 for(;read < count && x < (*&line.count);)
6169 buffer[read++] = (*&line.buffer)[x++];
6176 buffer[read++] = '\n';
6181 if(x == (*&line.count) && (*&line.next))
6190 editBox.line = editBox.selLine = line;
6191 editBox.x = editBox.selX = x;
6192 editBox.y = editBox.selY = y;
6197 bool Seek(int pos, FileSeekMode mode)
6200 EditBox editBox = this.editBox;
6201 EditLine line = editBox.line;
6203 if(mode == FileSeekMode::start)
6205 pos = pos - this.pos;
6215 for(;read < pos && line; line = (*&line.next))
6217 int numBytes = Min(pos - read, (*&line.count) - x);
6220 read += numBytes; x += numBytes;
6223 /*for(;read < pos && x < (*&line.count);)
6251 for(;read < pos && line; line = (*&line.prev))
6253 int numBytes = Min(pos - read, x);
6259 /*for(;read < pos && x > 0;)
6269 x = (*&(*&line.prev).count);
6285 editBox.line = editBox.selLine = line;
6286 editBox.x = editBox.selX = x;
6287 editBox.y = editBox.selY = y;
6292 bool Puts(char * string)
6294 EditBox editBox = this.editBox;
6295 BufferLocation start { editBox.line, editBox.y, editBox.x };
6299 editBox.AddS(string);
6301 pos.line = editBox.line;
6305 this.start.AdjustAdd(start, pos);
6306 sel.AdjustAdd(start, pos);
6311 // NOTE: BYTE, NOT UNICODE CHARACTER!
6314 EditBox editBox = this.editBox;
6315 BufferLocation start = { editBox.line, editBox.y, editBox.x };
6320 utf8Bytes[numBytes++] = ch;
6321 utf8Bytes[numBytes] = 0;
6322 if(UTF8Validate(utf8Bytes))
6324 editBox.AddCh(UTF8_GET_CHAR(utf8Bytes, numBytes));
6326 pos.line = editBox.line;
6329 this.start.AdjustAdd(start, pos);
6330 sel.AdjustAdd(start, pos);
6337 bool Getc(char * ch)
6339 return Read(ch, 1, 1) ? true : false;
6342 void DeleteBytes(uint count)
6344 EditBox editBox = this.editBox;
6347 BufferLocation pos { editBox.line, editBox.y, editBox.x };
6348 BufferLocation end = pos;
6353 for(;c < count && end.line && end.x < end.line.count;)
6358 if(c < count && end.line && end.line.next)
6360 end.line = end.line.next;
6369 start.AdjustDelete(pos, end);
6370 sel.AdjustDelete(pos, end);
6372 editBox.DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true);