1
2
3
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(153u, 201u, 239u);
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& left, const SourcePos& right)
89 {
90 return left.line == right.line && left.column == right.column;
91 }
92
93 public bool operator<(const SourcePos& left, const 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 none, start, end
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 = 0, changed = 1 << 0, painting = 1 << 1, readOnly = 1 << 2, fixed = 1 << 3, dirty = 1 << 4, ccdirty = 1 << 5,
231 mouseExtendSelection = 1 << 6, ccOpen = 1 << 7
232 }
233
234 public TextView(const FontFamily& fontFamily_, float fontSize_, const Color& backgroundColor, const Color& textColor_, const Point& location,
235 const Size& size, Dock dock, Anchors anchors) :
236 base("System.Windows.TextView", DoubleClickWindowClassStyle(),
237 cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP), DefaultExtendedWindowStyle(), backgroundColor,
238 "textView", location, size, dock, anchors), flags(Flags.none), drawFormat(StringAlignment.near, StringAlignment.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& location, const Size& size, Dock dock, Anchors anchors) :
260 this(FontFamily("Consolas"), 10.000000f, Color.White(), Color.Black(), location, size, dock, anchors)
261 {
262 }
263 public TextView(TextViewCreateParams& createParams) :
264 base(createParams.controlCreateParams),
265 flags(Flags.none),
266 drawFormat(StringAlignment.near, StringAlignment.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(fontFamily, fontSize, FontStyle.regular, Unit.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(fontFamily, fontSize, FontStyle.regular, Unit.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* undoMenuItem, MenuItem* redoMenuItem)
361 {
362 return editCommandList.SetMenuItems(undoMenuItem, redoMenuItem);
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 line, int 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(x, y);
428 TranslateContentLocation(loc);
429 return loc;
430 }
431 public Point CCPos() const
432 {
433 Point ccPos = CaretPos();
434 ccPos.Offset(0, cast<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 line, int 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& contentLocation, int& line, int& 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(tl, 1);
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(1, 1 + 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& line, int& 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& line, int& 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& line, int& col)
590 {
591 if (Prev(line, col))
592 {
593 uchar c = GetCharAt(line, col);
594 while (System.Windows.IsWhiteSpace(c) || IsWordSeparator(c))
595 {
596 if (col == 1 || !Prev(line, col))
597 {
598 return;
599 }
600 c = GetCharAt(line, col);
601 }
602 c = GetCharAt(line, col);
603 while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
604 {
605 if (col == 1 || !Prev(line, col))
606 {
607 return;
608 }
609 c = GetCharAt(line, col);
610 }
611 c = GetCharAt(line, col);
612 if (System.Windows.IsWhiteSpace(c) || IsWordSeparator(c))
613 {
614 Next(line, col);
615 }
616 }
617 }
618 public void NextWord(int& line, int& col)
619 {
620 if (Next(line, col))
621 {
622 uchar c = GetCharAt(line, col);
623 while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
624 {
625 if (col == 1 || !Next(line, col))
626 {
627 return;
628 }
629 c = GetCharAt(line, col);
630 }
631 c = GetCharAt(line, col);
632 while (System.Windows.IsWhiteSpace(c))
633 {
634 if (col == 1 || !Next(line, col))
635 {
636 return;
637 }
638 c = GetCharAt(line, col);
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 line, int 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(lineNumber, columnNumber);
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 line, int 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.graphics, args.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& graphics, const 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(0, 0);
764 Size size = GetSize();
765 for (int i = 0; i < n; ++i;)
766 {
767 if (IsLinePartiallyVisible(i + 1))
768 {
769 auto drawBackgroundResult = DrawSelectionBackground(graphics, i + 1, origin, lineNumberFieldLength);
770 if (drawBackgroundResult.Error())
771 {
772 return Result<bool>(ErrorId(drawBackgroundResult.GetErrorId()));
773 }
774 auto drawLineResult = DrawLine(graphics, i, origin);
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& graphics, int line, const PointF& origin, int 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(1, GetLineLength(line));
800 rect = RectF(pt, SizeF(charWidth * lineLength, charHeight));
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(pt, SizeF(charWidth * selectionLength, charHeight));
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(pt, SizeF(charWidth * selectionLength, charHeight));
814 }
815 else if (line == selection.end.line)
816 {
817 int selectionLength = selection.end.column - 1;
818 rect = RectF(pt, SizeF(charWidth * selectionLength, charHeight));
819 }
820 Brush* brush = GetOrInsertBrush(selectionBackgroundColor);
821 auto fillRectangleResult = graphics.FillRectangle(*brush, rect);
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& graphics, int lineIndex, const 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, *font, PointF(0, 0), 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(charWidth, charRect.size.w / maxLineLength);
901 charHeight = Max(charHeight, charRect.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(textWidth, textHeight));
915 if (result.Error()) return result;
916 return Result<bool>(true);
917 }
918 private void FixColumn(int& column, int 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.location, line, column);
940 if (line >= 1 && line <= lines.Count())
941 {
942 FixColumn(column, line);
943 mouseSelectionStart = SourcePos(line, column);
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(1, Min(lineNumber, cast<int>(lines.Count())));
954 int columnNumber = cast<int>(loc.x / charWidth) + 1;
955 columnNumber = Max(columnNumber, 1 + LineNumberFieldLength());
956 int lineLength = GetLineLength(lineNumber);
957 columnNumber = Min(columnNumber, lineLength + 1 + LineNumberFieldLength());
958 if (update)
959 {
960 update = false;
961 auto result = Invalidate();
962 if (result.Error()) return result;
963 }
964 auto result = SetCaretLineCol(lineNumber, columnNumber);
965 if (result.Error()) return result;
966 if (args.buttons == MouseButtons.rbutton)
967 {
968 RightClickEventArgs rightClickArgs(this, loc);
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.location, line, column);
989 if (line >= 1 && line <= lines.Count())
990 {
991 FixColumn(column, line);
992 mouseSelectionEnd = SourcePos(line, column);
993 if (mouseSelectionStart != mouseSelectionEnd)
994 {
995 result = ExtendSelection(mouseSelectionStart, mouseSelectionEnd);
996 if (result.Error()) return result;
997 result = InvalidateLines(selection.start.line, selection.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.location, line, column);
1015 if (line >= 1 && line <= lines.Count())
1016 {
1017 FixColumn(column, line);
1018 mouseSelectionEnd = SourcePos(line, column);
1019 if (mouseSelectionStart != mouseSelectionEnd)
1020 {
1021 result = ExtendSelection(mouseSelectionStart, mouseSelectionEnd);
1022 if (result.Error()) return result;
1023 result = InvalidateLines(selection.start.line, selection.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(start, end);
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(start, end);
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(start, end);
1115 if (result.Error()) return result;
1116 result = InvalidateLines(selection.start.line, selection.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(start, end);
1128 if (result.Error()) return result;
1129 result = InvalidateLines(selection.start.line, selection.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(start, end);
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(start, end);
1176 if (result.Error()) return result;
1177 }
1178 result = InvalidateLines(selection.start.line, selection.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(columnNumber, lineLength + 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(columnNumber, lineLength + 1 + LineNumberFieldLength());
1219 end.column = Min(end.column, lineLength + 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(start, end);
1229 if (result.Error()) return result;
1230 result = InvalidateLines(selection.start.line, selection.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(columnNumber, lineLength + 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(columnNumber, lineLength + 1 + LineNumberFieldLength());
1265 end.column = Min(end.column, lineLength + 1);
1266 result = ExtendSelection(start, end);
1267 if (result.Error()) return result;
1268 result = InvalidateLines(selection.start.line, selection.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(lineNumber, cast<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(lineNumber, cast<int>(lines.Count()) + 1);
1301 end.line = Min(end.line, cast<int>(lines.Count()) + 1);
1302 result = ExtendSelection(start, end);
1303 if (result.Error()) return result;
1304 result = InvalidateLines(selection.start.line, selection.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(lineNumber, 1);
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(lineNumber, 1);
1336 end.line = Max(end.line, 1);
1337 result = ExtendSelection(start, end);
1338 if (result.Error()) return result;
1339 result = InvalidateLines(selection.start.line, selection.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(1, 1);
1349 SourcePos end(cast<int>(lines.Count() + 1), 1);
1350 result = ExtendSelection(start, end);
1351 if (result.Error()) return result;
1352 result = InvalidateLines(selection.start.line, selection.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(start, end);
1375 if (result.Error()) return result;
1376 result = InvalidateLines(selection.start.line, selection.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(start, end);
1399 if (result.Error()) return result;
1400 result = InvalidateLines(selection.start.line, selection.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(line, col);
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(line, col);
1424 lineNumber = line;
1425 end.line = line;
1426 columnNumber = col + LineNumberFieldLength();
1427 end.column = col;
1428 result = ExtendSelection(start, end);
1429 if (result.Error()) return result;
1430 result = InvalidateLines(selection.start.line, selection.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(line, col);
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(line, col);
1454 lineNumber = line;
1455 end.line = line;
1456 columnNumber = col + LineNumberFieldLength();
1457 end.column = col;
1458 result = ExtendSelection(start, end);
1459 if (result.Error()) return result;
1460 result = InvalidateLines(selection.start.line, selection.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(lineIndex, columnIndex);
1518 if (result.Error()) return result;
1519 result = DeleteChar(lineIndex, columnIndex, 0, 0, false);
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(lineIndex, columnIndex);
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(lineIndex, columnIndex);
1564 if (result.Error()) return result;
1565 result = NewLine(lineIndex, columnIndex);
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(lineIndex, columnIndex);
1582 if (result.Error()) return result;
1583 result = Tab(lineIndex, columnIndex);
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(lineIndex, columnIndex);
1607 if (result.Error()) return result;
1608 result = Backtab(lineIndex, columnIndex);
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(lineNumber, columnNumber);
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(lineIndex, columnIndex, c);
1700 if (result.Error()) return result;
1701 result = InsertChar(lineIndex, columnIndex, c);
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 lineIndex, int columnIndex, uchar 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(lineIndex, columnIndex, c, removeIndent));
1747 if (result.Error()) return result;
1748 return Result<bool>(true);
1749 }
1750 [nodiscard]
1751 public Result<bool> InsertChar(int lineIndex, int columnIndex, uchar c)
1752 {
1753 while (lineIndex >= lines.Count())
1754 {
1755 lines.Add(ustring());
1756 }
1757 ustring& line = lines[lineIndex];
1758 line.Insert(columnIndex, c);
1759 if (c == '}' && line.StartsWith(ustring(' ', IndentSize())))
1760 {
1761 line.Remove(0, IndentSize());
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 + 1, columnIndex + 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(line, columnIndex);
1780 SetDirty();
1781 SetCCDirty();
1782 return Result<bool>(true);
1783 }
1784 [nodiscard]
1785 public Result<bool> InsertText(int lineIndex, int columnIndex, const 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(lineIndex, columnIndex, linesToInsert.Front());
1802 if (result.Error()) return result;
1803 result = InsertIntoLine(lineIndex, columnIndex, linesToInsert.Front());
1804 if (result.Error()) return result;
1805 }
1806 else
1807 {
1808 auto result = AddInsertLinesCommand(lineIndex, columnIndex, linesToInsert);
1809 if (result.Error()) return result;
1810 result = InsertLines(lineIndex, columnIndex, linesToInsert);
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 lineIndex, int columnIndex, const ustring& text)
1819 {
1820 return editCommandList.AddCommand(new InsertIntoLineCommand(lineIndex, columnIndex, text));
1821 }
1822 [nodiscard]
1823 public Result<bool> InsertIntoLine(int lineIndex, int columnIndex, const ustring& text)
1824 {
1825 lines[lineIndex].Insert(columnIndex, text);
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 + 1, 1 + 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 + 1, lineIndex + 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 lineIndex, int columnIndex, long count)
1845 {
1846 lines[lineIndex].Remove(columnIndex, count);
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 + 1, 1 + 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 + 1, lineIndex + 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 lineIndex, int columnIndex, const List<ustring>& linesToInsert)
1866 {
1867 return editCommandList.AddCommand(new InsertLinesCommand(lineIndex, columnIndex, linesToInsert));
1868 }
1869 [nodiscard]
1870 public Result<bool> InsertLines(int lineIndex, int columnIndex, const 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() + lineIndex, lineToInsert);
1881 SetLineNumberFieldLength(cast<int>(lines.Count()));
1882 LineEventArgs insertedArgs(lineIndex, indentLineIndex);
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(lineIndex, 1 + LineNumberFieldLength() + cast<int>(lines[lineIndex - 1].Length()));
1904 if (result.Error()) return result;
1905 }
1906 else
1907 {
1908 result = SetCaretLineCol(lineIndex, 1 + 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 + 1, cast<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 lineIndex, int columnIndex, const List<ustring>& linesToDelete)
1923 {
1924 if (linesToDelete.Count() == 1)
1925 {
1926 ustring& line = lines[lineIndex];
1927 line.Remove(columnIndex, linesToDelete.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(lineIndex, 1 + 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 lineIndex, int 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(lineIndex, columnIndex, c));
1964 if (result.Error()) return result;
1965 }
1966 else
1967 {
1968 if (lines.Count() > lineIndex + 1)
1969 {
1970 auto result = editCommandList.AddCommand(new DeleteCharCommand(lineIndex, columnIndex, '\0'));
1971 if (result.Error()) return result;
1972 }
1973 }
1974 return Result<bool>(true);
1975 }
1976 [nodiscard]
1977 public Result<bool> DeleteChar(int lineIndex, int columnIndex, int indent, int numSpaces, bool removeIndent)
1978 {
1979 if (lineIndex >= lines.Count()) return Result<bool>(false);
1980 ustring& line = lines[lineIndex];
1981 if (removeIndent)
1982 {
1983 line.Insert(0, ustring(' ', IndentSize()));
1984 }
1985 if (columnIndex < line.Length())
1986 {
1987 line.Remove(columnIndex, 1);
1988 LineEventArgs args(lineIndex, -1);
1989 auto result = OnLineChanged(args);
1990 if (result.Error()) return result;
1991 SetCCText(line, columnIndex);
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 + 1, columnIndex + 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 lineIndex, int 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& line, int 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 lineIndex, int 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(lineIndex, columnIndex, indent, numSpaces));
2085 if (result.Error()) return result;
2086 return Result<bool>(true);
2087 }
2088 [nodiscard]
2089 public Result<bool> NewLine(int lineIndex, int 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(columnIndex, line.Length() - columnIndex);
2105 lineChanged = true;
2106 }
2107 ustring trimmedLine = System.Windows.TrimEnd(line);
2108 if (trimmedLine.Length() != line.Length())
2109 {
2110 Swap(line, trimmedLine);
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 + 1, toInsert);
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 + 1, indentLineIndex);
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 + 1, 1 + 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 lineIndex, int columnIndex)
2155 {
2156 return editCommandList.AddCommand(new TabCommand(lineIndex, columnIndex));
2157 }
2158 [nodiscard]
2159 public Result<bool> Tab(int lineIndex, int columnIndex)
2160 {
2161 while (lineIndex >= lines.Count())
2162 {
2163 lines.Add(ustring());
2164 }
2165 ustring& line = lines[lineIndex];
2166 line.Insert(columnIndex, ustring(' ', 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 + 1, columnIndex + 1 + LineNumberFieldLength() + IndentSize());
2178 if (result.Error()) return result;
2179 result = ScrollToCaret();
2180 if (result.Error()) return result;
2181 result = InvalidateLineCol(lineIndex + 1, columnIndex + 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 lineIndex, int 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(lineIndex, columnIndex, numSpaces));
2205 }
2206 [nodiscard]
2207 public Result<bool> Backtab(int lineIndex, int 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(col, numSpaces);
2229 LineEventArgs args(lineIndex, -1);
2230 auto result = OnLineChanged(args);
2231 if (result.Error()) return result;
2232 result = SetCaretLineCol(lineIndex + 1, columnIndex + 1 + LineNumberFieldLength() - numSpaces);
2233 if (result.Error()) return result;
2234 result = ScrollToCaret();
2235 if (result.Error()) return result;
2236 result = InvalidateLineCol(lineIndex + 1, 1 + 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 lineIndex, int columnIndex, int numSpaces)
2245 {
2246 ustring& line = lines[lineIndex];
2247 line.Insert(columnIndex - numSpaces, ustring(' ', numSpaces));
2248 LineEventArgs args(lineIndex, -1);
2249 auto result = OnLineChanged(args);
2250 result = SetCaretLineCol(lineIndex + 1, columnIndex + 1 + LineNumberFieldLength() - numSpaces);
2251 if (result.Error()) return result;
2252 result = ScrollToCaret();
2253 if (result.Error()) return result;
2254 result = InvalidateLineCol(lineIndex + 1, 1 + 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 lineIndex, int columnIndex, int numSpaces)
2262 {
2263 ustring& line = lines[lineIndex];
2264 line.Remove(columnIndex - numSpaces, numSpaces);
2265 LineEventArgs args(lineIndex, -1);
2266 auto result = OnLineChanged(args);
2267 if (result.Error()) return result;
2268 result = SetCaretLineCol(lineIndex + 1, columnIndex + 1 + LineNumberFieldLength() - numSpaces);
2269 if (result.Error()) return result;
2270 result = ScrollToCaret();
2271 if (result.Error()) return result;
2272 result = InvalidateLineCol(lineIndex + 1, 1 + 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(), null, 1, cast<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(1u, caretTimerPeriod);
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.location, line, column);
2430 uchar c = GetCharAt(line, column);
2431 while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
2432 {
2433 if (column == 1 || !Prev(line, column))
2434 {
2435 break;
2436 }
2437 c = GetCharAt(line, column);
2438 }
2439 c = GetCharAt(line, column);
2440 if (System.Windows.IsWhiteSpace(c) || IsWordSeparator(c))
2441 {
2442 Next(line, column);
2443 }
2444 SourcePos start(line, column);
2445 c = GetCharAt(line, column);
2446 while (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
2447 {
2448 if (!Next(line, column))
2449 {
2450 break;
2451 }
2452 c = GetCharAt(line, column);
2453 }
2454 if (!System.Windows.IsWhiteSpace(c) && !IsWordSeparator(c))
2455 {
2456 column = column + 1;
2457 }
2458 SourcePos end(line, column);
2459 result = ExtendSelection(start, end);
2460 if (result.Error()) return result;
2461 result = SetCaretLineCol(start.line, start.column + LineNumberFieldLength());
2462 if (result.Error()) return result;
2463 result = ScrollToCaret();
2464 if (result.Error()) return result;
2465 result = InvalidateLineCol(start.line, start.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(x, y);
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 + 1, 1 + 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(0, ustring(' ', 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& line, int 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(0, caretLine - 1);
2632 int caretColumnIndex = Max(0, caretColumn - 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(0, caretColumnIndex);
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(0, caretColumnIndex);
2654 line = line.Substring(0, startColumndIndex) + 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 + 1, newEndColumnIndex + 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(0, caretColumn - 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(0, startIndex)).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 - 1, selection.end.column - selection.start.column);
2744 int indent = MinIndent(s);
2745 SelectionData selectionData(Unindent(s, indent), indent, 0);
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(0, selection.end.column - 1).IsEmpty())
2759 {
2760 ustring lastLine = lines[selection.end.line - 1].Substring(0, selection.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(s, indent), indent, numTrailingSpaces);
2776 return selectionData;
2777 }
2778 }
2779 [nodiscard]
2780 public Result<bool> InsertSelection(const Selection& selectionToInsert, const SelectionData& selectionData, bool 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(0, ustring(' ', selectionData.indent));
2792 }
2793 lines.Insert(lines.Begin() + lineIndex, selectedText);
2794 LineEventArgs insertArgs(lineIndex, -1);
2795 auto result = OnLineInserted(insertArgs);
2796 if (result.Error()) return result;
2797 result = SetCaretLineCol(lineIndex + 1, 1 + 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(0, ustring(' ', selectionData.indent));
2807 }
2808 lines[lineIndex].Insert(selectionToInsert.start.column - 1, selectedText);
2809 LineEventArgs changeArgs(lineIndex, -1);
2810 auto result = OnLineChanged(changeArgs);
2811 if (result.Error()) return result;
2812 result = SetCaretLineCol(lineIndex + 1, selectionToInsert.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(0, ustring(' ', selectionData.indent));
2828 }
2829 lines[startLineIndex].Insert(selectionToInsert.start.column - 1, firstSelectionLine);
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(0, ustring(' ', selectionData.indent));
2841 }
2842 lines.Insert(lines.Begin() + startLineIndex, firstSelectionLine);
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(0, ustring(' ', selectionData.indent));
2855 }
2856 lines.Insert(lines.Begin() + i, selectionLine);
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(0, ustring(' ', selectionData.indent));
2868 }
2869 lastLine.Append(' ', selectionData.numTrailingSpaces);
2870 }
2871 if (wholeLine)
2872 {
2873 lines.Insert(lines.Begin() + endLineIndex, lastLine);
2874 LineEventArgs insertArgs(endLineIndex, -1);
2875 auto result = OnLineInserted(insertArgs);
2876 if (result.Error()) return result;
2877 }
2878 else
2879 {
2880 lines[endLineIndex].Insert(0, lastLine);
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.line, selection.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(selection, selectionData, wholeLine));
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.line, 1 + 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 - 1, selection.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 + 1, selection.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 - 1, GetLineLength(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(0, selection.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.line, selection.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(0, selection.end.column - 1).IsEmpty()) continue;
3019 lines[i].Insert(0, indentString);
3020 LineEventArgs changeArgs(i, -1);
3021 auto result = OnLineChanged(changeArgs);
3022 if (result.Error()) return result;
3023 }
3024 auto result = InvalidateLines(selection.start.line, selection.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(0, selection.end.column - 1);
3048 }
3049 if (line.StartsWith(indentString))
3050 {
3051 lines[i].Remove(0, IndentSize());
3052 LineEventArgs changeArgs(i, -1);
3053 auto result = OnLineChanged(changeArgs);
3054 if (result.Error()) return result;
3055 }
3056 }
3057 auto result = InvalidateLines(selection.start.line, selection.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& start, const 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<Color, Brush*>.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<LineChangedEventHandler, LineEventArgs>& LineChangedEvent() const
3259 {
3260 return lineChangedEvent;
3261 }
3262 public Event<LineDeletedEventHandler, LineEventArgs>& LineDeletedEvent() const
3263 {
3264 return lineDeletedEvent;
3265 }
3266 public Event<LineInsertedEventHandler, LineEventArgs>& LineInsertedEvent() const
3267 {
3268 return lineInsertedEvent;
3269 }
3270 public Event<GotoCaretLineEventHandler, ControlEventArgs>& 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 lineNumber, int 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.x, cast<int>(charHeight + 1.500000f));
3339 Rect rect(pt, sz);
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 startLineNumber, int endLineNumber)
3352 {
3353 if (charHeight != 0 && charWidth != 0)
3354 {
3355 Point pt(0, cast<int>(charHeight * (startLineNumber - 1) - charHeight));
3356 TranslateContentLocation(pt);
3357 Size size = GetSize();
3358 Size sz(size.w, cast<int>(charHeight * (endLineNumber - startLineNumber + 1) + 2 * charHeight + 1.500000f));
3359 Rect rect(pt, sz);
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<Color, Brush*> 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<LineChangedEventHandler, LineEventArgs> lineChangedEvent;
3404 private Event<LineDeletedEventHandler, LineEventArgs> lineDeletedEvent;
3405 private Event<LineInsertedEventHandler, LineEventArgs> lineInsertedEvent;
3406 private Event<GotoCaretLineEventHandler, ControlEventArgs> 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(0, i);
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& s, int 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(¤tUserKey);
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