1 namespace gui::controls;
12 #define XOFFSET (3 + (/*style.lineNumbers?6 * this.space.w:*/0))
14 #define YOFFSET (style.multiLine ? 1 : ((clientSize.h + 1 - space.h) / 2))
16 #define IS_ALUNDER(ch) (/*(ch) == '_' || */CharMatchCategories((ch), letters|numbers|marks|connector))
18 #define UTF8_IS_FIRST(x) (__extension__({ byte b = x; (!(b) || !((b) & 0x80) || ((b) & 0x40)); }))
19 #define UTF8_NUM_BYTES(x) (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
20 #define UTF8_GET_CHAR(string, numBytes) \
24 byte b = (string)[0]; \
29 if(b & 0x80 && b & 0x40) \
44 for(i = 0; i<numBytes; i++) \
47 ch |= (string)[i] & mask; \
55 bool autoEmpty:1, readOnly:1, multiLine:1, stuckCaret:1, freeCaret:1, select:1, hScroll:1, vScroll:1, smartHome:1;
56 bool noCaret:1, noSelect:1, tabKey:1, useTab:1, tabSel:1, allCaps:1, syntax:1, wrap:1;
59 bool inMultiLineComment:1, inPrep:1, escaped:1;
61 bool recomputeSyntax:1;
62 bool cursorFollowsView:1;
64 // bool lineNumbers:1;
68 void UnregisterClass_EditBox()
71 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
80 extern int __ecereVMethodID_class_OnFree;
83 static class ArrayImpl
97 void ** array = (void **)((ArrayImpl)this).array;
98 if(type.type == normalClass || type.type == noHeadClass)
100 for(c = 0; c<size; c++)
101 type._vTbl[__ecereVMethodID_class_OnFree](type, array[c]);
103 // TODO: Call OnFree for structClass
104 delete ((ArrayImpl)this).array;
113 if(((ArrayImpl)this).array)
117 ((ArrayImpl)this).array = renew0 ((ArrayImpl)this).array byte[type.typeSize * value];
120 ((ArrayImpl)this).array = new0 byte[value * type.typeSize];
129 memcpy(((ArrayImpl)this).array, value, type.typeSize * size);
134 public class UndoAction : struct
137 subclass(UndoAction) type;
138 virtual void Undo(void * data) { type.Undo(this, data); }
139 virtual void Redo(void * data) { type.Redo(this, data); }
141 virtual void Print(void * data) { type.Print(this, data); }
145 if(((Class)type).Destructor)
146 ((void (*)(void *))((Class)type).Destructor)(this);
151 class ArrayUndoActions : OldArray
153 type = class(UndoAction);
157 public class UndoBuffer
159 ArrayUndoActions actions { size = 8 };
174 UndoAction action = actions._[--curAction];
176 /*Print("Undoing: ");
177 action.Print(data);*/
188 if(curAction < count)
190 UndoAction action = actions._[curAction];
193 /*Print("Redoing: ");
194 action.Print(data);*/
202 void Record(UndoAction action)
204 if(!dontRecord && !insideRedo)
206 if(curAction < count)
209 for(c = curAction; c < count; c++)
215 if(count >= actions.size)
216 actions.size += actions.size / 2;
219 /*Print("Recording: ");
220 action.Print(data);*/
222 actions._[count++] = action;
225 if(actions.size > count + count / 2 && count + count / 2 >= 8)
226 actions.size = count + count / 2;
233 static class AddCharAction : UndoAction
237 int addedSpaces, addedTabs;
238 type = class(AddCharAction);
240 void Undo(EditBox editBox)
242 editBox.GoToPosition(null, (ch == '\n') ? (y + 1) : y, (ch == '\n') ? 0 : (x + 1));
244 if(addedTabs || addedSpaces)
245 editBox.DelCh(editBox.line, y, x - (addedSpaces + addedTabs), editBox.line, y, x, false);
246 editBox.UpdateDirty();
249 void Redo(EditBox editBox)
251 editBox.GoToPosition(null, y, x);
253 editBox.UpdateDirty();
256 void Print(EditBox editBox)
258 PrintLn("AddChar: y = ", y, "x = ", x, ", ch = ", ch, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
263 static class AddTextAction : UndoAction
267 int addedSpaces, addedTabs;
268 type = class(AddTextAction);
271 void Print(EditBox editBox)
273 PrintLn("AddText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs);
281 void Undo(EditBox editBox)
286 editBox.GoToPosition(null, y1, x1);
288 l2 = editBox.lines.first;
289 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
291 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
292 if(addedTabs || addedSpaces)
293 editBox.DelCh(editBox.line, y1, x1 - (addedSpaces + addedTabs), editBox.line, y1, x1, false);
295 editBox.SetViewToCursor(true);
299 void Redo(EditBox editBox)
301 editBox.GoToPosition(null, y1, x1);
302 editBox.PutS(string);
306 static class DelTextAction : UndoAction
312 type = class(DelTextAction);
315 void Print(EditBox editBox)
317 PrintLn("DelText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", string = ", string, ", addedSpaces = ", addedSpaces, ", placeAfter = ", placeAfter);
320 void Undo(EditBox editBox)
322 editBox.GoToPosition(null, y1, x1);
323 editBox.PutS(string);
327 editBox.GoToPosition(null, y1, x1);
330 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
331 //editBox.SetViewToCursor(true);
334 editBox.DelCh(editBox.line, y1, x1 - addedSpaces, editBox.line, y1, x1, false);
340 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
341 //editBox.SetViewToCursor(true);
344 editBox.DelCh(editBox.selLine, y1, x1 - addedSpaces, editBox.selLine, y1, x1, false);
348 void Redo(EditBox editBox)
353 editBox.GoToPosition(null, y1, x1);
356 l2 = editBox.lines.first;
357 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
359 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
360 editBox.SetViewToCursor(true);
370 static class ReplaceTextAction : UndoAction
372 int y1, x1, y2, x2, y3, x3;
376 int addedSpaces, addedTabs;
378 type = class(ReplaceTextAction);
381 void Print(EditBox editBox)
383 PrintLn("ReplaceText: y1 = ", y1, "x1 = ", x1, ", y2 = ", y2, ", x2 = ", x2, ", y3 = ", y3, ", x3 = ", x3, ", oldString = ", oldString, ", newString = ", newString, ", addedSpaces = ", addedSpaces, ", addedTabs = ", addedTabs, ", placeAfter = ", placeAfter);
386 void Undo(EditBox editBox)
391 editBox.GoToPosition(null, y1, x1);
393 l3 = editBox.lines.first;
394 for(c = 0; c < y3 && l3; c++, l3 = l3.next);
396 editBox.DelCh(l1, y1, x1, l3, y3, x3, true);
398 editBox.PutS(oldString);
399 if(addedSpaces || addedTabs)
400 editBox.DelCh(l1, y1, x1 - (addedSpaces + addedTabs), l1, y1, x1, false);
402 //editBox.PutS(oldString);
405 editBox.GoToPosition(null, y1, x1);
408 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
414 { int c; editBox.selLine = editBox.lines.first; for(c = 0; c < editBox.selY && editBox.selLine; c++, editBox.selLine = editBox.selLine.next); }
418 void Redo(EditBox editBox)
423 editBox.GoToPosition(null, y1, x1);
425 l2 = editBox.lines.first;
426 for(c = 0; c < y2 && l2; c++, l2 = l2.next);
428 editBox.DelCh(l1, y1, x1, l2, y2, x2, true);
430 editBox.PutS(newString);
440 static class MoveTextAction : UndoAction
442 int fy1, fx1, fy2, fx2;
444 type = class(MoveTextAction);
446 void Undo(EditBox editBox)
451 void Redo(EditBox editBox)
457 public class EditLine : struct
469 // Only works for single line edit for now...
470 EditBox editBox = this.editBox;
474 get { return this ? buffer : null; }
476 property EditLine prev { get { return this ? prev : null; } };
477 property EditLine next { get { return this ? next : null; } };
478 property int count { get { return this ? count : 0; } };
485 // This makes sure the buffer always contains at least count characters
486 // Keeps a count/2 pad for avoiding always reallocating memory.
487 bool AdjustBuffer(int count)
495 newSize = (count + (count >> 1));
497 // Shrink down the buffer
500 buffer = new char[newSize];
501 if(!buffer) return false;
505 CopyBytes(buffer, this.buffer, count);
508 this.buffer = buffer;
512 // Increase the buffer
513 else if(size < count)
515 buffer = new char[newSize];
516 if(!buffer) return false;
520 CopyBytes(buffer, this.buffer, this.count + 1); // size);
523 this.buffer = buffer;
532 public struct BufferLocation
537 void AdjustDelete(BufferLocation start, BufferLocation end)
539 // Location is before, nothing to do
540 if(y < start.y || (y == start.y && x < start.x))
542 // Location is inside deleted bytes, point to the start
543 if((y >= start.y && (y > start.y || x >= start.x)) &&
544 (y >= end.y && (y > end.y || x >= end.x)))
549 // Location is on another line
551 y -= end.y - start.y;
552 // Location is the last touched line
559 //if(start.line == end.line)
560 x -= end.x - start.x;
569 // Assuming no carriage return before first character ???? fixed?
570 void AdjustAdd(BufferLocation start, BufferLocation end)
572 int numLines = end.y - start.y;
579 if(x > start.x || (x == start.x /*&& (numLines ? true : false)*/))
582 for(c = 0, line = start.line; c<numLines; c++)
585 //x += numLines ? end.x : (end.x - start.x);
586 x += end.x - start.x;
593 public enum EditBoxFindResult { notFound, found, wrapped };
595 static char * keyWords1[] =
598 "return","break","continue","default","switch","case","if","else","for","while", "do","long","short",
599 "void", "char","int","float","double","unsigned","static", "extern", "struct", "union", "typedef","enum",
601 "#include", "#define", "#pragma", "#if", "#else", "#elif", "#ifdef", "#ifndef", "#endif", "#undef", "#line",
602 "__attribute__", "__stdcall", "_stdcall",
603 "__declspec", "goto",
604 "inline", "__inline__", "_inline", "__inline", "__typeof","__extension__",
605 "asm", "__asm", "_asm", "volatile", "#cpu", "__stdcall__",
608 "class", "private", "public",
610 "delete", "new", "new0", "renew", "renew0", "define",
613 "dllexport", "dllimport", "stdcall",
614 "subclass", "__on_register_module", "namespace", "using",
615 "typed_object", "any_object", "incref", "register", "watch", "stopwatching", "firewatchers", "watchable", "class_designer",
616 "class_fixed", "class_no_expansion", "isset", "class_default_property", "property_category", "class_data", "class_property",
617 "virtual", "thisclass","unichar", "dbtable", "dbindex", "database_open", "dbfield",
620 "uint", "uint32", "uint16", "uint64", "bool", "byte", "int64",
623 "this", "true", "false", "null", "value",
632 static char * keyWords2[] =
634 "defined", "warning", null
637 static char ** keyWords[] = { keyWords1, keyWords2 };
638 #define NUM_KEYWORD_GROUPS (sizeof(keyWords) / sizeof(char **))
639 static Color colorGroups[] =
644 //static int * keyLen[NUM_KEYWORD_GROUPS];
645 static int keyLen[NUM_KEYWORD_GROUPS][sizeof(keyWords1)];
648 static FileFilter filters[] =
650 { "All files", null },
652 "Text Files (*.txt)",
656 static FileListConfig fileListConfig = { "", "", filters, sizeof(filters), null, 0 };
658 static char searchString[1025], replaceString[1025];
659 static bool matchCase = false, wholeWord = false, searchUp = false;
661 static GoToDialog goToDialog
663 autoCreate = false, isModal = true, text = "Go To"
666 public class EditBox : CommonControl
668 class_property(icon) = "<:ecere>controls/editBox.png";
672 virtual bool Window::NotifyModified(EditBox editBox);
673 virtual void Window::NotifyCaretMove(EditBox editBox, int line, int charPos);
674 virtual void Window::NotifyUpdate(EditBox editBox);
675 virtual bool Window::NotifyDoubleClick(EditBox editBox, EditLine line, Modifiers mods);
676 virtual void Window::NotifyOvrToggle(EditBox editBox, bool overwrite);
677 virtual bool Window::NotifyKeyDown(EditBox editBox, Key key, unichar ch);
679 virtual bool Window::NotifyCharsAdded(EditBox editBox, BufferLocation before, BufferLocation after, bool pasteOperation);
680 virtual bool Window::NotifyCharsDeleted(EditBox editBox, BufferLocation beforeLoc, BufferLocation after, bool pasteOperation);
681 virtual bool Window::NotifyDropped(EditBox editBox, int x, int y);
683 virtual bool Window::NotifyUnsetModified(EditBox editBox);
685 // Why was this commented out?
686 // It is required otherwise updating font property from property sheet doesn't immediately reflect in form designer,
687 // and the scrollArea property isn't compared properly either.
691 if(font) ComputeFont();
692 SetInitSize(initSize);
698 style.vScroll = true;
704 style.hScroll = true;
708 property bool textHorzScroll { property_category "Behavior" set { style.hScroll = value; } get { return style.hScroll; } }; // Should cut the text on set to false
709 property bool textVertScroll { property_category "Behavior" set { style.vScroll = value; } get { return style.vScroll; } };
710 property bool readOnly
712 property_category "Behavior"
715 style.readOnly = value;
716 itemEditCut.disabled = value || !selection;
717 itemEditDelete.disabled = value || !selection;
718 itemEditPaste.disabled = value;
720 get { return style.readOnly; }
722 property bool multiLine { property_category "Behavior" set { style.multiLine = value; } get { return style.multiLine; } };
723 property bool freeCaret { property_category "Behavior" set { style.freeCaret = value; } get { return style.freeCaret; } };
724 property bool tabKey { property_category "Behavior" set { style.tabKey = value; } get { return style.tabKey; } };
725 property int tabSize { property_category "Behavior" set { tabSize = value; } get { return tabSize; } };
726 property bool tabSelection { property_category "Behavior" set { style.tabSel = value; if(value) style.tabKey = true; } get { return style.tabSel; } };
727 property bool smartHome { property_category "Behavior" set { style.smartHome = value; } get { return style.smartHome; } };
728 property bool autoEmpty { property_category "Behavior" set { style.autoEmpty = value; } get { return style.autoEmpty; } };
729 property bool noCaret { property_category "Behavior" set { style.noCaret = value; if(value) { style.readOnly = true; style.stuckCaret = true; } } get { return style.noCaret; } };
730 property int maxLineSize { property_category "Behavior" set { maxLineSize = value; } get { return maxLineSize; } };
731 property int maxNumLines { property_category "Behavior" set { maxLines = value; } get { return maxLines; } };
732 property bool useTab { property_category "Behavior" set { style.useTab = value; itemEditInsertTab.checked = value; } get { return style.useTab; } };
733 property bool syntaxHighlighting { property_category "Appearance" set { style.syntax = value; } get { return style.syntax; } };
734 property bool noSelect { property_category "Behavior" set { style.noSelect = value; } get { return style.noSelect; } };
735 property bool allCaps { property_category "Behavior" set { style.allCaps = value; } get { return style.allCaps; } };
736 property bool wrap { set { style.wrap = value; Update(null); } get { return style.wrap; } };
737 //property bool lineNumbers { set { style.lineNumbers = value; } get { return style.lineNumbers; } };
738 property int numLines { get { return this ? lineCount : 0; } };
739 property int lineNumber { get { return y; } }; // TODO: Change to property of EditLine this.line.number
740 property int column { get { return col; } }; // TODO: Add Set
741 property int charPos { get { return x; } }; // TODO: Add Set
742 property EditLine firstLine { get { return lines.first; } }; // Change these to a List<EditLine>... (this.lines[10].text)
743 property EditLine lastLine { get { return lines.last; } };
744 property EditLine line { get { return this.line; } }; // TODO: Add Set this.line = this.lines[10]
745 property char * contents
747 property_category "Data"
753 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
756 //SetViewToCursor(true);
764 char * buffer = null;
767 /* Can't implement this right now because of memory leak... Need string reference counting...
774 for(line = lines.first; line; line = line.next)
775 len += strlen(line.buffer);
777 buffer = new char[len+1];
779 for(line = lines.first; line; line = line.next)
781 int lineLen = strlen(line.buffer);
782 memcpy(buffer + len, line.buffer, lineLen);
788 buffer = this.line ? this.line.buffer : null;
793 property bool overwrite { get { return overwrite; } };
794 property bool caretFollowsScrolling { get { return style.cursorFollowsView; } set { style.cursorFollowsView = value; } }
796 property char * multiLineContents
800 char * buffer = null;
807 for(line = lines.first; line; line = line.next)
808 len += strlen(line.buffer)+1;
810 buffer = new char[len+1];
812 for(line = lines.first; line; line = line.next)
814 int lineLen = strlen(line.buffer);
815 memcpy(buffer + len, line.buffer, lineLen);
817 if(line.next) buffer[len++] = '\n';
830 return this.line.buffer;
835 void SetLineText(char * text)
839 EditLine_SetText(this.line, text);
844 // selectionStart.line, selectionStart.column (With Set)
845 // selection.line1, selection.line2, selection.column1, selection.column2 (Read only)
859 // Position of Caret (Not necessarily displayed position)
862 // Position of beginning of block (Block ends at (x,y))
864 // line is line at carret, selLine is line at beginning of block
865 EditLine line, selLine, dropLine;
866 // Mouse selection Moving virtual caret
871 // ViewX is x offset in pixels, ViewY is y offset in lines
873 // viewLine is first displayed line
876 // start and end of area to redraw
879 // MaxLine is the longest line
881 // MaxLength is the longest line's length
884 // MouseSelect is true once button is pressed, overwrite is true if mode is on
885 bool mouseSelect, mouseMove, overwrite, wordSelect;
886 // Timer is used for mouse selection scrolling
889 window = this, delay = 0.1;
898 OnMouseMove(mouseX, mouseY, -1);
903 // (mouseX,mouseY) is the position of the mouse in the edit box
908 void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
911 bool rightButtonDown;
914 UndoBuffer undoBuffer { data = this };
920 Menu editMenu { menu, "Edit", e };
923 editMenu, "Cut\tCtrl+X", t, disabled = true;
925 bool NotifySelect(MenuItem item, Modifiers mods)
927 if(!(style.readOnly))
932 MenuItem itemEditCopy
934 editMenu, "Copy\tCtrl+C", c, disabled = true;
936 bool NotifySelect(MenuItem item, Modifiers mods)
942 MenuItem itemEditPaste
944 editMenu, "Paste\tCtrl+V", p;
946 bool NotifySelect(MenuItem item, Modifiers mods)
948 if(!(style.readOnly))
953 MenuItem itemEditDelete
955 editMenu, "Delete\tDel", d, disabled = true;
957 bool NotifySelect(MenuItem item, Modifiers mods)
959 if(!(style.readOnly))
964 MenuDivider { editMenu };
965 MenuItem itemEditSelectAll
967 editMenu, "Select All\tCtrl+A", a;
969 bool NotifySelect(MenuItem item, Modifiers mods)
975 MenuDivider { editMenu };
976 MenuItem itemEditUndo
978 editMenu, "Undo\tCtrl+Z", u;
981 bool NotifySelect(MenuItem item, Modifiers mods)
987 MenuItem itemEditRedo
989 editMenu, "Redo\tCtrl+Y", o;
992 bool NotifySelect(MenuItem item, Modifiers mods)
998 MenuDivider { editMenu };
1001 editMenu, "Find Previous\tShift-F3", e, shiftF3;
1003 bool NotifySelect(MenuItem item, Modifiers mods)
1006 Find(searchString, wholeWord, matchCase, false);
1008 itemEditFind.NotifySelect(this, item, mods);
1014 editMenu, "Find Next\tF3", n, f3;
1016 bool NotifySelect(MenuItem item, Modifiers mods)
1019 Find(searchString, wholeWord, matchCase, true);
1021 itemEditFind.NotifySelect(this, item, mods);
1025 MenuItem itemEditFind
1027 editMenu, "Find...\tCtrl+F", f, ctrlF;
1029 bool NotifySelect(MenuItem item, Modifiers mods)
1033 editBox = this, master = master, isModal = true, searchString = searchString, matchCase = matchCase, wholeWord = wholeWord,
1034 searchUp = searchUp;
1037 // Fix dialog from above shouldn't be visible
1038 // void NotifyDestroyed(FindDialog dialog, DialogResult result)
1039 void NotifyDestroyed(Window window, DialogResult result)
1041 FindDialog dialog = (FindDialog) window;
1042 searchUp = dialog.searchUp;
1043 strcpy(searchString, dialog.searchString);
1044 matchCase = dialog.matchCase;
1045 wholeWord = dialog.wholeWord;
1054 editMenu, "Replace...\tCtrl+R", r, ctrlR;
1056 bool NotifySelect(MenuItem item, Modifiers mods)
1058 ReplaceDialog dialog
1062 searchString = searchString,
1063 replaceString = replaceString,
1064 matchCase = matchCase,
1065 wholeWord = wholeWord,
1069 // void NotifyDestroyed(ReplaceDialog dialog, DialogResult result)
1070 void NotifyDestroyed(Window window, DialogResult result)
1072 ReplaceDialog dialog = (ReplaceDialog)window;
1073 char * replace = dialog.replaceString;
1075 strcpy(replaceString, replace);
1076 strcpy(searchString, dialog.searchString);
1077 matchCase = dialog.matchCase;
1078 wholeWord = dialog.wholeWord;
1085 MenuDivider { editMenu };
1088 editMenu, "Go To...\tCtrl+G", g, ctrlG;
1090 bool NotifySelect(MenuItem item, Modifiers mods)
1092 goToDialog.editBox = this;
1093 goToDialog.master = master;
1094 goToDialog.Create();
1098 MenuDivider { editMenu };
1099 MenuItem itemEditInsertTab
1101 editMenu, "Insert Tabs", i, checkable = true;
1103 bool NotifySelect(MenuItem item, Modifiers mods)
1105 style.useTab = item.checked;
1111 snapVertScroll = true;
1112 snapHorzScroll = true;
1116 static bool syntaxInit = false;
1121 for(g = 0; g<NUM_KEYWORD_GROUPS; g++)
1123 for(c = 0; keyWords[g][c]; c++);
1124 //keyLen[g] = new int[c];
1125 for(c = 0; keyWords[g][c]; c++)
1127 keyLen[g][c] = strlen(keyWords[g][c]);
1132 FontExtent = Display::FontExtent;
1134 lines.offset = (uint)&((EditLine)0).prev;
1136 style = EditBoxBits { hScroll = true };
1138 // cursor = guiApp.GetCursor(IBeam);
1140 // Default Properties
1142 maxLineSize = 1024;*/
1145 maxLineSize = MAXINT;
1150 mouseSelect = this.mouseMove = false;
1153 x = selX = selY = 0;
1156 line = selLine = null;
1162 endY = clientSize.h;
1165 // Default space to 8 in case window is not constructed
1168 undoBuffer.dontRecord++;
1170 undoBuffer.dontRecord--;
1172 viewLine = lines.first;
1173 style.recomputeSyntax = true;
1179 UpdateCaretPosition(true);
1185 lines.Free(EditLine::Free);
1188 void FlushBuffer(Surface surface, EditLine line, int wc, int * renderStart, int * x, int y, int numSpaces, Box box)
1190 int count = wc - *renderStart;
1193 if(y + space.h >= box.top && y <= box.bottom)
1199 //FontExtent(display, font, line.buffer + *renderStart, count, &w, null);
1200 surface.TextFont(font);
1201 surface.TextExtent(line.buffer + *renderStart, count, &w, null);
1202 if(*x + w + XOFFSET > 0)
1203 surface.WriteText(XOFFSET + *x,y, line.buffer + *renderStart, count);
1208 w = numSpaces; // * space.w;
1209 if(*x + w + XOFFSET > 0 && surface.GetTextOpacity())
1210 surface.Area(XOFFSET + *x - 1, y, XOFFSET + *x + w, y + space.h-1);
1211 // WHATS UP WITH THIS... surface.Area(XOFFSET + *x, y, XOFFSET + *x + w, y + space.h-1);
1219 int CheckColors(EditLine line, int wc, bool selection, int selX, int editX, bool *selected,
1220 Color selectionForeground, Color selectionBackground, Color textColor, Color *foreground, Color *background, bool *opacity, bool *overwrite)
1226 if((wc == selX && line == selLine) || (wc == editX && line == this.line))
1230 *foreground = (*selected) ? selectionForeground : textColor;
1231 *background = selectionBackground;
1232 *opacity = *selected;
1239 if(this.overwrite && active)
1241 if((style.stuckCaret && wc == line.count && !line.next) ||
1242 (!mouseMove && line == this.line && wc == editX))
1251 void FigureStartSyntaxStates(EditLine firstLine, bool reset)
1255 bool inMultiLineComment = reset ? false : style.inMultiLineComment;
1256 bool inString = false;
1257 bool inQuotes = false;
1258 bool inPrep = reset ? false : style.inPrep;
1259 bool inSingleLineComment = false;
1260 bool escaped = reset ? false : style.escaped;
1261 bool continuedSingleLineComment = false;
1263 EditLine line = reset ? lines.first : firstLine;
1264 // int maxBackUp = 1000, c;
1265 // for(c = 0, line = viewLine; c < maxBackUp && line && line.prev; line = line.prev);
1266 for(; line != viewLine; line = line.next)
1268 char * text = line.buffer;
1271 bool lastWasStar = false;
1272 bool firstWord = true;
1273 if(!escaped) inPrep = false;
1274 inSingleLineComment = continuedSingleLineComment;
1280 for(c = 0; (ch = text[c]); c++)
1282 bool wasEscaped = escaped;
1283 bool backLastWasStar = lastWasStar;
1285 lastWasStar = false;
1288 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1290 if(text[c+1] == '/')
1292 inSingleLineComment = true;
1294 else if(text[c+1] == '*')
1296 inMultiLineComment = true;
1299 else if(backLastWasStar)
1300 inMultiLineComment = false;
1304 if(!c || text[c-1] != '/') lastWasStar = true;
1306 else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && ch == '\"')
1308 if(inString && !wasEscaped)
1317 else if(!inSingleLineComment && !inMultiLineComment && !inString && ch == '\'')
1319 if(inQuotes && !wasEscaped)
1333 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && ch == '#')
1345 continuedSingleLineComment = false;
1346 if(line.count && line.text[line.count - 1] == '\\')
1347 continuedSingleLineComment = true;
1349 style.inMultiLineComment = inMultiLineComment;
1350 style.inPrep = inPrep;
1351 style.escaped = escaped;
1355 /*void OnDrawOverChildren(Surface surface)
1357 if(style.lineNumbers)
1359 int currentLineNumber = this.viewY + 1;
1362 for(i = 0; i * space.h < box.height; i++)
1364 // ********* LINE NUMBERING *********
1365 surface.SetForeground(Color{60, 60, 60});
1366 //Highlight current line
1367 if(this.caretY / space.h == currentLineNumber - 1)
1368 surface.SetBackground(Color{220, 220, 220});
1370 surface.SetBackground(Color{230, 230, 230});
1371 surface.textOpacity = true;
1373 sprintf(lineText,"%5u ", currentLineNumber % 100000);
1374 if(currentLineNumber > this.lineCount)
1375 surface.WriteText(0,i*space.h+1," ",6);
1377 surface.WriteText(0,i*space.h+1,lineText,6);
1379 currentLineNumber++;
1384 void OnRedraw(Surface surface)
1388 bool selected = false, selection = true;
1390 Color selectionBackground = SELECTION_COLOR, selectionForeground = white;
1391 Color defaultTextColor = property::foreground;
1394 int maxW = clientSize.w;
1396 Color foreground, background;
1399 // Overwrite Caret Stuff
1400 bool overWrite = false;
1401 int overWriteX, overWriteY;
1404 // ****** SYNTAX STATES ******
1405 bool inMultiLineComment = style.inMultiLineComment;
1406 bool inString = false;
1407 bool inQuotes = false;
1408 bool inPrep = style.inPrep;
1409 bool inSingleLineComment = false;
1410 bool escaped = style.escaped;
1411 bool continuedSingleLineComment = false;
1412 // ****** ************* ******
1415 defaultTextColor = Color { 85, 85, 85 };
1416 textColor = defaultTextColor;
1419 Abs(selectionBackground.r - property::background.r) +
1420 Abs(selectionBackground.g - property::background.g) +
1421 Abs(selectionBackground.b - property::background.b) < 92)
1423 selectionBackground = activeBorder;
1424 selectionForeground = SELECTION_COLOR;
1427 surface.TextFont(this.font);
1429 surface.SetBackground(GDefaultPalette()[RND_Get(1, 15)]);
1430 surface.Area(0,0,MAXINT,MAXINT);
1435 surface.SetBackground(activeBorder);
1436 surface.Area(0,0,clientSize.w, clientSize.h);
1439 if(this.selX == this.x && this.selY == this.y)
1449 editX = Min(this.x,this.line.count);
1450 selX = Min(this.selX,this.selLine.count);
1453 selected = (this.selY < this.viewY) ^ (this.y < this.viewY);
1455 foreground = selected ? selectionForeground : textColor;
1456 background = selectionBackground;
1462 surface.SetForeground(foreground);
1463 surface.SetBackground(background);
1464 surface.TextOpacity(opacity);
1466 surface.GetBox(box);
1468 for(line = this.viewLine; line; line = line.next)
1470 int x = -this.viewX;
1474 Color newTextColor = textColor = defaultTextColor;
1475 bool lineComplete = false;
1478 // ****** SYNTAX HIGHLIGHTING ******
1479 bool lastWasStar = false;
1480 bool firstWord = true;
1481 if(!escaped) inPrep = false;
1482 inSingleLineComment = continuedSingleLineComment;
1486 // *********************************
1488 /* === DEBUGGING TOOL FOR MAXLINE ===
1490 if(line == this.maxLine)
1492 surface.SetBackground(GREEN|0xFF000000);
1493 surface.TextOpacity(true);
1497 surface.TextOpacity(selected ? true : false);
1498 surface.SetBackground(selected ? SELECTION_COLOR|0xFF000000 : BLACK|0xFF000000);
1502 if(line == this.selLine && line == this.line)
1504 end = Max(line.count, this.x);
1505 end = Max(end, this.selX);
1507 else if(line == this.selLine)
1508 end = Max(line.count, this.selX);
1509 else if(line == this.line)
1510 end = Max(line.count, this.x);
1518 bool spacing = true;
1519 bool cantHaveWords = false;
1526 lineComplete = false;
1529 textColor = newTextColor;
1531 surface.SetForeground(textColor);
1534 for(; c<end && !cantHaveWords;)
1538 bufferLen += wordLen;
1542 /*if(line.count > 4000)
1549 for(; c<line.count; c++)
1551 unichar ch = line.buffer[c];
1552 unichar bf = (wordLen == 1) ? line.buffer[c-1] : 0;
1553 //if(ch == ' ' || ch == '\t' || (wordLen && (ch == '(' || ch == ')' || ch == ';' || ch == ':')) || (wordLen == 1 && line.buffer[c-1] == '('))
1554 if(CharMatchCategories(ch, separators) || /*ch == ' ' ||*/ ch == '\t' ||
1555 (wordLen && !CharMatchCategories(ch, numbers|letters|marks|connector) && ch != '#' /*&& ch != '_'*/) ||
1556 (bf && !CharMatchCategories(bf, numbers|letters|separators|marks|connector) && bf != '#' && bf != '\t' /*&& bf != '_' && bf != ' '*/))
1564 for(; c<line.count; c++)
1566 unichar ch = line.buffer[c];
1567 if(ch == '\t' || ch == ' ')
1569 cantHaveWords = true;
1573 if(ch != ' ' && ch != '\t')
1578 if(c == line.count && c < end)
1587 cantHaveWords = true;
1592 lastWasStar = false;
1598 bool backEscaped = escaped;
1599 bool backLastWasStar = lastWasStar;
1600 bool backInMultiLineComment = inMultiLineComment;
1601 bool backInString = inString;
1602 bool backInQuotes = inQuotes;
1603 bool backInPrep = inPrep;
1604 bool backInSingleLineComment = inSingleLineComment;
1606 char * word = line.buffer + c - wordLen;
1608 bool wasEscaped = escaped;
1610 lastWasStar = false;
1612 // Determine Syntax Highlighting
1613 newTextColor = defaultTextColor;
1616 if(inSingleLineComment || inMultiLineComment)
1618 newTextColor = dimGray;
1622 newTextColor = crimson;
1626 newTextColor = crimson;
1630 newTextColor = green;
1632 if(wordLen == 1 && word[0] == '/')
1634 if(!inSingleLineComment && !inMultiLineComment && !inQuotes && !inString)
1638 inSingleLineComment = true;
1639 newTextColor = dimGray;
1641 else if(word[1] == '*')
1643 inMultiLineComment = true;
1644 newTextColor = dimGray;
1647 else if(backLastWasStar)
1648 inMultiLineComment = false;
1650 else if(wordLen == 1 && word[0] == '*')
1652 if(!c || word[-1] != '/')
1655 else if(!inSingleLineComment && !inMultiLineComment && !inQuotes && wordLen == 1 && word[0] == '\"')
1657 if(inString && !wasEscaped)
1664 newTextColor = crimson;
1667 else if(!inSingleLineComment && !inMultiLineComment && !inString && wordLen == 1 && word[0] == '\'')
1669 if(inQuotes && !wasEscaped)
1674 newTextColor = crimson;
1677 else if(wordLen == 1 && word[0] == '\\')
1682 else if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment &&
1683 ( ( isdigit(word[0]) /*&& (!c || word[-1] == ' ' || word[-1] == '\t')*/ ) || (word[0] == '.' && isdigit(word[1]))))
1685 newTextColor = teal;
1689 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment && word[0] == '#')
1694 newTextColor = green;
1697 if(!inQuotes && !inString && !inMultiLineComment && !inSingleLineComment)
1699 for(g = 0; g < ((inPrep && word[0] != '#') ? 2 : 1); g++)
1701 char ** keys = keyWords[g];
1702 int * len = keyLen[g];
1703 for(ccc = 0; keys[ccc]; ccc++)
1705 if(len[ccc] == wordLen && !strncmp(keys[ccc], word, wordLen))
1707 newTextColor = colorGroups[g];
1716 // If highlighting for this word is different, break
1717 if(newTextColor != textColor)
1721 // Better solution than going back?
1724 // Reset syntax flags
1725 escaped = backEscaped;
1726 lastWasStar = backLastWasStar;
1727 inMultiLineComment = backInMultiLineComment;
1728 inString = backInString;
1729 inQuotes = backInQuotes;
1730 inPrep = backInPrep;
1731 inSingleLineComment = backInSingleLineComment;
1736 textColor = newTextColor;
1738 surface.SetForeground(textColor);
1744 // If we're not breaking, this can't be rendered as spacing anymore
1747 // Do word wrapping here
1753 FontExtent(display, font, line.buffer + start, bufferLen + wordLen, &tw, null);
1758 w += numSpaces * space.w;
1760 if(x + viewX + w > maxW)
1763 lineComplete = true;
1768 bufferLen += wordLen;
1773 int renderStart = start;
1778 // Render checking if we need to split because of selection or to find where to draw insert caret
1779 for(wc = start; wc < start + bufferLen; wc++)
1781 flush = CheckColors(line, wc, selection, selX, editX, &selected, selectionForeground,
1782 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite);
1783 if(overWrite == true)
1785 overWriteCh = (wc < line.count) ? line.buffer[wc] : ' ';
1786 if(overWriteCh == '\t') overWriteCh = ' ';
1791 FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1792 if(overWrite == true)
1800 surface.TextOpacity(opacity);
1801 surface.SetBackground(background);
1802 surface.SetForeground(foreground);
1809 if(wc < line.count && line.buffer[wc] == '\t')
1811 numSpaces += (tabSize * space.w) - ((x + numSpaces + viewX) % (tabSize * space.w));
1815 numSpaces += space.w;
1819 FlushBuffer(surface, line, wc, &renderStart, &x, y, numSpaces, box);
1824 if(CheckColors(line, c, selection, selX, editX, &selected, selectionForeground,
1825 selectionBackground, textColor, &foreground, &background, &opacity, &overWrite))
1827 if(overWrite == true)
1834 surface.TextOpacity(opacity);
1835 surface.SetBackground(background);
1836 surface.SetForeground(foreground);
1839 if(style.freeCaret && selected)
1841 surface.SetBackground(selectionBackground);
1842 surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1843 // TEST: surface.Area(x + XOFFSET,y,clientSize.w-1,y+this.space.h-1);
1848 if(style.freeCaret && selected)
1850 surface.SetBackground(selectionBackground);
1851 surface.Area(x + XOFFSET - 1,y,clientSize.w-1,y+this.space.h-1);
1855 continuedSingleLineComment = false;
1856 if(line.count && line.text[line.count - 1] == '\\')
1857 continuedSingleLineComment = true;
1860 if(y > box.bottom) // >=clientSize.h)
1866 surface.TextOpacity(true);
1867 surface.SetForeground(black);
1868 surface.SetBackground(Color {255,255,85});
1869 surface.WriteText(XOFFSET + overWriteX,overWriteY, &overWriteCh, 1);
1873 void FixScrollArea()
1875 if(style.hScroll || style.vScroll)
1877 int width = maxLength + XOFFSET;
1878 int height = lineCount * space.h;
1879 if(style.freeCaret && line)
1884 width = Max(line.length + (x - line.count) * space.w, maxLength);
1888 if(selX > selLine.count)
1889 width = Max(selLine.length + (selX - selLine.count) * space.w, maxLength);
1894 SetScrollLineStep(8, space.h);
1895 SetScrollArea(width, height, true);
1899 void ComputeLength(EditLine line)
1906 for(c = 0; c < line.count; )
1915 for(len = 0; c < line.count; c += numBytes)
1917 ch = line.buffer[c];
1918 numBytes = UTF8_NUM_BYTES(ch);
1920 if(ch == ' ' || ch == '\t')
1927 if(!len && ch == ' ')
1932 else if(!len && ch == '\t')
1934 w = (tabSize * space.w) - (x % (tabSize * space.w));
1938 FontExtent(display, font, line.buffer + start, len, &w, null);
1949 if(line.length > this.maxLength)
1951 this.maxLine = line;
1952 this.maxLength = line.length;
1961 this.maxLine = null;
1963 for(line = lines.first; line; line = line.next)
1965 if(line.length > this.maxLength)
1967 this.maxLength = line.length;
1968 this.maxLine = line;
1975 if(this.selY != this.y)
1977 else if(this.selX != this.x) // commented out to erase caret: if(this.selX != this.x)
1981 void ComputeColumn()
1983 // Adjust new edit X position according to tabs
1984 int c, position = 0;
1987 for(c = 0; c<this.line.count && c<this.x; c+= nb)
1989 ch = UTF8_GET_CHAR(this.line.buffer + c, nb);
1990 // TODO: MIGHT WANT TO RETHINK WHAT COLUMN SHOULD BE REGARDING TABS
1992 position += this.tabSize - (position % this.tabSize);
1996 position += this.x - c;
1997 this.col = position;
2000 int DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter)
2002 return _DelCh(l1, y1, c1, l2, y2, c2, placeAfter, null);
2005 bool HasCommentOrEscape(EditLine line)
2007 bool hadComment = strstr(line.buffer, "/*") || strstr(line.buffer, "*/");
2012 for(c = line.count-1; c >= 0; c--)
2014 char ch = line.buffer[c];
2020 else //if(ch != ' ' && ch != '\t')
2027 int _DelCh(EditLine l1, int y1, int c1, EditLine l2, int y2, int c2, bool placeAfter, int * addedSpacesPtr)
2029 EditLine line = l1, next;
2031 int oldCount1, oldCount2;
2032 int y, firstViewY, firstY, firstDropY, firstSelY;
2035 DelTextAction action = null;
2036 bool hadComment = false;
2040 hadComment = HasCommentOrEscape(line);
2042 if(y2 > y1 || c2 > c1)
2044 if(start < l1.count)
2046 while(!UTF8_IS_FIRST(l1.buffer[start]) && start)
2050 oldCount1 = l1.count;
2052 while(c1 < oldCount1)
2054 byte ch = buffer[c1];
2055 if(UTF8_IS_FIRST(ch)) break;
2059 oldCount2 = l2.count;
2061 while(c2 < oldCount2)
2063 byte ch = buffer[c2];
2064 if(UTF8_IS_FIRST(ch)) break;
2069 if(!undoBuffer.dontRecord && (y2 > y1 || c2 > c1))
2074 len = GetText(null, l1, y1, start, l2, y2, c2, false, false);
2075 string = new char[len];
2076 action = DelTextAction { y1 = y1, x1 = start, y2 = y2, x2 = c2, string = string, placeAfter = placeAfter };
2077 GetText(string, l1, y1, start, l2, y2, c2, false, false);
2081 //oldCount1 = l1.count;
2082 //oldCount2 = l2.count;
2085 BufferLocation before = { l1,y1,c1}, after = { l2,y2,c2 };
2086 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
2089 if(c2 > oldCount2) c2 = oldCount2;
2090 if(!(style.freeCaret))
2091 if(c1 > oldCount1) c1 = oldCount1;
2092 newLineCount = c1 + l2.count-c2;
2098 buffer = new char[line.size ? line.size : 1];
2100 buffer = new char[line.size];
2102 CopyBytes(buffer,l2.buffer,oldCount1 + 1/*line.count + 1*//*line.size*/);
2107 if(!line.AdjustBuffer(newLineCount))
2111 /*if(newLineCount > 4000 || newLineCount < 0)
2112 printf("Warning");*/
2114 line.count = newLineCount;
2116 memmove(l1.buffer+c1, buffer+c2,line.count-c1);
2121 action.addedSpaces = c1-oldCount1;
2123 if(action.addedSpaces > action.x1)
2129 if(addedSpacesPtr) *addedSpacesPtr = c1-oldCount1;
2130 FillBytes(l1.buffer+oldCount1,' ',c1-oldCount1);
2132 line.buffer[line.count] = '\0';
2139 this.dropX -= c2-c1;
2140 this.dropX = Max(this.dropX,0);
2142 this.x = Max(this.x,0);
2149 firstViewY = this.viewY;
2151 firstDropY = this.dropY;
2152 firstSelY = this.selY;
2153 for(line = l1;line;line = next, y++)
2161 if(line == this.viewLine)
2163 if(this.viewLine.next)
2165 this.viewLine = this.viewLine.next;
2167 style.recomputeSyntax = true;
2171 this.viewLine = this.viewLine.prev;
2173 style.recomputeSyntax = true;
2176 else if(y < firstViewY)
2178 if(line == this.line)
2182 this.line = this.line.next;
2183 this.x = this.line.count;
2188 this.line = this.line.prev;
2189 this.x = this.line.count;
2196 if(line == this.dropLine)
2198 if(this.dropLine.next)
2200 this.dropLine = this.dropLine.next;
2201 this.dropX = this.dropLine.count;
2205 this.dropLine = this.dropLine.prev;
2206 this.dropX = this.dropLine.count;
2210 else if(y < firstDropY)
2212 if(line == this.selLine)
2214 if(this.selLine.next)
2216 this.selLine = this.selLine.next;
2217 this.selX = this.selLine.count;
2221 this.selLine = this.selLine.prev;
2222 this.selX = this.selLine.count;
2226 else if(y < firstSelY)
2230 if(line == l2) break;
2234 if(style.syntax && (hadComment || HasCommentOrEscape(this.line)))
2237 style.recomputeSyntax = true;
2242 bool DelSel(int * addedSpacesPtr)
2244 if(this.line != this.selLine || this.x != this.selX)
2246 if(this.selY < this.y)
2248 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2251 this.line = this.selLine;
2253 else if(this.selY > this.y)
2255 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2258 this.selLine = this.line;
2260 else if(this.selX < this.x)
2262 _DelCh(this.selLine, this.selY, this.selX, this.line, this.y, this.x, true, addedSpacesPtr);
2265 this.line = this.selLine;
2269 _DelCh(this.line, this.y, this.x, this.selLine, this.selY, this.selX, false, addedSpacesPtr);
2272 this.selLine = this.line;
2280 bool AddToLine(char * stringLine, int count, bool LFComing, int * addedSpacesPtr, int * addedTabsPtr)
2282 bool hadComment = false;
2283 // Add the line here
2284 EditLine line = this.line;
2286 // The purpose of this is solely to lock a max number of characters if no HSCROLLING is present
2287 if(!style.hScroll && created)
2289 int endX = (style.freeCaret) ? this.x : Min(this.x, line.count);
2294 // Lock if no place to display.
2297 else if(endX > this.x)
2298 max = Max(this.x, line.count);
2302 for(x = 0, c = 0; c < max+count; )
2307 if(c < Min(this.x, line.count))
2308 string = line.buffer + c;
2311 else if(c < endX + count)
2312 string = stringLine + c - endX;
2314 string = line.buffer + c - endX - count;
2318 w = (tabSize * space.w) - (x % (tabSize * space.w));
2322 numBytes = UTF8_NUM_BYTES(*string);
2323 FontExtent(display, this.font, string, numBytes, &w, null);
2327 if(x >= clientSize.w)
2332 c += numBytes; // - 1;
2338 int addedSpaces = 0;
2341 // Add blank spaces if EES_FREECARET
2342 if(this.x > line.count)
2352 for(c = 0; c<line.count; c++)
2354 if(this.line.buffer[c] == '\t')
2355 position += this.tabSize - (position % this.tabSize);
2359 wantedPosition = position + (this.x - line.count);
2361 // A tab is too much...
2362 if(position + (this.tabSize - (position % this.tabSize)) > wantedPosition)
2363 addedSpaces = wantedPosition - position;
2368 position += this.tabSize - (position % this.tabSize);
2369 // Add as many tabs as needed
2370 addedTabs += (wantedPosition - position) / this.tabSize;
2371 position += (addedTabs-1) * this.tabSize;
2372 // Finish off with spaces
2373 addedSpaces = wantedPosition - position;
2377 addedSpaces = this.x - line.count;
2380 this.x = Min(this.x, line.count);
2382 if(line.count + count + addedSpaces + addedTabs > this.maxLineSize)
2388 hadComment = HasCommentOrEscape(line);
2390 // Adjust the size of the line
2391 if(!line.AdjustBuffer(line.count+count+addedTabs+addedSpaces))
2395 BufferLocation before = { this.line, this.y, this.x }, after = { this.line, this.y, this.x };
2398 memmove(line.buffer+this.x+count, line.buffer+this.x,line.count-this.x);
2399 CopyBytes(line.buffer + this.x + addedTabs + addedSpaces, stringLine, count);
2402 *addedTabsPtr = addedTabs;
2403 FillBytes(line.buffer+line.count,'\t',addedTabs);
2405 if(addedTabs > 4000 || addedTabs < 0)
2408 line.count += addedTabs;
2414 FillBytes(line.buffer+line.count,' ',addedSpaces);
2416 if(addedSpaces > 4000 || addedSpaces < 0)
2419 line.count += addedSpaces;
2420 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
2422 else if(addedSpacesPtr)
2423 *addedSpacesPtr = 0;
2425 if(count > 4000 || count < 0)
2428 line.count += count;
2429 this.x += count + addedTabs + addedSpaces;
2432 this.selLine = this.line;
2434 line.buffer[line.count] = '\0';
2436 ComputeLength(line);
2441 hasComment = HasCommentOrEscape(line);
2442 if(!undoBuffer.insideRedo)
2444 int backDontRecord = undoBuffer.dontRecord;
2445 undoBuffer.dontRecord = 0;
2446 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
2447 undoBuffer.dontRecord = backDontRecord;
2449 if(style.syntax && (hadComment || hasComment || line != this.line))
2451 style.recomputeSyntax = true;
2459 void Emptyline(EditLine line, int y)
2462 DelCh(line, y, 0, line.next, y+1, 0, true);
2464 DelCh(line, y, 0, line, y, line.count, true);
2467 void GoToEnd(bool deselect)
2471 this.line = this.lines.last;
2472 if(this.y != this.lineCount-1)
2474 else if (this.x != this.line.count)
2475 DirtyLine(this.lineCount-1);
2476 this.y = this.lineCount-1;
2477 this.x = this.line.count;
2484 void GoToHome(bool deselect)
2488 this.line = this.lines.first;
2491 else if (this.x !=0)
2492 DirtyLine(this.lineCount-1);
2501 // Returns true if it needs scrolling
2502 bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
2508 bool needHScroll = false;
2515 line = this.viewLine ? (void *)this.viewLine.prev : null;
2520 line = (void *)this.lines.first;
2525 py = Min(py, clientSize.h);
2527 py = Min(py, this.lineCount);
2529 for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
2535 if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
2538 px = Min(px,clientSize.w+this.space.w);
2542 *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
2545 if(tline) *tline = line;
2548 // Prevent divide by 0 from non valid this.font
2550 return (y < this.viewY) || needHScroll;
2552 return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
2556 // Minimal Update Management Functions
2560 this.endY = clientSize.h-1;
2561 // ErrorLog("DirtyAll\n");
2564 void DirtyEnd(int y)
2566 if((y - this.viewY)*this.space.h < this.startY)
2567 this.startY = (y - this.viewY)*this.space.h+ YOFFSET;
2568 this.endY = clientSize.h-1;
2569 //ErrorLog("DirtyEnd %d\n", y);
2572 void DirtyLine(int y)
2576 if((y - this.viewY)*this.space.h < this.startY)
2577 this.startY = (y - this.viewY)*this.space.h + YOFFSET;
2578 if((y - this.viewY+1)*this.space.h > this.endY)
2579 this.endY = (y - this.viewY+1)*this.space.h-1 + YOFFSET;
2581 //ErrorLog("DirtyLine %d\n", y);
2588 if(style.recomputeSyntax)
2590 FigureStartSyntaxStates(lines.first, true);
2591 style.recomputeSyntax = false;
2594 if(this.startY > this.endY) return;
2595 if(this.startY <= 0 && this.endY >= clientSize.h-1)
2601 box.right = clientSize.w-1;
2602 box.top = this.startY;
2603 box.bottom = this.endY;
2606 this.startY = clientSize.h;
2610 bool IsMouseOnSelection()
2612 bool mouseOnSelection = false;
2615 int minY = Min(this.selY, this.y);
2616 int maxY = Max(this.selY, this.y);
2617 int minX = Min(this.selX, this.x);
2618 int maxX = Max(this.selX, this.x);
2620 FindMouse(this.mouseX - this.space.w / 2, this.mouseY, &x, &y, null, false);
2622 if(maxX != minX || maxY != minY)
2624 if(y > minY && y < maxY)
2625 mouseOnSelection = true;
2626 else if(y == minY && y == maxY)
2627 mouseOnSelection = (x < maxX && x >= minX);
2631 mouseOnSelection = (x >= this.selX);
2632 else if(y == this.y)
2633 mouseOnSelection = (x >= this.x);
2638 mouseOnSelection = (x < this.selX);
2639 else if(y == this.y)
2640 mouseOnSelection = (x < this.x);
2643 return mouseOnSelection;
2646 void UpdateCaretPosition(bool setCaret)
2650 if(mouseMove || (!overwrite && !style.noCaret))
2652 int max = this.mouseMove ? this.dropX : this.x;
2653 int y = this.mouseMove ? this.dropY : this.y;
2654 EditLine line = this.mouseMove ? this.dropLine : this.line;
2656 if(!(style.freeCaret))
2657 max = Min(max, line.count);
2659 if(FontExtent && display)
2661 for(c = 0; c < max; )
2670 for(len = 0; c < Min(max, line.count); c += numBytes)
2672 ch = line.buffer[c];
2673 numBytes = UTF8_NUM_BYTES(ch);
2675 if(ch == ' ' || ch == '\t')
2682 if(!len && ch == ' ')
2687 else if(!len && ch == '\t')
2689 w = (tabSize * space.w) - (x % (tabSize * space.w));
2693 FontExtent(display, this.font, line.buffer + start, len, &w, null);
2705 caretY = y * this.space.h;
2706 SetCaret(x + XOFFSET-2, y * space.h + YOFFSET, space.h);
2711 NotifyCaretMove(master, this, y + 1, x + 1);
2717 void SelectionEnables()
2719 if((x != selX || y != selY) && !selection)
2723 itemEditCut.disabled = false;
2724 itemEditDelete.disabled = false;
2726 itemEditCopy.disabled = false;
2728 this.selection = true;
2730 else if((x == selX && y == selY) && selection)
2732 itemEditCut.disabled = true;
2733 itemEditCopy.disabled = true;
2734 itemEditDelete.disabled = true;
2736 this.selection = false;
2740 void SetSelectCursor()
2742 if(!inactive || !style.noSelect)
2745 cursor = guiApp.GetCursor(arrow);
2746 else if(this.mouseSelect)
2747 cursor = guiApp.GetCursor(iBeam);
2750 if(IsMouseOnSelection())
2751 cursor = guiApp.GetCursor(arrow);
2753 cursor = guiApp.GetCursor(iBeam);
2758 int AdjustXPosition(EditLine line, int position, bool half, int * px, int max, int sc)
2761 int x = px ? *px : 0;
2768 if(c < Min(max, line.count))
2772 for(len = 0; c < Min(max, line.count); c += numBytes)
2774 ch = line.buffer[c];
2775 numBytes = UTF8_NUM_BYTES(ch);
2777 if(ch == ' ' || ch == '\t')
2784 if(!len && ch == ' ')
2789 else if(!len && ch == '\t')
2791 w = (tabSize * space.w) - (x % (tabSize * space.w));
2795 FontExtent(display, font, line.buffer + start, len, &w, null);
2799 if(style.freeCaret && c < max)
2808 if(x + (((half && len == 1) ? (w / 2) : w)) >= position)
2813 int a = start + len;
2816 while(a > 0 && !UTF8_IS_FIRST(line.buffer[--a]));
2820 FontExtent(display, font, line.buffer + start, a - start, &w, null);
2823 if(position > x + (half ? ((w + lastW) / 2) : lastW)) break;
2827 return Min(this.maxLineSize - 1, start + len);
2833 void SetCursorToViewX()
2835 bool selecting = this.x != selX || y != selY;
2837 // Horizontal Adjustment
2839 int c = AdjustXPosition(line, viewX, false, &x, MAXINT, 0);
2844 c = AdjustXPosition(line, viewX + clientSize.w - 1, false, &x, MAXINT, c);
2856 UpdateCaretPosition(false);
2862 void SetCursorToViewY()
2865 EditLine oldLine = this.line;
2867 bool selecting = this.x != this.selX || this.y != this.selY;
2869 numLines = clientSize.h / this.space.h;
2871 // Vertical Adjustment
2872 if(this.viewY > this.y)
2874 this.y = this.viewY;
2875 this.line = this.viewLine;
2878 if(this.viewY + numLines <= this.y)
2882 this.y = this.viewY-1;
2883 for(c = 0, line = this.viewLine; line && c<numLines; line = line.next, c++)
2890 if(this.line != oldLine)
2892 this.x = AdjustXPosition(this.line, caretX, true, null, MAXINT, 0);
2900 this.selLine = this.line;
2903 UpdateCaretPosition(false);
2910 bool SaveFile(char * fileName)
2912 File f = eFile_Open(fileName, FO_WRITE);
2916 eWindow_SetModified(false);
2917 eInstance_Delete(f);
2924 bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
2928 if(!style.multiLine)
2930 x = (active && this.active && !style.readOnly) ? line.count : 0;
2933 SetViewToCursor(true);
2938 if(!active && modified)
2941 if(!NotifyModified(master, this))
2944 *goOnWithActivation = false;
2951 if(timer) timer.Stop();
2953 mouseSelect = false;
2959 bool OnResizing(int *w, int *h)
2965 *w = space.h * 80 / 14;
2969 void OnResize(int w, int h)
2972 if(!hasHorzScroll && !hasVertScroll && viewLine)
2973 SetViewToCursor(true);
2975 //if(!hasHorzScroll && !hasVertScroll && viewLine)
2977 SetViewToCursor(true);
2982 bool OnMiddleButtonDown(int x, int y, Modifiers mods)
2984 if(style.readOnly) return true;
2989 bool OnRightButtonDown(int x, int y, Modifiers mods)
2991 this.rightButtonDown = true;
2996 bool OnRightButtonUp(int x, int y, Modifiers mods)
2999 if(!parent.inactive && rightButtonDown)
3002 Menu contextMenu { };
3004 MenuItem { contextMenu, "Cut\tCtrl+X", t, NotifySelect = itemEditCut.NotifySelect, disabled = !selection || style.readOnly };
3005 MenuItem { contextMenu, "Copy\tCtrl+C", c, NotifySelect = itemEditCopy.NotifySelect, disabled = !selection };
3006 MenuItem { contextMenu, "Paste\tCtrl+V", p, NotifySelect = itemEditPaste.NotifySelect, disabled = style.readOnly };
3007 MenuItem { contextMenu, "Delete\tDel", d, NotifySelect = itemEditDelete.NotifySelect, disabled = !selection || style.readOnly };
3008 MenuDivider { contextMenu };
3009 MenuItem { contextMenu, "Select All\tCtrl+A", a, NotifySelect = itemEditSelectAll.NotifySelect };
3011 popup = PopupMenu { master = this, menu = contextMenu,
3013 nonClient = true, interim = false, parent = parent,
3014 position = { x + clientStart.x + parent.clientStart.x + position.x, y + cientStart.y + parent.sy + clientStart.y + position.y };
3016 position = { x + clientStart.x + absPosition.x - guiApp.desktop.position.x, y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
3020 rightButtonDown = false;
3024 bool OnLeftButtonDown(int mx, int my, Modifiers mods)
3029 if(style.noSelect) return true;
3031 if(!mods.isActivate)
3037 mouseX = mx - XOFFSET;
3040 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3042 //PrintLn("OnLeftButtonDown: ", x, ", ", y);
3048 else if(IsMouseOnSelection() && !mods.isActivate)
3058 if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
3060 if(mods.shift && !mods.isActivate)
3075 this.selLine = this.line;
3084 UpdateCaretPosition(true);
3088 bool OnLeftButtonUp(int x, int y, Modifiers mods)
3092 mouseSelect = false;
3103 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3105 //PrintLn("MouseMove: ", x, ", ", y);
3111 mouseMove = IsMouseOnSelection();
3115 int size = SelSize();
3118 char * text = new char[size+1];
3122 GetSel(text, false);
3124 if(Max(selY, this.y) == dropY)
3128 if(this.dropX > this.selX)
3129 moveX = this.x - this.selX;
3133 if(this.dropX > this.x)
3134 moveX = this.selX - this.x;
3138 this.dropX -= moveX;
3139 this.selX = this.x = this.dropX;
3140 this.selY = this.y = this.dropY;
3141 this.selLine = this.line = this.dropLine;
3143 SetViewToCursor(true);
3148 byte * c = ((EditBox)this).multiLineContents, * c2;
3149 int l1 = c ? strlen(c) : 0, l2;
3152 c2 = ((EditBox)this).multiLineContents;
3153 l2 = c2 ? strlen(c2) : 0;
3154 if(l1 != l2 || (l1 && strcmp(c, c2)))
3184 FindMouse(mouseX, mouseY, &x, &y, &line, true);
3186 //PrintLn("Dropped: ", x, ", ", y);
3188 NotifyDropped(master, this, x, y);
3195 bool OnMouseMove(int mx, int my, Modifiers mods)
3201 if(mods != -1 && mods.isSideEffect)
3206 if(style.noSelect) return true;
3207 if(wordSelect) return true;
3208 mouseX = mx - XOFFSET;
3211 needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
3213 if(this.mouseMove || this.mouseSelect)
3222 ((style.hScroll) || (style.vScroll)))
3229 DirtyLine(this.dropY);
3232 DirtyLine(this.dropY);
3233 this.dropLine = line;
3234 SetViewToCursor(true);
3236 //PrintLn("MouseMove: ", "dropX = ", x, ", dropY = ", y);
3239 else if(this.mouseSelect)
3241 DirtyLine(this.selY);
3248 SetViewToCursor(true);
3251 //PrintLn("MouseSelect: ", "x = ", x, ", y = ", y);
3258 bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
3263 //PrintLn("OnLeftDoubleClick: ", mx, ", ", my, ", mods = ", mods);
3267 if(style.noSelect) return true;
3268 FindMouse(mx, my, &x, &y, &line, false);
3269 if(!NotifyDoubleClick(master, this, line, mods))
3276 for(c = x; c >= 0; c--)
3279 while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
3280 ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3287 for(c = start; c<line.count; c += numBytes)
3289 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3300 this.line = this.selLine = line;
3301 this.wordSelect = (c != start);
3313 Menu fileMenu { menu, "File", F };
3314 saveDialog = fileDialog;
3315 MenuItem { fileMenu, "Save\tCtrl+S", S, CtrlS, NotifySelect = MenuFileSave };
3316 MenuItem { fileMenu, "Save As...", A, NotifySelect = MenuFileSaveAs };
3326 FontExtent(display, font, " ", 1, (int *)&space.w, (int *)&space.h);
3327 FontExtent(display, font, "W", 1, (int *)&large.w, (int *)&large.h);
3329 space.w = Max(space.w, 1);
3330 large.w = Max(large.w, 1);
3331 space.h = Max(space.h, 1);
3332 large.h = Max(large.h, 1);
3336 for(line = lines.first; line; line = line.next)
3337 ComputeLength(line);
3342 SetViewToCursor(true);
3350 bool OnLoadGraphics()
3352 FontExtent = Display::FontExtent;
3355 // UpdateCaretPosition(true);
3359 void OnUnloadGraphics()
3364 bool OnKeyHit(Key key, unichar ch)
3366 bool shift = (key.shift) ? true : false;
3368 //PrintLn("OnKeyHit: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
3370 if(!ch && !key.alt && !key.ctrl)
3372 key.code = (SmartKey)key.code;
3375 switch(key.code) //(ch || key.alt || key.ctrl) ? key.code : (Key)(SmartKey)key.code)
3378 if(style.readOnly) break;
3379 if(style.stuckCaret) GoToEnd(true);
3380 if(!(style.freeCaret))
3382 this.x = Min(this.x, this.line.count);
3388 EditLine line = this.line;
3390 for(y = this.y; y>= 0; y--)
3392 c = (y == this.y) ? (this.x-1) : line.count-1;
3394 // Slow down when going on lines...
3395 if(y != this.y) break;
3399 //if(this.line.buffer[c] != '\t' && this.line.buffer[c] != ' ')
3400 if(IS_ALUNDER(line.buffer[c]))
3405 if(!IS_ALUNDER(line.buffer[c]))
3407 //if(this.line.buffer[c] == '\t' || this.line.buffer[c] == ' ')
3421 DelCh(line,y,c+1,this.line,this.y,this.x, true);
3422 this.x = this.selX = Min(c+1, line.count);
3423 this.y = this.selY = y;
3424 this.line = this.selLine = line;
3425 SetViewToCursor(true);
3436 if(style.readOnly) break;
3437 if(style.stuckCaret) break;
3446 if(this.line != this.selLine || this.x != this.selX)
3449 SetViewToCursor(true);
3455 if(this.x < this.line.count)
3459 //Can't delete right away as that would change the line.count
3460 for(i = this.x; i < this.line.count; i++)
3462 if(!IS_ALUNDER(this.line.buffer[i]))
3464 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3468 for(; i < this.line.count; i++)
3470 //Delete trailing whitespace
3471 if(IS_ALUNDER(this.line.buffer[i]))
3473 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3476 SetViewToCursor(true);
3479 else if(this.line.next)
3481 DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3482 SetViewToCursor(true);
3488 if(!(style.freeCaret))
3490 this.selX = this.x = Min(this.x, this.line.count);
3493 if(this.x < this.line.count)
3495 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, false);
3497 else if(this.line.next)
3499 DelCh(this.line, this.y, this.x, this.line.next, this.y+1, 0, false);
3501 SetViewToCursor(true);
3510 if(!key.alt && !key.ctrl)
3514 bool stuffAfter = false;
3518 if(style.stuckCaret) GoToEnd(true);
3519 if(style.readOnly) break;
3520 if(!(style.multiLine)) break;
3522 for(c = 0; c<this.line.count && c<this.x; c++)
3524 if(this.line.buffer[c] == '\t')
3525 position += this.tabSize - (position % this.tabSize);
3526 else if(this.line.buffer[c] == ' ')
3534 if(this.x < this.line.count)
3537 //If last character is a { indent one tab
3538 if(this.line.buffer[this.x - 1] == '{')
3540 //Except if the next non space character is a }
3541 bool indent = false;
3543 for(i = this.x; i < this.line.size; i++)
3544 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
3546 if(this.line.buffer[i] != '}')
3551 position += this.tabSize;
3554 addString = new char[position + 2];
3555 addString[len++] = '\n';
3556 addString[len] = '\0';
3558 if(stuffAfter || !style.freeCaret)
3560 for(c = 0; c<position; )
3562 if(style.useTab && c + this.tabSize <= position)
3564 addString[len++] = '\t';
3569 addString[len++] = ' ';
3573 addString[len] = '\0';
3577 if(!stuffAfter && style.freeCaret)
3579 this.x = this.selX = position;
3583 SetViewToCursor(true);
3593 if(style.stuckCaret) break;
3594 if(!(style.freeCaret))
3596 this.x = Min(this.x, this.line.count);
3597 this.selX = Min(this.selX, this.selLine.count);
3600 if(!shift) SelDirty();
3603 bool foundAlpha = false;
3606 EditLine line, lastLine;
3609 for(line = this.line; (line && !found); line = line.prev, y--)
3614 if(this.x == 0 && line != this.line)
3623 if(line == this.line) start = this.x -1; else start = line.count-1;
3624 start = Min(start, line.count-1);
3626 for(c = start; c >= 0;)
3629 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3647 byte ch = line.buffer[c];
3648 if(UTF8_IS_FIRST(ch)) break;
3652 // No next word found,
3653 if(!found && ( this.x > 0 || (!line.count && this.x)))
3667 this.line = lastLine;
3678 byte * buffer = line.buffer;
3681 byte ch = buffer[x];
3682 if(UTF8_IS_FIRST(ch)) break;
3699 if(!shift) Deselect();
3700 SetViewToCursor(true);
3706 if(style.stuckCaret) break;
3707 if(!(style.freeCaret))
3709 this.x = Min(this.x, this.line.count);
3710 this.selX = Min(this.selX, this.selLine.count);
3713 if(!shift) SelDirty();
3714 if(!shift && (this.x != this.selX || this.y != this.selY));
3717 bool onAChar = false;
3718 if(this.selX != this.x || this.selY != this.y)
3720 if(this.x<this.line.count)
3721 if(this.line.buffer[this.x] != '\t' && this.line.buffer[this.x] !=' ')
3723 if(key.shift && onAChar &&
3724 ((this.y > this.selY)||((this.selY == this.y)&&(this.x >= this.selX))))
3726 bool foundAlpha = false;
3728 EditLine line, lastLine;
3730 int lastC, lastY, lastNumBytes;
3732 for(line = this.line; (line && !found); line = line.next, y++)
3734 int start = (line == this.line) ? this.x : 0;
3737 for(c = start; c < line.count; c += numBytes)
3739 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3744 lastNumBytes = numBytes;
3754 if(!found && (c != this.x || line != this.line))
3767 this.x = lastC + lastNumBytes;
3769 this.line = lastLine;
3776 bool foundAlpha = false;
3781 for(line = this.line; (line && !found); line = line.next, y++)
3783 int start = (line == this.line) ? this.x : 0;
3786 for(c = start; c < line.count; c += numBytes)
3788 unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
3803 // No next word found,
3804 if(!found && (c != this.x || line != this.line))
3808 this.x = line.count;
3820 if(x < line.count || (style.freeCaret && line.count < maxLineSize))
3824 byte * buffer = line.buffer;
3827 byte ch = buffer[x];
3828 if(UTF8_IS_FIRST(ch)) break;
3849 if(!shift) Deselect();
3850 SetViewToCursor(true);
3857 if(!style.vScroll || hasVertScroll) break;
3863 if(style.stuckCaret) break;
3865 if(!shift) SelDirty();
3875 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
3879 if(!shift) Deselect();
3881 SetViewToCursor(false);
3884 if(caretY == this.y * space.h)
3890 if(!style.freeCaret)
3891 this.x = Min(this.x, line.count);
3901 int sx = 0, sy = this.y * space.h;
3902 char * text = line.text;
3903 int maxW = clientSize.w - sx;
3904 display.FontExtent(font, " ", 1, null, &th);
3908 int startPos = textPos;
3911 bool lineComplete = false;
3913 if(!style.wrap && caretY == MAXINT)
3916 //textPos = line.count;
3917 //lineComplete = true;
3920 for(; (style.freeCaret || textPos < line.count) && !lineComplete;)
3924 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
3927 len = (nextSpace - (text + textPos));
3929 len = line.count - textPos;
3931 if(textPos < line.count)
3933 display.FontExtent(font, text + textPos, len, &w, null);
3935 if(nextSpace) { w += space.w; len++; }
3937 if(style.wrap && x + width + w > maxW && x > 0)
3939 lineComplete = true;
3949 if((!style.freeCaret && textPos >= line.count) || (sy == caretY - th && caretX <= x + width + sx))
3953 while(this.x > 0 && x + sx > caretX && this.x > startPos)
3956 if(this.x > line.count)
3959 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
3960 len = this.x - startPos;
3961 display.FontExtent(font, text + startPos, len, &x, null);
3965 if(!shift) Deselect();
3967 SetViewToCursor(false);
3971 if(sy == caretY - th || textPos >= line.count)
3973 if(textPos >= line.count)
3975 int c = textPos - 1;
3976 while(c > 0 && text[c] == ' ') c--;
3980 this.x = line.count;
3983 if(!shift) Deselect();
3985 SetViewToCursor(false);
3990 } while(textPos < line.count);
3993 if(!shift) Deselect();
3995 SetViewToCursor(false);
4004 int x = AdjustXPosition(this.line, this.line.prev, true, null, MAXINT, 0);
4005 if(!shift) SelDirty();
4006 this.line = this.line.prev;
4012 if(!shift) Deselect();
4016 SetViewToCursor(false);
4022 return style.multiLine ? false : true;
4026 if(!style.vScroll || hasVertScroll)
4033 if(style.stuckCaret) break;
4037 int sx = 0, sy = this.y * this.space.h;
4038 int maxW = clientSize.w - sx;
4039 char * text = line.buffer;
4041 if(!shift) SelDirty();
4047 if(AdjustXPosition(line, maxW, this.x, line.count, true, null, MAXINT, 0) <= line.count)
4057 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
4060 if(!shift) Deselect();
4062 if(this.selX != this.x || this.selY != this.y)
4064 SetViewToCursor(false);
4067 while(!textPos || (style.freeCaret || textPos<line.count))
4069 int startPos = textPos;
4072 bool lineComplete = false;
4073 if(!style.wrap && sy <= caretY)
4075 textPos = line.count;
4076 lineComplete = true;
4078 for(; (style.freeCaret || textPos<line.count) && !lineComplete;)
4082 char * nextSpace = (textPos < line.count) ? strchr(text + textPos, ' ') : (text + textPos);
4085 len = (nextSpace - (text + textPos));
4087 len = line.count - textPos;
4089 if(textPos < line.count)
4091 display.FontExtent(font, text + textPos, len, &w, &th);
4093 if(nextSpace) { w += space.w; len++; }
4094 if(style.wrap && x + width + w > maxW && x > 0)
4096 lineComplete = true;
4106 if(sy > caretY && ((!style.freeCaret && textPos >= line.count) || caretX <= x + width + sx))
4110 while(this.x > 0 && x + sx > caretX && textPos > startPos)
4113 if(this.x > line.count)
4116 while(this.x > 0 && !UTF8_IS_FIRST(text[--this.x]));
4118 len = this.x - startPos;
4119 display.FontExtent(font, text + startPos, len, &x, null);
4122 if(!shift) Deselect();
4125 SetViewToCursor(false);
4131 this.x = line.count;
4134 if(!shift) Deselect();
4137 SetViewToCursor(false);
4140 else if(textPos >= line.count && line.next)
4146 sy = this.y * this.space.h;
4147 sx = 0; //textBlock.startX;
4153 sx = 0; //textBlock.startX;
4161 this.x = Min(this.x, line.count);
4162 //int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4164 if(!shift) Deselect();
4167 if(this.selX != this.x || this.selY != this.y)
4170 SetViewToCursor(false);
4177 int x = AdjustXPosition(this.line, this.line.next, true, null, MAXINT, 0);
4178 if(!shift) SelDirty();
4179 this.line = this.line.next;
4183 if(!shift) Deselect();
4186 if(this.selX != this.x || this.selY != this.y)
4194 return style.multiLine ? false : true;
4197 if(style.stuckCaret) break;
4198 if(!(style.freeCaret))
4199 this.selX = Min(this.selX, this.selLine.count);
4201 if(!shift) SelDirty();
4204 this.line = this.lines.first;
4205 if(this.y != 0 || this.x != 0)
4215 EditLine line = this.line;
4217 for(c=0; line.buffer[c]; c++)
4218 if(line.buffer[c] != ' ' && line.buffer[c] != '\t')
4220 if(c != 0 || this.x)
4230 DirtyLine(this.y);*/
4235 if(!shift) Deselect();
4236 SetViewToCursor(true);
4242 if(style.stuckCaret) break;
4243 if(!(style.freeCaret))
4244 this.selX = Min(this.selX, this.selLine.count);
4246 if(!shift) SelDirty();
4251 else if(this.x != this.line.count)
4253 this.x = this.line.count;
4254 //DirtyLine(this.y);
4257 if(!shift) Deselect();
4258 SetViewToCursor(true);
4263 if(style.tabKey && !key.ctrl)
4265 if(this.selY != this.y && style.tabSel)
4267 EditLine firstLine, lastLine;
4271 // Do multi line selection tabbing here
4272 if(this.selY < this.y)
4274 firstLine = this.selLine;
4275 lastLine = this.line;
4281 // Selecting going up
4282 firstLine = this.line;
4283 lastLine = this.selLine;
4290 for(line = firstLine; line; line = line.next, y++)
4292 if(line != lastLine || x)
4296 BufferLocation before = { line, y, 0 }, after = { line, y, 0 };
4298 for(c=0; c<line.count && lastC < this.tabSize; c++, lastC++)
4300 if(line.buffer[c] == '\t')
4305 else if(line.buffer[c] != ' ')
4310 NotifyCharsDeleted(master, this, &before, &after, this.pasteOperation);
4313 int len = GetText(null, line, y, 0, line, y, lastC, false, false);
4314 char * string = new char[len];
4315 DelTextAction action { y1 = y, x1 = 0, y2 = y, x2 = lastC, string = string, placeAfter = true };
4316 GetText(string, line, y, 0, line, y, lastC, false, false);
4319 memmove(line.buffer,line.buffer+lastC,line.size-lastC);
4320 if(!line.AdjustBuffer(line.count-lastC))
4326 if(line == lastLine) break;
4331 for(line = firstLine; line; line = line.next, y++)
4335 if(line != lastLine || x)
4339 if(line.count + 1 <= this.maxLineSize)
4341 BufferLocation before = { line, y, 0 }, after = { line, y, 1 };
4343 if(!line.AdjustBuffer(line.count+1))
4346 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4348 AddCharAction action { ch = '\t', x = 0, y = y };
4352 memmove(line.buffer+1,line.buffer,line.size-1);
4354 line.buffer[0] = '\t';
4359 if(line.count + this.tabSize <= this.maxLineSize)
4362 BufferLocation before = { line, y, 0 }, after = { line, y, this.tabSize };
4363 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4365 if(!line.AdjustBuffer(line.count+this.tabSize))
4369 char * string = new char[this.tabSize + 1];
4370 memset(string, ' ', this.tabSize);
4371 string[this.tabSize] = '\0';
4372 Record(AddTextAction { string = string, x1 = 0, y1 = y, x2 = this.tabSize, y2 = y });
4375 memmove(line.buffer+this.tabSize,line.buffer,line.size-(this.tabSize));
4376 line.count+=this.tabSize;
4377 for(c=0; c<this.tabSize; c++)
4378 line.buffer[c] = ' ';
4384 if(line == lastLine) break;
4398 char * addString = new char[this.tabSize + 1];
4400 if(!(style.freeCaret))
4402 this.x = Min(this.x, this.line.count);
4407 for(c=start; ((c == start) || ((c) % this.tabSize)); c++)
4409 addString[len++] = ' ';
4417 SetViewToCursor(true);
4424 if(!(style.hScroll) || hasHorzScroll) break;
4425 if(this.viewX < this.maxLength)
4427 //this.viewX+=this.space.w*this.tabSize;
4429 SetScrollPosition((this.viewX + this.space.w*this.tabSize), this.viewY * this.space.h);
4436 if(!shift) Deselect();
4445 if(!(style.hScroll) || hasHorzScroll) break;
4448 //this.viewX-=this.space.w*this.tabSize;
4449 //this.viewX = Max(this.viewX,0);
4451 SetScrollPosition((this.viewX-this.space.w*this.tabSize), this.viewY * this.space.h);
4452 // SetCursorToView();
4459 if(!shift) Deselect();
4473 if(!(style.readOnly))
4479 this.overwrite ^= 1;
4480 UpdateCaretPosition(true);
4485 NotifyOvrToggle(master, this, this.overwrite);
4496 if(style.noSelect) break;
4499 //Save current view position
4500 int tempX = this.viewX;
4501 int tempY = this.viewY;
4505 this.selLine = this.lines.first;
4506 this.y = this.lineCount-1;
4507 this.line = this.lines.last;
4508 this.x = this.line.count;
4511 SetViewToCursor(true);
4513 //Restore previous view position
4514 SetScrollPosition(tempX, tempY * this.space.h);
4520 // TOCHECK: Was there any good reason why we weren't returning false here?
4521 return false; // break;
4526 if(!(style.readOnly))
4534 if(style.readOnly) break;
4540 if(style.readOnly) break;
4545 if(style.readOnly) break;
4549 if(style.readOnly) break;
4553 if(style.readOnly) break;
4554 if(key.shift && key.code == rightBracket)
4556 //Only indent back if you are exactly at one tab.
4558 bool whitespace = true;
4564 EditLine line = this.line;
4566 //Only remove one tab if there is nothing else on the line.
4567 for(i = 0; i < this.line.count; i++)
4569 if(this.line.buffer[i] != ' ' && this.line.buffer[i] != '\t')
4583 for(pos = line.count - 1; pos >= 0; pos--)
4585 char c = line.buffer[pos];
4589 indentwidth += this.tabSize;
4591 //Counting backwards, so when you find a character, indentation is reset
4601 //Place the } to get an undo:
4604 this.x = this.line.count;
4608 putsize = indentwidth;
4610 putsize = indentwidth / this.tabSize + indentwidth % this.tabSize;
4612 newline = new char[putsize+2];
4613 newline[putsize] = '}';
4614 newline[putsize+1] = '\0';
4618 for(; i < indentwidth / this.tabSize; i++)
4620 for(;i < putsize; i++)
4628 } else if(!key.ctrl && !key.alt && ch != 128 && ch >= 32)
4640 void OnHScroll(ScrollBarAction action, int position, Key key)
4643 //PrintLn("OnHScroll: ", action, ", pos = ", position, ", key = ", key);
4645 this.viewX = position;
4646 if(action != setRange)
4648 if(!this.mouseMove && style.cursorFollowsView)
4655 void OnVScroll(ScrollBarAction action, int position, Key key)
4657 int oldViewY = this.viewY;
4660 //PrintLn("OnVScroll: ", action, ", pos = ", position, ", key = ", key);
4662 position /= this.space.h;
4664 if(position < this.viewY)
4666 for(; position < this.viewY && this.viewLine.prev; this.viewLine = this.viewLine.prev, this.viewY--);
4667 style.recomputeSyntax = true;
4669 else if(position > this.viewY)
4671 EditLine oldViewLine = viewLine;
4672 for(; position > this.viewY && this.viewLine.next; this.viewLine = this.viewLine.next, this.viewY++);
4673 FigureStartSyntaxStates(oldViewLine, false);
4676 if(action != setRange)
4678 if(!this.mouseMove && style.cursorFollowsView && !SelSize()) SetCursorToViewY();
4681 if(this.x != this.selX || this.y != this.selY)
4685 Scroll(0, (this.viewY - oldViewY) * this.space.h);
4688 int numLines = clientSize.h / this.space.h;
4690 if(Abs(this.viewY - oldViewY) < numLines)
4693 if(this.viewY > oldViewY)
4695 for(y = oldViewY; y <this.viewY; y++)
4696 DirtyLine(y + numLines);
4700 for(y = this.viewY; y <oldViewY; y++)
4701 DirtyLine(y + numLines);
4706 // Fix dirt of stuff before first line...
4707 if(this.viewY - oldViewY > 0)
4709 Box box { 0,0, clientSize.w-1, YOFFSET-1 };
4716 bool _AddCh(unichar ch, int * addedSpacesPtr, int * addedTabsPtr)
4721 ReplaceTextAction replaceAction = null;
4722 AddCharAction addCharAction = null;
4723 int addedSpaces = 0, addedTabs = 0;
4725 if(ch == '\r') return true;
4726 if(style.stuckCaret /*|EES_READONLY)*/ )
4729 if(ch == '\n' && !(style.multiLine) && this.line) return false;
4731 if(!undoBuffer.dontRecord)
4733 if(selX != x || selY != y)
4738 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
4739 oldString = new char[len];
4740 UTF32toUTF8Len(&ch, 1, buffer, 4);
4741 newString = CopyString(buffer);
4742 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
4744 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
4745 if(selY < y || (selY == y && selX < x))
4747 replaceAction.x1 = selX;
4748 replaceAction.y1 = selY;
4749 replaceAction.x2 = x;
4750 replaceAction.y2 = y;
4754 replaceAction.x1 = x;
4755 replaceAction.y1 = y;
4756 replaceAction.x2 = selX;
4757 replaceAction.y2 = selY;
4759 Record(replaceAction);
4760 undoBuffer.dontRecord++;
4764 addCharAction = AddCharAction { y = y, x = x, ch = ch };
4765 Record(addCharAction);
4771 DelSel(&addedSpaces);
4772 if(this.lineCount+1 > this.maxLines)
4775 Emptyline(this.lines.first,0);
4779 if(!(style.vScroll))
4781 // Make sure it fits, but we need a default line is this.font is too big for window
4782 if(this.space.h * (this.lineCount+1) > clientSize.h && this.line)
4785 if((this.y >= 0) && this.y < this.viewY)
4790 line = EditLine { };
4793 lines.Insert(this.line, line);
4794 line.editBox = this;
4800 // If we're displacing the lines from a current line ...
4803 if(this.line.buffer)
4806 if(this.line.count < endX) endX = this.line.count;
4807 length = this.line.count - endX;
4810 if(!line.AdjustBuffer(length))
4814 if(this.line.buffer)
4816 CopyBytes(line.buffer,this.line.buffer+endX, length+1);
4818 if(endX > 4000 || endX < 0)
4821 this.line.count = endX;
4822 this.line.buffer[this.line.count] = '\0';
4823 this.line.AdjustBuffer(this.line.count);
4824 ComputeLength(this.line);
4828 BufferLocation before = { this.line, this.y, this.x }, after;
4834 ComputeLength(this.line);
4837 if(length > 4000 || length < 0)
4840 line.count = length;
4844 line.buffer[line.count] = '\0';
4847 after.line = this.line, after.y = this.y, after.x = this.x;
4849 NotifyCharsAdded(master, this, &before, &after, this.pasteOperation);
4855 int count = UTF32toUTF8Len(&ch, 1, string, 5);
4856 DelSel(&addedSpaces);
4857 result = AddToLine(string, count, false, addedSpaces ? null : &addedSpaces, &addedTabs);
4858 if(addedSpacesPtr) *addedSpacesPtr = addedSpaces;
4859 if(addedTabsPtr) *addedTabsPtr = addedTabs;
4863 this.selLine = this.line;
4867 replaceAction.x3 = x;
4868 replaceAction.y3 = y;
4869 replaceAction.addedSpaces = addedSpaces;
4870 replaceAction.addedTabs = addedTabs;
4871 undoBuffer.dontRecord--;
4875 addCharAction.addedSpaces = addedSpaces;
4876 addCharAction.addedTabs = addedTabs;
4883 /****************************************************************************
4885 ****************************************************************************/
4888 bool AddCh(unichar ch)
4890 return _AddCh(ch, null, null);
4895 this.modified = true;
4896 NotifyUpdate(master, this);
4897 modifiedDocument = true;
4900 void Delete(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
4903 DelCh(line1, y1, x1, line2, y2, x2, false);
4904 SetViewToCursor(true);
4912 itemEditUndo.disabled = undoBuffer.curAction == 0;
4913 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4914 if(savedAction == undoBuffer.curAction)
4916 modifiedDocument = false;
4918 NotifyUnsetModified(master, this);
4925 itemEditUndo.disabled = undoBuffer.curAction == 0;
4926 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4927 if(savedAction == undoBuffer.curAction)
4929 modifiedDocument = false;
4931 NotifyUnsetModified(master, this);
4935 void Record(UndoAction action)
4937 if(!undoBuffer.dontRecord)
4939 undoBuffer.Record(action);
4940 itemEditUndo.disabled = undoBuffer.curAction == 0;
4941 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
4948 this.lines.first, 0,0,
4949 this.lines.last, this.lines.count-1, strlen(((EditLine)this.lines.last).buffer));
4952 void Select(EditLine line1, int y1, int x1, EditLine line2, int y2, int x2)
4957 this.selLine = line1 ? (EditLine)line1 : this.lines.first;
4958 this.x = line2 ? x2 : ((EditLine)this.lines.last).count;
4959 this.y = line2 ? y2 : (this.lineCount-1);
4960 this.line = line2 ? (EditLine)line2 : this.lines.last;
4963 SetViewToCursor(true);
4967 // TODO: Fix this vs modifiedDocument window property
4968 void SetModified(bool flag)
4972 this.modified = false;
4973 if(flag && !NotifyModified(master, this))
4974 this.modified = true;
4979 bool AddS(char * string)
4986 int addedSpaces = 0, addedTabs = 0;
4987 AddTextAction action = null;
4988 ReplaceTextAction replaceAction = null;
4990 this.pasteOperation = true;
4992 if(style.stuckCaret /*|EES_READONLY)*/ )
4995 if(!undoBuffer.dontRecord)
4997 char * placeString = CopyString(string);
4998 if(!style.multiLine)
5002 for(i = 0; (ch = placeString[i]); i++)
5005 placeString[i] = '\0';
5006 placeString = renew placeString byte[i+1];
5011 if(selX != x || selY != y)
5015 int len = GetText(null, selLine, selY, selX, this.line, y, x, false, false);
5016 oldString = new char[len];
5017 newString = placeString;
5018 GetText(oldString, selLine, selY, selX, this.line, y, x, false, false);
5020 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5021 if(selY < y || (selY == y && selX < x))
5023 replaceAction.x1 = selX;
5024 replaceAction.y1 = selY;
5025 replaceAction.x2 = x;
5026 replaceAction.y2 = y;
5030 replaceAction.x1 = x;
5031 replaceAction.y1 = y;
5032 replaceAction.x2 = selX;
5033 replaceAction.y2 = selY;
5035 Record(replaceAction);
5039 if(string[0] == '\n')
5040 action = AddTextAction { y1 = y, x1 = Min(this.line.count, x), string = placeString };
5042 action = AddTextAction { y1 = y, x1 = x, string = placeString };
5050 undoBuffer.dontRecord++;
5051 DelSel(&addedSpaces);
5055 for(c = 0; string[c]; c++)
5057 if(string[c] == '\n' || string[c] == '\r')
5059 if(!AddToLine(line,count, true, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5064 if(string[c] == '\n')
5072 // Reset for next line
5076 if(string[c] == '\r' && *line == '\n')
5088 // Why was this here?
5091 // Add the line here
5093 if(!AddToLine(line,count,false, addedSpaces ? null : &addedSpaces, addedTabs ? null : &addedTabs))
5099 undoBuffer.dontRecord--;
5104 action.addedSpaces = addedSpaces;
5105 action.addedTabs = addedTabs;
5107 else if(replaceAction)
5109 replaceAction.y3 = y;
5110 replaceAction.x3 = x;
5111 replaceAction.addedSpaces = addedSpaces;
5112 replaceAction.addedTabs = addedTabs;
5115 UpdateCaretPosition(true);
5117 this.pasteOperation = false;
5130 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5131 SetViewToCursor(true);
5137 void PutCh(unichar ch)
5141 if((ch >= 32 /*&& ch <=126*/) || ch == '\n')
5142 //if((ch >= 32) || ch == '\n')
5144 int addedSpaces = 0, addedTabs = 0;
5145 ReplaceTextAction replaceAction = null;
5146 AddCharAction addCharAction = null;
5149 ch = (ch < 128) ? toupper(ch) : ch; // TODO: UNICODE TO UPPER
5151 if(this.x < this.line.count && this.overwrite)
5156 int len = GetText(null, line, y, x, line, y, x+1, false, false);
5157 oldString = new char[len];
5158 UTF32toUTF8Len(&ch, 1, buffer, 4);
5159 newString = CopyString(buffer);
5160 GetText(oldString, line, y, x, line, y, x+1, false, false);
5161 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = false };
5162 replaceAction.x1 = x;
5163 replaceAction.y1 = y;
5164 replaceAction.x2 = x+1;
5165 replaceAction.y2 = y;
5166 Record(replaceAction);
5168 undoBuffer.dontRecord++;
5169 DelCh(this.line, this.y, this.x, this.line, this.y, this.x+1, true);
5170 undoBuffer.dontRecord--;
5172 else if(!undoBuffer.dontRecord)
5174 if(selX != x || selY != y)
5179 int len = GetText(null, line, y, x, selLine, selY, selX, false, false);
5180 oldString = new char[len];
5181 UTF32toUTF8Len(&ch, 1, buffer, 4);
5182 newString = CopyString(buffer);
5183 GetText(oldString, line, y, x, selLine, selY, selX, false, false);
5184 replaceAction = ReplaceTextAction { newString = newString, oldString = oldString, placeAfter = true };
5185 if(selY < y || (selY == y && selX < x))
5187 replaceAction.x1 = selX;
5188 replaceAction.y1 = selY;
5189 replaceAction.x2 = x;
5190 replaceAction.y2 = y;
5194 replaceAction.x1 = x;
5195 replaceAction.y1 = y;
5196 replaceAction.x2 = selX;
5197 replaceAction.y2 = selY;
5199 Record(replaceAction);
5203 addCharAction = AddCharAction { y = y, x = x, ch = ch };
5204 Record(addCharAction);
5207 undoBuffer.dontRecord++;
5208 result = _AddCh(ch, &addedSpaces, &addedTabs);
5211 replaceAction.x3 = x;
5212 replaceAction.y3 = y;
5213 replaceAction.addedSpaces = addedSpaces;
5214 replaceAction.addedTabs = addedTabs;
5218 addCharAction.addedSpaces = addedSpaces;
5219 addCharAction.addedTabs = addedTabs;
5221 undoBuffer.dontRecord--;
5225 if(result) SetViewToCursor(true);
5229 void PutS(char * string)
5234 SetViewToCursor(true);
5239 void Printf(char * format, ...)
5243 char temp[MAX_F_STRING];
5245 va_start(args, format);
5246 vsprintf(temp, format, args);
5252 void SetContents(char * format, ...)
5257 DelCh(this.lines.first, 0, 0, this.lines.last, this.lineCount-1, ((EditLine)(this.lines.last)).count, true);
5260 char temp[MAX_F_STRING];
5262 va_start(args, format);
5263 vsprintf(temp, format, args);
5279 x -= 1 + DelCh(line, y, x-1, line, y, x, true);
5282 else if(this.line.prev)
5284 EditLine line = this.line.prev;
5288 DelCh(line, this.y-1, x, this.line, this.y, this.x, true);
5296 this.selLine = this.line;
5301 SetViewToCursor(true);
5306 Emptyline(this.line,this.y);
5307 this.selX = this.x = 0;
5310 this.selLine = this.line;
5312 SetViewToCursor(true);
5322 SetViewToCursor(true);
5330 SetViewToCursor(true);
5334 bool GoToLineNum(int lineNum)
5339 EditLine line = this.lines.first;
5340 for(c = 0; c < lineNum && line; c++, line = line.next);
5350 SetViewToCursor(true);
5357 bool GoToPosition(EditLine line, int y, int x)
5369 for(line = this.lines.first, c = 0; c<y && line; c++, line = line.next);
5383 SetViewToCursor(true);
5390 void SetViewToCursor(bool setCaret)
5400 bool dontScroll = false;
5406 selected = selX != this.x || selY != y;
5413 checkLine = dropLine;
5419 checkLine = this.line;
5424 numLines = clientSize.h / space.h;
5426 // This is broken. The EditBox now doesn't do anything different when adding to it,
5427 // regardless of the previous scrolling position. It should be read and then set again
5428 // if one wishes to preserve it.
5429 /* // Don't scroll view to cursor if we're in a EES_NOCARET box
5430 if(style.noCaret && this.viewY < lineCount - numLines - 1)
5434 // Horizontal Adjustment
5435 if(!dontScroll && checkLine)
5439 dropX = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5442 this.x = AdjustXPosition(this.line, MAXINT, true, &x, checkX, 0);
5448 if(x + space.w >= this.viewX + clientSize.w && clientSize.w >= space.w)
5449 viewX = x - clientSize.w+space.w;
5450 if(x < this.viewX + clientSize.w/2 - space.w)
5451 viewX = Max(0, x - clientSize.w/2 + space.w);
5459 // Vertical Adjustment
5460 if(viewY > checkY) viewY = checkY;
5461 if(viewY + numLines <= checkY)
5463 if(clientSize.h >= space.h)
5465 for(line = viewLine; line && (viewY + numLines <= checkY); line = line.next)
5475 for(;dropLine && dropLine.prev && dropY >= numLines;)
5477 dropLine = dropLine.prev;
5481 for(;this.line && this.line.prev && this.y >= numLines;)
5483 this.line = this.line.prev;
5488 SetScrollPosition(viewX, viewY * this.space.h);
5490 UpdateCaretPosition(setCaret);
5496 selLine = this.line;
5506 void CenterOnCursor()
5508 int numLines = clientSize.h / this.space.h;
5509 int y = this.y - numLines / 2;
5511 bool figureSyntax = false;
5512 EditLine oldViewLine = viewLine;
5513 if(y > this.lineCount - numLines) y = this.lineCount-numLines;
5518 for(;y < this.viewY; y++)
5520 this.viewLine = this.viewLine.prev;
5521 style.recomputeSyntax = true;
5523 for(;y > this.viewY; y--)
5525 this.viewLine = this.viewLine.next;
5526 figureSyntax = true;
5529 FigureStartSyntaxStates(oldViewLine, false);
5533 SetScrollPosition(this.viewX, viewY * this.space.h);
5534 UpdateCaretPosition(true);
5538 void SetCursorToView()
5549 numLines = clientSize.h / this.space.h;
5553 for(c=0, line = this.viewLine.next; line && c<numLines && (this.viewY < this.lineCount - numLines); line = line.next, c++);
5554 SetScrollPosition(this.viewX, (this.viewY + c) * this.space.h);
5558 EditLine oldLine = this.line;
5559 bool lastOne = false;
5560 EditLine oldViewLine = this.viewLine;
5561 bool figureSyntax = false;
5563 if(this.y >= this.lineCount-1) return;
5565 for(c=0, line = this.line.next; line && c<numLines; line = line.next, c++)
5567 if(this.viewY + numLines < this.lines.count)
5569 this.viewLine = this.viewLine.next;
5571 figureSyntax = true;
5573 else if(c && !lastOne)
5582 FigureStartSyntaxStates(oldViewLine, false);
5583 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5586 SetViewToCursor(false);
5595 if(this.y == 0) return;
5597 numLines = clientSize.h / this.space.h;
5601 for(c=0, line = this.viewLine.prev; line && c<numLines; line = line.prev, c++);
5602 SetScrollPosition(this.viewX, (this.viewY - c) * this.space.h);
5606 EditLine oldLine = this.line;
5608 for(c=0, line = this.line.prev; line && c<numLines; line = line.prev, c++)
5613 if(this.viewLine.prev)
5615 this.viewLine = this.viewLine.prev;
5617 style.recomputeSyntax = true;
5621 this.x = AdjustXPosition(line, caretX, true, null, MAXINT, 0);
5624 SetViewToCursor(false);
5631 if(this.viewLine.prev)
5634 // this.viewLine = this.viewLine.prev;
5637 SetScrollPosition(this.viewX, (this.viewY - 1) * this.space.h);
5644 if(this.viewLine.next)
5647 // this.viewLine = this.viewLine.next;
5650 SetScrollPosition(this.viewX, (this.viewY + 1) * this.space.h);
5657 EditLine l1, l2, line;
5658 int x1, x2, nx1, nx2;
5663 if(!this.selLine) return 0;
5664 if(this.selLine == this.line && this.selX == this.x) return 0;
5665 if(this.selY < this.y)
5672 else if(this.selY > this.y)
5679 else if(this.selX < this.x)
5681 l1 = l2 = this.line;
5687 l1 = l2 = this.line;
5691 nx1 = Min(x1,l1.count);
5692 nx2 = Min(x2,l2.count);
5694 // Find Number of Bytes Needed
5696 for(line = l1; line; line = line.next)
5698 if(line == l1) start = nx1; else start = 0;
5699 if(line == l2) end = nx2; else end = line.count;
5701 if(style.freeCaret && line == l2)
5704 count = Max(x2-Max(x1,l1.count),0);
5706 count = Max(x2-l2.count,0);
5710 if(line == l2) break;
5711 // Add Carriage Return / line Feed
5718 void GetSelPos(EditLine * l1, int *y1, int *x1, EditLine * l2, int * y2, int * x2, bool reorder)
5722 if(!reorder || this.selY < this.y)
5724 if(l1) *l1 = this.selLine;
5725 if(y1) *y1 = this.selY;
5726 if(x1) *x1 = this.selX;
5727 if(l2) *l2 = this.line;
5728 if(y2) *y2 = this.y;
5729 if(x2) *x2 = this.x;
5731 else if(this.selY > this.y)
5733 if(l1) *l1 = this.line;
5734 if(y1) *y1 = this.y;
5735 if(x1) *x1 = this.x;
5736 if(l2) *l2 = this.selLine;
5737 if(y2) *y2 = this.selY;
5738 if(x2) *x2 = this.selX;
5740 else if(this.selX < this.x)
5742 if(l1) *l1 = this.line;
5743 if(y1) *y1 = this.selY;
5744 if(x1) *x1 = this.selX;
5745 if(l2) *l2 = this.line;
5746 if(y2) *y2 = this.y;
5747 if(x2) *x2 = this.x;
5751 if(l1) *l1 = this.line;
5752 if(y1) *y1 = this.y;
5753 if(x1) *x1 = this.x;
5754 if(l2) *l2 = this.line;
5755 if(y2) *y2 = this.selY;
5756 if(x2) *x2 = this.selX;
5761 void SetSelPos(EditLine l1, int y1, int x1, EditLine l2, int y2, int x2)
5763 if(this && (this.selY != y1 || this.selX != x1 || this.y != y2 || this.x != x2))
5765 this.selLine = (EditLine)l1;
5768 this.line = (EditLine)l2;
5772 SetViewToCursor(true);
5776 int GetText(char * text, EditLine _l1, int _y1, int _x1, EditLine _l2, int _y2, int _x2, bool addCr, bool addSpaces)
5778 EditLine l1, l2, line;
5779 int x1, x2, nx1, nx2;
5810 nx1 = Min(x1,l1.count);
5811 nx2 = Min(x2,l2.count);
5814 for(line = l1; line; line = line.next)
5816 if(line == l1) start = nx1; else start = 0;
5817 if(line == l2) end = nx2; else end = line.count;
5820 CopyBytes(text, line.buffer + start, end - start);
5823 numChars += end-start;
5825 if(style.freeCaret && line == l2 && addSpaces)
5828 count = Max(x2-Max(x1,l1.count),0);
5830 count = Max(x2-l2.count,0);
5833 FillBytes(text,' ',count);
5839 if(line == l2) break;
5851 // '\0' terminate Terminate
5858 void GetSel(char * text, bool addCr)
5860 GetText(text, line, y, x, selLine, selY, selX, addCr, true);
5866 this.selLine = this.line;
5876 int size = SelSize();
5879 // Try to allocate memory
5880 ClipBoard clipBoard { };
5881 if(clipBoard.Allocate(size+1))
5883 GetSel(clipBoard.memory, true);
5896 ClipBoard clipBoard { };
5897 if(clipBoard.Load())
5898 PutS(clipBoard.memory);
5909 SetViewToCursor(true);
5914 void DeleteSelection()
5919 SetViewToCursor(true);
5925 void Save(File f, bool lf)
5928 savedAction = undoBuffer.curAction;
5930 for(line = this.lines.first; line; line = line.next)
5932 f.Write(line.buffer, line.count,1);
5935 if(lf) f.Putc('\r');
5941 #define BUFFER_SIZE 16384
5944 undoBuffer.dontRecord++;
5947 char buffer[BUFFER_SIZE];
5951 int count = f.Read(buffer, 1, BUFFER_SIZE-1);
5952 buffer[count] = '\0';
5958 undoBuffer.dontRecord--;
5959 undoBuffer.count = 0;
5960 undoBuffer.curAction = 0;
5961 itemEditUndo.disabled = undoBuffer.curAction == 0;
5962 itemEditRedo.disabled = undoBuffer.curAction == undoBuffer.count;
5965 EditBoxFindResult Find(char * text, bool matchWord, bool matchCase, bool isSearchDown)
5969 bool firstPass = true;
5970 EditBoxFindResult result = found;
5972 if(!this.line) return notFound;
5975 for(line = this.line;;)
5983 line = this.lines.first;
5989 line = this.lines.last;
5990 num = this.lineCount - 1;
5996 string = SearchString(line.buffer, firstPass ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
5998 string = RSearchString(line.buffer,text,firstPass ? (Min(this.x,line.count)-1) : line.count,matchCase,matchWord);
6002 Select((void *)line,num,string - line.buffer,(void *)line,num,string - line.buffer + strlen(text));
6005 if(line == this.line && !firstPass) break;
6023 EditBoxFindResult FindInSelection(char * text, bool matchWord, bool matchCase, EditLine l2, int y2, int x2)
6027 int searchLen = strlen(text);
6028 for(line = (EditLine)this.line, y = this.y; y <= y2; line = line.next, y++)
6030 char * string = SearchString(line.buffer, (y == this.y) ? Min(this.x,line.count) : 0,text,matchCase,matchWord);
6031 if(string && (y < y2 || (string - line.buffer) + searchLen <= x2))
6033 Select((void *)line,y,string - line.buffer,(void *)line,y,string - line.buffer + strlen(text));
6040 bool OnKeyDown(Key key, unichar ch)
6043 //PrintLn("OnKeyDown: code = ", key.code, ", mods = ", key.modifiers, ", ch = ", (int)ch);
6045 if(!NotifyKeyDown(master, this, key, ch))
6055 this.mouseMove = false;
6056 OnLeftButtonUp(0,0,0);
6057 SetViewToCursor(true);
6067 public class EditBoxStream : File
6070 BufferLocation start, sel;
6077 EditBox editBox = this.editBox;
6079 editBox.x = start.x;
6080 editBox.y = start.y;
6081 editBox.line = start.line;
6083 editBox.selX = sel.x;
6084 editBox.selY = sel.y;
6085 editBox.selLine = sel.line;
6087 editBox.SetViewToCursor(true);
6088 //editBox.ComputeColumn();
6092 property EditBox editBox
6100 start.line = value.line;
6104 sel.line = value.selLine;
6107 value.GoToHome(true);
6110 get { return editBox; }
6113 uint Read(byte * buffer, uint size, uint count)
6116 EditBox editBox = this.editBox;
6117 EditLine line = editBox.line;
6123 for(;read < count && line; line = (*&line.next))
6125 int numBytes = Min(count - read, (*&line.count) - x);
6128 memcpy(buffer + read, (*&line.buffer) + x, numBytes);
6133 for(;read < count && x < (*&line.count);)
6135 buffer[read++] = (*&line.buffer)[x++];
6142 buffer[read++] = '\n';
6147 if(x == (*&line.count) && (*&line.next))
6156 editBox.line = editBox.selLine = line;
6157 editBox.x = editBox.selX = x;
6158 editBox.y = editBox.selY = y;
6163 bool Seek(int pos, FileSeekMode mode)
6166 EditBox editBox = this.editBox;
6167 EditLine line = editBox.line;
6169 if(mode == FileSeekMode::start)
6171 pos = pos - this.pos;
6181 for(;read < pos && line; line = (*&line.next))
6183 int numBytes = Min(pos - read, (*&line.count) - x);
6186 read += numBytes; x += numBytes;
6189 /*for(;read < pos && x < (*&line.count);)
6217 for(;read < pos && line; line = (*&line.prev))
6219 int numBytes = Min(pos - read, x);
6225 /*for(;read < pos && x > 0;)
6235 x = (*&(*&line.prev).count);
6251 editBox.line = editBox.selLine = line;
6252 editBox.x = editBox.selX = x;
6253 editBox.y = editBox.selY = y;
6258 bool Puts(char * string)
6260 EditBox editBox = this.editBox;
6261 BufferLocation start { editBox.line, editBox.y, editBox.x };
6265 editBox.AddS(string);
6267 pos.line = editBox.line;
6271 this.start.AdjustAdd(start, pos);
6272 sel.AdjustAdd(start, pos);
6277 // NOTE: BYTE, NOT UNICODE CHARACTER!
6280 EditBox editBox = this.editBox;
6281 BufferLocation start = { editBox.line, editBox.y, editBox.x };
6286 utf8Bytes[numBytes++] = ch;
6287 utf8Bytes[numBytes] = 0;
6288 if(UTF8Validate(utf8Bytes))
6290 editBox.AddCh(UTF8_GET_CHAR(utf8Bytes, numBytes));
6292 pos.line = editBox.line;
6295 this.start.AdjustAdd(start, pos);
6296 sel.AdjustAdd(start, pos);
6303 bool Getc(char * ch)
6305 return Read(ch, 1, 1) ? true : false;
6308 void DeleteBytes(uint count)
6310 EditBox editBox = this.editBox;
6313 BufferLocation pos { editBox.line, editBox.y, editBox.x };
6314 BufferLocation end = pos;
6319 for(;c < count && end.line && end.x < end.line.count;)
6324 if(c < count && end.line && end.line.next)
6326 end.line = end.line.next;
6335 start.AdjustDelete(pos, end);
6336 sel.AdjustDelete(pos, end);
6338 editBox.DelCh(pos.line, pos.y, pos.x, end.line, end.y, end.x, true);