1 // =================================
   2 // Copyright (c) 2024 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 using System;
   7 using System.Collections;
   8 using System.IO;
   9 
  10 namespace System.Windows
  11 {
  12     public static class WordSeparators
  13     {
  14         static WordSeparators()
  15         {
  16             wordSeparators = u"`~!@#$%^&*()-=+[{]}\\|;:\'\",.<>/?";
  17         }
  18         public static bool IsWordSeparatorChar(uchar c)
  19         {
  20             return wordSeparators.Find(c) != -1;
  21         }
  22         private static ustring wordSeparators;
  23     }
  24 
  25     public bool IsWordSeparator(uchar c)
  26     {
  27         return WordSeparators.IsWordSeparatorChar(c);
  28     }
  29 
  30     public bool IsWhiteSpace(uchar c)
  31     {
  32         auto whiteSpaceResult = System.IsWhiteSpace(c);
  33         if (whiteSpaceResult.Error())
  34         {
  35             return false;
  36         }
  37         return whiteSpaceResult.Value();
  38     }
  39 
  40     public static class CCStopChars
  41     {
  42         static CCStopChars()
  43         {
  44             ccStopChars = u" ,()[]{};\"\'<=|&^!+/%~:";
  45         }
  46         public static bool IsCCStopChar(uchar c)
  47         {
  48             return ccStopChars.Find(c) != -1;
  49         }
  50         private static ustring ccStopChars;
  51     }
  52 
  53     public bool IsCCStopChar(uchar c)
  54     {
  55         return CCStopChars.IsCCStopChar(c);
  56     }
  57     public bool IsEmptyOrSpaceLine(const ustring& line)
  58     {
  59         if (line.IsEmpty()) return true;
  60         for (uchar c : line)
  61         {
  62             if (c != ' ') return false;
  63         }
  64         return true;
  65     }
  66 
  67     public System.Windows.Color DefaultTextViewSelectionBackgroundColor()
  68     {
  69         return System.Windows.Color(153u201u239u);
  70     }
  71 
  72     public class SourcePos
  73     {
  74         public SourcePos() : line(0)column(0)
  75         {
  76         }
  77         public SourcePos(int line_int column_) : line(line_)column(column_)
  78         {
  79         }
  80         public bool IsValid() const
  81         {
  82             return line != 0 && column != 0;
  83         }
  84         public int line;
  85         public int column;
  86     }
  87 
  88     public bool operator==(const SourcePos& leftconst SourcePos& right)
  89     {
  90         return left.line == right.line && left.column == right.column;
  91     }
  92 
  93     public bool operator<(const SourcePos& leftconst SourcePos& right)
  94     {
  95         if (left.line < right.line) return true;
  96         if (left.line > right.line) return false;
  97         return left.column < right.column;
  98     }
  99 
 100     public class Selection
 101     {
 102         public enum Fix : sbyte
 103         {
 104             nonestartend
 105         }
 106         public Selection() : fixed(Fix.none)start()end()
 107         {
 108         }
 109         public bool IsEmpty() const
 110         {
 111             return !start.IsValid() && !end.IsValid();
 112         }
 113         public Fix fixed;
 114         public SourcePos start;
 115         public SourcePos end;
 116     }
 117 
 118     public class SelectionData
 119     {
 120         public SelectionData() : selectedText()indent(0)numTrailingSpaces(0)
 121         {
 122         }
 123         public SelectionData(const ustring& selectedText_int indent_int numTrailingSpaces_) : 
 124             selectedText(selectedText_)indent(indent_)numTrailingSpaces(numTrailingSpaces_)
 125         {
 126         }
 127         public ustring selectedText;
 128         public int indent;
 129         public int numTrailingSpaces;
 130     }
 131 
 132     public const uint defaultCaretTimerPeriod = 5000u;
 133 
 134     public class LineEventArgs
 135     {
 136         public LineEventArgs(int lineIndex_int indentLineIndex_) : lineIndex(lineIndex_)indentLineIndex(indentLineIndex_)
 137         {
 138         }
 139         public int lineIndex;
 140         public int indentLineIndex;
 141     }
 142 
 143     public class delegate void CaretPosChangedEventHandler();
 144     public class delegate void LinesChangedEventHandler();
 145     public class delegate void LineChangedEventHandler(LineEventArgs& args);
 146     public class delegate void LineDeletedEventHandler(LineEventArgs& args);
 147     public class delegate void LineInsertedEventHandler(LineEventArgs& args);
 148     public class delegate void GotoCaretLineEventHandler(ControlEventArgs& args);
 149     public class delegate void SelectionChangedEventHandler();
 150     public class delegate void DirtyChangedEventHandler();
 151     public class delegate void CCDirtyChangedEventHandler();
 152     public class delegate void CCTextChangedEventHandler();
 153     public class delegate void CCEventHandler();
 154     public class delegate void CCNextEventHandler();
 155     public class delegate void CCPrevEventHandler();
 156     public class delegate void CCNextPageEventHandler();
 157     public class delegate void CCPrevPageEventHandler();
 158     public class delegate void CCSelectEventHandler();
 159     public class delegate void EscapePressedHandler();
 160     public class delegate void CopyEventHandler();
 161     public class delegate void CutEventHandler();
 162     public class delegate void PasteEventHandler();
 163 
 164     public ControlCreateParams& TextViewControlCreateParams(ControlCreateParams& controlCreateParams)
 165     {
 166         return controlCreateParams.SetWindowClassName("System.Windows.TextView").SetWindowClassStyle(DoubleClickWindowClassStyle()).
 167             SetWindowStyle(cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP)).
 168             SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW).SetBackgroundColor(Color.White());
 169     }
 170 
 171     public class TextViewCreateParams
 172     {
 173         public TextViewCreateParams(ControlCreateParams& controlCreateParams_) : 
 174             controlCreateParams(controlCreateParams_)
 175             fontFamilyName("Consolas")
 176             fontSize(10.000000f)
 177             padding()
 178             textColor(Color.Black())
 179             selectionBackgroundColor(DefaultTextViewSelectionBackgroundColor())
 180             indentSize(1)
 181         {
 182         }
 183         public TextViewCreateParams& Defaults()
 184         {
 185             return *this;
 186         }
 187         public TextViewCreateParams& SetFontFamilyName(const string& fontFamilyName_)
 188         {
 189             fontFamilyName = fontFamilyName_;
 190             return *this;
 191         }
 192         public TextViewCreateParams& SetFontSize(float fontSize_)
 193         {
 194             fontSize = fontSize_;
 195             return *this;
 196         }
 197         public TextViewCreateParams& SetPadding(const Padding& padding_)
 198         {
 199             padding = padding_;
 200             return *this;
 201         }
 202         public TextViewCreateParams& SetTextColor(const Color& textColor_)
 203         {
 204             textColor = textColor_;
 205             return *this;
 206         }
 207         public TextViewCreateParams& SetSelectionBackgroundColor(const Color& selectionBackgroundColor_)
 208         {
 209             selectionBackgroundColor = selectionBackgroundColor_;
 210             return *this;
 211         }
 212         public TextViewCreateParams& SetIndentSize(int indentSize_)
 213         {
 214             indentSize = indentSize_;
 215             return *this;
 216         }
 217         public ControlCreateParams& controlCreateParams;
 218         public string fontFamilyName;
 219         public float fontSize;
 220         public Padding padding;
 221         public Color textColor;
 222         public Color selectionBackgroundColor;
 223         public int indentSize;
 224     }
 225 
 226     public class TextView : Control
 227     {
 228         private enum Flags : sbyte
 229         {
 230             none = 0changed = 1 << 0painting = 1 << 1readOnly = 1 << 2fixed = 1 << 3dirty = 1 << 4ccdirty = 1 << 5
 231             mouseExtendSelection = 1 << 6ccOpen = 1 << 7
 232         }
 233 
 234         public TextView(const FontFamily& fontFamily_float fontSize_const Color& backgroundColorconst Color& textColor_const Point& location
 235             const Size& sizeDock dockAnchors anchors) : 
 236             base("System.Windows.TextView"DoubleClickWindowClassStyle()
 237                 cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP)DefaultExtendedWindowStyle()backgroundColor
 238             "textView"locationsizedockanchors)flags(Flags.none)drawFormat(StringAlignment.nearStringAlignment.near)
 239             fontFamily(fontFamily_)fontSize(fontSize_)
 240             charWidth(0)charHeight(0)textWidth(0)textHeight(0)maxLineLength(0)cursor()caretLine(1)caretColumn(1)
 241             caretTimerPeriod(defaultCaretTimerPeriod)update(false)topLine(1)topLineDiff(0.000000f)leftCol(1)leftColDiff(0.000000f)
 242             padding()selectionBackgroundColor(DefaultTextViewSelectionBackgroundColor())textColor(textColor_)editCommandList(this)selection()
 243             indentSize(1)
 244         {
 245             auto cursorResult = LoadStandardCursor(StandardCursorId.IDC_IBEAM);
 246             if (cursorResult.Error())
 247             {
 248                 SetErrorId(cursorResult.GetErrorId());
 249                 return;
 250             }
 251             cursor = Rvalue(cursorResult.Value());
 252             auto initResult = Init();
 253             if (initResult.Error())
 254             {
 255                 SetErrorId(initResult.GetErrorId());
 256                 return;
 257             }
 258         }
 259         public TextView(const Point& locationconst Size& sizeDock dockAnchors anchors) : 
 260             this(FontFamily("Consolas")10.000000fColor.White()Color.Black()locationsizedockanchors)
 261         {
 262         }
 263         public TextView(TextViewCreateParams& createParams) : 
 264             base(createParams.controlCreateParams)
 265             flags(Flags.none)
 266             drawFormat(StringAlignment.nearStringAlignment.near)
 267             fontFamily(createParams.fontFamilyName)
 268             fontSize(createParams.fontSize)
 269             charWidth(0)charHeight(0)textWidth(0)textHeight(0)maxLineLength(0)cursor()caretLine(1)caretColumn(1)
 270             caretTimerPeriod(defaultCaretTimerPeriod)update(false)topLine(1)topLineDiff(0.000000f)leftCol(1)leftColDiff(0.000000f)
 271             padding(createParams.padding)selectionBackgroundColor(createParams.selectionBackgroundColor)textColor(createParams.textColor)
 272             editCommandList(this)selection()indentSize(createParams.indentSize)
 273         {
 274             auto cursorResult = LoadStandardCursor(StandardCursorId.IDC_IBEAM);
 275             if (cursorResult.Error())
 276             {
 277                 SetErrorId(cursorResult.GetErrorId());
 278                 return;
 279             }
 280             cursor = Rvalue(cursorResult.Value());
 281             auto initResult = Init();
 282             if (initResult.Error())
 283             {
 284                 SetErrorId(initResult.GetErrorId());
 285             }
 286         }
 287         private Result<bool> Init()
 288         {
 289             uint caretTimeOut = 0u;
 290             bool succeeded = ReadCaretTimeoutFromRegistry(caretTimeOut);
 291             if (succeeded)
 292             {
 293                 caretTimerPeriod = caretTimeOut;
 294             }
 295             Font* font = new Font(fontFamilyfontSizeFontStyle.regularUnit.point);
 296             if (font->Error())
 297             {
 298                 return Result<bool>(ErrorId(font->GetErrorId()));
 299             }
 300             fonts.Add(UniquePtr<Font>(font));
 301             SetChanged();
 302             measureString = "// this file has been semiautomatically generated from \'D:/work/soulng-project/sngcm/ast/AstReader.hpp\' using cpp2cm version 1.0.0";
 303             return Result<bool>(true);
 304         }
 305         public const System.Windows.Color& GetTextColor() const
 306         {
 307             return textColor;
 308         }
 309         public void SetTextColor(const System.Windows.Color& textColor_)
 310         {
 311             textColor = textColor_;
 312         }
 313         public const System.Windows.Color& GetSelectionBackgroundColor() const
 314         {
 315             return selectionBackgroundColor;
 316         }
 317         public void SetSelectionBackgroundColor(const System.Windows.Color& selectionBackgroundColor_)
 318         {
 319             selectionBackgroundColor = selectionBackgroundColor_;
 320         }
 321         public inline List<ustring>& Lines()
 322         {
 323             return lines;
 324         }
 325         public inline const List<ustring>& Lines() const
 326         {
 327             return lines;
 328         }
 329         public void AddLine(const ustring& line)
 330         {
 331             lines.Add(line);
 332             SetMaxLineLength();
 333         }
 334         [nodiscard]
 335         public Result<bool> SetFont(const FontFamily& fontFamily_float fontSize_)
 336         {
 337             fontFamily = fontFamily_;
 338             fontSize = fontSize_;
 339             fonts.Clear();
 340             auto result = OnFontChanged();
 341             if (result.Error())
 342             {
 343                 return Result<bool>(ErrorId(result.GetErrorId()));
 344             }
 345             SetChanged();
 346             return Result<bool>(true);
 347         }
 348         [nodiscard]
 349         protected virtual Result<bool> OnFontChanged()
 350         {
 351             UniquePtr<Font> font = new Font(fontFamilyfontSizeFontStyle.regularUnit.point);
 352             if (font->Error())
 353             {
 354                 return Result<bool>(ErrorId(font->GetErrorId()));
 355             }
 356             fonts.Add(Rvalue(font));
 357             return Result<bool>(true);
 358         }
 359         [nodiscard]
 360         public Result<bool> SetUndoRedoMenuItems(MenuItem* undoMenuItemMenuItem* redoMenuItem)
 361         {
 362             return editCommandList.SetMenuItems(undoMenuItemredoMenuItem);
 363         }
 364         [nodiscard]
 365         public Result<bool> Select()
 366         {
 367             return editCommandList.UpdateMenuItems();
 368         }
 369         [nodiscard]
 370         public Result<bool> Undo()
 371         {
 372             return editCommandList.Undo();
 373         }
 374         [nodiscard]
 375         public Result<bool> Redo()
 376         {
 377             return editCommandList.Redo();
 378         }
 379         public inline float CharWidth() const
 380         {
 381             return charWidth;
 382         }
 383         public inline float CharHeight() const
 384         {
 385             return charHeight;
 386         }
 387         public inline int TextWidth() const
 388         {
 389             return textWidth;
 390         }
 391         public inline int TextHeight() const
 392         {
 393             return textHeight;
 394         }
 395         public inline FontFamily& GetFontFamily() const
 396         {
 397             return fontFamily;
 398         }
 399         public inline float FontSize() const
 400         {
 401             return fontSize;
 402         }
 403         public inline const List<UniquePtr<Font>>& Fonts() const
 404         {
 405             return fonts;
 406         }
 407         public inline List<UniquePtr<Font>>& Fonts()
 408         {
 409             return fonts;
 410         }
 411         [nodiscard]
 412         public Result<bool> SetCaretLineCol(int lineint column)
 413         {
 414             if (caretLine != line || caretColumn != column)
 415             {
 416                 caretLine = line;
 417                 caretColumn = column;
 418                 auto result = SetCaretLocation();
 419                 if (result.Error()) return result;
 420             }
 421             return Result<bool>(true);
 422         }
 423         public Point CaretPos() const
 424         {
 425             int x = cast<int>(padding.left + (caretColumn - 1) * charWidth);
 426             int y = cast<int>(padding.top + (caretLine - 1) * charHeight);
 427             Point loc(xy);
 428             TranslateContentLocation(loc);
 429             return loc;
 430         }
 431         public Point CCPos() const
 432         {
 433             Point ccPos = CaretPos();
 434             ccPos.Offset(0cast<int>(charHeight + 0.500000f));
 435             return ccPos;
 436         }
 437         public inline int TopLine() const
 438         {
 439             return topLine;
 440         }
 441         public inline float TopLineDiff() const
 442         {
 443             return topLineDiff;
 444         }
 445         public inline int LeftColumn() const
 446         {
 447             return leftCol;
 448         }
 449         public inline float LeftColumnDiff() const
 450         {
 451             return leftColDiff;
 452         }
 453         public inline int CaretLine() const
 454         {
 455             return caretLine;
 456         }
 457         public inline int CaretColumn() const
 458         {
 459             return caretColumn - LineNumberFieldLength();
 460         }
 461         [nodiscard]
 462         public Result<bool> SetTopLineCol(int lineint column)
 463         {
 464             Point newOrigin(cast<int>((column - 1) * charWidth)cast<int>((line - 1) * charHeight));
 465             topLine = cast<int>(newOrigin.y / charHeight + 1.500000f);
 466             topLineDiff = (topLine - 1) * charHeight - newOrigin.y;
 467             leftCol = cast<int>(newOrigin.x / charWidth + 1.500000f);
 468             leftColDiff = (leftCol - 1) * charWidth - newOrigin.x;
 469             SetContentLocation(newOrigin);
 470             return Invalidate();
 471         }
 472         public inline int GetVisibleLineCount() const
 473         {
 474             Size size = GetSize();
 475             return cast<int>(size.h / charHeight);
 476         }
 477         public inline int GetVisibleColumnCount() const
 478         {
 479             Size size = GetSize();
 480             return cast<int>(size.w / charWidth);
 481         }
 482         public inline bool IsLinePartiallyVisible(int line) const
 483         {
 484             return line >= (topLine - 1) && line < (topLine + GetVisibleLineCount() + 1);
 485         }
 486         public inline bool IsLineVisible(int line) const
 487         {
 488             return line >= topLine && line < topLine + GetVisibleLineCount();
 489         }
 490         public void GetLineColumn(const Point& contentLocationint& lineint& column) const
 491         {
 492             line = cast<int>(contentLocation.y / charHeight + 1);
 493             column = cast<int>(contentLocation.x / charWidth + 1) - LineNumberFieldLength();
 494         }
 495         [nodiscard]
 496         public Result<bool> EnsureLineVisible(int line)
 497         {
 498             auto result = Invalidate();
 499             if (result.Error()) return result;
 500             Application.ProcessMessages();
 501             if (IsLineVisible(line))
 502             {
 503                 return Result<bool>(true);
 504             }
 505             int tl = Max(cast<int>(1)line - GetVisibleLineCount() / 2);
 506             result = SetTopLineCol(tl1);
 507             if (result.Error()) return result;
 508             return Result<bool>(true);
 509         }
 510         [nodiscard]
 511         public virtual Result<bool> Clear()
 512         {
 513             ustring emptyContent;
 514             return SetTextContent(emptyContent);
 515         }
 516         [nodiscard]
 517         public Result<bool> SetTextContent(const ustring& textContent)
 518         {
 519             maxLineLength = 0;
 520             maxLineIndex = 0;
 521             lines = SplitTextIntoLines(textContent);
 522             lineStartIndeces = CalculateLineStartIndeces(textContent);
 523             SetMaxLineLength();
 524             OnLinesChanged();
 525             SetContentChanged();
 526             SetChanged();
 527             auto result = SetCaretLineCol(11 + LineNumberFieldLength());
 528             if (result.Error()) return result;
 529             OnContentLocationChanged();
 530             result = Invalidate();
 531             if (result.Error()) return result;
 532             update = true;
 533             return Result<bool>(true);
 534         }
 535         public int GetLineLength(int lineNumber)
 536         {
 537             int lineLength = 0;
 538             if (lineNumber >= 1 && lineNumber <= lines.Count())
 539             {
 540                 lineLength = cast<int>(lines[lineNumber - 1].Length());
 541             }
 542             return lineLength;
 543         }
 544         public bool Prev(int& lineint& col)
 545         {
 546             if (col > 1)
 547             {
 548                 --col;
 549                 return true;
 550             }
 551             else
 552             {
 553                 if (line > 1)
 554                 {
 555                     --line;
 556                     int lineLength = GetLineLength(line);
 557                     if (lineLength > 0)
 558                     {
 559                         col = lineLength;
 560                     }
 561                     else
 562                     {
 563                         col = 1;
 564                     }
 565                     return true;
 566                 }
 567             }
 568             return false;
 569         }
 570         public bool Next(int& lineint& col)
 571         {
 572             int lineLength = GetLineLength(line);
 573             if (col < lineLength)
 574             {
 575                 ++col;
 576                 return true;
 577             }
 578             else
 579             {
 580                 if (line < lines.Count())
 581                 {
 582                     ++line;
 583                     col = 1;
 584                     return true;
 585                 }
 586             }
 587             return false;
 588         }
 589         public void PrevWord(int& lineint& col)
 590         {
 591             if (Prev(linecol))
 592             {
 593                 uchar c = GetCharAt(linecol);
 594                 while (System.Windows.IsWhiteSpace(c) || IsWordSeparator(c))
 595                 {
 596                     if (col == 1 || !Prev(linecol))
 597                     {
 598                         return;
 599                     }
 600                     c = GetCharAt(linecol);
 601                 }
 602                 c = GetCharAt(linecol);
 603                 while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
 604                 {
 605                     if (col == 1 || !Prev(linecol))
 606                     {
 607                         return;
 608                     }
 609                     c = GetCharAt(linecol);
 610                 }
 611                 c = GetCharAt(linecol);
 612                 if (System.Windows.IsWhiteSpace(c) || IsWordSeparator(c))
 613                 {
 614                     Next(linecol);
 615                 }
 616             }
 617         }
 618         public void NextWord(int& lineint& col)
 619         {
 620             if (Next(linecol))
 621             {
 622                 uchar c = GetCharAt(linecol);
 623                 while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
 624                 {
 625                     if (col == 1 || !Next(linecol))
 626                     {
 627                         return;
 628                     }
 629                     c = GetCharAt(linecol);
 630                 }
 631                 c = GetCharAt(linecol);
 632                 while (System.Windows.IsWhiteSpace(c))
 633                 {
 634                     if (col == 1 || !Next(linecol))
 635                     {
 636                         return;
 637                     }
 638                     c = GetCharAt(linecol);
 639                 }
 640             }
 641         }
 642         private void GotoCaretLine()
 643         {
 644             ControlEventArgs args(this);
 645             OnGotoCaretLine(args);
 646         }
 647         protected virtual void OnGotoCaretLine(ControlEventArgs& args)
 648         {
 649             gotoCaretLineEvent.Fire(args);
 650         }
 651         public uchar GetCharAt(int lineint column) const
 652         {
 653             if (line < 1)
 654             {
 655                 line = 1;
 656             }
 657             else if (line > lines.Count())
 658             {
 659                 line = cast<int>(lines.Count());
 660             }
 661             int lineLength = GetLineLength(line);
 662             if (column < 1)
 663             {
 664                 column = 1;
 665             }
 666             else
 667             {
 668                 if (lineLength > 0 && column > lineLength)
 669                 {
 670                     column = lineLength;
 671                 }
 672             }
 673             if (line >= 1 && line <= lines.Count() && column >= 1 && column <= lineLength)
 674             {
 675                 return lines[line - 1][column - 1];
 676             }
 677             return '\0';
 678         }
 679         [nodiscard]
 680         public Result<bool> SetCaretPosByCharIndex(int charIndex)
 681         {
 682             List<int>.ConstIterator it = LowerBound(lineStartIndeces.CBegin()lineStartIndeces.CEnd()charIndex);
 683             if (it != lineStartIndeces.CBegin() && it == lineStartIndeces.CEnd())
 684             {
 685                 --it;
 686             }
 687             if (it >= lineStartIndeces.CBegin() && it != lineStartIndeces.CEnd())
 688             {
 689                 if (*it > charIndex)
 690                 {
 691                     --it;
 692                 }
 693             }
 694             int lineNumber = cast<int>(it - lineStartIndeces.CBegin() + 1);
 695             int columnNumber = 1 + LineNumberFieldLength();
 696             if (it >= lineStartIndeces.CBegin() && it != lineStartIndeces.CEnd())
 697             {
 698                 int lineStartCharIndex = *it;
 699                 columnNumber = charIndex - lineStartCharIndex + 1 + LineNumberFieldLength();
 700             }
 701             auto result = SetCaretLineCol(lineNumbercolumnNumber);
 702             if (result.Error()) return result;
 703             result = ScrollToCaret();
 704             if (result.Error()) return result;
 705             return Result<bool>(true);
 706         }
 707         public int GetCharIndex(int lineint column) const
 708         {
 709             if (line >= 1 && line <= lineStartIndeces.Count())
 710             {
 711                 int lineStartIndex = lineStartIndeces[line - 1];
 712                 int lineLength = GetLineLength(line);
 713                 if (column >= 1 && column <= lineLength)
 714                 {
 715                     return lineStartIndex + column - 1;
 716                 }
 717             }
 718             return -1;
 719         }
 720         [nodiscard]
 721         protected override Result<bool> OnPaint(PaintEventArgs& args)
 722         {
 723             auto paintResult = PaintContent(args.graphicsargs.clipRect);
 724             if (paintResult.Error())
 725             {
 726                 return Result<bool>(ErrorId(paintResult.GetErrorId()));
 727             }
 728             return base->OnPaint(args);
 729         }
 730         protected virtual Result<bool> PaintContent(Graphics& graphicsconst Rect& clipRect)
 731         {
 732             if (Changed())
 733             {
 734                 ResetChanged();
 735                 auto measureResult = Measure(graphics);
 736                 if (measureResult.Error())
 737                 {
 738                     return Result<bool>(ErrorId(measureResult.GetErrorId()));
 739                 }
 740                 if (Focused())
 741                 {
 742                     auto result = SetCaretLocation();
 743                     if (result.Error()) return result;
 744                     result = ShowCaret();
 745                     if (result.Error()) return result;
 746                 }
 747                 OnContentLocationChanged();
 748             }
 749             TextRenderingHint prevRenderingHint = graphics.GetTextRenderingHint();
 750             auto setTextRenderingHintResult = graphics.SetTextRenderingHint(TextRenderingHint.clearTypeGridFit);
 751             if (setTextRenderingHintResult.Error())
 752             {
 753                 return Result<bool>(ErrorId(setTextRenderingHintResult.GetErrorId()));
 754             }
 755             auto clearResult = graphics.Clear(BackgroundColor());
 756             if (clearResult.Error())
 757             {
 758                 return Result<bool>(ErrorId(clearResult.GetErrorId()));
 759             }
 760             int n = cast<int>(lines.Count());
 761             SetLineNumberFieldLength(n);
 762             int lineNumberFieldLength = LineNumberFieldLength();
 763             PointF origin(00);
 764             Size size = GetSize();
 765             for (int i = 0; i < n; ++i;)
 766             {
 767                 if (IsLinePartiallyVisible(i + 1))
 768                 {
 769                     auto drawBackgroundResult = DrawSelectionBackground(graphicsi + 1originlineNumberFieldLength);
 770                     if (drawBackgroundResult.Error())
 771                     {
 772                         return Result<bool>(ErrorId(drawBackgroundResult.GetErrorId()));
 773                     }
 774                     auto drawLineResult = DrawLine(graphicsiorigin);
 775                     if (drawLineResult.Error())
 776                     {
 777                         return Result<bool>(ErrorId(drawLineResult.GetErrorId()));
 778                     }
 779                 }
 780                 origin.y = origin.y + charHeight;
 781             }
 782             auto setTextRenderngHintResult = graphics.SetTextRenderingHint(prevRenderingHint);
 783             if (setTextRenderngHintResult.Error())
 784             {
 785                 return Result<bool>(ErrorId(setTextRenderngHintResult.GetErrorId()));
 786             }
 787             return Result<bool>(true);
 788         }
 789         private Result<bool> DrawSelectionBackground(Graphics& graphicsint lineconst PointF& originint lineNumberFieldLength)
 790         {
 791             if (selection.IsEmpty()) return Result<bool>(true);
 792             if (line < selection.start.line) return Result<bool>(true);
 793             if (line > selection.end.line) return Result<bool>(true);
 794             PointF pt(origin);
 795             pt.x = pt.x + charWidth * lineNumberFieldLength;
 796             RectF rect;
 797             if (line > selection.start.line && line < selection.end.line)
 798             {
 799                 int lineLength = Max(1GetLineLength(line));
 800                 rect = RectF(ptSizeF(charWidth * lineLengthcharHeight));
 801             }
 802             else if (selection.start.line == selection.end.line)
 803             {
 804                 pt.x = pt.x + charWidth * (selection.start.column - 1);
 805                 int selectionLength = selection.end.column - selection.start.column;
 806                 rect = RectF(ptSizeF(charWidth * selectionLengthcharHeight));
 807             }
 808             else if (line == selection.start.line)
 809             {
 810                 pt.x = pt.x + charWidth * (selection.start.column - 1);
 811                 int lineLength = GetLineLength(line);
 812                 int selectionLength = lineLength - selection.start.column + 1;
 813                 rect = RectF(ptSizeF(charWidth * selectionLengthcharHeight));
 814             }
 815             else if (line == selection.end.line)
 816             {
 817                 int selectionLength = selection.end.column - 1;
 818                 rect = RectF(ptSizeF(charWidth * selectionLengthcharHeight));
 819             }
 820             Brush* brush = GetOrInsertBrush(selectionBackgroundColor);
 821             auto fillRectangleResult = graphics.FillRectangle(*brushrect);
 822             if (fillRectangleResult.Error())
 823             {
 824                 return Result<bool>(ErrorId(fillRectangleResult.GetErrorId()));
 825             }
 826             return Result<bool>(true);
 827         }
 828         protected virtual Result<bool> DrawLine(Graphics& graphicsint lineIndexconst PointF& origin)
 829         {
 830             if (!fonts.IsEmpty())
 831             {
 832                 PointF pt(origin);
 833                 const ustring& line = lines[lineIndex];
 834                 auto utf8Result = ToUtf8(line);
 835                 if (utf8Result.Error())
 836                 {
 837                     return Result<bool>(ErrorId(utf8Result.GetErrorId()));
 838                 }
 839                 string s(Rvalue(utf8Result.Value()));
 840                 Brush* brush = GetOrInsertBrush(textColor);
 841                 auto drawResult = graphics.DrawString(s*fonts[0]pt*brush);
 842                 if (drawResult.Error())
 843                 {
 844                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
 845                 }
 846             }
 847             return Result<bool>(true);
 848         }
 849         protected virtual Result<bool> Measure(Graphics& graphics)
 850         {
 851             charWidth = 0;
 852             charHeight = 0;
 853             string ms;
 854             bool measure = false;
 855             if (IsFixed())
 856             {
 857                 ms = measureString;
 858                 auto utf32Result = ToUtf32(measureString);
 859                 if (utf32Result.Error())
 860                 {
 861                     return Result<bool>(ErrorId(utf32Result.GetErrorId()));
 862                 }
 863                 maxLineLength = cast<int>(utf32Result.Value().Length());
 864                 measure = true;
 865             }
 866             else
 867             {
 868                 if (!lines.IsEmpty() && maxLineLength > 0)
 869                 {
 870                     auto utf8Result = ToUtf8(lines[maxLineIndex]);
 871                     if (utf8Result.Error())
 872                     {
 873                         return Result<bool>(ErrorId(utf8Result.GetErrorId()));
 874                     }
 875                     ms = Rvalue(utf8Result.Value());
 876                     measure = true;
 877                 }
 878             }
 879             if (measure)
 880             {
 881                 for (const UniquePtr<Font>& font : fonts)
 882                 {
 883                     TextRenderingHint prevRenderingHint = graphics.GetTextRenderingHint();
 884                     auto setTextRenderingHint = graphics.SetTextRenderingHint(TextRenderingHint.clearTypeGridFit);
 885                     if (setTextRenderingHint.Error())
 886                     {
 887                         return Result<bool>(ErrorId(setTextRenderingHint.GetErrorId()));
 888                     }
 889                     auto measureResult = graphics.MeasureStringRectF(ms*fontPointF(00)drawFormat);
 890                     if (measureResult.Error())
 891                     {
 892                         return Result<bool>(ErrorId(measureResult.GetErrorId()));
 893                     }
 894                     RectF charRect = measureResult.Value();
 895                     setTextRenderingHint = graphics.SetTextRenderingHint(prevRenderingHint);
 896                     if (setTextRenderingHint.Error())
 897                     {
 898                         return Result<bool>(ErrorId(setTextRenderingHint.GetErrorId()));
 899                     }
 900                     charWidth = Max(charWidthcharRect.size.w / maxLineLength);
 901                     charHeight = Max(charHeightcharRect.size.h);
 902                 }
 903             }
 904             SetScrollUnits(cast<int>(charHeight + 0.500000f)cast<int>(2 * (charWidth + 0.500000f)));
 905             auto result = SetTextExtent();
 906             if (result.Error()) return result;
 907             return Result<bool>(true);
 908         }
 909         [nodiscard]
 910         public Result<bool> SetTextExtent()
 911         {
 912             textWidth = cast<int>(maxLineLength * charWidth);
 913             textHeight = cast<int>(lines.Count() * charHeight);
 914             auto result = SetContentSize(Size(textWidthtextHeight));
 915             if (result.Error()) return result;
 916             return Result<bool>(true);
 917         }
 918         private void FixColumn(int& columnint line)
 919         {
 920             if (column < 1) column = 1;
 921             int n = GetLineLength(line);
 922             if (column > n + 1)
 923             {
 924                 column = n + 1;
 925             }
 926         }
 927         [nodiscard]
 928         protected override Result<bool> OnMouseDown(MouseEventArgs& args)
 929         {
 930             auto result = base->OnMouseDown(args);
 931             if (result.Error()) return result;
 932             SetFocus(this);
 933             result = ResetSelection();
 934             if (result.Error()) return result;
 935             if (args.buttons == MouseButtons.lbutton && !lines.IsEmpty())
 936             {
 937                 int line = 0;
 938                 int column = 0;
 939                 GetLineColumn(args.locationlinecolumn);
 940                 if (line >= 1 && line <= lines.Count())
 941                 {
 942                     FixColumn(columnline);
 943                     mouseSelectionStart = SourcePos(linecolumn);
 944                     SetMouseExtendSelection();
 945                 }
 946             }
 947             if (charHeight != 0 && charWidth != 0)
 948             {
 949                 if ((args.buttons == MouseButtons.lbutton || args.buttons == MouseButtons.rbutton) && args.clicks == 1)
 950                 {
 951                     Point loc = args.location;
 952                     int lineNumber = cast<int>(loc.y / charHeight) + 1;
 953                     lineNumber = Max(1Min(lineNumbercast<int>(lines.Count())));
 954                     int columnNumber = cast<int>(loc.x / charWidth) + 1;
 955                     columnNumber = Max(columnNumber1 + LineNumberFieldLength());
 956                     int lineLength = GetLineLength(lineNumber);
 957                     columnNumber = Min(columnNumberlineLength + 1 + LineNumberFieldLength());
 958                     if (update)
 959                     {
 960                         update = false;
 961                         auto result = Invalidate();
 962                         if (result.Error()) return result;
 963                     }
 964                     auto result = SetCaretLineCol(lineNumbercolumnNumber);
 965                     if (result.Error()) return result;
 966                     if (args.buttons == MouseButtons.rbutton)
 967                     {
 968                         RightClickEventArgs rightClickArgs(thisloc);
 969                         auto result = OnRightClick(rightClickArgs);
 970                         if (result.Error()) return result;
 971                         if (rightClickArgs.errorId != 0)
 972                         {
 973                             return Result<bool>(ErrorId(rightClickArgs.errorId));
 974                         }
 975                     }
 976                 }
 977             }
 978             return Result<bool>(true);
 979         }
 980         protected override Result<bool> OnMouseMove(MouseEventArgs& args)
 981         {
 982             auto result = base->OnMouseMove(args);
 983             if (result.Error()) return result;
 984             if (MouseExtendSelection())
 985             {
 986                 int line = 0;
 987                 int column = 0;
 988                 GetLineColumn(args.locationlinecolumn);
 989                 if (line >= 1 && line <= lines.Count())
 990                 {
 991                     FixColumn(columnline);
 992                     mouseSelectionEnd = SourcePos(linecolumn);
 993                     if (mouseSelectionStart != mouseSelectionEnd)
 994                     {
 995                         result = ExtendSelection(mouseSelectionStartmouseSelectionEnd);
 996                         if (result.Error()) return result;
 997                         result = InvalidateLines(selection.start.lineselection.end.line);
 998                         if (result.Error()) return result;
 999                     }
1000                 }
1001             }
1002             return Result<bool>(true);
1003         }
1004         [nodiscard]
1005         protected override Result<bool> OnMouseUp(MouseEventArgs& args)
1006         {
1007             auto result = base->OnMouseUp(args);
1008             if (result.Error()) return result;
1009             if (MouseExtendSelection())
1010             {
1011                 ResetMouseExtendSelection();
1012                 int line = 0;
1013                 int column = 0;
1014                 GetLineColumn(args.locationlinecolumn);
1015                 if (line >= 1 && line <= lines.Count())
1016                 {
1017                     FixColumn(columnline);
1018                     mouseSelectionEnd = SourcePos(linecolumn);
1019                     if (mouseSelectionStart != mouseSelectionEnd)
1020                     {
1021                         result = ExtendSelection(mouseSelectionStartmouseSelectionEnd);
1022                         if (result.Error()) return result;
1023                         result = InvalidateLines(selection.start.lineselection.end.line);
1024                         if (result.Error()) return result;
1025                     }
1026                 }
1027             }
1028             return Result<bool>(true);
1029         }
1030         [nodiscard]
1031         protected override Result<bool> OnKeyDown(KeyEventArgs& args)
1032         {
1033             auto result = base->OnKeyDown(args);
1034             if (result.Error()) return result;
1035             if (args.errorId != 0) return Result<bool>(ErrorId(args.errorId));
1036             if (!args.handled)
1037             {
1038                 bool scrolled = false;
1039                 int lineNumber = caretLine;
1040                 int columnNumber = caretColumn;
1041                 switch (args.key)
1042                 {
1043                     case Keys.escape:
1044                     {
1045                         OnEscape();
1046                         args.handled = true;
1047                         break;
1048                     }
1049                     case Keys.home:
1050                     {
1051                         result = ResetSelection();
1052                         if (result.Error()) return result;
1053                         columnNumber = 1 + LineNumberFieldLength();
1054                         args.handled = true;
1055                         break;
1056                     }
1057                     case cast<Keys>(Keys.shiftModifier | Keys.home):
1058                     {
1059                         SourcePos start(CaretLine()CaretColumn());
1060                         SourcePos end(CaretLine()1);
1061                         result = ExtendSelection(startend);
1062                         if (result.Error()) return result;
1063                         columnNumber = 1 + LineNumberFieldLength();
1064                         result = InvalidateLineCol(CaretLine()columnNumber);
1065                         if (result.Error()) return result;
1066                         args.handled = true;
1067                         break;
1068                     }
1069                     case Keys.end:
1070                     {
1071                         result = ResetSelection();
1072                         if (result.Error()) return result;
1073                         columnNumber = GetLineLength(lineNumber) + 1 + LineNumberFieldLength();
1074                         args.handled = true;
1075                         break;
1076                     }
1077                     case cast<Keys>(Keys.shiftModifier | Keys.end):
1078                     {
1079                         SourcePos start(CaretLine()CaretColumn());
1080                         SourcePos end(CaretLine()GetLineLength(lineNumber) + 1);
1081                         result = ExtendSelection(startend);
1082                         if (result.Error()) return result;
1083                         columnNumber = GetLineLength(lineNumber) + 1 + LineNumberFieldLength();
1084                         result = InvalidateLineCol(CaretLine()1 + LineNumberFieldLength());
1085                         if (result.Error()) return result;
1086                         args.handled = true;
1087                         break;
1088                     }
1089                     case Keys.left:
1090                     {
1091                         result = ResetSelection();
1092                         if (result.Error()) return result;
1093                         if (columnNumber > 1 + LineNumberFieldLength())
1094                         {
1095                             --columnNumber;
1096                         }
1097                         else if (lineNumber > 1)
1098                         {
1099                             --lineNumber;
1100                             int lineLength = GetLineLength(lineNumber);
1101                             columnNumber = lineLength + 1 + LineNumberFieldLength();
1102                         }
1103                         args.handled = true;
1104                         break;
1105                     }
1106                     case cast<Keys>(Keys.shiftModifier | Keys.left):
1107                     {
1108                         SourcePos start(CaretLine()CaretColumn());
1109                         if (columnNumber > 1 + LineNumberFieldLength())
1110                         {
1111                             SourcePos end(start);
1112                             --columnNumber;
1113                             --end.column;
1114                             result = ExtendSelection(startend);
1115                             if (result.Error()) return result;
1116                             result = InvalidateLines(selection.start.lineselection.end.line);
1117                             if (result.Error()) return result;
1118                         }
1119                         else if (lineNumber > 1)
1120                         {
1121                             SourcePos end(start);
1122                             --lineNumber;
1123                             --end.line;
1124                             int lineLength = GetLineLength(lineNumber);
1125                             columnNumber = lineLength + 1 + LineNumberFieldLength();
1126                             end.column = lineLength + 1;
1127                             result = ExtendSelection(startend);
1128                             if (result.Error()) return result;
1129                             result = InvalidateLines(selection.start.lineselection.end.line);
1130                             if (result.Error()) return result;
1131                         }
1132                         args.handled = true;
1133                         break;
1134                     }
1135                     case Keys.right:
1136                     {
1137                         result = ResetSelection();
1138                         if (result.Error()) return result;
1139                         if (lineNumber <= lines.Count())
1140                         {
1141                             int lineLength = GetLineLength(lineNumber);
1142                             if (columnNumber < lineLength + 1 + LineNumberFieldLength())
1143                             {
1144                                 ++columnNumber;
1145                             }
1146                             else
1147                             {
1148                                 ++lineNumber;
1149                                 columnNumber = 1 + LineNumberFieldLength();
1150                             }
1151                         }
1152                         args.handled = true;
1153                         break;
1154                     }
1155                     case cast<Keys>(Keys.shiftModifier | Keys.right):
1156                     {
1157                         SourcePos start(CaretLine()CaretColumn());
1158                         if (lineNumber <= lines.Count())
1159                         {
1160                             SourcePos end(start);
1161                             int lineLength = GetLineLength(lineNumber);
1162                             if (columnNumber < lineLength + 1 + LineNumberFieldLength())
1163                             {
1164                                 ++columnNumber;
1165                                 ++end.column;
1166                                 result = ExtendSelection(startend);
1167                                 if (result.Error()) return result;
1168                             }
1169                             else
1170                             {
1171                                 ++lineNumber;
1172                                 ++end.line;
1173                                 columnNumber = 1 + LineNumberFieldLength();
1174                                 end.column = 1;
1175                                 result = ExtendSelection(startend);
1176                                 if (result.Error()) return result;
1177                             }
1178                             result = InvalidateLines(selection.start.lineselection.end.line);
1179                             if (result.Error()) return result;
1180                         }
1181                         args.handled = true;
1182                         break;
1183                     }
1184                     case Keys.down:
1185                     {
1186                         if (CCOpen())
1187                         {
1188                             OnCCNext();
1189                         }
1190                         else
1191                         {
1192                             result = ResetSelection();
1193                             if (result.Error()) return result;
1194                             if (lineNumber < lines.Count())
1195                             {
1196                                 ++lineNumber;
1197                                 int lineLength = GetLineLength(lineNumber);
1198                                 columnNumber = Min(columnNumberlineLength + 1 + LineNumberFieldLength());
1199                             }
1200                             else
1201                             {
1202                                 lineNumber = cast<int>(lines.Count()) + 1;
1203                                 columnNumber = 1 + LineNumberFieldLength();
1204                             }
1205                         }
1206                         args.handled = true;
1207                         break;
1208                     }
1209                     case cast<Keys>(Keys.shiftModifier | Keys.down):
1210                     {
1211                         SourcePos start(CaretLine()CaretColumn());
1212                         SourcePos end(start);
1213                         if (lineNumber < lines.Count())
1214                         {
1215                             ++lineNumber;
1216                             ++end.line;
1217                             int lineLength = GetLineLength(lineNumber);
1218                             columnNumber = Min(columnNumberlineLength + 1 + LineNumberFieldLength());
1219                             end.column = Min(end.columnlineLength + 1);
1220                         }
1221                         else
1222                         {
1223                             lineNumber = cast<int>(lines.Count()) + 1;
1224                             end.line = cast<int>(lines.Count()) + 1;
1225                             columnNumber = 1 + LineNumberFieldLength();
1226                             end.column = 1;
1227                         }
1228                         result = ExtendSelection(startend);
1229                         if (result.Error()) return result;
1230                         result = InvalidateLines(selection.start.lineselection.end.line);
1231                         if (result.Error()) return result;
1232                         args.handled = true;
1233                         break;
1234                     }
1235                     case Keys.up:
1236                     {
1237                         if (CCOpen())
1238                         {
1239                             OnCCPrev();
1240                         }
1241                         else
1242                         {
1243                             result = ResetSelection();
1244                             if (result.Error()) return result;
1245                             if (lineNumber > 1)
1246                             {
1247                                 --lineNumber;
1248                                 int lineLength = GetLineLength(lineNumber);
1249                                 columnNumber = Min(columnNumberlineLength + 1 + LineNumberFieldLength());
1250                             }
1251                         }
1252                         args.handled = true;
1253                         break;
1254                     }
1255                     case cast<Keys>(Keys.shiftModifier | Keys.up):
1256                     {
1257                         if (lineNumber > 1)
1258                         {
1259                             SourcePos start(CaretLine()CaretColumn());
1260                             SourcePos end(start);
1261                             --lineNumber;
1262                             --end.line;
1263                             int lineLength = GetLineLength(lineNumber);
1264                             columnNumber = Min(columnNumberlineLength + 1 + LineNumberFieldLength());
1265                             end.column = Min(end.columnlineLength + 1);
1266                             result = ExtendSelection(startend);
1267                             if (result.Error()) return result;
1268                             result = InvalidateLines(selection.start.lineselection.end.line);
1269                             if (result.Error()) return result;
1270                         }
1271                         args.handled = true;
1272                         break;
1273                     }
1274                     case Keys.pageDown:
1275                     {
1276                         if (CCOpen())
1277                         {
1278                             OnCCNextPage();
1279                         }
1280                         else
1281                         {
1282                             result = ResetSelection();
1283                             if (result.Error()) return result;
1284                             Size size = GetSize();
1285                             int windowLines = cast<int>(size.h / charHeight);
1286                             lineNumber = lineNumber + windowLines;
1287                             lineNumber = Min(lineNumbercast<int>(lines.Count()) + 1);
1288                         }
1289                         args.handled = true;
1290                         break;
1291                     }
1292                     case cast<Keys>(Keys.shiftModifier | Keys.pageDown):
1293                     {
1294                         SourcePos start(CaretLine()CaretColumn());
1295                         SourcePos end(start);
1296                         Size size = GetSize();
1297                         int windowLines = cast<int>(size.h / charHeight);
1298                         lineNumber = lineNumber + windowLines;
1299                         end.line = end.line + windowLines;
1300                         lineNumber = Min(lineNumbercast<int>(lines.Count()) + 1);
1301                         end.line = Min(end.linecast<int>(lines.Count()) + 1);
1302                         result = ExtendSelection(startend);
1303                         if (result.Error()) return result;
1304                         result = InvalidateLines(selection.start.lineselection.end.line);
1305                         if (result.Error()) return result;
1306                         args.handled = true;
1307                         break;
1308                     }
1309                     case Keys.pageUp:
1310                     {
1311                         if (CCOpen())
1312                         {
1313                             OnCCPrevPage();
1314                         }
1315                         else
1316                         {
1317                             result = ResetSelection();
1318                             if (result.Error()) return result;
1319                             Size size = GetSize();
1320                             int windowLines = cast<int>(size.h / charHeight);
1321                             lineNumber = lineNumber - windowLines;
1322                             lineNumber = Max(lineNumber1);
1323                         }
1324                         args.handled = true;
1325                         break;
1326                     }
1327                     case cast<Keys>(Keys.shiftModifier | Keys.pageUp):
1328                     {
1329                         SourcePos start(CaretLine()CaretColumn());
1330                         SourcePos end(start);
1331                         Size size = GetSize();
1332                         int windowLines = cast<int>(size.h / charHeight);
1333                         lineNumber = lineNumber - windowLines;
1334                         end.line = end.line - windowLines;
1335                         lineNumber = Max(lineNumber1);
1336                         end.line = Max(end.line1);
1337                         result = ExtendSelection(startend);
1338                         if (result.Error()) return result;
1339                         result = InvalidateLines(selection.start.lineselection.end.line);
1340                         if (result.Error()) return result;
1341                         args.handled = true;
1342                         break;
1343                     }
1344                     case cast<Keys>(Keys.controlModifier | Keys.a):
1345                     {
1346                         result = ResetSelection();
1347                         if (result.Error()) return result;
1348                         SourcePos start(11);
1349                         SourcePos end(cast<int>(lines.Count() + 1)1);
1350                         result = ExtendSelection(startend);
1351                         if (result.Error()) return result;
1352                         result = InvalidateLines(selection.start.lineselection.end.line);
1353                         if (result.Error()) return result;
1354                         args.handled = true;
1355                         break;
1356                     }
1357                     case cast<Keys>(Keys.controlModifier | Keys.home):
1358                     {
1359                         result = ResetSelection();
1360                         if (result.Error()) return result;
1361                         lineNumber = 1;
1362                         columnNumber = 1 + LineNumberFieldLength();
1363                         args.handled = true;
1364                         break;
1365                     }
1366                     case cast<Keys>(Keys.shiftModifier | Keys.controlModifier | Keys.home):
1367                     {
1368                         SourcePos start(CaretLine()CaretColumn());
1369                         SourcePos end(start);
1370                         lineNumber = 1;
1371                         end.line = 1;
1372                         columnNumber = 1 + LineNumberFieldLength();
1373                         end.column = 1;
1374                         result = ExtendSelection(startend);
1375                         if (result.Error()) return result;
1376                         result = InvalidateLines(selection.start.lineselection.end.line);
1377                         if (result.Error()) return result;
1378                         args.handled = true;
1379                         break;
1380                     }
1381                     case cast<Keys>(Keys.controlModifier | Keys.end):
1382                     {
1383                         result = ResetSelection();
1384                         if (result.Error()) return result;
1385                         lineNumber = cast<int>(lines.Count() + 1);
1386                         columnNumber = 1 + LineNumberFieldLength();
1387                         args.handled = true;
1388                         break;
1389                     }
1390                     case cast<Keys>(Keys.shiftModifier | Keys.controlModifier | Keys.end):
1391                     {
1392                         SourcePos start(CaretLine()CaretColumn());
1393                         SourcePos end(start);
1394                         lineNumber = cast<int>(lines.Count() + 1);
1395                         end.line = cast<int>(lines.Count() + 1);
1396                         columnNumber = 1 + LineNumberFieldLength();
1397                         end.column = 1;
1398                         result = ExtendSelection(startend);
1399                         if (result.Error()) return result;
1400                         result = InvalidateLines(selection.start.lineselection.end.line);
1401                         if (result.Error()) return result;
1402                         args.handled = true;
1403                         break;
1404                     }
1405                     case cast<Keys>(Keys.controlModifier | Keys.left):
1406                     {
1407                         result = ResetSelection();
1408                         if (result.Error()) return result;
1409                         int line = lineNumber;
1410                         int col = columnNumber - LineNumberFieldLength();
1411                         PrevWord(linecol);
1412                         lineNumber = line;
1413                         columnNumber = col + LineNumberFieldLength();
1414                         args.handled = true;
1415                         break;
1416                     }
1417                     case cast<Keys>(Keys.shiftModifier | Keys.controlModifier | Keys.left):
1418                     {
1419                         SourcePos start(CaretLine()CaretColumn());
1420                         SourcePos end(start);
1421                         int line = lineNumber;
1422                         int col = columnNumber - LineNumberFieldLength();
1423                         PrevWord(linecol);
1424                         lineNumber = line;
1425                         end.line = line;
1426                         columnNumber = col + LineNumberFieldLength();
1427                         end.column = col;
1428                         result = ExtendSelection(startend);
1429                         if (result.Error()) return result;
1430                         result = InvalidateLines(selection.start.lineselection.end.line);
1431                         if (result.Error()) return result;
1432                         args.handled = true;
1433                         break;
1434                     }
1435                     case cast<Keys>(Keys.controlModifier | Keys.right):
1436                     {
1437                         result = ResetSelection();
1438                         if (result.Error()) return result;
1439                         int line = lineNumber;
1440                         int col = columnNumber - LineNumberFieldLength();
1441                         NextWord(linecol);
1442                         lineNumber = line;
1443                         columnNumber = col + LineNumberFieldLength();
1444                         args.handled = true;
1445                         break;
1446                     }
1447                     case cast<Keys>(Keys.shiftModifier | Keys.controlModifier | Keys.right):
1448                     {
1449                         SourcePos start(CaretLine()CaretColumn());
1450                         SourcePos end(start);
1451                         int line = lineNumber;
1452                         int col = columnNumber - LineNumberFieldLength();
1453                         NextWord(linecol);
1454                         lineNumber = line;
1455                         end.line = line;
1456                         columnNumber = col + LineNumberFieldLength();
1457                         end.column = col;
1458                         result = ExtendSelection(startend);
1459                         if (result.Error()) return result;
1460                         result = InvalidateLines(selection.start.lineselection.end.line);
1461                         if (result.Error()) return result;
1462                         args.handled = true;
1463                         break;
1464                     }
1465                     case cast<Keys>(Keys.controlModifier | Keys.down):
1466                     {
1467                         if (lines.Count() < GetVisibleLineCount())
1468                         {
1469                             args.handled = true;
1470                             return Result<bool>(true);
1471                         }
1472                         result = ResetSelection();
1473                         if (result.Error()) return result;
1474                         auto scrollResult = ScrollLineDown();
1475                         if (scrollResult.Error())
1476                         {
1477                             return scrollResult;
1478                         }
1479                         scrolled = true;
1480                         args.handled = true;
1481                         break;
1482                     }
1483                     case cast<Keys>(Keys.controlModifier | Keys.up):
1484                     {
1485                         if (lines.Count() < GetVisibleLineCount())
1486                         {
1487                             args.handled = true;
1488                             return Result<bool>(true);
1489                         }
1490                         result = ResetSelection();
1491                         if (result.Error()) return result;
1492                         auto scrollResult = ScrollLineUp();
1493                         if (scrollResult.Error())
1494                         {
1495                             return Result<bool>(ErrorId(scrollResult.GetErrorId()));
1496                         }
1497                         scrolled = true;
1498                         args.handled = true;
1499                         break;
1500                     }
1501                     case cast<Keys>(Keys.controlModifier | Keys.k):
1502                     {
1503                         result = ResetSelection();
1504                         if (result.Error()) return result;
1505                         GotoCaretLine();
1506                         args.handled = true;
1507                         break;
1508                     }
1509                     case Keys.delete_:
1510                     {
1511                         if (!IsReadOnly())
1512                         {
1513                             if (selection.IsEmpty())
1514                             {
1515                                 int lineIndex = caretLine - 1;
1516                                 int columnIndex = CaretColumn() - 1;
1517                                 auto result = AddDeleteCharCommand(lineIndexcolumnIndex);
1518                                 if (result.Error()) return result;
1519                                 result = DeleteChar(lineIndexcolumnIndex00false);
1520                                 if (result.Error()) return result;
1521                             }
1522                             else
1523                             {
1524                                 auto result = AddRemoveSelectionCommand();
1525                                 if (result.Error()) return result;
1526                                 result = RemoveSelection();
1527                                 if (result.Error()) return result;
1528                             }
1529                             args.handled = true;
1530                             return Result<bool>(true);
1531                         }
1532                         break;
1533                     }
1534                     case Keys.back:
1535                     {
1536                         if (!IsReadOnly())
1537                         {
1538                             int lineIndex = caretLine - 1;
1539                             int columnIndex = CaretColumn() - 1;
1540                             result = Backspace(lineIndexcolumnIndex);
1541                             if (result.Error()) return result;
1542                             args.handled = true;
1543                             return Result<bool>(true);
1544                         }
1545                         break;
1546                     }
1547                     case Keys.enter:
1548                     {
1549                         if (CCOpen())
1550                         {
1551                             OnCCSelect();
1552                             args.handled = true;
1553                             return Result<bool>(true);
1554                         }
1555                         else
1556                         {
1557                             if (!IsReadOnly())
1558                             {
1559                                 result = ResetSelection();
1560                                 if (result.Error()) return result;
1561                                 int lineIndex = caretLine - 1;
1562                                 int columnIndex = CaretColumn() - 1;
1563                                 result = AddNewLineCommand(lineIndexcolumnIndex);
1564                                 if (result.Error()) return result;
1565                                 result = NewLine(lineIndexcolumnIndex);
1566                                 if (result.Error()) return result;
1567                                 args.handled = true;
1568                                 return Result<bool>(true);
1569                             }
1570                         }
1571                         break;
1572                     }
1573                     case Keys.tab:
1574                     {
1575                         if (!IsReadOnly())
1576                         {
1577                             if (selection.IsEmpty())
1578                             {
1579                                 int lineIndex = caretLine - 1;
1580                                 int columnIndex = CaretColumn() - 1;
1581                                 result = AddTabCommand(lineIndexcolumnIndex);
1582                                 if (result.Error()) return result;
1583                                 result = Tab(lineIndexcolumnIndex);
1584                                 if (result.Error()) return result;
1585                             }
1586                             else
1587                             {
1588                                 result = AddIndentSelectionCommand();
1589                                 if (result.Error()) return result;
1590                                 result = IndentSelection();
1591                                 if (result.Error()) return result;
1592                             }
1593                             args.handled = true;
1594                             return Result<bool>(true);
1595                         }
1596                         break;
1597                     }
1598                     case cast<Keys>(Keys.shiftModifier | Keys.tab):
1599                     {
1600                         if (!IsReadOnly())
1601                         {
1602                             if (selection.IsEmpty())
1603                             {
1604                                 int lineIndex = caretLine - 1;
1605                                 int columnIndex = CaretColumn() - 1;
1606                                 auto result = AddBacktabCommand(lineIndexcolumnIndex);
1607                                 if (result.Error()) return result;
1608                                 result = Backtab(lineIndexcolumnIndex);
1609                                 if (result.Error()) return result;
1610                             }
1611                             else
1612                             {
1613                                 auto result = AddUnindentSelectionCommand();
1614                                 if (result.Error()) return result;
1615                                 result = UnindentSelection();
1616                                 if (result.Error()) return result;
1617                             }
1618                             args.handled = true;
1619                             return Result<bool>(true);
1620                         }
1621                         break;
1622                     }
1623                     case cast<Keys>(Keys.controlModifier | Keys.insert):
1624                     {
1625                         if (!selection.IsEmpty())
1626                         {
1627                             OnCopy();
1628                         }
1629                         args.handled = true;
1630                         break;
1631                     }
1632                     case cast<Keys>(Keys.shiftModifier | Keys.insert):
1633                     {
1634                         OnPaste();
1635                         args.handled = true;
1636                         break;
1637                     }
1638                     case cast<Keys>(Keys.shiftModifier | Keys.delete_):
1639                     {
1640                         if (!selection.IsEmpty())
1641                         {
1642                             OnCut();
1643                         }
1644                         args.handled = true;
1645                         break;
1646                     }
1647                     case cast<Keys>(Keys.altModifier | Keys.back):
1648                     {
1649                         result = Undo();
1650                         if (result.Error()) return result;
1651                         break;
1652                     }
1653                     case Keys.f4:
1654                     {
1655                         result = Redo();
1656                         if (result.Error()) return result;
1657                         break;
1658                     }
1659                 }
1660                 if (args.handled && !scrolled)
1661                 {
1662                     result = SetCaretLineCol(lineNumbercolumnNumber);
1663                     if (result.Error()) return result;
1664                     result = ScrollToCaret();
1665                     if (result.Error()) return result;
1666                 }
1667             }
1668             return Result<bool>(true);
1669         }
1670         [nodiscard]
1671         protected override Result<bool> OnKeyPress(KeyPressEventArgs& args)
1672         {
1673             auto result = base->OnKeyPress(args);
1674             if (result.Error()) return result;
1675             if (IsReadOnly()) return Result<bool>(false);
1676             if (!args.handled && WinKeyPressed(VK_CONTROL) && args.keyChar == ' ')
1677             {
1678                 OnCC();
1679                 args.handled = true;
1680                 return Result<bool>(true);
1681             }
1682             if (!WinKeyPressed(VK_MENU) && WinKeyPressed(VK_CONTROL))
1683             {
1684                 return Result<bool>(true);
1685             }
1686             if (!args.handled)
1687             {
1688                 editCommandList.BeginGroup();
1689                 if (!selection.IsEmpty())
1690                 {
1691                     result = AddRemoveSelectionCommand();
1692                     if (result.Error()) return result;
1693                     result = RemoveSelection();
1694                     if (result.Error()) return result;
1695                 }
1696                 uchar c(args.keyChar);
1697                 int lineIndex = caretLine - 1;
1698                 int columnIndex = CaretColumn() - 1;
1699                 result = AddInsertCharCommand(lineIndexcolumnIndexc);
1700                 if (result.Error()) return result;
1701                 result = InsertChar(lineIndexcolumnIndexc);
1702                 if (result.Error()) return result;
1703                 result = editCommandList.EndGroup();
1704                 if (result.Error()) return result;
1705                 args.handled = true;
1706             }
1707             return Result<bool>(true);
1708         }
1709         protected virtual void OnEscape()
1710         {
1711             escapePressedEvent.Fire();
1712         }
1713         protected virtual void OnCCNext()
1714         {
1715             ccNextEvent.Fire();
1716         }
1717         protected virtual void OnCCPrev()
1718         {
1719             ccPrevEvent.Fire();
1720         }
1721         protected virtual void OnCCNextPage()
1722         {
1723             ccNextPageEvent.Fire();
1724         }
1725         protected virtual void OnCCPrevPage()
1726         {
1727             ccPrevPageEvent.Fire();
1728         }
1729         protected virtual void OnCCSelect()
1730         {
1731             ccSelectEvent.Fire();
1732         }
1733         [nodiscard]
1734         private Result<bool> AddInsertCharCommand(int lineIndexint columnIndexuchar c)
1735         {
1736             bool removeIndent = false;
1737             while (lineIndex >= lines.Count())
1738             {
1739                 lines.Add(ustring());
1740             }
1741             ustring& line = lines[lineIndex];
1742             if (c == '}' && line.StartsWith(ustring(' 'IndentSize())))
1743             {
1744                 removeIndent = true;
1745             }
1746             auto result = editCommandList.AddCommand(new InsertCharCommand(lineIndexcolumnIndexcremoveIndent));
1747             if (result.Error()) return result;
1748             return Result<bool>(true);
1749         }
1750         [nodiscard]
1751         public Result<bool> InsertChar(int lineIndexint columnIndexuchar c)
1752         {
1753             while (lineIndex >= lines.Count())
1754             {
1755                 lines.Add(ustring());
1756             }
1757             ustring& line = lines[lineIndex];
1758             line.Insert(columnIndexc);
1759             if (c == '}' && line.StartsWith(ustring(' 'IndentSize())))
1760             {
1761                 line.Remove(0IndentSize());
1762                 columnIndex = columnIndex - IndentSize();
1763             }
1764             if (line.Length() > maxLineLength)
1765             {
1766                 maxLineLength = cast<int>(line.Length());
1767                 maxLineIndex = lineIndex;
1768                 auto result = SetTextExtent();
1769                 if (result.Error()) return result;
1770             }
1771             LineEventArgs args(lineIndex-1);
1772             auto result = OnLineChanged(args);
1773             if (result.Error()) return result;
1774             result = SetCaretLineCol(lineIndex + 1columnIndex + 1 + LineNumberFieldLength());
1775             if (result.Error()) return result;
1776             KeyEventArgs rightArgs(Keys.right);
1777             result = OnKeyDown(rightArgs);
1778             if (result.Error()) return result;
1779             SetCCText(linecolumnIndex);
1780             SetDirty();
1781             SetCCDirty();
1782             return Result<bool>(true);
1783         }
1784         [nodiscard]
1785         public Result<bool> InsertText(int lineIndexint columnIndexconst ustring& text)
1786         {
1787             if (text.IsEmpty()) return Result<bool>(false);
1788             editCommandList.BeginGroup();
1789             if (!selection.IsEmpty())
1790             {
1791                 lineIndex = selection.start.line - 1;
1792                 columnIndex = selection.start.column - 1;
1793                 auto result = AddRemoveSelectionCommand();
1794                 if (result.Error()) return result;
1795                 result = RemoveSelection();
1796                 if (result.Error()) return result;
1797             }
1798             List<ustring> linesToInsert = SplitTextIntoLines(text);
1799             if (linesToInsert.Count() == 1 && !IsEmptyOrSpaceLine(lines[lineIndex]))
1800             {
1801                 auto result = AddInsertIntoLineCommand(lineIndexcolumnIndexlinesToInsert.Front());
1802                 if (result.Error()) return result;
1803                 result = InsertIntoLine(lineIndexcolumnIndexlinesToInsert.Front());
1804                 if (result.Error()) return result;
1805             }
1806             else
1807             {
1808                 auto result = AddInsertLinesCommand(lineIndexcolumnIndexlinesToInsert);
1809                 if (result.Error()) return result;
1810                 result = InsertLines(lineIndexcolumnIndexlinesToInsert);
1811                 if (result.Error()) return result;
1812             }
1813             auto result = editCommandList.EndGroup();
1814             if (result.Error()) return result;
1815             return Result<bool>(true);
1816         }
1817         [nodiscard]
1818         public Result<bool> AddInsertIntoLineCommand(int lineIndexint columnIndexconst ustring& text)
1819         {
1820             return editCommandList.AddCommand(new InsertIntoLineCommand(lineIndexcolumnIndextext));
1821         }
1822         [nodiscard]
1823         public Result<bool> InsertIntoLine(int lineIndexint columnIndexconst ustring& text)
1824         {
1825             lines[lineIndex].Insert(columnIndextext);
1826             LineEventArgs args(lineIndex-1);
1827             auto result = OnLineChanged(args);
1828             if (result.Error()) return result;
1829             result = SetTextExtent();
1830             if (result.Error()) return result;
1831             result = SetCaretLineCol(lineIndex + 11 + LineNumberFieldLength() + columnIndex + cast<int>(text.Length()));
1832             if (result.Error()) return result;
1833             result = ScrollToCaret();
1834             if (result.Error()) return result;
1835             result = Invalidate();
1836             if (result.Error()) return result;
1837             result = InvalidateLines(lineIndex + 1lineIndex + 1);
1838             if (result.Error()) return result;
1839             SetDirty();
1840             SetCCDirty();
1841             return Result<bool>(true);
1842         }
1843         [nodiscard]
1844         public Result<bool> RemoveFromLine(int lineIndexint columnIndexlong count)
1845         {
1846             lines[lineIndex].Remove(columnIndexcount);
1847             LineEventArgs args(lineIndex-1);
1848             auto result = OnLineChanged(args);
1849             if (result.Error()) return result;
1850             result = SetTextExtent();
1851             if (result.Error()) return result;
1852             result = SetCaretLineCol(lineIndex + 11 + LineNumberFieldLength() + columnIndex);
1853             if (result.Error()) return result;
1854             result = ScrollToCaret();
1855             if (result.Error()) return result;
1856             result = Invalidate();
1857             if (result.Error()) return result;
1858             result = InvalidateLines(lineIndex + 1lineIndex + 1);
1859             if (result.Error()) return result;
1860             SetDirty();
1861             SetCCDirty();
1862             return Result<bool>(true);
1863         }
1864         [nodiscard]
1865         public Result<bool> AddInsertLinesCommand(int lineIndexint columnIndexconst List<ustring>& linesToInsert)
1866         {
1867             return editCommandList.AddCommand(new InsertLinesCommand(lineIndexcolumnIndexlinesToInsert));
1868         }
1869         [nodiscard]
1870         public Result<bool> InsertLines(int lineIndexint columnIndexconst List<ustring>& linesToInsert)
1871         {
1872             int indent = 0;
1873             if (lineIndex > 0 && lineIndex < lines.Count())
1874             {
1875                 indent = GetIndent(lines[lineIndex - 1]lineIndex - 1);
1876             }
1877             int indentLineIndex = lineIndex - 1;
1878             for (const ustring& lineToInsert : linesToInsert)
1879             {
1880                 lines.Insert(lines.Begin() + lineIndexlineToInsert);
1881                 SetLineNumberFieldLength(cast<int>(lines.Count()));
1882                 LineEventArgs insertedArgs(lineIndexindentLineIndex);
1883                 auto result = OnLineInserted(insertedArgs);
1884                 if (result.Error()) return result;
1885                 ++lineIndex;
1886             }
1887             bool lineDeleted = false;
1888             if (lineIndex < lines.Count())
1889             {
1890                 if (IsEmptyOrSpaceLine(lines[lineIndex]))
1891                 {
1892                     lines.Remove(lines.Begin() + lineIndex);
1893                     LineEventArgs args(lineIndex-1);
1894                     auto result = OnLineDeleted(args);
1895                     if (result.Error()) return result;
1896                     lineDeleted = true;
1897                 }
1898             }
1899             auto result = SetTextExtent();
1900             if (result.Error()) return result;
1901             if (lineDeleted)
1902             {
1903                 result = SetCaretLineCol(lineIndex1 + LineNumberFieldLength() + cast<int>(lines[lineIndex - 1].Length()));
1904                 if (result.Error()) return result;
1905             }
1906             else
1907             {
1908                 result = SetCaretLineCol(lineIndex1 + LineNumberFieldLength() + indent);
1909                 if (result.Error()) return result;
1910             }
1911             result = ScrollToCaret();
1912             if (result.Error()) return result;
1913             result = Invalidate();
1914             if (result.Error()) return result;
1915             result = InvalidateLines(lineIndex + 1cast<int>(lineIndex + linesToInsert.Count() + 1));
1916             if (result.Error()) return result;
1917             SetDirty();
1918             SetCCDirty();
1919             return Result<bool>(true);
1920         }
1921         [nodiscard]
1922         public Result<bool> DeleteLines(int lineIndexint columnIndexconst List<ustring>& linesToDelete)
1923         {
1924             if (linesToDelete.Count() == 1)
1925             {
1926                 ustring& line = lines[lineIndex];
1927                 line.Remove(columnIndexlinesToDelete.Front().Length());
1928                 LineEventArgs args(lineIndex-1);
1929                 auto result = OnLineChanged(args);
1930                 if (result.Error()) return result;
1931             }
1932             else
1933             {
1934                 for (int i = 0; i < linesToDelete.Count(); ++i;)
1935                 {
1936                     lines.Remove(lines.Begin() + lineIndex);
1937                     LineEventArgs deleteArgs(lineIndex-1);
1938                     auto result = OnLineDeleted(deleteArgs);
1939                     if (result.Error()) return result;
1940                 }
1941             }
1942             auto result = SetTextExtent();
1943             if (result.Error()) return result;
1944             SetLineNumberFieldLength(cast<int>(lines.Count()));
1945             result = SetCaretLineCol(lineIndex1 + LineNumberFieldLength());
1946             if (result.Error()) return result;
1947             result = ScrollToCaret();
1948             if (result.Error()) return result;
1949             result = Invalidate();
1950             if (result.Error()) return result;
1951             SetDirty();
1952             SetCCDirty();
1953             return Result<bool>(true);
1954         }
1955         [nodiscard]
1956         private Result<bool> AddDeleteCharCommand(int lineIndexint columnIndex)
1957         {
1958             if (lineIndex >= lines.Count()) return Result<bool>(false);
1959             ustring& line = lines[lineIndex];
1960             if (columnIndex < line.Length())
1961             {
1962                 uchar c = line[columnIndex];
1963                 auto result = editCommandList.AddCommand(new DeleteCharCommand(lineIndexcolumnIndexc));
1964                 if (result.Error()) return result;
1965             }
1966             else
1967             {
1968                 if (lines.Count() > lineIndex + 1)
1969                 {
1970                     auto result = editCommandList.AddCommand(new DeleteCharCommand(lineIndexcolumnIndex'\0'));
1971                     if (result.Error()) return result;
1972                 }
1973             }
1974             return Result<bool>(true);
1975         }
1976         [nodiscard]
1977         public Result<bool> DeleteChar(int lineIndexint columnIndexint indentint numSpacesbool removeIndent)
1978         {
1979             if (lineIndex >= lines.Count()) return Result<bool>(false);
1980             ustring& line = lines[lineIndex];
1981             if (removeIndent)
1982             {
1983                 line.Insert(0ustring(' 'IndentSize()));
1984             }
1985             if (columnIndex < line.Length())
1986             {
1987                 line.Remove(columnIndex1);
1988                 LineEventArgs args(lineIndex-1);
1989                 auto result = OnLineChanged(args);
1990                 if (result.Error()) return result;
1991                 SetCCText(linecolumnIndex);
1992             }
1993             else
1994             {
1995                 if (lines.Count() > lineIndex + 1)
1996                 {
1997                     if (numSpaces > 0)
1998                     {
1999                         line.Append(ustring(' 'numSpaces));
2000                     }
2001                     ustring nextLine = lines[lineIndex + 1];
2002                     line.Append(nextLine.Substring(indent));
2003                     LineEventArgs changedArgs(lineIndex-1);
2004                     auto result = OnLineChanged(changedArgs);
2005                     if (result.Error()) return result;
2006                     lines.Remove(lines.Begin() + lineIndex + 1);
2007                     LineEventArgs deletedArgs(lineIndex + 1-1);
2008                     result = OnLineDeleted(deletedArgs);
2009                     if (result.Error()) return result;
2010                     result = SetTextExtent();
2011                     if (result.Error()) return result;
2012                     ResetCCText();
2013                 }
2014             }
2015             auto result = SetCaretLineCol(lineIndex + 1columnIndex + 1 + LineNumberFieldLength());
2016             if (result.Error()) return result;
2017             SetDirty();
2018             SetCCDirty();
2019             return Result<bool>(true);
2020         }
2021         [nodiscard]
2022         private Result<bool> Backspace(int lineIndexint columnIndex)
2023         {
2024             if (lineIndex != 0 || columnIndex != 0)
2025             {
2026                 KeyEventArgs leftArgs(Keys.left);
2027                 auto result = OnKeyDown(leftArgs);
2028                 if (result.Error()) return result;
2029                 KeyEventArgs deleteArgs(Keys.delete_);
2030                 result = OnKeyDown(deleteArgs);
2031                 if (result.Error()) return result;
2032             }
2033             return Result<bool>(true);
2034         }
2035         protected virtual int GetIndent(const ustring& lineint lineIndex)
2036         {
2037             for (int i = 0; i < line.Length(); ++i;)
2038             {
2039                 if (line[i] != ' ')
2040                 {
2041                     return i;
2042                 }
2043             }
2044             return 0;
2045         }
2046         protected virtual int RemoveIndent(int lineIndex) const
2047         {
2048             return 0;
2049         }
2050         [nodiscard]
2051         public Result<bool> AddNewLineCommand(int lineIndexint columnIndex)
2052         {
2053             while (lineIndex >= lines.Count())
2054             {
2055                 lines.Add(ustring());
2056             }
2057             ustring& line = lines[lineIndex];
2058             int indent = 0;
2059             bool countIndent = false;
2060             int i = columnIndex - 1;
2061             while (i >= 0)
2062             {
2063                 if (line[i] != ' ')
2064                 {
2065                     countIndent = true;
2066                     break;
2067                 }
2068                 --i;
2069             }
2070             if (countIndent)
2071             {
2072                 for (int i = 0; i < columnIndex - 1; ++i;)
2073                 {
2074                     if (line[i] == ' ') ++indent; else break;
2075                 }
2076             }
2077             int numSpaces = 0;
2078             i = columnIndex - 1;
2079             while (i >= 0 && line[i] == ' ')
2080             {
2081                 ++numSpaces;
2082                 --i;
2083             }
2084             auto result = editCommandList.AddCommand(new NewLineCommand(lineIndexcolumnIndexindentnumSpaces));
2085             if (result.Error()) return result;
2086             return Result<bool>(true);
2087         }
2088         [nodiscard]
2089         public Result<bool> NewLine(int lineIndexint columnIndex)
2090         {
2091             while (lineIndex >= lines.Count())
2092             {
2093                 lines.Add(ustring());
2094             }
2095             ustring& line = lines[lineIndex];
2096             if (columnIndex > line.Length())
2097             {
2098                 columnIndex = cast<int>(line.Length());
2099             }
2100             ustring toInsert = System.Windows.TrimEnd(line.Substring(columnIndex));
2101             bool lineChanged = false;
2102             if (!toInsert.IsEmpty())
2103             {
2104                 line.Remove(columnIndexline.Length() - columnIndex);
2105                 lineChanged = true;
2106             }
2107             ustring trimmedLine = System.Windows.TrimEnd(line);
2108             if (trimmedLine.Length() != line.Length())
2109             {
2110                 Swap(linetrimmedLine);
2111                 lineChanged = true;
2112             }
2113             if (lineChanged)
2114             {
2115                 LineEventArgs changedArgs(lineIndex-1);
2116                 auto result = OnLineChanged(changedArgs);
2117                 if (result.Error()) return result;
2118             }
2119             lines.Insert(lines.Begin() + lineIndex + 1toInsert);
2120             SetLineNumberFieldLength(cast<int>(lines.Count()));
2121             int indentLineIndex = lineIndex;
2122             while (indentLineIndex >= 0 && IsEmptyOrSpaceLine(lines[indentLineIndex]))
2123             {
2124                 --indentLineIndex;
2125             }
2126             if (indentLineIndex < 0)
2127             {
2128                 indentLineIndex = 0;
2129             }
2130             LineEventArgs insertedArgs(lineIndex + 1indentLineIndex);
2131             auto result = OnLineInserted(insertedArgs);
2132             if (result.Error()) return result;
2133             result = SetTextExtent();
2134             if (result.Error()) return result;
2135             result = SetCaretLineCol(lineIndex + 1 + 11 + LineNumberFieldLength() + GetIndent(lines[indentLineIndex]indentLineIndex));
2136             if (result.Error()) return result;
2137             result = ScrollToCaret();
2138             if (result.Error()) return result;
2139             result = Invalidate();
2140             if (result.Error()) return result;
2141             SetDirty();
2142             SetCCDirty();
2143             return Result<bool>(true);
2144         }
2145         public inline int IndentSize() const
2146         {
2147             return indentSize;
2148         }
2149         public void SetIndentSize(int indentSize_)
2150         {
2151             indentSize = indentSize_;
2152         }
2153         [nodiscard]
2154         private Result<bool> AddTabCommand(int lineIndexint columnIndex)
2155         {
2156             return editCommandList.AddCommand(new TabCommand(lineIndexcolumnIndex));
2157         }
2158         [nodiscard]
2159         public Result<bool> Tab(int lineIndexint columnIndex)
2160         {
2161             while (lineIndex >= lines.Count())
2162             {
2163                 lines.Add(ustring());
2164             }
2165             ustring& line = lines[lineIndex];
2166             line.Insert(columnIndexustring(' 'IndentSize()));
2167             if (line.Length() > maxLineLength)
2168             {
2169                 maxLineLength = cast<int>(line.Length());
2170                 maxLineIndex = lineIndex;
2171                 auto result = SetTextExtent();
2172                 if (result.Error()) return result;
2173             }
2174             LineEventArgs args(lineIndex-1);
2175             auto result = OnLineChanged(args);
2176             if (result.Error()) return result;
2177             result = SetCaretLineCol(lineIndex + 1columnIndex + 1 + LineNumberFieldLength() + IndentSize());
2178             if (result.Error()) return result;
2179             result = ScrollToCaret();
2180             if (result.Error()) return result;
2181             result = InvalidateLineCol(lineIndex + 1columnIndex + 1);
2182             if (result.Error()) return result;
2183             SetDirty();
2184             SetCCDirty();
2185             return Result<bool>(true);
2186         }
2187         [nodiscard]
2188         private Result<bool> AddBacktabCommand(int lineIndexint columnIndex)
2189         {
2190             ustring& line = lines[lineIndex];
2191             int targetCol = Max(cast<int>(1)columnIndex - IndentSize() + 1);
2192             int numSpaces = 0;
2193             for (int i = columnIndex; i >= targetCol; --i;)
2194             {
2195                 if (line[i - 1] == ' ')
2196                 {
2197                     ++numSpaces;
2198                 }
2199                 else
2200                 {
2201                     break;
2202                 }
2203             }
2204             return editCommandList.AddCommand(new BacktabCommand(lineIndexcolumnIndexnumSpaces));
2205         }
2206         [nodiscard]
2207         public Result<bool> Backtab(int lineIndexint columnIndex)
2208         {
2209             if (lineIndex >= lines.Count()) return Result<bool>(false);
2210             ustring& line = lines[lineIndex];
2211             int targetCol = Max(cast<int>(1)columnIndex - IndentSize() + 1);
2212             int numSpaces = 0;
2213             int col = 0;
2214             for (int i = columnIndex; i >= targetCol; --i;)
2215             {
2216                 if (line[i - 1] == ' ')
2217                 {
2218                     col = i - 1;
2219                     ++numSpaces;
2220                 }
2221                 else
2222                 {
2223                     break;
2224                 }
2225             }
2226             if (numSpaces > 0)
2227             {
2228                 line.Remove(colnumSpaces);
2229                 LineEventArgs args(lineIndex-1);
2230                 auto result = OnLineChanged(args);
2231                 if (result.Error()) return result;
2232                 result = SetCaretLineCol(lineIndex + 1columnIndex + 1 + LineNumberFieldLength() - numSpaces);
2233                 if (result.Error()) return result;
2234                 result = ScrollToCaret();
2235                 if (result.Error()) return result;
2236                 result = InvalidateLineCol(lineIndex + 11 + col + LineNumberFieldLength());
2237                 if (result.Error()) return result;
2238                 SetDirty();
2239                 SetCCDirty();
2240             }
2241             return Result<bool>(true);
2242         }
2243         [nodiscard]
2244         public Result<bool> AddSpaces(int lineIndexint columnIndexint numSpaces)
2245         {
2246             ustring& line = lines[lineIndex];
2247             line.Insert(columnIndex - numSpacesustring(' 'numSpaces));
2248             LineEventArgs args(lineIndex-1);
2249             auto result = OnLineChanged(args);
2250             result = SetCaretLineCol(lineIndex + 1columnIndex + 1 + LineNumberFieldLength() - numSpaces);
2251             if (result.Error()) return result;
2252             result = ScrollToCaret();
2253             if (result.Error()) return result;
2254             result = InvalidateLineCol(lineIndex + 11 + LineNumberFieldLength());
2255             if (result.Error()) return result;
2256             SetDirty();
2257             SetCCDirty();
2258             return Result<bool>(true);
2259         }
2260         [nodiscard]
2261         public Result<bool> RemoveSpaces(int lineIndexint columnIndexint numSpaces)
2262         {
2263             ustring& line = lines[lineIndex];
2264             line.Remove(columnIndex - numSpacesnumSpaces);
2265             LineEventArgs args(lineIndex-1);
2266             auto result = OnLineChanged(args);
2267             if (result.Error()) return result;
2268             result = SetCaretLineCol(lineIndex + 1columnIndex + 1 + LineNumberFieldLength() - numSpaces);
2269             if (result.Error()) return result;
2270             result = ScrollToCaret();
2271             if (result.Error()) return result;
2272             result = InvalidateLineCol(lineIndex + 11 + LineNumberFieldLength());
2273             if (result.Error()) return result;
2274             SetDirty();
2275             SetCCDirty();
2276             return Result<bool>(true);
2277         }
2278         protected virtual void OnCopy()
2279         {
2280             copyEvent.Fire();
2281         }
2282         protected virtual void OnPaste()
2283         {
2284             pasteEvent.Fire();
2285         }
2286         protected virtual void OnCut()
2287         {
2288             cutEvent.Fire();
2289         }
2290         protected override void SetContentLocationInternal(const Point& contentLocation)
2291         {
2292             topLine = cast<int>(contentLocation.y / charHeight + 1.500000f);
2293             topLineDiff = (topLine - 1) * charHeight - contentLocation.y;
2294             leftCol = cast<int>(contentLocation.x / charWidth + 1.500000f);
2295             leftColDiff = (leftCol - 1) * charWidth - contentLocation.x;
2296             base->SetContentLocationInternal(contentLocation);
2297         }
2298         [nodiscard]
2299         public Result<bool> ScrollToCaret()
2300         {
2301             if (lines.IsEmpty() || charHeight == 0) return Result<bool>(false);
2302             Size size = GetSize();
2303             int windowLines = cast<int>(size.h / charHeight);
2304             int windowCols = cast<int>(size.w / charWidth);
2305             Point currentOrigin = ContentLocation();
2306             int topLineNumber = cast<int>(currentOrigin.y / charHeight + 1);
2307             int leftColNumber = cast<int>(currentOrigin.x / charWidth + 1);
2308             int oldTopLineNumber = topLineNumber;
2309             if (caretLine > topLineNumber)
2310             {
2311                 while (caretLine - topLineNumber + 1 >= windowLines)
2312                 {
2313                     ++topLineNumber;
2314                 }
2315             }
2316             else if (caretLine < topLineNumber)
2317             {
2318                 topLineNumber = caretLine;
2319             }
2320             else
2321             {
2322                 topLineNumber = Max(cast<int>(1)caretLine - 1);
2323             }
2324             int oldLeftColNumber = leftColNumber;
2325             if (caretColumn >= leftColNumber)
2326             {
2327                 while (caretColumn - leftColNumber + 2 >= windowCols)
2328                 {
2329                     ++leftColNumber;
2330                 }
2331             }
2332             else
2333             {
2334                 if (caretColumn == 1 + LineNumberFieldLength())
2335                 {
2336                     leftColNumber = 1;
2337                 }
2338                 else
2339                 {
2340                     leftColNumber = caretColumn;
2341                 }
2342             }
2343             if (update || caretLine == 1 || topLineNumber != oldTopLineNumber || leftColNumber != oldLeftColNumber)
2344             {
2345                 update = false;
2346                 Point newOrigin(cast<int>((leftColNumber - 1) * charWidth)cast<int>((topLineNumber - 1) * charHeight));
2347                 topLine = topLineNumber;
2348                 topLineDiff = 0.000000f;
2349                 leftCol = leftColNumber;
2350                 leftColDiff = 0.000000f;
2351                 SetContentLocation(newOrigin);
2352                 auto result = SetCaretLocation();
2353                 if (result.Error()) return result;
2354                 result = Invalidate();
2355                 if (result.Error()) return result;
2356             }
2357             return Result<bool>(true);
2358         }
2359         public virtual int LineNumberFieldLength() const
2360         {
2361             return 0;
2362         }
2363         protected virtual void SetLineNumberFieldLength(int lineCount)
2364         {
2365         }
2366         [nodiscard]
2367         protected override Result<bool> CreateCaret()
2368         {
2369             if (charHeight != 0)
2370             {
2371                 auto result = System.Windows.API.CreateCaret(Handle()null1cast<int>(charHeight));
2372                 if (result.Error()) return result;
2373             }
2374             else
2375             {
2376                 auto result = base->CreateCaret();
2377                 if (result.Error()) return result;
2378             }
2379             auto result = SetTimer(1ucaretTimerPeriod);
2380             if (result.Error()) return result;
2381             return Result<bool>(true);
2382         }
2383         [nodiscard]
2384         protected override Result<bool> OnTimer(TimerEventArgs& args)
2385         {
2386             auto result = base->OnTimer(args);
2387             if (result.Error()) return result;
2388             if (Focused())
2389             {
2390                 auto result = HideCaret();
2391                 if (result.Error()) return result;
2392                 result = ShowCaret();
2393                 if (result.Error()) return result;
2394             }
2395             return Result<bool>(true);
2396         }
2397         [nodiscard]
2398         protected override Result<bool> OnLostFocus()
2399         {
2400             auto result = base->OnLostFocus();
2401             if (result.Error()) return result;
2402             result = KillTimer(1u);
2403             if (result.Error()) return result;
2404             return Result<bool>(true);
2405         }
2406         [nodiscard]
2407         protected override Result<bool> OnClick(ClickEventArgs& args)
2408         {
2409             auto result = base->OnClick(args);
2410             if (result.Error()) return result;
2411             SetFocus();
2412             result = ResetSelection();
2413             if (result.Error()) return result;
2414             return Result<bool>(true);
2415         }
2416         [nodiscard]
2417         protected override Result<bool> OnMouseDoubleClick(MouseEventArgs& args)
2418         {
2419             auto result = base->OnMouseDoubleClick(args);
2420             if (result.Error()) return result;
2421             ResetMouseExtendSelection();
2422             mouseSelectionStart = SourcePos();
2423             mouseSelectionEnd = SourcePos();
2424             SetFocus();
2425             result = ResetSelection();
2426             if (result.Error()) return result;
2427             int line = 0;
2428             int column = 0;
2429             GetLineColumn(args.locationlinecolumn);
2430             uchar c = GetCharAt(linecolumn);
2431             while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
2432             {
2433                 if (column == 1 || !Prev(linecolumn))
2434                 {
2435                     break;
2436                 }
2437                 c = GetCharAt(linecolumn);
2438             }
2439             c = GetCharAt(linecolumn);
2440             if (System.Windows.IsWhiteSpace(c) || IsWordSeparator(c))
2441             {
2442                 Next(linecolumn);
2443             }
2444             SourcePos start(linecolumn);
2445             c = GetCharAt(linecolumn);
2446             while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
2447             {
2448                 if (!Next(linecolumn))
2449                 {
2450                     break;
2451                 }
2452                 c = GetCharAt(linecolumn);
2453             }
2454             if (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
2455             {
2456                 column = column + 1;
2457             }
2458             SourcePos end(linecolumn);
2459             result = ExtendSelection(startend);
2460             if (result.Error()) return result;
2461             result = SetCaretLineCol(start.linestart.column + LineNumberFieldLength());
2462             if (result.Error()) return result;
2463             result = ScrollToCaret();
2464             if (result.Error()) return result;
2465             result = InvalidateLineCol(start.linestart.column);
2466             if (result.Error()) return result;
2467             return Result<bool>(true);
2468         }
2469         protected override Result<bool> SetCursor()
2470         {
2471             SetCursor(&cursor);
2472             return Result<bool>(true);
2473         }
2474         [nodiscard]
2475         protected override Result<bool> SetCaretLocation()
2476         {
2477             int x = cast<int>(padding.left + (caretColumn - 1) * charWidth);
2478             int y = cast<int>(padding.top + (caretLine - 1) * charHeight);
2479             Point caretPos(xy);
2480             TranslateContentLocation(caretPos);
2481             if (CaretCreated())
2482             {
2483                 auto result = SetCaretPos(caretPos);
2484                 if (result.Error()) return result;
2485                 OnCaretPosChanged();
2486             }
2487             return Result<bool>(true);
2488         }
2489         protected virtual void OnCaretPosChanged()
2490         {
2491             caretPosChangedEvent.Fire();
2492         }
2493         protected virtual void OnLinesChanged()
2494         {
2495             linesChangedEvent.Fire();
2496         }
2497         [nodiscard]
2498         protected virtual Result<bool> OnLineChanged(LineEventArgs& args)
2499         {
2500             lineChangedEvent.Fire(args);
2501             return InvalidateLineCol(args.lineIndex + 11 + LineNumberFieldLength());
2502         }
2503         [nodiscard]
2504         protected virtual Result<bool> OnLineDeleted(LineEventArgs& args)
2505         {
2506             lineDeletedEvent.Fire(args);
2507             return Invalidate();
2508         }
2509         [nodiscard]
2510         protected virtual Result<bool> OnLineInserted(LineEventArgs& args)
2511         {
2512             lineInsertedEvent.Fire(args);
2513             int lineIndex = args.lineIndex;
2514             ustring& line = Lines()[lineIndex];
2515             int indent = 0;
2516             if (args.indentLineIndex >= 0 && args.indentLineIndex < Lines().Count())
2517             {
2518                 indent = GetIndent(Lines()[args.indentLineIndex]args.indentLineIndex);
2519             }
2520             if (indent != 0)
2521             {
2522                 line.Insert(0ustring(' 'indent));
2523             }
2524             return Invalidate();
2525         }
2526         public inline int MaxLineLength() const
2527         {
2528             return maxLineLength;
2529         }
2530         public inline int MaxLineIndex() const
2531         {
2532             return maxLineIndex;
2533         }
2534         protected void SetMaxLineLength()
2535         {
2536             maxLineLength = 0;
2537             maxLineIndex = 0;
2538             int n = cast<int>(lines.Count());
2539             for (int i = 0; i < n; ++i;)
2540             {
2541                 const ustring& line = lines[i];
2542                 if (line.Length() > maxLineLength)
2543                 {
2544                     maxLineLength = cast<int>(line.Length());
2545                     maxLineIndex = i;
2546                 }
2547             }
2548         }
2549         public inline const Padding& GetPadding() const
2550         {
2551             return padding;
2552         }
2553         [nodiscard]
2554         public Result<bool> SetPadding(const Padding& padding_)
2555         {
2556             if (padding != padding_)
2557             {
2558                 padding = padding_;
2559                 auto result = Invalidate();
2560                 if (result.Error()) return result;
2561             }
2562             return Result<bool>(true);
2563         }
2564         protected virtual void OnSelectionChanged()
2565         {
2566             selectionChangedEvent.Fire();
2567         }
2568         protected virtual void OnDirtyChanged()
2569         {
2570             dirtyChangedEvent.Fire();
2571         }
2572         protected virtual void OnCCDirtyChanged()
2573         {
2574             ccdirtyChangedEvent.Fire();
2575         }
2576         protected virtual void OnCC()
2577         {
2578             ccEvent.Fire();
2579         }
2580         [nodiscard]
2581         public Result<bool> SaveText(const string& filePath)
2582         {
2583             auto fileResult = File.CreateText(filePath);
2584             if (fileResult.Error())
2585             {
2586                 return Result<bool>(ErrorId(fileResult.GetErrorId()));
2587             }
2588             StreamWriter& writer = fileResult.Value();
2589             for (const ustring& line : lines)
2590             {
2591                 auto result = writer.WriteLine(line);
2592                 if (result.Error()) return result;
2593             }
2594             return Result<bool>(true);
2595         }
2596         public void SetCCText(const ustring& lineint columnIndex)
2597         {
2598             cctext.Clear();
2599             while (columnIndex >= line.Length())
2600             {
2601                 --columnIndex;
2602             }
2603             while (columnIndex >= 0)
2604             {
2605                 if (IsCCStopChar(line[columnIndex]))
2606                 {
2607                     break;
2608                 }
2609                 cctext.Append(line[columnIndex]);
2610                 --columnIndex;
2611             }
2612             Reverse(cctext.Begin()cctext.End());
2613             OnCCTextChanged();
2614         }
2615         public void ResetCCText()
2616         {
2617             cctext.Clear();
2618             OnCCTextChanged();
2619         }
2620         protected virtual void OnCCTextChanged()
2621         {
2622             cctextChangedEvent.Fire();
2623         }
2624         public inline const ustring& GetCCText() const
2625         {
2626             return cctext;
2627         }
2628         [nodiscard]
2629         public Result<bool> ReplaceCCText(const ustring& replacement)
2630         {
2631             int caretLineIndex = Max(0caretLine - 1);
2632             int caretColumnIndex = Max(0caretColumn - 1);
2633             if (caretLineIndex < lines.Count())
2634             {
2635                 ustring& line = lines[caretLineIndex];
2636                 while (caretColumnIndex > line.Length())
2637                 {
2638                     --caretColumnIndex;
2639                 }
2640                 int endColumnIndex = Max(0caretColumnIndex);
2641                 while (caretColumnIndex > 0)
2642                 {
2643                     if (caretColumnIndex < line.Length())
2644                     {
2645                         if (IsCCStopChar(line[caretColumnIndex]))
2646                         {
2647                             ++caretColumnIndex;
2648                             break;
2649                         }
2650                     }
2651                     --caretColumnIndex;
2652                 }
2653                 int startColumndIndex = Max(0caretColumnIndex);
2654                 line = line.Substring(0startColumndIndex) + replacement + line.Substring(endColumnIndex);
2655                 int newEndColumnIndex = startColumndIndex + cast<int>(replacement.Length());
2656                 LineEventArgs args(caretLineIndex-1);
2657                 auto result = OnLineChanged(args);
2658                 if (result.Error()) return result;
2659                 result = SetCaretLineCol(caretLineIndex + 1newEndColumnIndex + 1 + LineNumberFieldLength());
2660                 if (result.Error()) return result;
2661                 result = ScrollToCaret();
2662                 if (result.Error()) return result;
2663                 result = Invalidate();
2664                 if (result.Error()) return result;
2665                 SetDirty();
2666                 SetCCDirty();
2667             }
2668             return Result<bool>(true);
2669         }
2670         public ustring GetCursorText() const
2671         {
2672             ustring cursorText;
2673             int caretIndex = Max(0caretColumn - 1);
2674             long n = lines.Count();
2675             for (long i = 0; i < n; ++i;)
2676             {
2677                 const ustring& line = lines[i];
2678                 int lineNumber = cast<int>(i + 1);
2679                 if (lineNumber == caretLine)
2680                 {
2681                     int startIndex = caretIndex;
2682                     while (startIndex > 0)
2683                     {
2684                         if (startIndex < line.Length())
2685                         {
2686                             bool exit = false;
2687                             switch (line[startIndex])
2688                             {
2689                                 case ';':
2690                                 case '{':
2691                                 case '}':
2692                                 {
2693                                     ++startIndex;
2694                                     exit = true;
2695                                     break;
2696                                 }
2697                             }
2698                             if (exit)
2699                             {
2700                                 break;
2701                             }
2702                         }
2703                         --startIndex;
2704                     }
2705                     cursorText.Append(line.Substring(0startIndex)).Append('$').Append(line.Substring(caretIndex)).Append('\n');
2706                 }
2707                 else
2708                 {
2709                     cursorText.Append(line).Append('\n');
2710                 }
2711             }
2712             return cursorText;
2713         }
2714         [nodiscard]
2715         public Result<bool> SetSelection(const Selection& selection_)
2716         {
2717             selection = selection_;
2718             auto result = Invalidate();
2719             if (result.Error()) return result;
2720             return Result<bool>(true);
2721         }
2722         [nodiscard]
2723         public Result<bool> ResetSelection()
2724         {
2725             if (!selection.IsEmpty())
2726             {
2727                 selection = Selection();
2728                 OnSelectionChanged();
2729                 auto result = Invalidate();
2730                 if (result.Error()) return result;
2731             }
2732             return Result<bool>(true);
2733         }
2734         public bool IsSelectionEmpty() const
2735         {
2736             return selection.IsEmpty();
2737         }
2738         public SelectionData GetSelection() const
2739         {
2740             if (selection.IsEmpty()) return SelectionData();
2741             if (selection.start.line == selection.end.line)
2742             {
2743                 ustring s = lines[selection.start.line - 1].Substring(selection.start.column - 1selection.end.column - selection.start.column);
2744                 int indent = MinIndent(s);
2745                 SelectionData selectionData(Unindent(sindent)indent0);
2746                 return selectionData;
2747             }
2748             else
2749             {
2750                 int numTrailingSpaces = 0;
2751                 ustring s = lines[selection.start.line - 1].Substring(selection.start.column - 1).Append(u"\n");
2752                 for (int i = selection.start.line; i < selection.end.line - 1; ++i;)
2753                 {
2754                     s.Append(lines[i]).Append(u"\n");
2755                 }
2756                 if (selection.end.line - 1 < lines.Count())
2757                 {
2758                     if (!lines[selection.end.line - 1].Substring(0selection.end.column - 1).IsEmpty())
2759                     {
2760                         ustring lastLine = lines[selection.end.line - 1].Substring(0selection.end.column - 1);
2761                         numTrailingSpaces = GetNumTrailingSpaces(lastLine);
2762                         s.Append(lastLine).Append(u"\n");
2763                     }
2764                 }
2765                 else
2766                 {
2767                     int i = selection.end.line - 1;
2768                     while (i >= lines.Count())
2769                     {
2770                         s.Append(u"\n");
2771                         --i;
2772                     }
2773                 }
2774                 int indent = MinIndent(s);
2775                 SelectionData selectionData(Unindent(sindent)indentnumTrailingSpaces);
2776                 return selectionData;
2777             }
2778         }
2779         [nodiscard]
2780         public Result<bool> InsertSelection(const Selection& selectionToInsertconst SelectionData& selectionDatabool wholeLine)
2781         {
2782             if (selectionToInsert.IsEmpty()) return Result<bool>(true);
2783             if (selectionToInsert.start.line == selectionToInsert.end.line)
2784             {
2785                 if (wholeLine)
2786                 {
2787                     int lineIndex = selectionToInsert.start.line - 1;
2788                     ustring selectedText = selectionData.selectedText;
2789                     if (selectionData.indent > 0)
2790                     {
2791                         selectedText.Insert(0ustring(' 'selectionData.indent));
2792                     }
2793                     lines.Insert(lines.Begin() + lineIndexselectedText);
2794                     LineEventArgs insertArgs(lineIndex-1);
2795                     auto result = OnLineInserted(insertArgs);
2796                     if (result.Error()) return result;
2797                     result = SetCaretLineCol(lineIndex + 11 + LineNumberFieldLength());
2798                     if (result.Error()) return result;
2799                 }
2800                 else
2801                 {
2802                     int lineIndex = selectionToInsert.start.line - 1;
2803                     ustring selectedText = selectionData.selectedText;
2804                     if (selectionData.indent > 0)
2805                     {
2806                         selectedText.Insert(0ustring(' 'selectionData.indent));
2807                     }
2808                     lines[lineIndex].Insert(selectionToInsert.start.column - 1selectedText);
2809                     LineEventArgs changeArgs(lineIndex-1);
2810                     auto result = OnLineChanged(changeArgs);
2811                     if (result.Error()) return result;
2812                     result = SetCaretLineCol(lineIndex + 1selectionToInsert.start.column + LineNumberFieldLength());
2813                     if (result.Error()) return result;
2814                 }
2815             }
2816             else
2817             {
2818                 List<ustring> selectionLines = SplitTextIntoLines(selectionData.selectedText);
2819                 int startLineIndex = selectionToInsert.start.line - 1;
2820                 int endLineIndex = selectionToInsert.end.line - 1;
2821                 int lineCount =  endLineIndex - startLineIndex + 1;
2822                 if (selectionToInsert.start.column > 1)
2823                 {
2824                     ustring firstSelectionLine = selectionLines[0];
2825                     if (selectionData.indent > 0)
2826                     {
2827                         firstSelectionLine.Insert(0ustring(' 'selectionData.indent));
2828                     }
2829                     lines[startLineIndex].Insert(selectionToInsert.start.column - 1firstSelectionLine);
2830                     LineEventArgs changeArgs(startLineIndex-1);
2831                     auto result = OnLineChanged(changeArgs);
2832                     if (result.Error()) return result;
2833                     ++startLineIndex;
2834                 }
2835                 else
2836                 {
2837                     ustring firstSelectionLine = selectionLines[0];
2838                     if (selectionData.indent > 0)
2839                     {
2840                         firstSelectionLine.Insert(0ustring(' 'selectionData.indent));
2841                     }
2842                     lines.Insert(lines.Begin() + startLineIndexfirstSelectionLine);
2843                     LineEventArgs insertArgs(startLineIndex-1);
2844                     auto result = OnLineInserted(insertArgs);
2845                     if (result.Error()) return result;
2846                     ++startLineIndex;
2847                 }
2848                 int n = endLineIndex;
2849                 for (int i = startLineIndex; i < n; ++i;)
2850                 {
2851                     ustring selectionLine = selectionLines[i - startLineIndex + 1];
2852                     if (selectionData.indent > 0)
2853                     {
2854                         selectionLine.Insert(0ustring(' 'selectionData.indent));
2855                     }
2856                     lines.Insert(lines.Begin() + iselectionLine);
2857                     LineEventArgs insertArgs(i-1);
2858                     auto result = OnLineInserted(insertArgs);
2859                     if (result.Error()) return result;
2860                 }
2861                 ustring lastLine;
2862                 if (selectionLines.Count() >= lineCount)
2863                 {
2864                     lastLine = selectionLines.Back();
2865                     if (selectionData.indent > 0)
2866                     {
2867                         lastLine.Insert(0ustring(' 'selectionData.indent));
2868                     }
2869                     lastLine.Append(' 'selectionData.numTrailingSpaces);
2870                 }
2871                 if (wholeLine)
2872                 {
2873                     lines.Insert(lines.Begin() + endLineIndexlastLine);
2874                     LineEventArgs insertArgs(endLineIndex-1);
2875                     auto result = OnLineInserted(insertArgs);
2876                     if (result.Error()) return result;
2877                 }
2878                 else
2879                 {
2880                     lines[endLineIndex].Insert(0lastLine);
2881                     LineEventArgs changeArgs(endLineIndex-1);
2882                     auto result = OnLineChanged(changeArgs);
2883                     if (result.Error()) return result;
2884                 }
2885             }
2886             selection = selectionToInsert;
2887             auto result = SetCaretLineCol(selection.end.lineselection.end.column + LineNumberFieldLength());
2888             if (result.Error()) return result;
2889             result = Invalidate();
2890             if (result.Error()) return result;
2891             SetDirty();
2892             SetCCDirty();
2893             return Result<bool>(true);
2894         }
2895         [nodiscard]
2896         public Result<bool> AddRemoveSelectionCommand()
2897         {
2898             if (selection.IsEmpty()) return Result<bool>(false);
2899             bool wholeLine = false;
2900             SelectionData selectionData = GetSelection();
2901             if (selection.start.line == selection.end.line)
2902             {
2903                 if (selection.end.column - selection.start.column >= GetLineLength(selection.start.line))
2904                 {
2905                     wholeLine = true;
2906                 }
2907             }
2908             else
2909             {
2910                 int endLineIndex = selection.end.line - 1;
2911                 if (selection.end.column > GetLineLength(endLineIndex + 1))
2912                 {
2913                     wholeLine = true;
2914                 }
2915             }
2916             return editCommandList.AddCommand(new RemoveSelectionCommand(selectionselectionDatawholeLine));
2917         }
2918         [nodiscard]
2919         public Result<bool> RemoveSelection()
2920         {
2921             if (selection.IsEmpty()) return Result<bool>(false);
2922             if (selection.start.line == selection.end.line)
2923             {
2924                 if (selection.end.column - selection.start.column >= GetLineLength(selection.start.line))
2925                 {
2926                     int lineIndex = selection.start.line - 1;
2927                     lines.Remove(lines.Begin() + lineIndex);
2928                     LineEventArgs deleteArgs(lineIndex-1);
2929                     auto result = OnLineDeleted(deleteArgs);
2930                     if (result.Error()) return result;
2931                     result = SetCaretLineCol(selection.start.line1 + LineNumberFieldLength());
2932                     if (result.Error()) return result;
2933                 }
2934                 else
2935                 {
2936                     int lineIndex = selection.start.line - 1;
2937                     lines[lineIndex].Remove(selection.start.column - 1selection.end.column - selection.start.column);
2938                     LineEventArgs changeArgs(lineIndex-1);
2939                     auto result = OnLineChanged(changeArgs);
2940                     if (result.Error()) return result;
2941                     result =  SetCaretLineCol(lineIndex + 1selection.start.column + LineNumberFieldLength());
2942                     if (result.Error()) return result;
2943                 }
2944             }
2945             else
2946             {
2947                 int startLineIndex = selection.start.line - 1;
2948                 int endLineIndex = selection.end.line - 1;
2949                 if (selection.start.column > 1)
2950                 {
2951                     lines[startLineIndex].Remove(selection.start.column - 1GetLineLength(startLineIndex + 1) - selection.start.column + 1);
2952                     LineEventArgs changeArgs(startLineIndex-1);
2953                     auto result = OnLineChanged(changeArgs);
2954                     if (result.Error()) return result;
2955                     ++startLineIndex;
2956                 }
2957                 else
2958                 {
2959                     lines.Remove(lines.Begin() + startLineIndex);
2960                     --endLineIndex;
2961                     LineEventArgs deleteArgs(startLineIndex-1);
2962                     auto result = OnLineDeleted(deleteArgs);
2963                     if (result.Error()) return result;
2964                 }
2965                 int n = endLineIndex;
2966                 for (int i = startLineIndex; i < n; ++i;)
2967                 {
2968                     lines.Remove(lines.Begin() + startLineIndex);
2969                     --endLineIndex;
2970                     LineEventArgs deleteArgs(startLineIndex-1);
2971                     auto result = OnLineDeleted(deleteArgs);
2972                     if (result.Error()) return result;
2973                 }
2974                 if (endLineIndex >= 0 && endLineIndex < lines.Count())
2975                 {
2976                     if (selection.end.column > GetLineLength(endLineIndex + 1))
2977                     {
2978                         lines.Remove(lines.Begin() + endLineIndex);
2979                         LineEventArgs deleteArgs(endLineIndex-1);
2980                         auto result = OnLineDeleted(deleteArgs);
2981                         if (result.Error()) return result;
2982                     }
2983                     else
2984                     {
2985                         lines[endLineIndex].Remove(0selection.end.column - 1);
2986                         LineEventArgs changeArgs(endLineIndex-1);
2987                         auto result = OnLineChanged(changeArgs);
2988                         if (result.Error()) return result;
2989                     }
2990                 }
2991                 auto result = SetCaretLineCol(selection.start.lineselection.start.column + LineNumberFieldLength());
2992                 if (result.Error()) return result;
2993             }
2994             auto result = ResetSelection();
2995             if (result.Error()) return result;
2996             result = Invalidate();
2997             if (result.Error()) return result;
2998             SetDirty();
2999             SetCCDirty();
3000             SetFocus();
3001             return Result<bool>(true);
3002         }
3003         [nodiscard]
3004         private Result<bool> AddIndentSelectionCommand()
3005         {
3006             return editCommandList.AddCommand(new IndentSelectionCommand(selection));
3007         }
3008         [nodiscard]
3009         public Result<bool> IndentSelection()
3010         {
3011             if (selection.IsEmpty()) return Result<bool>(false);
3012             int startLineIndex = selection.start.line - 1;
3013             int endLineIndex = selection.end.line - 1;
3014             ustring indentString(' 'IndentSize());
3015             for (int i = startLineIndex; i <= endLineIndex; ++i;)
3016             {
3017                 if (lines[i].IsEmpty()) continue;
3018                 if (i == selection.end.line - 1 && lines[i].Substring(0selection.end.column - 1).IsEmpty()) continue;
3019                 lines[i].Insert(0indentString);
3020                 LineEventArgs changeArgs(i-1);
3021                 auto result = OnLineChanged(changeArgs);
3022                 if (result.Error()) return result;
3023             }
3024             auto result = InvalidateLines(selection.start.lineselection.end.line);
3025             if (result.Error()) return result;
3026             SetDirty();
3027             SetCCDirty();
3028             return Result<bool>(true);
3029         }
3030         [nodiscard]
3031         public Result<bool> AddUnindentSelectionCommand()
3032         {
3033             return editCommandList.AddCommand(new UnindentSelectionCommand(selection));
3034         }
3035         [nodiscard]
3036         public Result<bool> UnindentSelection()
3037         {
3038             if (selection.IsEmpty()) return Result<bool>(false);
3039             int startLineIndex = selection.start.line - 1;
3040             int endLineIndex = selection.end.line - 1;
3041             ustring indentString(' 'IndentSize());
3042             for (int i = startLineIndex; i <= endLineIndex; ++i;)
3043             {
3044                 ustring line = lines[i];
3045                 if (i == endLineIndex)
3046                 {
3047                     line = line.Substring(0selection.end.column - 1);
3048                 }
3049                 if (line.StartsWith(indentString))
3050                 {
3051                     lines[i].Remove(0IndentSize());
3052                     LineEventArgs changeArgs(i-1);
3053                     auto result = OnLineChanged(changeArgs);
3054                     if (result.Error()) return result;
3055                 }
3056             }
3057             auto result = InvalidateLines(selection.start.lineselection.end.line);
3058             if (result.Error()) return result;
3059             SetDirty();
3060             SetCCDirty();
3061             return Result<bool>(true);
3062         }
3063         [nodiscard]
3064         public Result<bool> ExtendSelection(const SourcePos& startconst SourcePos& end)
3065         {
3066             switch (selection.fixed)
3067             {
3068                 case Selection.Fix.none:
3069                 {
3070                     if (start < end)
3071                     {
3072                         selection.fixed = Selection.Fix.start;
3073                         selection.start = start;
3074                         selection.end = end;
3075                     }
3076                     else
3077                     {
3078                         selection.fixed = Selection.Fix.end;
3079                         selection.end = start;
3080                         selection.start = end;
3081                     }
3082                     break;
3083                 }
3084                 case Selection.Fix.start:
3085                 {
3086                     if (end == selection.start)
3087                     {
3088                         auto result = ResetSelection();
3089                         if (result.Error()) return result;
3090                     }
3091                     else if (end > selection.start)
3092                     {
3093                         selection.end = end;
3094                     }
3095                     else
3096                     {
3097                         selection.fixed = Selection.Fix.end;
3098                         selection.end = start;
3099                         selection.start = end;
3100                     }
3101                     break;
3102                 }
3103                 case Selection.Fix.end:
3104                 {
3105                     if (end == selection.end)
3106                     {
3107                         auto result = ResetSelection();
3108                         if (result.Error()) return result;
3109                     }
3110                     else if (end < selection.end)
3111                     {
3112                         selection.start = end;
3113                     }
3114                     else
3115                     {
3116                         selection.fixed = Selection.Fix.start;
3117                         selection.start = start;
3118                         selection.end = end;
3119                     }
3120                     break;
3121                 }
3122             }
3123             OnSelectionChanged();
3124             return Result<bool>(true);
3125         }
3126         public Brush* GetOrInsertBrush(const Color& color)
3127         {
3128             HashMap<ColorBrush*>.ConstIterator it = colorBrushMap.CFind(color);
3129             if (it != colorBrushMap.CEnd())
3130             {
3131                 return it->second;
3132             }
3133             Brush* brush = new SolidBrush(color);
3134             brushes.Add(UniquePtr<Brush>(brush));
3135             colorBrushMap[color] = brush;
3136             return brush;
3137         }
3138         protected inline bool Changed() const
3139         {
3140             return (flags & Flags.changed) != Flags.none;
3141         }
3142         protected inline void SetChanged()
3143         {
3144             flags = cast<Flags>(flags | Flags.changed);
3145         }
3146         protected inline void ResetChanged()
3147         {
3148             flags = cast<Flags>(flags & ~Flags.changed);
3149         }
3150         public inline bool Painting() const
3151         {
3152             return (flags &  Flags.painting) != Flags.none;
3153         }
3154         private inline void SetPainting()
3155         {
3156             flags = cast<Flags>(flags | Flags.painting);
3157         }
3158         private inline void ResetPainting()
3159         {
3160             flags = cast<Flags>(flags & ~Flags.painting);
3161         }
3162         public inline bool IsReadOnly() const
3163         {
3164             return (flags & Flags.readOnly) != Flags.none;
3165         }
3166         public void SetReadOnly()
3167         {
3168             flags = cast<Flags>(flags | Flags.readOnly);
3169         }
3170         public void ResetReadOnly()
3171         {
3172             flags = cast<Flags>(flags & ~Flags.readOnly);
3173         }
3174         public inline bool IsFixed() const
3175         {
3176             return (flags & Flags.fixed) != Flags.none;
3177         }
3178         public void SetFixed()
3179         {
3180             flags = cast<Flags>(flags | Flags.fixed);
3181         }
3182         public void ResetFixed()
3183         {
3184             flags = cast<Flags>(flags & ~Flags.fixed);
3185         }
3186         public inline bool IsDirty() const
3187         {
3188             return (flags & Flags.dirty) != Flags.none;
3189         }
3190         public void SetDirty()
3191         {
3192             if (!IsDirty())
3193             {
3194                 flags = cast<Flags>(flags | Flags.dirty);
3195                 OnDirtyChanged();
3196             }
3197         }
3198         public void ResetDirty()
3199         {
3200             if (IsDirty())
3201             {
3202                 flags = cast<Flags>(flags & ~Flags.dirty);
3203                 OnDirtyChanged();
3204             }
3205         }
3206         public inline bool IsCCDirty() const
3207         {
3208             return (flags & Flags.ccdirty) != Flags.none;
3209         }
3210         public void SetCCDirty()
3211         {
3212             if (!IsCCDirty())
3213             {
3214                 flags = cast<Flags>(flags | Flags.ccdirty);
3215                 OnCCDirtyChanged();
3216             }
3217         }
3218         public void ResetCCDirty()
3219         {
3220             if (IsCCDirty())
3221             {
3222                 flags = cast<Flags>(flags & ~Flags.ccdirty);
3223                 OnCCDirtyChanged();
3224             }
3225         }
3226         private inline bool MouseExtendSelection() const
3227         {
3228             return (flags & Flags.mouseExtendSelection) != Flags.none;
3229         }
3230         private void SetMouseExtendSelection()
3231         {
3232             flags = cast<Flags>(flags | Flags.mouseExtendSelection);
3233         }
3234         private void ResetMouseExtendSelection()
3235         {
3236             flags = cast<Flags>(flags & ~Flags.mouseExtendSelection);
3237         }
3238         public inline bool CCOpen() const
3239         {
3240             return (flags & Flags.ccOpen) != Flags.none;
3241         }
3242         public void SetCCOpen()
3243         {
3244             flags = cast<Flags>(flags | Flags.ccOpen);
3245         }
3246         public void ResetCCOpen()
3247         {
3248             flags = cast<Flags>(flags & ~Flags.ccOpen);
3249         }
3250         public Event<CaretPosChangedEventHandler>& CaretPosChangedEvent() const
3251         {
3252             return caretPosChangedEvent;
3253         }
3254         public Event<LinesChangedEventHandler>& LinesChangedEvent() const
3255         {
3256             return linesChangedEvent;
3257         }
3258         public Event<LineChangedEventHandlerLineEventArgs>& LineChangedEvent() const
3259         {
3260             return lineChangedEvent;
3261         }
3262         public Event<LineDeletedEventHandlerLineEventArgs>& LineDeletedEvent() const
3263         {
3264             return lineDeletedEvent;
3265         }
3266         public Event<LineInsertedEventHandlerLineEventArgs>& LineInsertedEvent() const
3267         {
3268             return lineInsertedEvent;
3269         }
3270         public Event<GotoCaretLineEventHandlerControlEventArgs>& GotoCaretLineEvent() const
3271         {
3272             return gotoCaretLineEvent;
3273         }
3274         public Event<SelectionChangedEventHandler>& SelectionChangedEvent() const
3275         {
3276             return selectionChangedEvent;
3277         }
3278         public Event<DirtyChangedEventHandler>& DirtyChangedEvent() const
3279         {
3280             return dirtyChangedEvent;
3281         }
3282         public Event<CCDirtyChangedEventHandler>& CCDirtyChangedEvent() const
3283         {
3284             return ccdirtyChangedEvent;
3285         }
3286         public Event<CCTextChangedEventHandler>& CCTextChangedEvent() const
3287         {
3288             return cctextChangedEvent;
3289         }
3290         public Event<CCEventHandler>& CCEvent() const
3291         {
3292             return ccEvent;
3293         }
3294         public Event<EscapePressedHandler>& EscapePressedEvent() const
3295         {
3296             return escapePressedEvent;
3297         }
3298         public Event<CCNextEventHandler>& CCNextEvent() const
3299         {
3300             return ccNextEvent;
3301         }
3302         public Event<CCPrevEventHandler>& CCPrevEvent() const
3303         {
3304             return ccPrevEvent;
3305         }
3306         public Event<CCNextPageEventHandler>& CCNextPageEvent() const
3307         {
3308             return ccNextPageEvent;
3309         }
3310         public Event<CCPrevPageEventHandler>& CCPrevPageEvent() const
3311         {
3312             return ccPrevPageEvent;
3313         }
3314         public Event<CCSelectEventHandler>& CCSelectEvent() const
3315         {
3316             return ccSelectEvent;
3317         }
3318         public Event<CopyEventHandler>& CopyEvent() const
3319         {
3320             return copyEvent;
3321         }
3322         public Event<CutEventHandler>& CutEvent() const
3323         {
3324             return cutEvent;
3325         }
3326         public Event<PasteEventHandler>& PasteEvent() const
3327         {
3328             return pasteEvent;
3329         }
3330         [nodiscard]
3331         public Result<bool> InvalidateLineCol(int lineNumberint columnNumber)
3332         {
3333             if (charHeight != 0 && charWidth != 0)
3334             {
3335                 Point pt(cast<int>(charWidth * (columnNumber - 1))cast<int>(charHeight * (lineNumber - 1)));
3336                 TranslateContentLocation(pt);
3337                 Size size = GetSize();
3338                 Size sz(size.w - pt.xcast<int>(charHeight + 1.500000f));
3339                 Rect rect(ptsz);
3340                 auto result = Invalidate(rect);
3341                 if (result.Error()) return result;
3342             }
3343             else
3344             {
3345                 auto result = Invalidate();
3346                 if (result.Error()) return result;
3347             }
3348             return Result<bool>(true);
3349         }
3350         [nodiscard]
3351         public Result<bool> InvalidateLines(int startLineNumberint endLineNumber)
3352         {
3353             if (charHeight != 0 && charWidth != 0)
3354             {
3355                 Point pt(0cast<int>(charHeight * (startLineNumber - 1) - charHeight));
3356                 TranslateContentLocation(pt);
3357                 Size size = GetSize();
3358                 Size sz(size.wcast<int>(charHeight * (endLineNumber - startLineNumber + 1) + 2 * charHeight + 1.500000f));
3359                 Rect rect(ptsz);
3360                 return Invalidate(rect);
3361             }
3362             else
3363             {
3364                 return Invalidate();
3365             }
3366         }
3367         public void SetFilePath(const string& filePath_)
3368         {
3369             filePath = filePath_;
3370         }
3371         public const string& FilePath() const
3372         {
3373             return filePath;
3374         }
3375         private Flags flags;
3376         private List<ustring> lines;
3377         private List<int> lineStartIndeces;
3378         private StringFormat drawFormat;
3379         private FontFamily fontFamily;
3380         private Color textColor;
3381         private List<UniquePtr<Font>> fonts;
3382         private List<UniquePtr<Brush>> brushes;
3383         private HashMap<ColorBrush*> colorBrushMap;
3384         private float fontSize;
3385         private float charWidth;
3386         private float charHeight;
3387         private int textWidth;
3388         private int textHeight;
3389         private int maxLineLength;
3390         private int maxLineIndex;
3391         private Cursor cursor;
3392         private int caretLine;
3393         private int caretColumn;
3394         private int topLine;
3395         private int leftCol;
3396         private float topLineDiff;
3397         private float leftColDiff;
3398         private uint caretTimerPeriod;
3399         private bool update;
3400         private Padding padding;
3401         private Event<CaretPosChangedEventHandler> caretPosChangedEvent;
3402         private Event<LinesChangedEventHandler> linesChangedEvent;
3403         private Event<LineChangedEventHandlerLineEventArgs> lineChangedEvent;
3404         private Event<LineDeletedEventHandlerLineEventArgs> lineDeletedEvent;
3405         private Event<LineInsertedEventHandlerLineEventArgs> lineInsertedEvent;
3406         private Event<GotoCaretLineEventHandlerControlEventArgs> gotoCaretLineEvent;
3407         private Event<SelectionChangedEventHandler> selectionChangedEvent;
3408         private Event<DirtyChangedEventHandler> dirtyChangedEvent;
3409         private Event<CCDirtyChangedEventHandler> ccdirtyChangedEvent;
3410         private Event<CCTextChangedEventHandler> cctextChangedEvent;
3411         private Event<CCEventHandler> ccEvent;
3412         private Event<CCNextEventHandler> ccNextEvent;
3413         private Event<CCPrevEventHandler> ccPrevEvent;
3414         private Event<CCNextPageEventHandler> ccNextPageEvent;
3415         private Event<CCPrevPageEventHandler> ccPrevPageEvent;
3416         private Event<CCSelectEventHandler> ccSelectEvent;
3417         private Event<EscapePressedHandler> escapePressedEvent;
3418         private Event<CopyEventHandler> copyEvent;
3419         private Event<CutEventHandler> cutEvent;
3420         private Event<PasteEventHandler> pasteEvent;
3421         private string measureString;
3422         private Selection selection;
3423         private SourcePos mouseSelectionStart;
3424         private SourcePos mouseSelectionEnd;
3425         private System.Windows.Color selectionBackgroundColor;
3426         private EditCommandList editCommandList;
3427         private int indentSize;
3428         private string filePath;
3429         private ustring cctext;
3430     }
3431 
3432     public ustring TrimEnd(const ustring& line)
3433     {
3434         long i = line.Length();
3435         while (i > 0 && System.Windows.IsWhiteSpace(line[i - 1]))
3436         {
3437             --i;
3438         }
3439         return line.Substring(0i);
3440     }
3441 
3442     public int GetNumTrailingSpaces(const ustring& line)
3443     {
3444         int numTrailingSpaces = 0;
3445         long i = line.Length();
3446         while (i > 0 && System.Windows.IsWhiteSpace(line[i - 1]))
3447         {
3448             --i;
3449             ++numTrailingSpaces;
3450         }
3451         return numTrailingSpaces;
3452     }
3453 
3454     public List<ustring> SplitTextIntoLines(const ustring& text)
3455     {
3456         List<ustring> lines;
3457         ustring line;
3458         int state = 0;
3459         for (uchar c : text)
3460         {
3461             switch (state)
3462             {
3463                 case 0:
3464                 {
3465                     switch (c)
3466                     {
3467                         case '\n':
3468                         {
3469                             lines.Add(System.Windows.TrimEnd(line));
3470                             line.Clear();
3471                             break;
3472                         }
3473                         case '\r':
3474                         {
3475                             state = 1;
3476                             break;
3477                         }
3478                         default:
3479                         {
3480                             line.Append(c);
3481                             break;
3482                         }
3483                     }
3484                     break;
3485                 }
3486                 case 1:
3487                 {
3488                     if (c == '\n')
3489                     {
3490                         lines.Add(System.Windows.TrimEnd(line));
3491                         line.Clear();
3492                         state = 0;
3493                     }
3494                     break;
3495                 }
3496             }
3497         }
3498         if (!line.IsEmpty())
3499         {
3500             lines.Add(System.Windows.TrimEnd(line));
3501         }
3502         return lines;
3503     }
3504 
3505     public int MinIndent(const ustring& s)
3506     {
3507         if (s.IsEmpty()) return 0;
3508         int state = 0;
3509         int indent = 0;
3510         int minIndent = MaxValue<int>();
3511         for (uchar c : s)
3512         {
3513             switch (state)
3514             {
3515                 case 0:
3516                 {
3517                     if (c == ' ')
3518                     {
3519                         ++indent;
3520                     }
3521                     else if (c == '\n')
3522                     {
3523                         if (indent < minIndent)
3524                         {
3525                             minIndent = indent;
3526                         }
3527                         indent = 0;
3528                     }
3529                     else
3530                     {
3531                         if (indent < minIndent)
3532                         {
3533                             minIndent = indent;
3534                         }
3535                         indent = 0;
3536                         state = 1;
3537                     }
3538                     break;
3539                 }
3540                 case 1:
3541                 {
3542                     if (c == '\n')
3543                     {
3544                         state = 0;
3545                     }
3546                     break;
3547                 }
3548             }
3549         }
3550         if (indent > 0 && indent < minIndent)
3551         {
3552             minIndent = indent;
3553         }
3554         if (minIndent == MaxValue<int>())
3555         {
3556             minIndent = 0;
3557         }
3558         return minIndent;
3559     }
3560 
3561     public ustring Unindent(const ustring& sint indent)
3562     {
3563         if (indent == 0) return s;
3564         ustring line;
3565         ustring result;
3566         int state = 0;
3567         for (uchar c : s)
3568         {
3569             switch (state)
3570             {
3571                 case 0:
3572                 {
3573                     switch (c)
3574                     {
3575                         case '\n':
3576                         {
3577                             result.Append(line.Substring(indent)).Append('\n');
3578                             line.Clear();
3579                             break;
3580                         }
3581                         case '\r':
3582                         {
3583                             state = 1;
3584                             break;
3585                         }
3586                         default:
3587                         {
3588                             line.Append(c);
3589                             break;
3590                         }
3591                     }
3592                     break;
3593                 }
3594                 case 1:
3595                 {
3596                     if (c == '\n')
3597                     {
3598                         result.Append(line.Substring(indent)).Append('\n');
3599                         line.Clear();
3600                         state = 0;
3601                     }
3602                     break;
3603                 }
3604             }
3605         }
3606         if (!line.IsEmpty())
3607         {
3608             result.Append(line.Substring(indent));
3609         }
3610         return result;
3611     }
3612 
3613     public List<int> CalculateLineStartIndeces(const ustring& text)
3614     {
3615         List<int> indeces;
3616         int state = 0;
3617         int n = cast<int>(text.Length());
3618         for (int i = 0; i < n; ++i;)
3619         {
3620             uchar c = text[i];
3621             switch (state)
3622             {
3623                 case 0:
3624                 {
3625                     indeces.Add(i);
3626                     if (c != '\n')
3627                     {
3628                         state = 1;
3629                     }
3630                     break;
3631                 }
3632                 case 1:
3633                 {
3634                     if (c == '\n')
3635                     {
3636                         state = 0;
3637                     }
3638                     break;
3639                 }
3640             }
3641         }
3642         return indeces;
3643     }
3644 
3645     internal bool ReadCaretTimeoutFromRegistry(uint& caretTimeout)
3646     {
3647         void* currentUserKey = null;
3648         bool openSucceeded = WinRegOpenCurrentUser(&currentUserKey);
3649         if (openSucceeded)
3650         {
3651             uint value = 0u;
3652             bool getValueSucceeded = WinRegGetDWordValue(currentUserKey"Control Panel\\Desktop""CaretTimeout"value);
3653             if (getValueSucceeded)
3654             {
3655                 caretTimeout = value;
3656             }
3657             WinRegCloseKey(currentUserKey);
3658             return getValueSucceeded;
3659         }
3660         return false;
3661     }
3662