From ed9627bc14cffcaddcdfc0e32051ce60510f7833 Mon Sep 17 00:00:00 2001 From: Alexey Yakovlev Date: Sun, 22 Apr 2012 22:50:29 +0400 Subject: [PATCH] Added FastColoredTextBox sources instead of the compiled binary. --- .../030.Irony.GrammarExplorer.2010.csproj | 3 - Irony.WinForms/035.Irony.WinForms.2010.csproj | 57 +- .../AboutFastColoredTextBox.txt | 0 .../FastColoredTextBox/AutocompleteItem.cs | 230 + .../FastColoredTextBox/AutocompleteMenu.cs | 565 ++ Irony.WinForms/FastColoredTextBox/Char.cs | 52 + .../FastColoredTextBox/CommandManager.cs | 213 + Irony.WinForms/FastColoredTextBox/Commands.cs | 466 ++ Irony.WinForms/FastColoredTextBox/Export.cs | 207 + .../FastColoredTextBox/FastColoredTextBox.cs | 4596 +++++++++++++++++ .../FastColoredTextBox.resx | 120 + .../FastColoredTextBox/FileTextSource.cs | 406 ++ .../FastColoredTextBox/FindForm.Designer.cs | 147 + Irony.WinForms/FastColoredTextBox/FindForm.cs | 120 + .../FastColoredTextBox/FindForm.resx | 120 + .../FastColoredTextBox/GoToForm.Designer.cs | 108 + Irony.WinForms/FastColoredTextBox/GoToForm.cs | 23 + .../FastColoredTextBox/GoToForm.resx | 120 + .../FastColoredTextBox/LimitedStack.cs | 105 + Irony.WinForms/FastColoredTextBox/Line.cs | 349 ++ .../FastColoredTextBox/LinesAccessor.cs | 98 + Irony.WinForms/FastColoredTextBox/Place.cs | 78 + .../FastColoredTextBox/PlatformType.cs | 75 + Irony.WinForms/FastColoredTextBox/Range.cs | 1042 ++++ .../ReplaceForm.Designer.cs | 198 + .../FastColoredTextBox/ReplaceForm.cs | 171 + .../FastColoredTextBox/ReplaceForm.resx | 120 + Irony.WinForms/FastColoredTextBox/Style.cs | 302 ++ .../FastColoredTextBox/SyntaxDescriptor.cs | 50 + .../FastColoredTextBox/SyntaxHighlighter.cs | 677 +++ .../FastColoredTextBox/TextSource.cs | 319 ++ .../FastColoredTextBox/TypeDescriptor.cs | 91 + .../FastColoredTextBox/VisualMarker.cs | 106 + .../FastColoredTextBox/FastColoredTextBox.XML | 1953 ------- .../FastColoredTextBox/FastColoredTextBox.dll | Bin 195072 -> 0 bytes 35 files changed, 11328 insertions(+), 1959 deletions(-) rename Libraries/FastColoredTextBox/about.txt => Irony.WinForms/FastColoredTextBox/AboutFastColoredTextBox.txt (100%) create mode 100644 Irony.WinForms/FastColoredTextBox/AutocompleteItem.cs create mode 100644 Irony.WinForms/FastColoredTextBox/AutocompleteMenu.cs create mode 100644 Irony.WinForms/FastColoredTextBox/Char.cs create mode 100644 Irony.WinForms/FastColoredTextBox/CommandManager.cs create mode 100644 Irony.WinForms/FastColoredTextBox/Commands.cs create mode 100644 Irony.WinForms/FastColoredTextBox/Export.cs create mode 100644 Irony.WinForms/FastColoredTextBox/FastColoredTextBox.cs create mode 100644 Irony.WinForms/FastColoredTextBox/FastColoredTextBox.resx create mode 100644 Irony.WinForms/FastColoredTextBox/FileTextSource.cs create mode 100644 Irony.WinForms/FastColoredTextBox/FindForm.Designer.cs create mode 100644 Irony.WinForms/FastColoredTextBox/FindForm.cs create mode 100644 Irony.WinForms/FastColoredTextBox/FindForm.resx create mode 100644 Irony.WinForms/FastColoredTextBox/GoToForm.Designer.cs create mode 100644 Irony.WinForms/FastColoredTextBox/GoToForm.cs create mode 100644 Irony.WinForms/FastColoredTextBox/GoToForm.resx create mode 100644 Irony.WinForms/FastColoredTextBox/LimitedStack.cs create mode 100644 Irony.WinForms/FastColoredTextBox/Line.cs create mode 100644 Irony.WinForms/FastColoredTextBox/LinesAccessor.cs create mode 100644 Irony.WinForms/FastColoredTextBox/Place.cs create mode 100644 Irony.WinForms/FastColoredTextBox/PlatformType.cs create mode 100644 Irony.WinForms/FastColoredTextBox/Range.cs create mode 100644 Irony.WinForms/FastColoredTextBox/ReplaceForm.Designer.cs create mode 100644 Irony.WinForms/FastColoredTextBox/ReplaceForm.cs create mode 100644 Irony.WinForms/FastColoredTextBox/ReplaceForm.resx create mode 100644 Irony.WinForms/FastColoredTextBox/Style.cs create mode 100644 Irony.WinForms/FastColoredTextBox/SyntaxDescriptor.cs create mode 100644 Irony.WinForms/FastColoredTextBox/SyntaxHighlighter.cs create mode 100644 Irony.WinForms/FastColoredTextBox/TextSource.cs create mode 100644 Irony.WinForms/FastColoredTextBox/TypeDescriptor.cs create mode 100644 Irony.WinForms/FastColoredTextBox/VisualMarker.cs delete mode 100644 Libraries/FastColoredTextBox/FastColoredTextBox.XML delete mode 100644 Libraries/FastColoredTextBox/FastColoredTextBox.dll diff --git a/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj b/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj index 26a6177..5836226 100644 --- a/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj +++ b/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj @@ -61,9 +61,6 @@ AllRules.ruleset - - ..\Libraries\FastColoredTextBox\FastColoredTextBox.dll - 3.5 diff --git a/Irony.WinForms/035.Irony.WinForms.2010.csproj b/Irony.WinForms/035.Irony.WinForms.2010.csproj index 917adc7..7546ad2 100644 --- a/Irony.WinForms/035.Irony.WinForms.2010.csproj +++ b/Irony.WinForms/035.Irony.WinForms.2010.csproj @@ -32,9 +32,6 @@ 4 - - ..\Libraries\FastColoredTextBox\FastColoredTextBox.dll - @@ -52,6 +49,48 @@ fmShowException.cs + + + Component + + + + + + + UserControl + + + + Form + + + FindForm.cs + + + Form + + + GoToForm.cs + + + + + + + + + Form + + + ReplaceForm.cs + + + + + + + @@ -72,6 +111,18 @@ fmShowException.cs + + FastColoredTextBox.cs + + + FindForm.cs + + + GoToForm.cs + + + ReplaceForm.cs + "; + + string html_end = "\r\n\r\n\r\n"; + + string begin_sample = String.Format(begin, 0, 0, 0, 0); + + int count_begin = enc.GetByteCount(begin_sample); + int count_html_begin = enc.GetByteCount(html_begin); + int count_html = enc.GetByteCount(html); + int count_html_end = enc.GetByteCount(html_end); + + string html_total = String.Format( + begin + , count_begin + , count_begin + count_html_begin + count_html + count_html_end + , count_begin + count_html_begin + , count_begin + count_html_begin + count_html + ) + html_begin + html + html_end; + + return new MemoryStream(enc.GetBytes(html_total)); + } + + + /// + /// Cut selected text into Clipboard + /// + public void Cut() + { + if (Selection.End != Selection.Start) + { + Copy(); + ClearSelected(); + } + } + + /// + /// Paste text from clipboard into selection position + /// + public void Paste() + { + string text = null; + var thread = new Thread(() => + { + if (Clipboard.ContainsText()) + text = Clipboard.GetText(); + } + ); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + + if (text != null) + InsertText(text); + } + + /// + /// Select all chars of text + /// + public void SelectAll() + { + Selection.SelectAll(); + } + + /// + /// Move caret to end of text + /// + public void GoEnd() + { + if (lines.Count > 0) + Selection.Start = new Place(lines[lines.Count - 1].Count, lines.Count - 1); + else + Selection.Start = new Place(0, 0); + + DoCaretVisible(); + } + + /// + /// Move caret to first position + /// + public void GoHome() + { + Selection.Start = new Place(0, 0); + VerticalScroll.Value = 0; + HorizontalScroll.Value = 0; + } + + /// + /// Clear text, styles, history, caches + /// + public void Clear() + { + Selection.BeginUpdate(); + try + { + Selection.SelectAll(); + ClearSelected(); + lines.Manager.ClearHistory(); + Invalidate(); + } + finally + { + Selection.EndUpdate(); + } + } + + /// + /// Clear buffer of styles + /// + public void ClearStylesBuffer() + { + for (int i = 0; i < Styles.Length; i++) + Styles[i] = null; + } + + /// + /// Clear style of all text + /// + public void ClearStyle(StyleIndex styleIndex) + { + foreach (Line line in lines) + line.ClearStyle(styleIndex); + + for (int i = 0; i < lineInfos.Count; i++) + SetVisibleState(i, VisibleState.Visible); + + Invalidate(); + } + + + /// + /// Clears undo and redo stacks + /// + public void ClearUndo() + { + lines.Manager.ClearHistory(); + } + + /// + /// Insert text into current selection position + /// + /// + public void InsertText(string text) + { + if (text == null) + return; + + lines.Manager.BeginAutoUndoCommands(); + try + { + if (Selection.Start != Selection.End) + lines.Manager.ExecuteCommand(new ClearSelectedCommand(TextSource)); + + lines.Manager.ExecuteCommand(new InsertTextCommand(TextSource, text)); + if (updating <= 0) + DoCaretVisible(); + } + finally + { + lines.Manager.EndAutoUndoCommands(); + } + // + Invalidate(); + } + + /// + /// Insert text into current selection position (with predefined style) + /// + /// + public void InsertText(string text, Style style) + { + if (text == null) + return; + + //remember last caret position + Place last = Selection.Start; + //insert text + InsertText(text); + //get range + var range = new Range(this, last, Selection.Start); + //set style for range + range.SetStyle(style); + } + + /// + /// Append string to end of the Text + /// + /// + public void AppendText(string text) + { + if (text == null) + return; + + Place oldStart = Selection.Start; + Place oldEnd = Selection.End; + + Selection.BeginUpdate(); + lines.Manager.BeginAutoUndoCommands(); + try + { + if (lines.Count > 0) + Selection.Start = new Place(lines[lines.Count - 1].Count, lines.Count - 1); + else + Selection.Start = new Place(0, 0); + + lines.Manager.ExecuteCommand(new InsertTextCommand(TextSource, text)); + } + finally + { + lines.Manager.EndAutoUndoCommands(); + Selection.Start = oldStart; + Selection.End = oldEnd; + Selection.EndUpdate(); + } + // + Invalidate(); + } + + /// + /// Returns index of the style in Styles + /// -1 otherwise + /// + /// + /// Index of the style in Styles + public int GetStyleIndex(Style style) + { + return Array.IndexOf(Styles, style); + } + + /// + /// Returns StyleIndex mask of given styles + /// + /// + /// StyleIndex mask of given styles + public StyleIndex GetStyleIndexMask(Style[] styles) + { + StyleIndex mask = StyleIndex.None; + foreach (Style style in styles) + { + int i = GetStyleIndex(style); + if (i >= 0) + mask |= Range.ToStyleIndex(i); + } + + return mask; + } + + internal int GetOrSetStyleLayerIndex(Style style) + { + int i = GetStyleIndex(style); + if (i < 0) + i = AddStyle(style); + return i; + } + + public static SizeF GetCharSize(Font font, char c) + { + Size sz2 = TextRenderer.MeasureText("<" + c.ToString() + ">", font); + Size sz3 = TextRenderer.MeasureText("<>", font); + + return new SizeF(sz2.Width - sz3.Width + 1, /*sz2.Height*/font.Height); + } + + [DllImport("Imm32.dll")] + public static extern IntPtr ImmGetContext(IntPtr hWnd); + + [DllImport("Imm32.dll")] + public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC); + + protected override void WndProc(ref Message m) + { + if (m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL) + if (m.WParam.ToInt32() != SB_ENDSCROLL) + Invalidate(); + + base.WndProc(ref m); + + if (ImeAllowed) + if (m.Msg == WM_IME_SETCONTEXT && m.WParam.ToInt32() == 1) + { + ImmAssociateContext(Handle, m_hImc); + } + } + + protected override void OnScroll(ScrollEventArgs se) + { + base.OnScroll(se); + OnVisibleRangeChanged(); + Invalidate(); + } + + private void InsertChar(char c) + { + lines.Manager.BeginAutoUndoCommands(); + try + { + if (Selection.Start != Selection.End) + lines.Manager.ExecuteCommand(new ClearSelectedCommand(TextSource)); + + lines.Manager.ExecuteCommand(new InsertCharCommand(TextSource, c)); + } + finally + { + lines.Manager.EndAutoUndoCommands(); + } + + Invalidate(); + } + + /// + /// Deletes selected chars + /// + public void ClearSelected() + { + if (Selection.Start != Selection.End) + { + lines.Manager.ExecuteCommand(new ClearSelectedCommand(TextSource)); + Invalidate(); + } + } + + /// + /// Deletes current line(s) + /// + public void ClearCurrentLine() + { + Selection.Expand(); + lines.Manager.ExecuteCommand(new ClearSelectedCommand(TextSource)); + if (Selection.Start.iLine == 0) + if (!Selection.GoRightThroughFolded()) return; + if (Selection.Start.iLine > 0) + lines.Manager.ExecuteCommand(new InsertCharCommand(TextSource, '\b')); //backspace + Invalidate(); + } + + private void Recalc() + { + if (!needRecalc) + return; + +#if debug + var sw = Stopwatch.StartNew(); + #endif + + needRecalc = false; + //calc min left indent + LeftIndent = LeftPadding; + long maxLineNumber = LinesCount + lineNumberStartValue - 1; + int charsForLineNumber = 2 + (maxLineNumber > 0 ? (int) Math.Log10(maxLineNumber) : 0); + if (Created) + { + if (ShowLineNumbers) + LeftIndent += charsForLineNumber*CharWidth + minLeftIndent + 1; + } + else + needRecalc = true; + //calc max line length and count of wordWrapLines + int maxLineLength = 0; + wordWrapLinesCount = 0; + + maxLineLength = RecalcMaxLineLength(); + + //adjust AutoScrollMinSize + int minWidth = LeftIndent + (maxLineLength)*CharWidth + 2; + if (wordWrap) + switch (WordWrapMode) + { + case WordWrapMode.WordWrapControlWidth: + case WordWrapMode.CharWrapControlWidth: + minWidth = 0; + break; + case WordWrapMode.WordWrapPreferredWidth: + case WordWrapMode.CharWrapPreferredWidth: + minWidth = LeftIndent + PreferredLineWidth*CharWidth + 2; + break; + } + AutoScrollMinSize = new Size(minWidth, wordWrapLinesCount*CharHeight + TopIndent); + +#if debug + sw.Stop(); + Console.WriteLine("Recalc: " + sw.ElapsedMilliseconds); + #endif + } + + private void RecalcScrollByOneLine(int iLine) + { + if (iLine >= lines.Count) + return; + + int maxLineLength = lines[iLine].Count; + int minWidth = LeftIndent + (maxLineLength)*CharWidth + 2; + if (AutoScrollMinSize.Width < minWidth) + AutoScrollMinSize = new Size(minWidth, AutoScrollMinSize.Height); + } + + private int RecalcMaxLineLength() + { + int maxLineLength = 0; + TextSource lines = this.lines; + int count = lines.Count; + int charHeight = CharHeight; + int topIndent = TopIndent; + + for (int i = 0; i < count; i++) + { + int lineLength = lines.GetLineLength(i); + LineInfo lineInfo = lineInfos[i]; + if (lineLength > maxLineLength && lineInfo.VisibleState == VisibleState.Visible) + maxLineLength = lineLength; + lineInfo.startY = wordWrapLinesCount*charHeight + topIndent; + wordWrapLinesCount += lineInfo.WordWrapStringsCount; + lineInfos[i] = lineInfo; + } + + return maxLineLength; + } + + private int GetMaxLineWordWrapedWidth() + { + if (wordWrap) + switch (wordWrapMode) + { + case WordWrapMode.WordWrapControlWidth: + case WordWrapMode.CharWrapControlWidth: + return ClientSize.Width; + case WordWrapMode.WordWrapPreferredWidth: + case WordWrapMode.CharWrapPreferredWidth: + return LeftIndent + PreferredLineWidth*CharWidth + 2; + } + + return int.MaxValue; + } + + private void RecalcWordWrap(int fromLine, int toLine) + { + int maxCharsPerLine = 0; + bool charWrap = false; + + switch (WordWrapMode) + { + case WordWrapMode.WordWrapControlWidth: + maxCharsPerLine = (ClientSize.Width - LeftIndent)/CharWidth; + break; + case WordWrapMode.CharWrapControlWidth: + maxCharsPerLine = (ClientSize.Width - LeftIndent)/CharWidth; + charWrap = true; + break; + case WordWrapMode.WordWrapPreferredWidth: + maxCharsPerLine = PreferredLineWidth; + break; + case WordWrapMode.CharWrapPreferredWidth: + maxCharsPerLine = PreferredLineWidth; + charWrap = true; + break; + } + + for (int iLine = fromLine; iLine <= toLine; iLine++) + if (lines.IsLineLoaded(iLine)) + { + if (!wordWrap) + lineInfos[iLine].CutOffPositions.Clear(); + else + { + LineInfo li = lineInfos[iLine]; + li.CalcCutOffs(maxCharsPerLine, ImeAllowed, charWrap, lines[iLine]); + lineInfos[iLine] = li; + } + } + needRecalc = true; + } + + protected override void OnClientSizeChanged(EventArgs e) + { + base.OnClientSizeChanged(e); + if (WordWrap) + { + RecalcWordWrap(0, lines.Count - 1); + Invalidate(); + } + OnVisibleRangeChanged(); + } + + /// + /// Scroll control for display defined rectangle + /// + /// + private void DoVisibleRectangle(Rectangle rect) + { + int oldV = VerticalScroll.Value; + int v = VerticalScroll.Value; + int h = HorizontalScroll.Value; + + if (rect.Bottom > ClientRectangle.Height) + v += rect.Bottom - ClientRectangle.Height; + else if (rect.Top < 0) + v += rect.Top; + + if (rect.Right > ClientRectangle.Width) + h += rect.Right - ClientRectangle.Width; + else if (rect.Left < LeftIndent) + h += rect.Left - LeftIndent; + // + if (!Multiline) + v = 0; + // + try + { + if (VerticalScroll.Visible) + VerticalScroll.Value = Math.Max(0, v); + if (HorizontalScroll.Visible) + HorizontalScroll.Value = Math.Max(0, h); + } + catch (ArgumentOutOfRangeException) + { + ; + } + + if (ShowScrollBars) + { + //some magic for update scrolls + base.AutoScrollMinSize -= new Size(1, 0); + base.AutoScrollMinSize += new Size(1, 0); + } + // + if (oldV != VerticalScroll.Value) + OnVisibleRangeChanged(); + } + + /// + /// Scroll control for display caret + /// + public void DoCaretVisible() + { + Invalidate(); + Recalc(); + Point car = PlaceToPoint(Selection.Start); + car.Offset(-CharWidth, 0); + DoVisibleRectangle(new Rectangle(car, new Size(2*CharWidth, 2*CharHeight))); + } + + /// + /// Scroll control left + /// + public void ScrollLeft() + { + Invalidate(); + HorizontalScroll.Value = 0; + AutoScrollMinSize -= new Size(1, 0); + AutoScrollMinSize += new Size(1, 0); + } + + /// + /// Scroll control for display selection area + /// + public void DoSelectionVisible() + { + if (lineInfos[Selection.End.iLine].VisibleState != VisibleState.Visible) + ExpandBlock(Selection.End.iLine); + + if (lineInfos[Selection.Start.iLine].VisibleState != VisibleState.Visible) + ExpandBlock(Selection.Start.iLine); + + Recalc(); + DoVisibleRectangle(new Rectangle(PlaceToPoint(new Place(0, Selection.End.iLine)), + new Size(2*CharWidth, 2*CharHeight))); + Point car = PlaceToPoint(Selection.Start); + Point car2 = PlaceToPoint(Selection.End); + car.Offset(-CharWidth, -ClientSize.Height/2); + DoVisibleRectangle(new Rectangle(car, + new Size(Math.Abs(car2.X - car.X), + /*Math.Abs(car2.Y-car.Y) + 2 * CharHeight*/ClientSize.Height))); + + Invalidate(); + } + + + protected override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + + if (e.KeyCode == Keys.ShiftKey) + lastModifiers &= ~Keys.Shift; + if (e.KeyCode == Keys.Alt) + lastModifiers &= ~Keys.Alt; + if (e.KeyCode == Keys.ControlKey) + lastModifiers &= ~Keys.Control; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + lastModifiers = e.Modifiers; + + handledChar = false; + + if (e.Handled) + { + handledChar = true; + return; + } + + switch (e.KeyCode) + { + case Keys.G: + if (e.Modifiers == Keys.Control) + ShowGoToDialog(); + break; + case Keys.F: + if (e.Modifiers == Keys.Control) + ShowFindDialog(); + break; + case Keys.H: + if (e.Modifiers == Keys.Control) + ShowReplaceDialog(); + break; + case Keys.C: + if (e.Modifiers == Keys.Control) + Copy(); + if (e.Modifiers == (Keys.Control | Keys.Shift)) + CommentSelected(); + break; + case Keys.X: + if (e.Modifiers == Keys.Control && !ReadOnly) + Cut(); + break; + case Keys.V: + if (e.Modifiers == Keys.Control && !ReadOnly) + Paste(); + break; + case Keys.A: + if (e.Modifiers == Keys.Control) + Selection.SelectAll(); + break; + case Keys.Z: + if (e.Modifiers == Keys.Control && !ReadOnly) + Undo(); + break; + case Keys.R: + if (e.Modifiers == Keys.Control && !ReadOnly) + Redo(); + break; + case Keys.U: + if (e.Modifiers == (Keys.Control | Keys.Shift)) + LowerCase(); + if (e.Modifiers == Keys.Control) + UpperCase(); + break; + case Keys.Tab: + if (e.Modifiers == Keys.Shift && !ReadOnly) + DecreaseIndent(); + break; + case Keys.OemMinus: + if (e.Modifiers == Keys.Control) + NavigateBackward(); + if (e.Modifiers == (Keys.Control | Keys.Shift)) + NavigateForward(); + break; + + case Keys.Back: + if (ReadOnly) break; + if (e.Modifiers == Keys.Alt) + Undo(); + else + if (e.Modifiers == Keys.None) + { + if (OnKeyPressing('\b')) //KeyPress event processed key + break; + if (Selection.End != Selection.Start) + ClearSelected(); + else + InsertChar('\b'); + OnKeyPressed('\b'); + }else + if (e.Modifiers == Keys.Control) + { + if (OnKeyPressing('\b')) //KeyPress event processed key + break; + if (Selection.End != Selection.Start) + ClearSelected(); + Selection.GoWordLeft(true); + ClearSelected(); + OnKeyPressed('\b'); + } + break; + + case Keys.Delete: + if (ReadOnly) break; + if (e.Modifiers == Keys.None) + { + if (OnKeyPressing((char) 0xff)) //KeyPress event processed key + break; + if (Selection.End != Selection.Start) + ClearSelected(); + else + { + if (Selection.GoRightThroughFolded()) + { + int iLine = Selection.Start.iLine; + InsertChar('\b'); + //if removed \n then trim spaces + if (iLine != Selection.Start.iLine && AutoIndent) + RemoveSpacesAfterCaret(); + } + } + OnKeyPressed((char) 0xff); + }else + if (e.Modifiers == Keys.Control) + { + if (OnKeyPressing((char)0xff)) //KeyPress event processed key + break; + if (Selection.End != Selection.Start) + ClearSelected(); + else + { + Selection.GoWordRight(true); + ClearSelected(); + } + OnKeyPressed((char)0xff); + } + break; + case Keys.Space: + if (ReadOnly) break; + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + { + if (OnKeyPressing(' ')) //KeyPress event processed key + break; + if (Selection.End != Selection.Start) + ClearSelected(); + else + { + //replace mode? select forward char + if (IsReplaceMode) + { + Selection.GoRight(true); + Selection.Inverse(); + } + + InsertChar(' '); + } + OnKeyPressed(' '); + } + break; + + case Keys.Left: + if (e.Modifiers == Keys.Control || e.Modifiers == (Keys.Control | Keys.Shift)) + Selection.GoWordLeft(e.Shift); + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + Selection.GoLeft(e.Shift); + break; + case Keys.Right: + if (e.Modifiers == Keys.Control || e.Modifiers == (Keys.Control | Keys.Shift)) + Selection.GoWordRight(e.Shift); + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + Selection.GoRight(e.Shift); + break; + case Keys.Up: + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + { + Selection.GoUp(e.Shift); + ScrollLeft(); + } + break; + case Keys.Down: + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + { + Selection.GoDown(e.Shift); + ScrollLeft(); + } + break; + case Keys.PageUp: + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + { + Selection.GoPageUp(e.Shift); + ScrollLeft(); + } + break; + case Keys.PageDown: + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + { + Selection.GoPageDown(e.Shift); + ScrollLeft(); + } + break; + case Keys.Home: + if (e.Modifiers == Keys.Control || e.Modifiers == (Keys.Control | Keys.Shift)) + Selection.GoFirst(e.Shift); + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + { + GoHome(e.Shift); + ScrollLeft(); + } + break; + case Keys.End: + if (e.Modifiers == Keys.Control || e.Modifiers == (Keys.Control | Keys.Shift)) + Selection.GoLast(e.Shift); + if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) + Selection.GoEnd(e.Shift); + break; + default: + if ((e.Modifiers & Keys.Control) != 0) + return; + if ((e.Modifiers & Keys.Alt) != 0) + return; + if (e.KeyCode == Keys.ShiftKey) + return; + break; + } + + e.Handled = true; + + DoCaretVisible(); + Invalidate(); + } + + private void GoHome(bool shift) + { + Selection.BeginUpdate(); + try + { + int iLine = Selection.Start.iLine; + int spaces = this[iLine].StartSpacesCount; + if (Selection.Start.iChar <= spaces) + Selection.GoHome(shift); + else + { + Selection.GoHome(shift); + for (int i = 0; i < spaces; i++) + Selection.GoRight(shift); + } + } + finally + { + Selection.EndUpdate(); + } + } + + /// + /// Convert selected text to upper case + /// + public void UpperCase() + { + Range old = Selection.Clone(); + SelectedText = SelectedText.ToUpper(); + Selection.Start = old.Start; + Selection.End = old.End; + } + + /// + /// Convert selected text to lower case + /// + public void LowerCase() + { + Range old = Selection.Clone(); + SelectedText = SelectedText.ToLower(); + Selection.Start = old.Start; + Selection.End = old.End; + } + + /// + /// Insert/remove comment prefix into selected lines + /// + public void CommentSelected() + { + CommentSelected(CommentPrefix); + } + + /// + /// Insert/remove comment prefix into selected lines + /// + public void CommentSelected(string commentPrefix) + { + if (string.IsNullOrEmpty(commentPrefix)) + return; + Selection.Normalize(); + bool isCommented = lines[Selection.Start.iLine].Text.TrimStart().StartsWith(commentPrefix); + if (isCommented) + RemoveLinePrefix(commentPrefix); + else + InsertLinePrefix(commentPrefix); + } + + /* + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if (keyData == Keys.Enter) + { + bool proc = ProcessKeyPress('\r'); + if (proc) + { + base.OnKeyDown(new KeyEventArgs(Keys.Enter)); + return true; + } + } + + return base.ProcessCmdKey(ref msg, keyData); + }*/ + + public void OnKeyPressing(KeyPressEventArgs args) + { + if (KeyPressing != null) + KeyPressing(this, args); + } + + private bool OnKeyPressing(char c) + { + var args = new KeyPressEventArgs(c); + OnKeyPressing(args); + return args.Handled; + } + + public void OnKeyPressed(char c) + { + var args = new KeyPressEventArgs(c); + if (KeyPressed != null) + KeyPressed(this, args); + } + + protected override bool ProcessMnemonic(char charCode) + { + if (Focused) + return ProcessKeyPress(charCode) || base.ProcessMnemonic(charCode); + else + return false; + } + + private bool ProcessKeyPress(char c) + { + if (handledChar) + return true; + + if (c == ' ') + return true; + + if (c == '\b' && (lastModifiers & Keys.Alt) != 0) + return true; + + if (char.IsControl(c) && c != '\r' && c != '\t') + return false; + + if (ReadOnly || !Enabled) + return false; + + + if (lastModifiers != Keys.None && + lastModifiers != Keys.Shift && + lastModifiers != (Keys.Control | Keys.Alt) && //ALT+CTRL is special chars (AltGr) + lastModifiers != (Keys.Shift | Keys.Control | Keys.Alt) && //SHIFT + ALT + CTRL is special chars (AltGr) + (lastModifiers != (Keys.Alt) || char.IsLetterOrDigit(c)) //may be ALT+LetterOrDigit is mnemonic code + ) + return false; //do not process Ctrl+? and Alt+? keys + + char sourceC = c; + if (OnKeyPressing(sourceC)) //KeyPress event processed key + return true; + + if (c == '\r' && !AcceptsReturn) + return false; + + //tab? + if (c == '\t') + { + if (!AcceptsTab) + return false; + + if (Selection.Start == Selection.End) + { + //insert tab as spaces + int spaces = TabLength - (Selection.Start.iChar%TabLength); + //replace mode? select forward chars + if (IsReplaceMode) + { + for (int i = 0; i < spaces; i++) + Selection.GoRight(true); + Selection.Inverse(); + } + + InsertText(new String(' ', spaces)); + } + else if ((lastModifiers & Keys.Shift) == 0) + IncreaseIndent(); + } + else + { + //replace \r on \n + if (c == '\r') + c = '\n'; + //replace mode? select forward char + if (IsReplaceMode) + { + Selection.GoRight(true); + Selection.Inverse(); + } + //insert char + InsertChar(c); + //do autoindent + if (AutoIndent) + { + DoCaretVisible(); + int needSpaces = CalcAutoIndent(Selection.Start.iLine); + if (this[Selection.Start.iLine].AutoIndentSpacesNeededCount != needSpaces) + { + DoAutoIndent(Selection.Start.iLine); + this[Selection.Start.iLine].AutoIndentSpacesNeededCount = needSpaces; + } + } + } + + DoCaretVisible(); + Invalidate(); + + OnKeyPressed(sourceC); + + return true; + } + + private void RemoveSpacesAfterCaret() + { + if (Selection.Start != Selection.End) + return; + Place end = Selection.Start; + while (Selection.CharAfterStart == ' ') + Selection.GoRight(true); + ClearSelected(); + } + + /// + /// Inserts autoindent's spaces in the line + /// + public virtual void DoAutoIndent(int iLine) + { + Place oldStart = Selection.Start; + // + int needSpaces = CalcAutoIndent(iLine); + // + int spaces = lines[iLine].StartSpacesCount; + int needToInsert = needSpaces - spaces; + if (needToInsert < 0) + needToInsert = -Math.Min(-needToInsert, spaces); + //insert start spaces + if (needToInsert == 0) + return; + Selection.Start = new Place(0, iLine); + if (needToInsert > 0) + InsertText(new String(' ', needToInsert)); + else + { + Selection.Start = new Place(0, iLine); + Selection.End = new Place(-needToInsert, iLine); + ClearSelected(); + } + + Selection.Start = new Place(Math.Min(lines[iLine].Count, Math.Max(0, oldStart.iChar + needToInsert)), iLine); + } + + /// + /// Returns needed start space count for the line + /// + public virtual int CalcAutoIndent(int iLine) + { + if (iLine < 0 || iLine >= LinesCount) return 0; + + + EventHandler calculator = AutoIndentNeeded; + if (calculator == null) + if (Language != Language.Custom && SyntaxHighlighter != null) + calculator = SyntaxHighlighter.AutoIndentNeeded; + else + calculator = CalcAutoIndentShiftByCodeFolding; + + int needSpaces = 0; + + var stack = new Stack(); + //calc indent for previous lines, find stable line + int i; + for (i = iLine - 1; i >= 0; i--) + { + var args = new AutoIndentEventArgs(i, lines[i].Text, i > 0 ? lines[i - 1].Text : "", TabLength); + calculator(this, args); + stack.Push(args); + if (args.Shift == 0 && args.LineText.Trim() != "") + break; + } + int indent = lines[i >= 0 ? i : 0].StartSpacesCount; + while (stack.Count != 0) + indent += stack.Pop().ShiftNextLines; + //clalc shift for current line + var a = new AutoIndentEventArgs(iLine, lines[iLine].Text, iLine > 0 ? lines[iLine - 1].Text : "", TabLength); + calculator(this, a); + needSpaces = indent + a.Shift; + + return needSpaces; + } + + internal virtual void CalcAutoIndentShiftByCodeFolding(object sender, AutoIndentEventArgs args) + { + //inset TAB after start folding marker + if (string.IsNullOrEmpty(lines[args.iLine].FoldingEndMarker) && + !string.IsNullOrEmpty(lines[args.iLine].FoldingStartMarker)) + { + args.ShiftNextLines = TabLength; + return; + } + //remove TAB before end folding marker + if (!string.IsNullOrEmpty(lines[args.iLine].FoldingEndMarker) && + string.IsNullOrEmpty(lines[args.iLine].FoldingStartMarker)) + { + args.Shift = -TabLength; + args.ShiftNextLines = -TabLength; + return; + } + } + + + private int GetMinStartSpacesCount(int fromLine, int toLine) + { + if (fromLine > toLine) + return 0; + + int result = int.MaxValue; + for (int i = fromLine; i <= toLine; i++) + { + int count = lines[i].StartSpacesCount; + if (count < result) + result = count; + } + + return result; + } + + /// + /// Undo last operation + /// + public void Undo() + { + lines.Manager.Undo(); + Invalidate(); + } + + /// + /// Redo + /// + public void Redo() + { + lines.Manager.Redo(); + Invalidate(); + } + + protected override bool IsInputKey(Keys keyData) + { + if (keyData == Keys.Tab && !AcceptsTab) + return false; + if (keyData == Keys.Enter && !AcceptsReturn) + return false; + + if ((keyData & Keys.Alt) == Keys.None) + { + Keys keys = keyData & Keys.KeyCode; + if (keys == Keys.Return) + return true; + } + + if ((keyData & Keys.Alt) != Keys.Alt) + { + switch ((keyData & Keys.KeyCode)) + { + case Keys.Prior: + case Keys.Next: + case Keys.End: + case Keys.Home: + case Keys.Left: + case Keys.Right: + case Keys.Up: + case Keys.Down: + return true; + + case Keys.Escape: + return false; + + case Keys.Tab: + return (keyData & Keys.Control) == Keys.None; + } + } + + return base.IsInputKey(keyData); + } + + [DllImport("User32.dll")] + private static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight); + + [DllImport("User32.dll")] + private static extern bool SetCaretPos(int x, int y); + + [DllImport("User32.dll")] + private static extern bool DestroyCaret(); + + [DllImport("User32.dll")] + private static extern bool ShowCaret(IntPtr hWnd); + + [DllImport("User32.dll")] + private static extern bool HideCaret(IntPtr hWnd); + + /// + /// Draw control + /// + protected override void OnPaint(PaintEventArgs e) + { + if (needRecalc) + Recalc(); +#if debug + var sw = Stopwatch.StartNew(); +#endif + visibleMarkers.Clear(); + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + // + Brush lineNumberBrush = new SolidBrush(LineNumberColor); + var servicePen = new Pen(ServiceLinesColor); + Brush changedLineBrush = new SolidBrush(ChangedLineColor); + Brush indentBrush = new SolidBrush(IndentBackColor); + var currentLinePen = new Pen(CurrentLineColor); + Brush currentLineBrush = new SolidBrush(Color.FromArgb(50, CurrentLineColor)); + //draw indent area + e.Graphics.FillRectangle(indentBrush, 0, 0, LeftIndentLine, ClientSize.Height); + if (LeftIndent > minLeftIndent) + e.Graphics.DrawLine(servicePen, LeftIndentLine, 0, LeftIndentLine, ClientSize.Height); + //draw preferred line width + if (PreferredLineWidth > 0) + e.Graphics.DrawLine(servicePen, + new Point(LeftIndent + PreferredLineWidth*CharWidth - HorizontalScroll.Value, 0), + new Point(LeftIndent + PreferredLineWidth*CharWidth - HorizontalScroll.Value, Height)); + // + int firstChar = HorizontalScroll.Value/CharWidth; + int lastChar = (HorizontalScroll.Value + ClientSize.Width)/CharWidth; + //draw chars + for (int iLine = YtoLineIndex(VerticalScroll.Value); iLine < lines.Count; iLine++) + { + Line line = lines[iLine]; + LineInfo lineInfo = lineInfos[iLine]; + // + if (lineInfo.startY > VerticalScroll.Value + ClientSize.Height) + break; + if (lineInfo.startY + lineInfo.WordWrapStringsCount*CharHeight < VerticalScroll.Value) + continue; + if (lineInfo.VisibleState == VisibleState.Hidden) + continue; + + int y = lineInfo.startY - VerticalScroll.Value; + // + e.Graphics.SmoothingMode = SmoothingMode.None; + //draw line background + if (lineInfo.VisibleState == VisibleState.Visible) + if (line.BackgroundBrush != null) + e.Graphics.FillRectangle(line.BackgroundBrush, + new Rectangle(LeftIndent, y, Width, + CharHeight*lineInfo.WordWrapStringsCount)); + //draw current line background + if (CurrentLineColor != Color.Transparent && iLine == Selection.Start.iLine) + if (Selection.Start == Selection.End) + e.Graphics.FillRectangle(currentLineBrush, new Rectangle(LeftIndent, y, Width, CharHeight)); + else + e.Graphics.DrawLine(currentLinePen, LeftIndent, y + CharHeight, Width, y + CharHeight); + //draw changed line marker + if (ChangedLineColor != Color.Transparent && line.IsChanged) + e.Graphics.FillRectangle(changedLineBrush, + new RectangleF(-10, y, LeftIndent - minLeftIndent - 2 + 10, CharHeight + 1)); + // + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + //OnPaint event + if (lineInfo.VisibleState == VisibleState.Visible) + OnPaintLine(new PaintLineEventArgs(iLine, + new Rectangle(LeftIndent, y, Width, + CharHeight*lineInfo.WordWrapStringsCount), + e.Graphics, e.ClipRectangle)); + //draw line number + if (ShowLineNumbers) + e.Graphics.DrawString((iLine + lineNumberStartValue).ToString(), Font, lineNumberBrush, + new RectangleF(-10, y, LeftIndent - minLeftIndent - 2 + 10, CharHeight), + new StringFormat(StringFormatFlags.DirectionRightToLeft)); + //create markers + if (lineInfo.VisibleState == VisibleState.StartOfHiddenBlock) + visibleMarkers.Add(new ExpandFoldingMarker(iLine, + new Rectangle(LeftIndentLine - 4, y + CharHeight/2 - 3, 8, + 8))); + if (!string.IsNullOrEmpty(line.FoldingStartMarker) && lineInfo.VisibleState == VisibleState.Visible && + string.IsNullOrEmpty(line.FoldingEndMarker)) + visibleMarkers.Add(new CollapseFoldingMarker(iLine, + new Rectangle(LeftIndentLine - 4, y + CharHeight/2 - 3, + 8, 8))); + if (lineInfo.VisibleState == VisibleState.Visible && !string.IsNullOrEmpty(line.FoldingEndMarker) && + string.IsNullOrEmpty(line.FoldingStartMarker)) + e.Graphics.DrawLine(servicePen, LeftIndentLine, y + CharHeight*lineInfo.WordWrapStringsCount - 1, + LeftIndentLine + 4, y + CharHeight*lineInfo.WordWrapStringsCount - 1); + //draw wordwrap strings of line + for (int iWordWrapLine = 0; iWordWrapLine < lineInfo.WordWrapStringsCount; iWordWrapLine++) + { + y = lineInfo.startY + iWordWrapLine*CharHeight - VerticalScroll.Value; + //draw chars + DrawLineChars(e, firstChar, lastChar, iLine, iWordWrapLine, y); + } + } + //draw brackets highlighting + if (BracketsStyle != null && leftBracketPosition != null && rightBracketPosition != null) + { + BracketsStyle.Draw(e.Graphics, PlaceToPoint(leftBracketPosition.Start), leftBracketPosition); + BracketsStyle.Draw(e.Graphics, PlaceToPoint(rightBracketPosition.Start), rightBracketPosition); + } + if (BracketsStyle2 != null && leftBracketPosition2 != null && rightBracketPosition2 != null) + { + BracketsStyle2.Draw(e.Graphics, PlaceToPoint(leftBracketPosition2.Start), leftBracketPosition2); + BracketsStyle2.Draw(e.Graphics, PlaceToPoint(rightBracketPosition2.Start), rightBracketPosition2); + } + // + e.Graphics.SmoothingMode = SmoothingMode.None; + //draw folding indicator + if ((startFoldingLine >= 0 || endFoldingLine >= 0) && Selection.Start == Selection.End) + if (endFoldingLine < lineInfos.Count) + { + //folding indicator + int startFoldingY = (startFoldingLine >= 0 ? lineInfos[startFoldingLine].startY : 0) - + VerticalScroll.Value + CharHeight/2; + int endFoldingY = (endFoldingLine >= 0 + ? lineInfos[endFoldingLine].startY + + (lineInfos[endFoldingLine].WordWrapStringsCount - 1)*CharHeight + : (WordWrapLinesCount + 1)*CharHeight) - VerticalScroll.Value + CharHeight; + + using (var indicatorPen = new Pen(Color.FromArgb(100, FoldingIndicatorColor), 4)) + e.Graphics.DrawLine(indicatorPen, LeftIndent - 5, startFoldingY, LeftIndent - 5, endFoldingY); + } + //draw markers + foreach (VisualMarker m in visibleMarkers) + m.Draw(e.Graphics, servicePen); + //draw caret + Point car = PlaceToPoint(Selection.Start); + + if (Focused && car.X >= LeftIndent) + { + int carWidth = IsReplaceMode ? CharWidth : 1; + CreateCaret(Handle, 0, carWidth, CharHeight + 1); + SetCaretPos(car.X, car.Y); + ShowCaret(Handle); + e.Graphics.DrawLine(Pens.Black, car.X, car.Y, car.X, car.Y + CharHeight); + } + else + HideCaret(Handle); + + //dispose resources + lineNumberBrush.Dispose(); + servicePen.Dispose(); + changedLineBrush.Dispose(); + indentBrush.Dispose(); + currentLinePen.Dispose(); + currentLineBrush.Dispose(); + // +#if debug + Console.WriteLine("OnPaint: "+ sw.ElapsedMilliseconds); +#endif + // + base.OnPaint(e); + } + + private void DrawLineChars(PaintEventArgs e, int firstChar, int lastChar, int iLine, int iWordWrapLine, int y) + { + Line line = lines[iLine]; + LineInfo lineInfo = lineInfos[iLine]; + int from = lineInfo.GetWordWrapStringStartPosition(iWordWrapLine); + int to = lineInfo.GetWordWrapStringFinishPosition(iWordWrapLine, line); + + int startX = LeftIndent - HorizontalScroll.Value; + if (startX < LeftIndent) + firstChar++; + + lastChar = Math.Min(to - from, lastChar); + + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + //folded block ? + if (lineInfo.VisibleState == VisibleState.StartOfHiddenBlock) + { + //rendering by FoldedBlockStyle + FoldedBlockStyle.Draw(e.Graphics, new Point(startX + firstChar*CharWidth, y), + new Range(this, from + firstChar, iLine, from + lastChar + 1, iLine)); + } + else + { + //render by custom styles + StyleIndex currentStyleIndex = StyleIndex.None; + int iLastFlushedChar = firstChar - 1; + + for (int iChar = firstChar; iChar <= lastChar; iChar++) + { + StyleIndex style = line[from + iChar].style; + if (currentStyleIndex != style) + { + FlushRendering(e.Graphics, currentStyleIndex, + new Point(startX + (iLastFlushedChar + 1)*CharWidth, y), + new Range(this, from + iLastFlushedChar + 1, iLine, from + iChar, iLine)); + iLastFlushedChar = iChar - 1; + currentStyleIndex = style; + } + } + FlushRendering(e.Graphics, currentStyleIndex, new Point(startX + (iLastFlushedChar + 1)*CharWidth, y), + new Range(this, from + iLastFlushedChar + 1, iLine, from + lastChar + 1, iLine)); + } + + //draw selection + if (Selection.End != Selection.Start && lastChar >= firstChar) + { + e.Graphics.SmoothingMode = SmoothingMode.None; + var textRange = new Range(this, from + firstChar, iLine, from + lastChar + 1, iLine); + textRange = Selection.GetIntersectionWith(textRange); + if (textRange != null && SelectionStyle != null) + SelectionStyle.Draw(e.Graphics, new Point(startX + (textRange.Start.iChar - from)*CharWidth, y), + textRange); + } + } + + private void FlushRendering(Graphics gr, StyleIndex styleIndex, Point pos, Range range) + { + if (range.End > range.Start) + { + int mask = 1; + bool hasTextStyle = false; + for (int i = 0; i < Styles.Length; i++) + { + if (Styles[i] != null && ((int) styleIndex & mask) != 0) + { + Style style = Styles[i]; + bool isTextStyle = style is TextStyle; + if (!hasTextStyle || !isTextStyle || AllowSeveralTextStyleDrawing) + //cancelling secondary rendering by TextStyle + style.Draw(gr, pos, range); //rendering + hasTextStyle |= isTextStyle; + } + mask = mask << 1; + } + //draw by default renderer + if (!hasTextStyle) + DefaultStyle.Draw(gr, pos, range); + } + } + + protected override void OnEnter(EventArgs e) + { + base.OnEnter(e); + mouseIsDrag = false; + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + if (e.Button == MouseButtons.Left) + { + VisualMarker marker = FindVisualMarkerForPoint(e.Location); + //click on marker + if (marker != null) + { + mouseIsDrag = false; + OnMarkerClick(e, marker); + return; + } + mouseIsDrag = true; + //click on text + Place oldEnd = Selection.End; + Selection.BeginUpdate(); + Selection.Start = PointToPlace(e.Location); + if ((lastModifiers & Keys.Shift) != 0) + Selection.End = oldEnd; + Selection.EndUpdate(); + Invalidate(); + return; + } + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + Invalidate(); + base.OnMouseWheel(e); + OnVisibleRangeChanged(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + if (e.Button == MouseButtons.Left && mouseIsDrag) + { + Place oldEnd = Selection.End; + Selection.BeginUpdate(); + Selection.Start = PointToPlace(e.Location); + Selection.End = oldEnd; + Selection.EndUpdate(); + DoCaretVisible(); + Invalidate(); + return; + } + + VisualMarker marker = FindVisualMarkerForPoint(e.Location); + if (marker != null) + Cursor = marker.Cursor; + else + Cursor = Cursors.IBeam; + } + + protected override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + VisualMarker m = FindVisualMarkerForPoint(e.Location); + if (m != null) + { + OnMarkerDoubleClick(m); + return; + } + + Place p = PointToPlace(e.Location); + int fromX = p.iChar; + int toX = p.iChar; + + for (int i = p.iChar; i < lines[p.iLine].Count; i++) + { + char c = lines[p.iLine][i].c; + if (char.IsLetterOrDigit(c) || c == '_') + toX = i + 1; + else + break; + } + + for (int i = p.iChar - 1; i >= 0; i--) + { + char c = lines[p.iLine][i].c; + if (char.IsLetterOrDigit(c) || c == '_') + fromX = i; + else + break; + } + + Selection.Start = new Place(toX, p.iLine); + Selection.End = new Place(fromX, p.iLine); + + Invalidate(); + } + + private int YtoLineIndex(int y) + { + int i = lineInfos.BinarySearch(new LineInfo(-10), new LineYComparer(y)); + i = i < 0 ? -i - 2 : i; + if (i < 0) return 0; + if (i > lines.Count - 1) return lines.Count - 1; + return i; + } + + /// + /// Gets nearest line and char position from coordinates + /// + /// Point + /// Line and char position + public Place PointToPlace(Point point) + { +#if debug + var sw = Stopwatch.StartNew(); + #endif + point.Offset(HorizontalScroll.Value, VerticalScroll.Value); + point.Offset(-LeftIndent, 0); + int iLine = YtoLineIndex(point.Y); + int y = 0; + + for (; iLine < lines.Count; iLine++) + { + y = lineInfos[iLine].startY + lineInfos[iLine].WordWrapStringsCount*CharHeight; + if (y > point.Y && lineInfos[iLine].VisibleState == VisibleState.Visible) + break; + } + if (iLine >= lines.Count) + iLine = lines.Count - 1; + if (lineInfos[iLine].VisibleState != VisibleState.Visible) + iLine = FindPrevVisibleLine(iLine); + // + int iWordWrapLine = lineInfos[iLine].WordWrapStringsCount; + do + { + iWordWrapLine--; + y -= CharHeight; + } while (y > point.Y); + if (iWordWrapLine < 0) iWordWrapLine = 0; + // + int start = lineInfos[iLine].GetWordWrapStringStartPosition(iWordWrapLine); + int finish = lineInfos[iLine].GetWordWrapStringFinishPosition(iWordWrapLine, lines[iLine]); + var x = (int) Math.Round((float) point.X/CharWidth); + x = x < 0 ? start : start + x; + if (x > finish) + x = finish + 1; + if (x > lines[iLine].Count) + x = lines[iLine].Count; + +#if debug + Console.WriteLine("PointToPlace: " + sw.ElapsedMilliseconds); + #endif + + return new Place(x, iLine); + } + + /// + /// Gets nearest absolute text position for given point + /// + /// Point + /// Position + public int PointToPosition(Point point) + { + return PlaceToPosition(PointToPlace(point)); + } + + /// + /// Fires TextChanging event + /// + public virtual void OnTextChanging(ref string text) + { + ClearBracketsPositions(); + + if (TextChanging != null) + { + var args = new TextChangingEventArgs {InsertingText = text}; + TextChanging(this, args); + text = args.InsertingText; + } + ; + } + + public virtual void OnTextChanging() + { + string temp = null; + OnTextChanging(ref temp); + } + + /// + /// Fires TextChanged event + /// + public virtual void OnTextChanged() + { + var r = new Range(this); + r.SelectAll(); + OnTextChanged(new TextChangedEventArgs(r)); + } + + /// + /// Fires TextChanged event + /// + public virtual void OnTextChanged(int fromLine, int toLine) + { + var r = new Range(this); + r.Start = new Place(0, Math.Min(fromLine, toLine)); + r.End = new Place(lines[Math.Max(fromLine, toLine)].Count, Math.Max(fromLine, toLine)); + OnTextChanged(new TextChangedEventArgs(r)); + } + + /// + /// Fires TextChanged event + /// + public virtual void OnTextChanged(Range r) + { + OnTextChanged(new TextChangedEventArgs(r)); + } + + public void BeginUpdate() + { + if (updating == 0) + updatingRange = null; + updating++; + } + + public void EndUpdate() + { + updating--; + + if (updating == 0 && updatingRange != null) + { + updatingRange.Expand(); + OnTextChanged(updatingRange); + } + } + + + /// + /// Fires TextChanged event + /// + protected virtual void OnTextChanged(TextChangedEventArgs args) + { + // + args.ChangedRange.Normalize(); + // + if (updating > 0) + { + if (updatingRange == null) + updatingRange = args.ChangedRange.Clone(); + else + { + if (updatingRange.Start.iLine > args.ChangedRange.Start.iLine) + updatingRange.Start = new Place(0, args.ChangedRange.Start.iLine); + if (updatingRange.End.iLine < args.ChangedRange.End.iLine) + updatingRange.End = new Place(lines[args.ChangedRange.End.iLine].Count, + args.ChangedRange.End.iLine); + updatingRange = updatingRange.GetIntersectionWith(Range); + } + return; + } + // +#if debug + var sw = Stopwatch.StartNew(); + #endif + IsChanged = true; + TextVersion++; + MarkLinesAsChanged(args.ChangedRange); + // + if (wordWrap) + RecalcWordWrap(args.ChangedRange.Start.iLine, args.ChangedRange.End.iLine); + // + base.OnTextChanged(args); + + //dalayed event stuffs + if (delayedTextChangedRange == null) + delayedTextChangedRange = args.ChangedRange.Clone(); + else + delayedTextChangedRange = delayedTextChangedRange.GetUnionWith(args.ChangedRange); + + needRiseTextChangedDelayed = true; + ResetTimer(timer2); + // + OnSyntaxHighlight(args); + // + if (TextChanged != null) + TextChanged(this, args); + // + if (BindingTextChanged != null) + BindingTextChanged(this, EventArgs.Empty); + // + base.OnTextChanged(EventArgs.Empty); + // +#if debug + Console.WriteLine("OnTextChanged: " + sw.ElapsedMilliseconds); + #endif + + OnVisibleRangeChanged(); + } + + private void MarkLinesAsChanged(Range range) + { + for (int iLine = range.Start.iLine; iLine <= range.End.iLine; iLine++) + if (iLine >= 0 && iLine < lines.Count) + lines[iLine].IsChanged = true; + } + + /// + /// Fires SelectionCnaged event + /// + public virtual void OnSelectionChanged() + { +#if debug + var sw = Stopwatch.StartNew(); + #endif + //find folding markers for highlighting + if (HighlightFoldingIndicator) + HighlightFoldings(); + // + needRiseSelectionChangedDelayed = true; + ResetTimer(timer); + + if (SelectionChanged != null) + SelectionChanged(this, new EventArgs()); + +#if debug + Console.WriteLine("OnSelectionChanged: "+ sw.ElapsedMilliseconds); + #endif + } + + //find folding markers for highlighting + private void HighlightFoldings() + { + int prevStartFoldingLine = startFoldingLine; + int prevEndFoldingLine = endFoldingLine; + // + startFoldingLine = -1; + endFoldingLine = -1; + const int maxLines = 2000; + // + string marker = null; + int counter = 0; + for (int i = Selection.Start.iLine; i >= Math.Max(Selection.Start.iLine - maxLines, 0); i--) + { + bool hasStartMarker = lines.LineHasFoldingStartMarker(i); + bool hasEndMarker = lines.LineHasFoldingEndMarker(i); + + if (hasEndMarker && hasStartMarker) + continue; + + if (hasStartMarker) + { + counter--; + if (counter == -1) //found start folding + { + startFoldingLine = i; + marker = lines[i].FoldingStartMarker; + break; + } + } + if (hasEndMarker && i != Selection.Start.iLine) + counter++; + } + if (startFoldingLine >= 0) + { + //find end of block + endFoldingLine = FindEndOfFoldingBlock(startFoldingLine); + if (endFoldingLine == startFoldingLine) + endFoldingLine = -1; + } + + if (startFoldingLine != prevStartFoldingLine || endFoldingLine != prevEndFoldingLine) + OnFoldingHighlightChanged(); + } + + protected virtual void OnFoldingHighlightChanged() + { + if (FoldingHighlightChanged != null) + FoldingHighlightChanged(this, EventArgs.Empty); + } + + protected override void OnGotFocus(EventArgs e) + { + SetAsCurrentTB(); + base.OnGotFocus(e); + //Invalidate(new Rectangle(PlaceToPoint(Selection.Start), new Size(2, CharHeight+1))); + Invalidate(); + } + + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + //Invalidate(new Rectangle(PlaceToPoint(Selection.Start), new Size(2, CharHeight+1))); + Invalidate(); + } + + /// + /// Gets absolute text position from line and char position + /// + /// Line and char position + /// Index of text char + public int PlaceToPosition(Place point) + { + if (point.iLine < 0 || point.iLine >= lines.Count || + point.iChar >= lines[point.iLine].Count + Environment.NewLine.Length) + return -1; + + int result = 0; + for (int i = 0; i < point.iLine; i++) + result += lines[i].Count + Environment.NewLine.Length; + result += point.iChar; + + return result; + } + + /// + /// Gets line and char position from absolute text position + /// + /// + /// + public Place PositionToPlace(int pos) + { + if (pos < 0) + return new Place(0, 0); + + for (int i = 0; i < lines.Count; i++) + { + int lineLength = lines[i].Count + Environment.NewLine.Length; + if (pos < lines[i].Count) + return new Place(pos, i); + if (pos < lineLength) + return new Place(lines[i].Count, i); + + pos -= lineLength; + } + + if (lines.Count > 0) + return new Place(lines[lines.Count - 1].Count, lines.Count - 1); + else + return new Place(0, 0); + //throw new ArgumentOutOfRangeException("Position out of range"); + } + + /// + /// Gets point for given line and char position + /// + /// Line and char position + /// Coordiantes + public Point PlaceToPoint(Place place) + { + if (place.iLine >= lineInfos.Count) + return new Point(); + int y = lineInfos[place.iLine].startY; + // + int iWordWrapIndex = lineInfos[place.iLine].GetWordWrapStringIndex(place.iChar); + y += iWordWrapIndex*CharHeight; + int x = (place.iChar - lineInfos[place.iLine].GetWordWrapStringStartPosition(iWordWrapIndex))*CharWidth; + // + y = y - VerticalScroll.Value; + x = LeftIndent + x - HorizontalScroll.Value; + + return new Point(x, y); + } + + /// + /// Get range of text + /// + /// Absolute start position + /// Absolute finish position + /// Range + public Range GetRange(int fromPos, int toPos) + { + var sel = new Range(this); + sel.Start = PositionToPlace(fromPos); + sel.End = PositionToPlace(toPos); + return sel; + } + + /// + /// Get range of text + /// + /// Line and char position + /// Line and char position + /// Range + public Range GetRange(Place fromPlace, Place toPlace) + { + return new Range(this, fromPlace, toPlace); + } + + /// + /// Finds ranges for given regex pattern + /// + /// Regex pattern + /// Enumeration of ranges + public IEnumerable GetRanges(string regexPattern) + { + var range = new Range(this); + range.SelectAll(); + // + foreach (Range r in range.GetRanges(regexPattern, RegexOptions.None)) + yield return r; + } + + /// + /// Finds ranges for given regex pattern + /// + /// Regex pattern + /// Enumeration of ranges + public IEnumerable GetRanges(string regexPattern, RegexOptions options) + { + var range = new Range(this); + range.SelectAll(); + // + foreach (Range r in range.GetRanges(regexPattern, options)) + yield return r; + } + + /// + /// Get text of given line + /// + /// Line index + /// Text + public string GetLineText(int iLine) + { + if (iLine < 0 || iLine >= lines.Count) + throw new ArgumentOutOfRangeException("Line index out of range"); + var sb = new StringBuilder(lines[iLine].Count); + foreach (Char c in lines[iLine]) + sb.Append(c.c); + return sb.ToString(); + } + + /// + /// Exapnds folded block + /// + /// + public void ExpandFoldedBlock(int iLine) + { + if (iLine < 0 || iLine >= lines.Count) + throw new ArgumentOutOfRangeException("Line index out of range"); + //find all hidden lines afetr iLine + int end = iLine; + for (; end < LinesCount - 1; end++) + { + if (lineInfos[end + 1].VisibleState != VisibleState.Hidden) + break; + } + ; + + ExpandBlock(iLine, end); + } + + /// + /// Expand collapsed block + /// + public void ExpandBlock(int fromLine, int toLine) + { + int from = Math.Min(fromLine, toLine); + int to = Math.Max(fromLine, toLine); + for (int i = from; i <= to; i++) + SetVisibleState(i, VisibleState.Visible); + needRecalc = true; + Invalidate(); + } + + /// + /// Expand collapsed block + /// + /// Any line inside collapsed block + public void ExpandBlock(int iLine) + { + if (lineInfos[iLine].VisibleState == VisibleState.Visible) + return; + + for (int i = iLine; i < LinesCount; i++) + if (lineInfos[i].VisibleState == VisibleState.Visible) + break; + else + { + SetVisibleState(i, VisibleState.Visible); + needRecalc = true; + } + + for (int i = iLine - 1; i >= 0; i--) + if (lineInfos[i].VisibleState == VisibleState.Visible) + break; + else + { + SetVisibleState(i, VisibleState.Visible); + needRecalc = true; + } + + Invalidate(); + } + + /// + /// Collapses all folding blocks + /// + public void CollapseAllFoldingBlocks() + { + for (int i = 0; i < LinesCount; i++) + if (lines.LineHasFoldingStartMarker(i)) + { + int iFinish = FindEndOfFoldingBlock(i); + if (iFinish >= 0) + { + CollapseBlock(i, iFinish); + i = iFinish; + } + } + + OnVisibleRangeChanged(); + } + + /// + /// Exapnds all folded blocks + /// + /// + public void ExpandAllFoldingBlocks() + { + for (int i = 0; i < LinesCount; i++) + SetVisibleState(i, VisibleState.Visible); + + OnVisibleRangeChanged(); + Invalidate(); + } + + /// + /// Collapses folding block + /// + /// Start folding line + public void CollapseFoldingBlock(int iLine) + { + if (iLine < 0 || iLine >= lines.Count) + throw new ArgumentOutOfRangeException("Line index out of range"); + if (string.IsNullOrEmpty(lines[iLine].FoldingStartMarker)) + throw new ArgumentOutOfRangeException("This line is not folding start line"); + //find end of block + int i = FindEndOfFoldingBlock(iLine); + //collapse + if (i >= 0) + CollapseBlock(iLine, i); + } + + private int FindEndOfFoldingBlock(int iStartLine) + { + int margin = 2000; + //find end of block + int counter = 0; + int i; + string marker = lines[iStartLine].FoldingStartMarker; + + for (i = iStartLine /*+1*/; i < LinesCount; i++) + { + if (lines.LineHasFoldingStartMarker(i) && + lines[i].FoldingStartMarker == marker) + counter++; + if (lines.LineHasFoldingEndMarker(i) && + lines[i].FoldingEndMarker == marker) + { + counter--; + if (counter <= 0) + return i; + } + margin--; + if (margin < 0) + break; + } + + return -1; + } + + /// + /// Collapse text block + /// + public void CollapseBlock(int fromLine, int toLine) + { + int from = Math.Min(fromLine, toLine); + int to = Math.Max(fromLine, toLine); + if (from == to) + return; + + //find first non empty line + for (; from <= to; from++) + { + if (GetLineText(from).Trim().Length > 0) + { + //hide lines + for (int i = from + 1; i <= to; i++) + SetVisibleState(i, VisibleState.Hidden); + SetVisibleState(from, VisibleState.StartOfHiddenBlock); + Invalidate(); + break; + } + } + //Move caret outside + from = Math.Min(fromLine, toLine); + to = Math.Max(fromLine, toLine); + int newLine = FindNextVisibleLine(to); + if (newLine == to) + newLine = FindPrevVisibleLine(from); + Selection.Start = new Place(0, newLine); + // + needRecalc = true; + Invalidate(); + } + + + internal int FindNextVisibleLine(int iLine) + { + if (iLine >= lines.Count - 1) return iLine; + int old = iLine; + do + iLine++; while (iLine < lines.Count - 1 && lineInfos[iLine].VisibleState != VisibleState.Visible); + + if (lineInfos[iLine].VisibleState != VisibleState.Visible) + return old; + else + return iLine; + } + + + internal int FindPrevVisibleLine(int iLine) + { + if (iLine <= 0) return iLine; + int old = iLine; + do + iLine--; while (iLine > 0 && lineInfos[iLine].VisibleState != VisibleState.Visible); + + if (lineInfos[iLine].VisibleState != VisibleState.Visible) + return old; + else + return iLine; + } + + private VisualMarker FindVisualMarkerForPoint(Point p) + { + foreach (VisualMarker m in visibleMarkers) + if (m.rectangle.Contains(p)) + return m; + return null; + } + + /// + /// Insert TAB into front of seletcted lines + /// + public void IncreaseIndent() + { + if (Selection.Start == Selection.End) + return; + Range old = Selection.Clone(); + int from = Math.Min(Selection.Start.iLine, Selection.End.iLine); + int to = Math.Max(Selection.Start.iLine, Selection.End.iLine); + BeginUpdate(); + Selection.BeginUpdate(); + lines.Manager.BeginAutoUndoCommands(); + for (int i = from; i <= to; i++) + { + if (lines[i].Count == 0) continue; + Selection.Start = new Place(0, i); + lines.Manager.ExecuteCommand(new InsertTextCommand(TextSource, new String(' ', TabLength))); + } + lines.Manager.EndAutoUndoCommands(); + Selection.Start = new Place(0, from); + Selection.End = new Place(lines[to].Count, to); + needRecalc = true; + Selection.EndUpdate(); + EndUpdate(); + Invalidate(); + } + + /// + /// Remove TAB from front of seletcted lines + /// + public void DecreaseIndent() + { + if (Selection.Start == Selection.End) + return; + Range old = Selection.Clone(); + int from = Math.Min(Selection.Start.iLine, Selection.End.iLine); + int to = Math.Max(Selection.Start.iLine, Selection.End.iLine); + BeginUpdate(); + Selection.BeginUpdate(); + lines.Manager.BeginAutoUndoCommands(); + for (int i = from; i <= to; i++) + { + Selection.Start = new Place(0, i); + Selection.End = new Place(Math.Min(lines[i].Count, TabLength), i); + if (Selection.Text.Trim() == "") + ClearSelected(); + } + lines.Manager.EndAutoUndoCommands(); + Selection.Start = new Place(0, from); + Selection.End = new Place(lines[to].Count, to); + needRecalc = true; + EndUpdate(); + Selection.EndUpdate(); + } + + /// + /// Insert autoindents into selected lines + /// + public void DoAutoIndent() + { + Range r = Selection.Clone(); + r.Normalize(); + // + BeginUpdate(); + Selection.BeginUpdate(); + lines.Manager.BeginAutoUndoCommands(); + // + for (int i = r.Start.iLine; i <= r.End.iLine; i++) + DoAutoIndent(i); + // + lines.Manager.EndAutoUndoCommands(); + Selection.Start = r.Start; + Selection.End = r.End; + Selection.Expand(); + // + Selection.EndUpdate(); + EndUpdate(); + } + + /// + /// Insert prefix into front of seletcted lines + /// + public void InsertLinePrefix(string prefix) + { + Range old = Selection.Clone(); + int from = Math.Min(Selection.Start.iLine, Selection.End.iLine); + int to = Math.Max(Selection.Start.iLine, Selection.End.iLine); + BeginUpdate(); + Selection.BeginUpdate(); + lines.Manager.BeginAutoUndoCommands(); + int spaces = GetMinStartSpacesCount(from, to); + for (int i = from; i <= to; i++) + { + Selection.Start = new Place(spaces, i); + lines.Manager.ExecuteCommand(new InsertTextCommand(TextSource, prefix)); + } + Selection.Start = new Place(0, from); + Selection.End = new Place(lines[to].Count, to); + needRecalc = true; + lines.Manager.EndAutoUndoCommands(); + Selection.EndUpdate(); + EndUpdate(); + Invalidate(); + } + + /// + /// Remove prefix from front of seletcted lines + /// + public void RemoveLinePrefix(string prefix) + { + Range old = Selection.Clone(); + int from = Math.Min(Selection.Start.iLine, Selection.End.iLine); + int to = Math.Max(Selection.Start.iLine, Selection.End.iLine); + BeginUpdate(); + Selection.BeginUpdate(); + lines.Manager.BeginAutoUndoCommands(); + for (int i = from; i <= to; i++) + { + string text = lines[i].Text; + string trimmedText = text.TrimStart(); + if (trimmedText.StartsWith(prefix)) + { + int spaces = text.Length - trimmedText.Length; + Selection.Start = new Place(spaces, i); + Selection.End = new Place(spaces + prefix.Length, i); + ClearSelected(); + } + } + Selection.Start = new Place(0, from); + Selection.End = new Place(lines[to].Count, to); + needRecalc = true; + lines.Manager.EndAutoUndoCommands(); + Selection.EndUpdate(); + EndUpdate(); + } + + /// + /// Begins AutoUndo block. + /// All changes of text between BeginAutoUndo() and EndAutoUndo() will be canceled in one operation Undo. + /// + public void BeginAutoUndo() + { + lines.Manager.BeginAutoUndoCommands(); + } + + /// + /// Ends AutoUndo block. + /// All changes of text between BeginAutoUndo() and EndAutoUndo() will be canceled in one operation Undo. + /// + public void EndAutoUndo() + { + lines.Manager.EndAutoUndoCommands(); + } + + public virtual void OnVisualMarkerClick(MouseEventArgs args, StyleVisualMarker marker) + { + if (VisualMarkerClick != null) + VisualMarkerClick(this, new VisualMarkerEventArgs(marker.Style, marker, args)); + } + + protected virtual void OnMarkerClick(MouseEventArgs args, VisualMarker marker) + { + if (marker is StyleVisualMarker) + { + OnVisualMarkerClick(args, marker as StyleVisualMarker); + return; + } + if (marker is CollapseFoldingMarker) + { + CollapseFoldingBlock((marker as CollapseFoldingMarker).iLine); + OnVisibleRangeChanged(); + Invalidate(); + return; + } + + if (marker is ExpandFoldingMarker) + { + ExpandFoldedBlock((marker as ExpandFoldingMarker).iLine); + OnVisibleRangeChanged(); + Invalidate(); + return; + } + + if (marker is FoldedAreaMarker) + { + //select folded block + int iStart = (marker as FoldedAreaMarker).iLine; + int iEnd = FindEndOfFoldingBlock(iStart); + if (iEnd < 0) + return; + Selection.BeginUpdate(); + Selection.Start = new Place(0, iStart); + Selection.End = new Place(lines[iEnd].Count, iEnd); + Selection.EndUpdate(); + Invalidate(); + return; + } + } + + protected virtual void OnMarkerDoubleClick(VisualMarker marker) + { + if (marker is FoldedAreaMarker) + { + ExpandFoldedBlock((marker as FoldedAreaMarker).iLine); + Invalidate(); + return; + } + } + + private void ClearBracketsPositions() + { + leftBracketPosition = null; + rightBracketPosition = null; + leftBracketPosition2 = null; + rightBracketPosition2 = null; + } + + private void HighlightBrackets(char LeftBracket, char RightBracket, ref Range leftBracketPosition, + ref Range rightBracketPosition) + { + if (Selection.Start != Selection.End) + return; + // + Range oldLeftBracketPosition = leftBracketPosition; + Range oldRightBracketPosition = rightBracketPosition; + Range range = Selection.Clone(); //need clone because we will move caret + int counter = 0; + int maxIterations = maxBracketSearchIterations; + while (range.GoLeftThroughFolded()) //move caret left + { + if (range.CharAfterStart == LeftBracket) counter++; + if (range.CharAfterStart == RightBracket) counter--; + if (counter == 1) + { + //highlighting + range.End = new Place(range.Start.iChar + 1, range.Start.iLine); + leftBracketPosition = range; + break; + } + // + maxIterations--; + if (maxIterations <= 0) break; + } + // + range = Selection.Clone(); //need clone because we will move caret + counter = 0; + maxIterations = maxBracketSearchIterations; + do + { + if (range.CharAfterStart == LeftBracket) counter++; + if (range.CharAfterStart == RightBracket) counter--; + if (counter == -1) + { + //highlighting + range.End = new Place(range.Start.iChar + 1, range.Start.iLine); + rightBracketPosition = range; + break; + } + // + maxIterations--; + if (maxIterations <= 0) break; + } while (range.GoRightThroughFolded()); //move caret right + + if (oldLeftBracketPosition != leftBracketPosition || + oldRightBracketPosition != rightBracketPosition) + Invalidate(); + } + + public virtual void OnSyntaxHighlight(TextChangedEventArgs args) + { +#if debug + Stopwatch sw = Stopwatch.StartNew(); + #endif + + Range range; + + switch (HighlightingRangeType) + { + case HighlightingRangeType.VisibleRange: + range = VisibleRange.GetUnionWith(args.ChangedRange); + break; + case HighlightingRangeType.AllTextRange: + range = Range; + break; + default: + range = args.ChangedRange; + break; + } + + if (SyntaxHighlighter != null) + { + if (Language == Language.Custom && !string.IsNullOrEmpty(DescriptionFile)) + SyntaxHighlighter.HighlightSyntax(DescriptionFile, range); + else + SyntaxHighlighter.HighlightSyntax(Language, range); + } + +#if debug + Console.WriteLine("OnSyntaxHighlight: "+ sw.ElapsedMilliseconds); + #endif + } + + private void InitializeComponent() + { + SuspendLayout(); + // + // FastColoredTextBox + // + Name = "FastColoredTextBox"; + ResumeLayout(false); + } + + /// + /// Prints range of text + /// + public void Print(Range range, PrintDialogSettings settings) + { + var wb = new WebBrowser(); + settings.printRange = range; + wb.Tag = settings; + wb.Visible = false; + wb.Location = new Point(-1000, -1000); + wb.Parent = this; + wb.Navigate("about:blank"); + wb.Navigated += ShowPrintDialog; + } + + /// + /// Prints all text + /// + public void Print(PrintDialogSettings settings) + { + Print(Range, settings); + } + + /// + /// Prints all text, without any dialog windows + /// + public void Print() + { + Print(Range, + new PrintDialogSettings + {ShowPageSetupDialog = false, ShowPrintDialog = false, ShowPrintPreviewDialog = false}); + } + + private void ShowPrintDialog(object sender, WebBrowserNavigatedEventArgs e) + { + var wb = sender as WebBrowser; + var settings = wb.Tag as PrintDialogSettings; + //prepare export with wordwrapping + var exporter = new ExportToHTML(); + exporter.UseBr = true; + exporter.UseForwardNbsp = true; + exporter.UseNbsp = false; + exporter.UseStyleTag = false; + //generate HTML + string HTML = exporter.GetHtml(settings.printRange); + //show print dialog + wb.Document.Body.InnerHtml = HTML; + if (settings.ShowPrintPreviewDialog) + wb.ShowPrintPreviewDialog(); + else + { + if (settings.ShowPageSetupDialog) + wb.ShowPageSetupDialog(); + + if (settings.ShowPrintDialog) + wb.ShowPrintDialog(); + else + wb.Print(); + } + //destroy webbrowser + wb.Parent = null; + wb.Dispose(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + if (SyntaxHighlighter != null) + SyntaxHighlighter.Dispose(); + timer.Dispose(); + timer2.Dispose(); + + if (findForm != null) + findForm.Dispose(); + + if (replaceForm != null) + replaceForm.Dispose(); + + if (Font != null) + Font.Dispose(); + + if (TextSource != null) + TextSource.Dispose(); + } + } + + protected virtual void OnPaintLine(PaintLineEventArgs e) + { + if (PaintLine != null) + PaintLine(this, e); + } + + internal void OnLineInserted(int index) + { + OnLineInserted(index, 1); + } + + internal void OnLineInserted(int index, int count) + { + if (LineInserted != null) + LineInserted(this, new LineInsertedEventArgs(index, count)); + } + + internal void OnLineRemoved(int index, int count, List removedLineIds) + { + if (count > 0) + if (LineRemoved != null) + LineRemoved(this, new LineRemovedEventArgs(index, count, removedLineIds)); + } + + /// + /// Open file binding mode + /// + /// + /// + public void OpenBindingFile(string fileName, Encoding enc) + { + try + { + var fts = new FileTextSource(this); + InitTextSource(fts); + fts.OpenFile(fileName, enc); + IsChanged = false; + OnVisibleRangeChanged(); + } + catch + { + InitTextSource(CreateTextSource()); + lines.InsertLine(0, TextSource.CreateLine()); + IsChanged = false; + throw; + } + } + + /// + /// Close file binding mode + /// + public void CloseBindingFile() + { + if (lines is FileTextSource) + { + var fts = lines as FileTextSource; + fts.CloseFile(); + + InitTextSource(CreateTextSource()); + lines.InsertLine(0, TextSource.CreateLine()); + IsChanged = false; + Invalidate(); + } + } + + /// + /// Save text to the file + /// + /// + /// + public void SaveToFile(string fileName, Encoding enc) + { + lines.SaveToFile(fileName, enc); + IsChanged = false; + OnVisibleRangeChanged(); + } + + /// + /// Set VisibleState of line + /// + public void SetVisibleState(int iLine, VisibleState state) + { + LineInfo li = lineInfos[iLine]; + li.VisibleState = state; + lineInfos[iLine] = li; + needRecalc = true; + } + + /// + /// Returns VisibleState of the line + /// + public VisibleState GetVisibleState(int iLine) + { + return lineInfos[iLine].VisibleState; + } + + /// + /// Shows Goto dialog form + /// + public void ShowGoToDialog() + { + var form = new GoToForm(); + form.TotalLineCount = LinesCount; + form.SelectedLineNumber = Selection.Start.iLine + 1; + + if (form.ShowDialog() == DialogResult.OK) + { + int line = Math.Min(LinesCount - 1, Math.Max(0, form.SelectedLineNumber - 1)); + Selection = new Range(this, 0, line, 0, line); + DoSelectionVisible(); + } + } + + /// + /// Occurs when undo/redo stack is changed + /// + public void OnUndoRedoStateChanged() + { + if (UndoRedoStateChanged != null) + UndoRedoStateChanged(this, EventArgs.Empty); + } + + #region Nested type: LineYComparer + + private class LineYComparer : IComparer + { + private readonly int Y; + + public LineYComparer(int Y) + { + this.Y = Y; + } + + #region IComparer Members + + public int Compare(LineInfo x, LineInfo y) + { + if (x.startY == -10) + return -y.startY.CompareTo(Y); + else + return x.startY.CompareTo(Y); + } + + #endregion + } + + #endregion + } + + public class PaintLineEventArgs : PaintEventArgs + { + public PaintLineEventArgs(int iLine, Rectangle rect, Graphics gr, Rectangle clipRect) : base(gr, clipRect) + { + LineIndex = iLine; + LineRect = rect; + } + + public int LineIndex { get; private set; } + public Rectangle LineRect { get; private set; } + } + + public class LineInsertedEventArgs : EventArgs + { + public LineInsertedEventArgs(int index, int count) + { + Index = index; + Count = count; + } + + /// + /// Inserted line index + /// + public int Index { get; private set; } + + /// + /// Count of inserted lines + /// + public int Count { get; private set; } + } + + public class LineRemovedEventArgs : EventArgs + { + public LineRemovedEventArgs(int index, int count, List removedLineIds) + { + Index = index; + Count = count; + RemovedLineUniqueIds = removedLineIds; + } + + /// + /// Removed line index + /// + public int Index { get; private set; } + + /// + /// Count of removed lines + /// + public int Count { get; private set; } + + /// + /// UniqueIds of removed lines + /// + public List RemovedLineUniqueIds { get; private set; } + } + + /// + /// TextChanged event argument + /// + public class TextChangedEventArgs : EventArgs + { + /// + /// Constructor + /// + public TextChangedEventArgs(Range changedRange) + { + ChangedRange = changedRange; + } + + /// + /// This range contains changed area of text + /// + public Range ChangedRange { get; set; } + } + + public class TextChangingEventArgs : EventArgs + { + public string InsertingText { get; set; } + public bool IsHandled { get; set; } + } + + public class TabChangingEventArgs : EventArgs + { + public TabChangingEventArgs(TabChangingReason reason) + { + Reason = reason; + } + + public TabChangingReason Reason { get; private set; } + public bool Cancel { get; set; } + } + + public enum TabChangingReason + { + Programm, + User + } + + public enum WordWrapMode + { + /// + /// Word wrapping by control width + /// + WordWrapControlWidth, + + /// + /// Word wrapping by preferred line width (PreferredLineWidth) + /// + WordWrapPreferredWidth, + + /// + /// Char wrapping by control width + /// + CharWrapControlWidth, + + /// + /// Char wrapping by preferred line width (PreferredLineWidth) + /// + CharWrapPreferredWidth + } + + public class PrintDialogSettings + { + public bool ShowPageSetupDialog; + public bool ShowPrintDialog; + public bool ShowPrintPreviewDialog = true; + internal Range printRange; + } + + public class AutoIndentEventArgs : EventArgs + { + public AutoIndentEventArgs(int iLine, string lineText, string prevLineText, int tabLength) + { + this.iLine = iLine; + LineText = lineText; + PrevLineText = prevLineText; + TabLength = tabLength; + } + + public int iLine { get; internal set; } + public int TabLength { get; internal set; } + public string LineText { get; internal set; } + public string PrevLineText { get; internal set; } + + /// + /// Additional spaces count for this line, relative to previous line + /// + public int Shift { get; set; } + + /// + /// Additional spaces count for next line, relative to previous line + /// + public int ShiftNextLines { get; set; } + } + + /// + /// Type of highlighting + /// + public enum HighlightingRangeType + { + /// + /// Highlight only changed range of text. Highest performance. + /// + ChangedRange, + + /// + /// Highlight visible range of text. Middle performance. + /// + VisibleRange, + + /// + /// Highlight all (visible and invisible) text. Lowest performance. + /// + AllTextRange + } +} \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/FastColoredTextBox.resx b/Irony.WinForms/FastColoredTextBox/FastColoredTextBox.resx new file mode 100644 index 0000000..5ea0895 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/FastColoredTextBox.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/FileTextSource.cs b/Irony.WinForms/FastColoredTextBox/FileTextSource.cs new file mode 100644 index 0000000..e9bc999 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/FileTextSource.cs @@ -0,0 +1,406 @@ +//#define debug + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace FastColoredTextBoxNS +{ + /// + /// This class contains the source text (chars and styles). + /// It stores a text lines, the manager of commands, undo/redo stack, styles. + /// + public class FileTextSource : TextSource, IDisposable + { + List sourceFileLinePositions = new List(); + FileStream fs; + Encoding fileEncoding; + System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); + + /// + /// Occurs when need to display line in the textbox + /// + public event EventHandler LineNeeded; + + /// + /// Occurs when need to save line in the file + /// + public event EventHandler LinePushed; + + public FileTextSource(FastColoredTextBox currentTB) + : base(currentTB) + { + timer.Interval = 10000; + timer.Tick += new EventHandler(timer_Tick); + timer.Enabled = true; + } + + void timer_Tick(object sender, EventArgs e) + { + timer.Enabled = false; + try + { + UnloadUnusedLines(); + } + finally + { + timer.Enabled = true; + } + } + + private void UnloadUnusedLines() + { + const int margin = 2000; + var iStartVisibleLine = CurrentTB.VisibleRange.Start.iLine; + var iFinishVisibleLine = CurrentTB.VisibleRange.End.iLine; + + int count = 0; + for (int i = 0; i < Count; i++) + if (base.lines[i] != null && !base.lines[i].IsChanged && Math.Abs(i - iFinishVisibleLine) > margin) + { + base.lines[i] = null; + count++; + } + #if debug + Console.WriteLine("UnloadUnusedLines: " + count); + #endif + } + + public void OpenFile(string fileName, Encoding enc) + { + Clear(); + + if (fs != null) + fs.Dispose(); + + //read lines of file + fs = new FileStream(fileName, FileMode.Open); + var length = fs.Length; + //read signature + enc = DefineEncoding(enc); + int shift = DefineShift(enc); + //first line + sourceFileLinePositions.Add((int)fs.Position); + base.lines.Add(null); + //other lines + while(fs.Position < length) + { + var b = fs.ReadByte(); + if (b == 10)// char \n + { + sourceFileLinePositions.Add((int)(fs.Position) + shift); + base.lines.Add(null); + } + } + + Line[] temp = new Line[100]; + var c = base.lines.Count; + base.lines.AddRange(temp); + base.lines.TrimExcess(); + base.lines.RemoveRange(c, temp.Length); + + + int[] temp2 = new int[100]; + c = base.lines.Count; + sourceFileLinePositions.AddRange(temp2); + sourceFileLinePositions.TrimExcess(); + sourceFileLinePositions.RemoveRange(c, temp.Length); + + + fileEncoding = enc; + + OnLineInserted(0, Count); + NeedRecalc(new TextChangedEventArgs(0, 1)); + } + + private int DefineShift(Encoding enc) + { + if (enc.IsSingleByte) + return 0; + + if (enc.HeaderName == "unicodeFFFE") + return 0;//UTF16 BE + + if (enc.HeaderName == "utf-16") + return 1;//UTF16 LE + + if (enc.HeaderName == "utf-32BE") + return 0;//UTF32 BE + + if (enc.HeaderName == "utf-32") + return 3;//UTF32 LE + + return 0; + } + + private Encoding DefineEncoding(Encoding enc) + { + int bytesPerSignature = 0; + byte[] signature = new byte[4]; + int c = fs.Read(signature, 0, 4); + if (signature[0] == 0xFF && signature[1] == 0xFE && signature[2] == 0x00 && signature[3] == 0x00 && c >= 4) + { + enc = Encoding.UTF32;//UTF32 LE + bytesPerSignature = 4; + } + else + if (signature[0] == 0x00 && signature[1] == 0x00 && signature[2] == 0xFE && signature[3] == 0xFF) + { + enc = new UTF32Encoding(true, true);//UTF32 BE + bytesPerSignature = 4; + } + else + if (signature[0] == 0xEF && signature[1] == 0xBB && signature[2] == 0xBF) + { + enc = Encoding.UTF8;//UTF8 + bytesPerSignature = 3; + } + else + if (signature[0] == 0xFE && signature[1] == 0xFF) + { + enc = Encoding.BigEndianUnicode;//UTF16 BE + bytesPerSignature = 2; + } + else + if (signature[0] == 0xFF && signature[1] == 0xFE) + { + enc = Encoding.Unicode;//UTF16 LE + bytesPerSignature = 2; + } + + fs.Seek(bytesPerSignature, SeekOrigin.Begin); + + return enc; + } + + public void CloseFile() + { + if(fs!=null) + fs.Dispose(); + fs = null; + } + + public override void SaveToFile(string fileName, Encoding enc) + { + // + var newLinePos = new List(Count); + //create temp file + var dir = Path.GetDirectoryName(fileName); + var tempFileName = Path.Combine(dir, Path.GetFileNameWithoutExtension(fileName) + ".tmp"); + + StreamReader sr = new StreamReader(fs, fileEncoding); + using (FileStream tempFs = new FileStream(tempFileName, FileMode.Create)) + using (StreamWriter sw = new StreamWriter(tempFs, enc)) + { + sw.Flush(); + + for (int i = 0; i < Count; i++) + { + newLinePos.Add((int)tempFs.Length); + + var sourceLine = ReadLine(sr, i);//read line from source file + string line; + + bool lineIsChanged = lines[i] != null && lines[i].IsChanged; + + if (lineIsChanged) + line = lines[i].Text; + else + line = sourceLine; + + //call event handler + if (LinePushed != null) + { + var args = new LinePushedEventArgs(sourceLine, i, lineIsChanged ? line : null); + LinePushed(this, args); + + if(args.SavedText != null) + line = args.SavedText; + } + + //save line to file + if (i == Count - 1) + sw.Write(line); + else + sw.WriteLine(line); + + sw.Flush(); + } + } + + //clear lines buffer + for (int i = 0; i < Count; i++) + lines[i] = null; + //deattach from source file + sr.Dispose(); + fs.Dispose(); + //delete target file + if (File.Exists(fileName)) + File.Delete(fileName); + //rename temp file + File.Move(tempFileName, fileName); + + //binding to new file + sourceFileLinePositions = newLinePos; + fs = new FileStream(fileName, FileMode.Open); + this.fileEncoding = enc; + } + + private string ReadLine(StreamReader sr, int i) + { + string line; + var filePos = sourceFileLinePositions[i]; + if (filePos < 0) + return ""; + fs.Seek(filePos, SeekOrigin.Begin); + sr.DiscardBufferedData(); + line = sr.ReadLine(); + return line; + } + + public override void ClearIsChanged() + { + foreach (var line in lines) + if(line!=null) + line.IsChanged = false; + } + + public override Line this[int i] + { + get + { + if (base.lines[i] != null) + return lines[i]; + else + LoadLineFromSourceFile(i); + + return lines[i]; + } + set + { + throw new NotImplementedException(); + } + } + + private void LoadLineFromSourceFile(int i) + { + var line = CreateLine(); + fs.Seek(sourceFileLinePositions[i], SeekOrigin.Begin); + StreamReader sr = new StreamReader(fs, fileEncoding); + + var s = sr.ReadLine(); + if (s == null) + s = ""; + + //call event handler + if(LineNeeded!=null) + { + var args = new LineNeededEventArgs(s, i); + LineNeeded(this, args); + s = args.DisplayedLineText; + if (s == null) + return; + } + + foreach (var c in s) + line.Add(new Char(c)); + base.lines[i] = line; + } + + public override void InsertLine(int index, Line line) + { + sourceFileLinePositions.Insert(index, -1); + base.InsertLine(index, line); + } + + public override void RemoveLine(int index, int count) + { + sourceFileLinePositions.RemoveRange(index, count); + base.RemoveLine(index, count); + } + + public override void Clear() + { + base.Clear(); + } + + public override int GetLineLength(int i) + { + if (base.lines[i] == null) + return 0; + else + return base.lines[i].Count; + } + + public override bool LineHasFoldingStartMarker(int iLine) + { + if (lines[iLine] == null) + return false; + else + return !string.IsNullOrEmpty(lines[iLine].FoldingStartMarker); + } + + public override bool LineHasFoldingEndMarker(int iLine) + { + if (lines[iLine] == null) + return false; + else + return !string.IsNullOrEmpty(lines[iLine].FoldingEndMarker); + } + + public void Dispose() + { + if (fs != null) + fs.Dispose(); + + timer.Dispose(); + } + + internal void UnloadLine(int iLine) + { + if (lines[iLine] != null && !lines[iLine].IsChanged) + lines[iLine] = null; + } + } + + public class LineNeededEventArgs : EventArgs + { + public string SourceLineText { get; private set; } + public int DisplayedLineIndex { get; private set; } + /// + /// This text will be displayed in textbox + /// + public string DisplayedLineText { get; set; } + + public LineNeededEventArgs(string sourceLineText, int displayedLineIndex) + { + this.SourceLineText = sourceLineText; + this.DisplayedLineIndex = displayedLineIndex; + this.DisplayedLineText = sourceLineText; + } + } + + public class LinePushedEventArgs : EventArgs + { + public string SourceLineText { get; private set; } + public int DisplayedLineIndex { get; private set; } + /// + /// This property contains only changed text. + /// If text of line is not changed, this property contains null. + /// + public string DisplayedLineText { get; private set; } + /// + /// This text will be saved in the file + /// + public string SavedText { get; set; } + + public LinePushedEventArgs(string sourceLineText, int displayedLineIndex, string displayedLineText) + { + this.SourceLineText = sourceLineText; + this.DisplayedLineIndex = displayedLineIndex; + this.DisplayedLineText = displayedLineText; + this.SavedText = displayedLineText; + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/FindForm.Designer.cs b/Irony.WinForms/FastColoredTextBox/FindForm.Designer.cs new file mode 100644 index 0000000..7564c6f --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/FindForm.Designer.cs @@ -0,0 +1,147 @@ +namespace FastColoredTextBoxNS +{ + partial class FindForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btClose = new System.Windows.Forms.Button(); + this.btFindNext = new System.Windows.Forms.Button(); + this.tbFind = new System.Windows.Forms.TextBox(); + this.cbRegex = new System.Windows.Forms.CheckBox(); + this.cbMatchCase = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.cbWholeWord = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // btClose + // + this.btClose.Location = new System.Drawing.Point(273, 72); + this.btClose.Name = "btClose"; + this.btClose.Size = new System.Drawing.Size(75, 23); + this.btClose.TabIndex = 5; + this.btClose.Text = "Close"; + this.btClose.UseVisualStyleBackColor = true; + this.btClose.Click += new System.EventHandler(this.btClose_Click); + // + // btFindNext + // + this.btFindNext.Location = new System.Drawing.Point(192, 72); + this.btFindNext.Name = "btFindNext"; + this.btFindNext.Size = new System.Drawing.Size(75, 23); + this.btFindNext.TabIndex = 4; + this.btFindNext.Text = "Find next"; + this.btFindNext.UseVisualStyleBackColor = true; + this.btFindNext.Click += new System.EventHandler(this.btFindNext_Click); + // + // tbFind + // + this.tbFind.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.tbFind.Location = new System.Drawing.Point(42, 12); + this.tbFind.Name = "tbFind"; + this.tbFind.Size = new System.Drawing.Size(306, 20); + this.tbFind.TabIndex = 0; + this.tbFind.TextChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + this.tbFind.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.tbFind_KeyPress); + // + // cbRegex + // + this.cbRegex.AutoSize = true; + this.cbRegex.Location = new System.Drawing.Point(249, 38); + this.cbRegex.Name = "cbRegex"; + this.cbRegex.Size = new System.Drawing.Size(57, 17); + this.cbRegex.TabIndex = 3; + this.cbRegex.Text = "Regex"; + this.cbRegex.UseVisualStyleBackColor = true; + this.cbRegex.CheckedChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + // + // cbMatchCase + // + this.cbMatchCase.AutoSize = true; + this.cbMatchCase.Location = new System.Drawing.Point(42, 38); + this.cbMatchCase.Name = "cbMatchCase"; + this.cbMatchCase.Size = new System.Drawing.Size(82, 17); + this.cbMatchCase.TabIndex = 1; + this.cbMatchCase.Text = "Match case"; + this.cbMatchCase.UseVisualStyleBackColor = true; + this.cbMatchCase.CheckedChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(33, 13); + this.label1.TabIndex = 5; + this.label1.Text = "Find: "; + // + // cbWholeWord + // + this.cbWholeWord.AutoSize = true; + this.cbWholeWord.Location = new System.Drawing.Point(130, 38); + this.cbWholeWord.Name = "cbWholeWord"; + this.cbWholeWord.Size = new System.Drawing.Size(113, 17); + this.cbWholeWord.TabIndex = 2; + this.cbWholeWord.Text = "Match whole word"; + this.cbWholeWord.UseVisualStyleBackColor = true; + this.cbWholeWord.CheckedChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + // + // FindForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(360, 103); + this.Controls.Add(this.cbWholeWord); + this.Controls.Add(this.label1); + this.Controls.Add(this.cbMatchCase); + this.Controls.Add(this.cbRegex); + this.Controls.Add(this.tbFind); + this.Controls.Add(this.btFindNext); + this.Controls.Add(this.btClose); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "FindForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Find"; + this.TopMost = true; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FindForm_FormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btClose; + private System.Windows.Forms.Button btFindNext; + private System.Windows.Forms.CheckBox cbRegex; + private System.Windows.Forms.CheckBox cbMatchCase; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckBox cbWholeWord; + public System.Windows.Forms.TextBox tbFind; + } +} \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/FindForm.cs b/Irony.WinForms/FastColoredTextBox/FindForm.cs new file mode 100644 index 0000000..e208978 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/FindForm.cs @@ -0,0 +1,120 @@ +using System; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Generic; + +namespace FastColoredTextBoxNS +{ + public partial class FindForm : Form + { + public static string RegexSpecSymbolsPattern = @"[\^\$\[\]\(\)\.\\\*\+\|\?\{\}]"; + bool firstSearch = true; + Place startPlace; + FastColoredTextBox tb; + + public FindForm(FastColoredTextBox tb) + { + InitializeComponent(); + this.tb = tb; + } + + private void btClose_Click(object sender, EventArgs e) + { + Close(); + } + + private void btFindNext_Click(object sender, EventArgs e) + { + FindNext(); + } + + private void FindNext() + { + try + { + string pattern = tbFind.Text; + RegexOptions opt = cbMatchCase.Checked ? RegexOptions.None : RegexOptions.IgnoreCase; + if (!cbRegex.Checked) + pattern = Regex.Replace(pattern, RegexSpecSymbolsPattern, "\\$0"); + if (cbWholeWord.Checked) + pattern = "\\b" + pattern + "\\b"; + // + Range range = tb.Selection.Clone(); + range.Normalize(); + // + if (firstSearch) + { + startPlace = range.Start; + firstSearch = false; + } + // + range.Start = range.End; + if (range.Start >= startPlace) + range.End = new Place(tb.GetLineLength(tb.LinesCount - 1), tb.LinesCount - 1); + else + range.End = startPlace; + // + foreach (var r in range.GetRangesByLines(pattern, opt)) + { + tb.Selection = r; + tb.DoSelectionVisible(); + tb.Invalidate(); + return; + } + // + if (range.Start >= startPlace && startPlace > Place.Empty) + { + tb.Selection.Start = new Place(0, 0); + FindNext(); + return; + } + MessageBox.Show("Not found"); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + private void tbFind_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == '\r') + { + btFindNext.PerformClick(); + e.Handled = true; + return; + } + if (e.KeyChar == '\x1b') + { + Hide(); + e.Handled = true; + return; + } + } + + private void FindForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + e.Cancel = true; + Hide(); + } + } + + protected override void OnActivated(EventArgs e) + { + tbFind.Focus(); + ResetSerach(); + } + + void ResetSerach() + { + firstSearch = true; + } + + private void cbMatchCase_CheckedChanged(object sender, EventArgs e) + { + ResetSerach(); + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/FindForm.resx b/Irony.WinForms/FastColoredTextBox/FindForm.resx new file mode 100644 index 0000000..5ea0895 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/FindForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/GoToForm.Designer.cs b/Irony.WinForms/FastColoredTextBox/GoToForm.Designer.cs new file mode 100644 index 0000000..ed7cb96 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/GoToForm.Designer.cs @@ -0,0 +1,108 @@ +namespace FastColoredTextBoxNS +{ + partial class GoToForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label = new System.Windows.Forms.Label(); + this.textBox = new System.Windows.Forms.TextBox(); + this.btnOk = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label + // + this.label.AutoSize = true; + this.label.Location = new System.Drawing.Point(12, 9); + this.label.Name = "label"; + this.label.Size = new System.Drawing.Size(96, 13); + this.label.TabIndex = 0; + this.label.Text = "Line Number (1/1):"; + // + // textBox + // + this.textBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox.Location = new System.Drawing.Point(12, 29); + this.textBox.Name = "textBox"; + this.textBox.Size = new System.Drawing.Size(296, 20); + this.textBox.TabIndex = 1; + // + // btnOk + // + this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOk.Location = new System.Drawing.Point(152, 71); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(75, 23); + this.btnOk.TabIndex = 2; + this.btnOk.Text = "OK"; + this.btnOk.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(233, 71); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // GoToForm + // + this.AcceptButton = this.btnOk; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(320, 106); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.Controls.Add(this.textBox); + this.Controls.Add(this.label); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "GoToForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Go To Line"; + this.TopMost = true; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label; + private System.Windows.Forms.TextBox textBox; + private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.Button btnCancel; + } +} \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/GoToForm.cs b/Irony.WinForms/FastColoredTextBox/GoToForm.cs new file mode 100644 index 0000000..160c494 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/GoToForm.cs @@ -0,0 +1,23 @@ +using System; +using System.Windows.Forms; + +namespace FastColoredTextBoxNS +{ + public partial class GoToForm : Form + { + public int SelectedLineNumber { get; set; } + public int TotalLineCount { get; set; } + + public GoToForm() + { + InitializeComponent(); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + this.label.Text = String.Format("Line number (1 - {0}):", this.TotalLineCount); + this.textBox.DataBindings.Add("Text", this, "SelectedLineNumber", true, DataSourceUpdateMode.OnPropertyChanged); + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/GoToForm.resx b/Irony.WinForms/FastColoredTextBox/GoToForm.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/GoToForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/LimitedStack.cs b/Irony.WinForms/FastColoredTextBox/LimitedStack.cs new file mode 100644 index 0000000..ebace9d --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/LimitedStack.cs @@ -0,0 +1,105 @@ +using System; + +namespace FastColoredTextBoxNS +{ + /// + /// Limited stack + /// + public class LimitedStack + { + T[] items; + int count; + int start; + + /// + /// Max stack length + /// + public int MaxItemCount + { + get { return items.Length; } + } + + /// + /// Current length of stack + /// + public int Count + { + get { return count; } + } + + /// + /// Constructor + /// + /// Maximum length of stack + public LimitedStack(int maxItemCount) + { + items = new T[maxItemCount]; + count = 0; + start = 0; + } + + /// + /// Pop item + /// + public T Pop() + { + if (count == 0) + throw new Exception("Stack is empty"); + + int i = LastIndex; + T item = items[i]; + items[i] = default(T); + + count--; + + return item; + } + + int LastIndex + { + get { return (start + count - 1) % items.Length; } + } + + /// + /// Peek item + /// + public T Peek() + { + if (count == 0) + return default(T); + + return items[LastIndex]; + } + + /// + /// Push item + /// + public void Push(T item) + { + if (count == items.Length) + start = (start + 1) % items.Length; + else + count++; + + items[LastIndex] = item; + } + + /// + /// Clear stack + /// + public void Clear() + { + items = new T[items.Length]; + count = 0; + start = 0; + } + + public T[] ToArray() + { + T[] result = new T[count]; + for (int i = 0; i < count; i++) + result[i] = items[(start + i) % items.Length]; + return result; + } + } +} \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/Line.cs b/Irony.WinForms/FastColoredTextBox/Line.cs new file mode 100644 index 0000000..3ef82d3 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/Line.cs @@ -0,0 +1,349 @@ +using System.Collections.Generic; +using System; +using System.Text; +using System.Drawing; + +namespace FastColoredTextBoxNS +{ + /// + /// Line of text + /// + public class Line : IList + { + protected List chars; + + public string FoldingStartMarker { get; set; } + public string FoldingEndMarker { get; set; } + /// + /// Text of line was changed + /// + public bool IsChanged { get; set; } + /// + /// Time of last visit of caret in this line + /// + /// This property can be used for forward/backward navigating + public DateTime LastVisit { get; set; } + /// + /// Background brush. + /// + public Brush BackgroundBrush { get; set;} + /// + /// Unique ID + /// + public int UniqueId { get; private set; } + /// + /// Count of needed start spaces for AutoIndent + /// + public int AutoIndentSpacesNeededCount + { + get; + internal set; + } + + internal Line(int uid) + { + this.UniqueId = uid; + chars = new List(); + } + + + /// + /// Clears style of chars, delete folding markers + /// + public void ClearStyle(StyleIndex styleIndex) + { + FoldingStartMarker = null; + FoldingEndMarker = null; + for (int i = 0; i < Count; i++) + { + Char c = this[i]; + c.style &= ~styleIndex; + this[i] = c; + } + } + + /// + /// Text of the line + /// + public virtual string Text + { + get{ + StringBuilder sb = new StringBuilder(Count); + foreach(Char c in this) + sb.Append(c.c); + return sb.ToString(); + } + } + + /// + /// Clears folding markers + /// + public void ClearFoldingMarkers() + { + FoldingStartMarker = null; + FoldingEndMarker = null; + } + + /// + /// Count of start spaces + /// + public int StartSpacesCount + { + get + { + int spacesCount = 0; + for (int i = 0; i < Count; i++) + if (this[i].c == ' ') + spacesCount++; + else + break; + return spacesCount; + } + } + + public int IndexOf(Char item) + { + return chars.IndexOf(item); + } + + public void Insert(int index, Char item) + { + chars.Insert(index, item); + } + + public void RemoveAt(int index) + { + chars.RemoveAt(index); + } + + public Char this[int index] + { + get + { + return chars[index]; + } + set + { + chars[index] = value; + } + } + + public void Add(Char item) + { + chars.Add(item); + } + + public void Clear() + { + chars.Clear(); + } + + public bool Contains(Char item) + { + return chars.Contains(item); + } + + public void CopyTo(Char[] array, int arrayIndex) + { + chars.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return chars.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(Char item) + { + return chars.Remove(item); + } + + public IEnumerator GetEnumerator() + { + return chars.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return chars.GetEnumerator() as System.Collections.IEnumerator; + } + + public virtual void RemoveRange(int index, int count) + { + chars.RemoveRange(index, count); + } + + public virtual void TrimExcess() + { + chars.TrimExcess(); + } + + public virtual void AddRange(IEnumerable collection) + { + chars.AddRange(collection); + } + } + + public struct LineInfo + { + List cutOffPositions; + //Y coordinate of line on screen + internal int startY;// = -1; + /// + /// Visible state + /// + public VisibleState VisibleState; + + public LineInfo(int startY) + { + cutOffPositions = null; + VisibleState = FastColoredTextBoxNS.VisibleState.Visible; + this.startY = startY; + } + /// + /// Positions for wordwrap cutoffs + /// + public List CutOffPositions + { + get + { + if (cutOffPositions == null) + cutOffPositions = new List(); + return cutOffPositions; + } + } + + /// + /// Count of wordwrap string count for this line + /// + public int WordWrapStringsCount + { + get + { + switch (VisibleState) + { + case VisibleState.Visible: + if (cutOffPositions == null) + return 1; + else + return cutOffPositions.Count + 1; + case VisibleState.Hidden: return 0; + case VisibleState.StartOfHiddenBlock: return 1; + } + + return 0; + } + } + + internal int GetWordWrapStringStartPosition(int iWordWrapLine) + { + return iWordWrapLine == 0 ? 0 : CutOffPositions[iWordWrapLine - 1]; + } + + internal int GetWordWrapStringFinishPosition(int iWordWrapLine, Line line) + { + if (WordWrapStringsCount <= 0) + return 0; + return iWordWrapLine == WordWrapStringsCount - 1 ? line.Count - 1 : CutOffPositions[iWordWrapLine] - 1; + } + + /// + /// Gets index of wordwrap string for given char position + /// + public int GetWordWrapStringIndex(int iChar) + { + if (cutOffPositions == null || cutOffPositions.Count == 0) return 0; + for (int i = 0; i < cutOffPositions.Count; i++) + if (cutOffPositions[i] >/*>=*/ iChar) + return i; + return cutOffPositions.Count; + } + + /// + /// Calculates wordwrap cutoffs + /// + internal void CalcCutOffs(int maxCharsPerLine, bool allowIME, bool charWrap, Line line) + { + int segmentLength = 0; + int cutOff = 0; + CutOffPositions.Clear(); + + for (int i = 0; i < line.Count; i++) + { + char c = line[i].c; + if (charWrap) + { + //char wrapping + cutOff = Math.Min(i + 1, line.Count - 1); + } + else + { + //word wrapping + if (allowIME && isCJKLetter(c))//in CJK languages cutoff can be in any letter + { + cutOff = i; + } + else + if (!char.IsLetterOrDigit(c) && c != '_') + cutOff = Math.Min(i + 1, line.Count - 1); + } + + segmentLength++; + + if (segmentLength == maxCharsPerLine) + { + if (cutOff == 0 || (cutOffPositions.Count > 0 && cutOff == cutOffPositions[cutOffPositions.Count - 1])) + cutOff = i + 1; + CutOffPositions.Add(cutOff); + segmentLength = 1 + i - cutOff; + } + } + } + + private bool isCJKLetter(char c) + { + int code = Convert.ToInt32(c); + return + (code >= 0x3300 && code <= 0x33FF) || + (code >= 0xFE30 && code <= 0xFE4F) || + (code >= 0xF900 && code <= 0xFAFF) || + (code >= 0x2E80 && code <= 0x2EFF) || + (code >= 0x31C0 && code <= 0x31EF) || + (code >= 0x4E00 && code <= 0x9FFF) || + (code >= 0x3400 && code <= 0x4DBF) || + (code >= 0x3200 && code <= 0x32FF) || + (code >= 0x2460 && code <= 0x24FF) || + (code >= 0x3040 && code <= 0x309F) || + (code >= 0x2F00 && code <= 0x2FDF) || + (code >= 0x31A0 && code <= 0x31BF) || + (code >= 0x4DC0 && code <= 0x4DFF) || + (code >= 0x3100 && code <= 0x312F) || + (code >= 0x30A0 && code <= 0x30FF) || + (code >= 0x31F0 && code <= 0x31FF) || + (code >= 0x2FF0 && code <= 0x2FFF) || + (code >= 0x1100 && code <= 0x11FF) || + (code >= 0xA960 && code <= 0xA97F) || + (code >= 0xD7B0 && code <= 0xD7FF) || + (code >= 0x3130 && code <= 0x318F) || + (code >= 0xAC00 && code <= 0xD7AF); + + } + } + + public enum VisibleState: byte + { + Visible, StartOfHiddenBlock, Hidden + } + + public enum IndentMarker + { + None, + Increased, + Decreased + } +} diff --git a/Irony.WinForms/FastColoredTextBox/LinesAccessor.cs b/Irony.WinForms/FastColoredTextBox/LinesAccessor.cs new file mode 100644 index 0000000..2d5d956 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/LinesAccessor.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FastColoredTextBoxNS +{ + class LinesAccessor : IList + { + IList ts; + + public LinesAccessor(IList ts) + { + this.ts = ts; + } + + public int IndexOf(string item) + { + for (int i = 0; i < ts.Count; i++) + if (ts[i].Text == item) + return i; + + return -1; + } + + public void Insert(int index, string item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + public string this[int index] + { + get + { + return ts[index].Text; + } + set + { + throw new NotImplementedException(); + } + } + + public void Add(string item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(string item) + { + for (int i = 0; i < ts.Count; i++) + if (ts[i].Text == item) + return true; + + return false; + } + + public void CopyTo(string[] array, int arrayIndex) + { + for (int i = 0; i < ts.Count; i++) + array[i + arrayIndex] = ts[i].Text; + } + + public int Count + { + get { return ts.Count; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public bool Remove(string item) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < ts.Count; i++) + yield return ts[i].Text; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/Place.cs b/Irony.WinForms/FastColoredTextBox/Place.cs new file mode 100644 index 0000000..3c62939 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/Place.cs @@ -0,0 +1,78 @@ + +namespace FastColoredTextBoxNS +{ + /// + /// Line index and char index + /// + public struct Place + { + public int iChar; + public int iLine; + + public Place(int iChar, int iLine) + { + this.iChar = iChar; + this.iLine = iLine; + } + + public void Offset(int dx, int dy) + { + iChar += dx; + iLine += dy; + } + + public static bool operator !=(Place p1, Place p2) + { + return !p1.Equals(p2); + } + + public static bool operator ==(Place p1, Place p2) + { + return p1.Equals(p2); + } + + public static bool operator <(Place p1, Place p2) + { + if (p1.iLine < p2.iLine) return true; + if (p1.iLine > p2.iLine) return false; + if (p1.iChar < p2.iChar) return true; + return false; + } + + public static bool operator <=(Place p1, Place p2) + { + if (p1.Equals(p2)) return true; + if (p1.iLine < p2.iLine) return true; + if (p1.iLine > p2.iLine) return false; + if (p1.iChar < p2.iChar) return true; + return false; + } + + public static bool operator >(Place p1, Place p2) + { + if (p1.iLine > p2.iLine) return true; + if (p1.iLine < p2.iLine) return false; + if (p1.iChar > p2.iChar) return true; + return false; + } + + public static bool operator >=(Place p1, Place p2) + { + if (p1.Equals(p2)) return true; + if (p1.iLine > p2.iLine) return true; + if (p1.iLine < p2.iLine) return false; + if (p1.iChar > p2.iChar) return true; + return false; + } + + public static Place Empty + { + get { return new Place(); } + } + + public override string ToString() + { + return "(" + iChar + "," + iLine + ")"; + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/PlatformType.cs b/Irony.WinForms/FastColoredTextBox/PlatformType.cs new file mode 100644 index 0000000..0c3da27 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/PlatformType.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +namespace FastColoredTextBoxNS +{ + public static class PlatformType + { + const ushort PROCESSOR_ARCHITECTURE_INTEL = 0; + const ushort PROCESSOR_ARCHITECTURE_IA64 = 6; + const ushort PROCESSOR_ARCHITECTURE_AMD64 = 9; + const ushort PROCESSOR_ARCHITECTURE_UNKNOWN = 0xFFFF; + + [StructLayout(LayoutKind.Sequential)] + struct SYSTEM_INFO + { + public ushort wProcessorArchitecture; + public ushort wReserved; + public uint dwPageSize; + public IntPtr lpMinimumApplicationAddress; + public IntPtr lpMaximumApplicationAddress; + public UIntPtr dwActiveProcessorMask; + public uint dwNumberOfProcessors; + public uint dwProcessorType; + public uint dwAllocationGranularity; + public ushort wProcessorLevel; + public ushort wProcessorRevision; + }; + + [DllImport("kernel32.dll")] + static extern void GetNativeSystemInfo(ref SYSTEM_INFO lpSystemInfo); + + [DllImport("kernel32.dll")] + static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo); + + public static Platform GetOperationSystemPlatform() + { + var sysInfo = new SYSTEM_INFO(); + + // WinXP and older - use GetNativeSystemInfo + if (Environment.OSVersion.Version.Major > 5 || + (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1)) + { + GetNativeSystemInfo(ref sysInfo); + } + // else use GetSystemInfo + else + { + GetSystemInfo(ref sysInfo); + } + + switch (sysInfo.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_IA64: + case PROCESSOR_ARCHITECTURE_AMD64: + return Platform.X64; + + case PROCESSOR_ARCHITECTURE_INTEL: + return Platform.X86; + + default: + return Platform.Unknown; + } + } + } + + public enum Platform + { + X86, + X64, + Unknown + } + +} diff --git a/Irony.WinForms/FastColoredTextBox/Range.cs b/Irony.WinForms/FastColoredTextBox/Range.cs new file mode 100644 index 0000000..d00cf87 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/Range.cs @@ -0,0 +1,1042 @@ +using System; +using System.Text; +using System.Drawing; +using System.Text.RegularExpressions; +using System.Collections.Generic; + +namespace FastColoredTextBoxNS +{ + /// + /// Diapason of text chars + /// + public class Range : IEnumerable + { + Place start; + Place end; + public readonly FastColoredTextBox tb; + int preferedPos = -1; + int updating = 0; + + string cachedText; + List cachedCharIndexToPlace; + int cachedTextVersion = -1; + + /// + /// Constructor + /// + public Range(FastColoredTextBox tb) + { + this.tb = tb; + } + + /// + /// Constructor + /// + public Range(FastColoredTextBox tb, int iStartChar, int iStartLine, int iEndChar, int iEndLine) + : this(tb) + { + start = new Place(iStartChar, iStartLine); + end = new Place(iEndChar, iEndLine); + } + + /// + /// Constructor + /// + public Range(FastColoredTextBox tb, Place start, Place end) + : this(tb) + { + this.start = start; + this.end = end; + } + + public bool Contains(Place place) + { + if (place.iLine < Math.Min(start.iLine, end.iLine)) return false; + if (place.iLine > Math.Max(start.iLine, end.iLine)) return false; + + Place s = start; + Place e = end; + + if (s.iLine > e.iLine || (s.iLine == e.iLine && s.iChar > e.iChar)) + { + var temp = s; + s = e; + e = temp; + } + + if (place.iLine == s.iLine && place.iChar < s.iChar) return false; + if (place.iLine == e.iLine && place.iChar > e.iChar) return false; + + return true; + } + + /// + /// Returns intersection with other range, + /// empty range returned otherwise + /// + /// + /// + public Range GetIntersectionWith(Range range) + { + Range r1 = this.Clone(); + Range r2 = range.Clone(); + r1.Normalize(); + r2.Normalize(); + Place newStart = r1.Start > r2.Start ? r1.Start : r2.Start; + Place newEnd = r1.End < r2.End ? r1.End : r2.End; + if (newEnd < newStart) + return new Range(tb, start, start); + return tb.GetRange(newStart, newEnd); + } + + /// + /// Returns union with other range. + /// + /// + /// + public Range GetUnionWith(Range range) + { + Range r1 = this.Clone(); + Range r2 = range.Clone(); + r1.Normalize(); + r2.Normalize(); + Place newStart = r1.Start < r2.Start ? r1.Start : r2.Start; + Place newEnd = r1.End > r2.End ? r1.End : r2.End; + + return tb.GetRange(newStart, newEnd); + } + + /// + /// Select all chars of control + /// + public void SelectAll() + { + Start = new Place(0, 0); + if (tb.LinesCount == 0) + Start = new Place(0, 0); + else + { + end = new Place(0, 0); + start = new Place(tb[tb.LinesCount - 1].Count, tb.LinesCount - 1); + } + if (this == tb.Selection) + tb.Invalidate(); + } + + /// + /// Start line and char position + /// + public Place Start + { + get { return start; } + set + { + end = start = value; + preferedPos = -1; + + OnSelectionChanged(); + } + } + + /// + /// Finish line and char position + /// + public Place End + { + get + { + return end; + } + set + { + end = value; + OnSelectionChanged(); + } + } + + /// + /// Text of range + /// + /// This property has not 'set' accessor because undo/redo stack works only with + /// FastColoredTextBox.Selection range. So, if you want to set text, you need to use FastColoredTextBox.Selection + /// and FastColoredTextBox.InsertText() mehtod. + /// + public string Text + { + get + { + int fromLine = Math.Min(end.iLine, start.iLine); + int toLine = Math.Max(end.iLine, start.iLine); + int fromChar = FromX; + int toChar = ToX; + if (fromLine < 0) return null; + // + StringBuilder sb = new StringBuilder(); + for (int y = fromLine; y <= toLine; y++) + { + int fromX = y == fromLine ? fromChar : 0; + int toX = y == toLine ? toChar - 1 : tb[y].Count - 1; + for (int x = fromX; x <= toX; x++) + sb.Append(tb[y][x].c); + if (y != toLine && fromLine != toLine) + sb.AppendLine(); + } + return sb.ToString(); + } + } + + internal void GetText(out string text, out List charIndexToPlace) + { + //try get cached text + if (tb.TextVersion == cachedTextVersion) + { + text = cachedText; + charIndexToPlace = cachedCharIndexToPlace; + return; + } + // + int fromLine = Math.Min(end.iLine, start.iLine); + int toLine = Math.Max(end.iLine, start.iLine); + int fromChar = FromX; + int toChar = ToX; + + StringBuilder sb = new StringBuilder((toLine - fromLine)*50); + charIndexToPlace = new List(sb.Capacity); + if (fromLine >= 0) + { + for (int y = fromLine; y <= toLine; y++) + { + int fromX = y == fromLine ? fromChar : 0; + int toX = y == toLine ? toChar - 1 : tb[y].Count - 1; + for (int x = fromX; x <= toX; x++) + { + sb.Append(tb[y][x].c); + charIndexToPlace.Add(new Place(x, y)); + } + if (y != toLine && fromLine != toLine) + foreach (char c in Environment.NewLine) + { + sb.Append(c); + charIndexToPlace.Add(new Place(tb[y].Count/*???*/, y)); + } + } + } + text = sb.ToString(); + charIndexToPlace.Add(End > Start ? End : Start); + //caching + cachedText = text; + cachedCharIndexToPlace = charIndexToPlace; + cachedTextVersion = tb.TextVersion; + } + + /// + /// Returns first char after Start place + /// + public char CharAfterStart + { + get + { + if (Start.iChar >= tb[Start.iLine].Count) + return '\n'; + else + return tb[Start.iLine][Start.iChar].c; + } + } + + /// + /// Returns first char before Start place + /// + public char CharBeforeStart + { + get + { + if (Start.iChar <= 0) + return '\n'; + else + return tb[Start.iLine][Start.iChar - 1].c; + } + } + + /// + /// Clone range + /// + /// + public Range Clone() + { + return (Range)MemberwiseClone(); + } + + /// + /// Return minimum of end.X and start.X + /// + internal int FromX + { + get + { + if (end.iLine < start.iLine) return end.iChar; + if (end.iLine > start.iLine) return start.iChar; + return Math.Min(end.iChar, start.iChar); + } + } + + /// + /// Return maximum of end.X and start.X + /// + internal int ToX + { + get + { + if (end.iLine < start.iLine) return start.iChar; + if (end.iLine > start.iLine) return end.iChar; + return Math.Max(end.iChar, start.iChar); + } + } + + /// + /// Move range right + /// + /// This method jump over folded blocks + public bool GoRight() + { + Place prevStart = start; + GoRight(false); + return prevStart != start; + } + + /// + /// Move range left + /// + /// This method can to go inside folded blocks + public bool GoRightThroughFolded() + { + if (start.iLine >= tb.LinesCount - 1 && start.iChar >= tb[tb.LinesCount - 1].Count) + return false; + + if (start.iChar < tb[start.iLine].Count) + start.Offset(1, 0); + else + start = new Place(0, start.iLine + 1); + + preferedPos = -1; + end = start; + OnSelectionChanged(); + return true; + } + + /// + /// Move range left + /// + /// This method jump over folded blocks + public bool GoLeft() + { + Place prevStart = start; + GoLeft(false); + return prevStart != start; + } + + /// + /// Move range left + /// + /// This method can to go inside folded blocks + public bool GoLeftThroughFolded() + { + if (start.iChar == 0 && start.iLine == 0) + return false; + + if (start.iChar > 0) + start.Offset(-1, 0); + else + start = new Place(tb[start.iLine - 1].Count, start.iLine - 1); + + preferedPos = -1; + end = start; + OnSelectionChanged(); + return true; + } + + public void GoLeft(bool shift) + { + if (start.iChar != 0 || start.iLine != 0) + { + if (start.iChar > 0 && tb.lineInfos[start.iLine].VisibleState == VisibleState.Visible) + start.Offset(-1, 0); + else + { + int i = tb.FindPrevVisibleLine(start.iLine); + if (i == start.iLine) return; + start = new Place(tb[i].Count, i); + } + } + + if (!shift) + end = start; + + OnSelectionChanged(); + + preferedPos = -1; + } + + public void GoRight(bool shift) + { + if (start.iLine < tb.LinesCount - 1 || start.iChar < tb[tb.LinesCount - 1].Count) + { + if (start.iChar < tb[start.iLine].Count && tb.lineInfos[start.iLine].VisibleState == VisibleState.Visible) + start.Offset(1, 0); + else + { + int i = tb.FindNextVisibleLine(start.iLine); + if (i == start.iLine) return; + start = new Place(0, i); + } + } + + if (!shift) + end = start; + + OnSelectionChanged(); + + preferedPos = -1; + } + + internal void GoUp(bool shift) + { + if (preferedPos < 0) + preferedPos = start.iChar - tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar)); + + int iWW = tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar); + if (iWW == 0) + { + if (start.iLine <= 0) return; + int i = tb.FindPrevVisibleLine(start.iLine); + if (i == start.iLine) return; + start.iLine = i; + iWW = tb.lineInfos[start.iLine].WordWrapStringsCount; + } + + if (iWW > 0) + { + int finish = tb.lineInfos[start.iLine].GetWordWrapStringFinishPosition(iWW - 1, tb[start.iLine]); + start.iChar = tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(iWW - 1) + preferedPos; + if (start.iChar > finish + 1) + start.iChar = finish + 1; + } + + if (!shift) + end = start; + + OnSelectionChanged(); + } + + internal void GoPageUp(bool shift) + { + if (preferedPos < 0) + preferedPos = start.iChar - tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar)); + + int pageHeight = tb.ClientRectangle.Height / tb.CharHeight - 1; + + for (int i = 0; i < pageHeight; i++) + { + int iWW = tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar); + if (iWW == 0) + { + if (start.iLine <= 0) break; + //pass hidden + int newLine = tb.FindPrevVisibleLine(start.iLine); + if (newLine == start.iLine) break; + start.iLine = newLine; + iWW = tb.lineInfos[start.iLine].WordWrapStringsCount; + } + + if (iWW > 0) + { + int finish = tb.lineInfos[start.iLine].GetWordWrapStringFinishPosition(iWW - 1, tb[start.iLine]); + start.iChar = tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(iWW - 1) + preferedPos; + if (start.iChar > finish + 1) + start.iChar = finish + 1; + } + } + + if (!shift) + end = start; + + OnSelectionChanged(); + } + + internal void GoDown(bool shift) + { + if (preferedPos < 0) + preferedPos = start.iChar - tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar)); + + int iWW = tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar); + if (iWW >= tb.lineInfos[start.iLine].WordWrapStringsCount - 1) + { + if (start.iLine >= tb.LinesCount - 1) return; + //pass hidden + int i = tb.FindNextVisibleLine(start.iLine); + if (i == start.iLine) return; + start.iLine = i; + iWW = -1; + } + + if (iWW < tb.lineInfos[start.iLine].WordWrapStringsCount - 1) + { + int finish = tb.lineInfos[start.iLine].GetWordWrapStringFinishPosition(iWW + 1, tb[start.iLine]); + start.iChar = tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(iWW + 1) + preferedPos; + if (start.iChar > finish + 1) + start.iChar = finish + 1; + } + + if (!shift) + end = start; + + OnSelectionChanged(); + } + + internal void GoPageDown(bool shift) + { + if (preferedPos < 0) + preferedPos = start.iChar - tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar)); + + int pageHeight = tb.ClientRectangle.Height / tb.CharHeight - 1; + + for (int i = 0; i < pageHeight; i++) + { + int iWW = tb.lineInfos[start.iLine].GetWordWrapStringIndex(start.iChar); + if (iWW >= tb.lineInfos[start.iLine].WordWrapStringsCount - 1) + { + if (start.iLine >= tb.LinesCount - 1) break; + //pass hidden + int newLine = tb.FindNextVisibleLine(start.iLine); + if (newLine == start.iLine) break; + start.iLine = newLine; + iWW = -1; + } + + if (iWW < tb.lineInfos[start.iLine].WordWrapStringsCount - 1) + { + int finish = tb.lineInfos[start.iLine].GetWordWrapStringFinishPosition(iWW + 1, tb[start.iLine]); + start.iChar = tb.lineInfos[start.iLine].GetWordWrapStringStartPosition(iWW + 1) + preferedPos; + if (start.iChar > finish + 1) + start.iChar = finish + 1; + } + } + + if (!shift) + end = start; + + OnSelectionChanged(); + } + + internal void GoHome(bool shift) + { + if (start.iLine < 0) + return; + + if (tb.lineInfos[start.iLine].VisibleState != VisibleState.Visible) + return; + + start = new Place(0, start.iLine); + + if (!shift) + end = start; + + OnSelectionChanged(); + + preferedPos = -1; + } + + internal void GoEnd(bool shift) + { + if (start.iLine < 0) + return; + if (tb.lineInfos[start.iLine].VisibleState != VisibleState.Visible) + return; + + start = new Place(tb[start.iLine].Count, start.iLine); + + if (!shift) + end = start; + + OnSelectionChanged(); + + preferedPos = -1; + } + + /// + /// Set style for range + /// + public void SetStyle(Style style) + { + //search code for style + int code = tb.GetOrSetStyleLayerIndex(style); + //set code to chars + SetStyle(ToStyleIndex(code)); + // + tb.Invalidate(); + } + + /// + /// Set style for given regex pattern + /// + public void SetStyle(Style style, string regexPattern) + { + //search code for style + StyleIndex layer = ToStyleIndex(tb.GetOrSetStyleLayerIndex(style)); + SetStyle(layer, regexPattern, RegexOptions.None); + } + + /// + /// Set style for given regex + /// + public void SetStyle(Style style, Regex regex) + { + //search code for style + StyleIndex layer = ToStyleIndex(tb.GetOrSetStyleLayerIndex(style)); + SetStyle(layer, regex); + } + + + /// + /// Set style for given regex pattern + /// + public void SetStyle(Style style, string regexPattern, RegexOptions options) + { + //search code for style + StyleIndex layer = ToStyleIndex(tb.GetOrSetStyleLayerIndex(style)); + SetStyle(layer, regexPattern, options); + } + + /// + /// Set style for given regex pattern + /// + public void SetStyle(StyleIndex styleLayer, string regexPattern, RegexOptions options) + { + if (Math.Abs(Start.iLine - End.iLine) > 1000) + options |= RegexOptions.Compiled; + // + foreach (var range in GetRanges(regexPattern, options)) + range.SetStyle(styleLayer); + // + tb.Invalidate(); + } + + /// + /// Set style for given regex pattern + /// + public void SetStyle(StyleIndex styleLayer, Regex regex) + { + foreach (var range in GetRanges(regex)) + range.SetStyle(styleLayer); + // + tb.Invalidate(); + } + + /// + /// Appends style to chars of range + /// + public void SetStyle(StyleIndex styleIndex) + { + //set code to chars + int fromLine = Math.Min(End.iLine, Start.iLine); + int toLine = Math.Max(End.iLine, Start.iLine); + int fromChar = FromX; + int toChar = ToX; + if (fromLine < 0) return; + // + for (int y = fromLine; y <= toLine; y++) + { + int fromX = y == fromLine ? fromChar : 0; + int toX = y == toLine ? toChar - 1 : tb[y].Count - 1; + for (int x = fromX; x <= toX; x++) + { + Char c = tb[y][x]; + c.style |= styleIndex; + tb[y][x] = c; + } + } + } + + /// + /// Sets folding markers + /// + /// Pattern for start folding line + /// Pattern for finish folding line + public void SetFoldingMarkers(string startFoldingPattern, string finishFoldingPattern) + { + SetFoldingMarkers(startFoldingPattern, finishFoldingPattern, RegexOptions.Compiled); + } + + /// + /// Sets folding markers + /// + /// Pattern for start folding line + /// Pattern for finish folding line + public void SetFoldingMarkers(string startFoldingPattern, string finishFoldingPattern, RegexOptions options) + { + foreach (var range in GetRanges(startFoldingPattern, options)) + tb[range.Start.iLine].FoldingStartMarker = startFoldingPattern; + + foreach (var range in GetRanges(finishFoldingPattern, options)) + tb[range.Start.iLine].FoldingEndMarker = startFoldingPattern; + // + tb.Invalidate(); + } + /// + /// Finds ranges for given regex pattern + /// + /// Regex pattern + /// Enumeration of ranges + public IEnumerable GetRanges(string regexPattern) + { + return GetRanges(regexPattern, RegexOptions.None); + } + + /// + /// Finds ranges for given regex pattern + /// + /// Regex pattern + /// Enumeration of ranges + public IEnumerable GetRanges(string regexPattern, RegexOptions options) + { + //get text + string text; + List charIndexToPlace; + GetText(out text, out charIndexToPlace); + //create regex + Regex regex = new Regex(regexPattern, options); + // + foreach (Match m in regex.Matches(text)) + { + Range r = new Range(this.tb); + //try get 'range' group, otherwise use group 0 + Group group = m.Groups["range"]; + if (!group.Success) + group = m.Groups[0]; + // + r.Start = charIndexToPlace[group.Index]; + r.End = charIndexToPlace[group.Index + group.Length]; + yield return r; + } + } + + /// + /// Finds ranges for given regex pattern. + /// Search is separately in each line. + /// This method requires less memory than GetRanges(). + /// + /// Regex pattern + /// Enumeration of ranges + public IEnumerable GetRangesByLines(string regexPattern, RegexOptions options) + { + Normalize(); + //create regex + Regex regex = new Regex(regexPattern, options); + // + var fts = tb.TextSource as FileTextSource; + //enumaerate lines + for (int iLine = Start.iLine; iLine <= End.iLine; iLine++) + { + // + bool isLineLoaded = fts != null ? fts.IsLineLoaded(iLine) : true; + // + var r = new Range(tb, new Place(0, iLine), new Place(tb[iLine].Count, iLine)); + if (iLine == Start.iLine || iLine == End.iLine) + r = r.GetIntersectionWith(this); + + foreach (var foundRange in r.GetRanges(regex)) + yield return foundRange; + + if (!isLineLoaded) + fts.UnloadLine(iLine); + } + } + + /// + /// Finds ranges for given regex + /// + /// Enumeration of ranges + public IEnumerable GetRanges(Regex regex) + { + //get text + string text; + List charIndexToPlace; + GetText(out text, out charIndexToPlace); + // + foreach (Match m in regex.Matches(text)) + { + Range r = new Range(this.tb); + //try get 'range' group, otherwise use group 0 + Group group = m.Groups["range"]; + if (!group.Success) + group = m.Groups[0]; + // + r.Start = charIndexToPlace[group.Index]; + r.End = charIndexToPlace[group.Index + group.Length]; + yield return r; + } + } + + /// + /// Clear styles of range + /// + public void ClearStyle(params Style[] styles) + { + try + { + ClearStyle(tb.GetStyleIndexMask(styles)); + } + catch { ;} + } + + /// + /// Clear styles of range + /// + public void ClearStyle(StyleIndex styleIndex) + { + //set code to chars + int fromLine = Math.Min(End.iLine, Start.iLine); + int toLine = Math.Max(End.iLine, Start.iLine); + int fromChar = FromX; + int toChar = ToX; + if (fromLine < 0) return; + // + for (int y = fromLine; y <= toLine; y++) + { + int fromX = y == fromLine ? fromChar : 0; + int toX = y == toLine ? toChar - 1 : tb[y].Count - 1; + for (int x = fromX; x <= toX; x++) + { + Char c = tb[y][x]; + c.style &= ~styleIndex; + tb[y][x] = c; + } + } + // + tb.Invalidate(); + } + + /// + /// Clear folding markers of all lines of range + /// + public void ClearFoldingMarkers() + { + //set code to chars + int fromLine = Math.Min(End.iLine, Start.iLine); + int toLine = Math.Max(End.iLine, Start.iLine); + if (fromLine < 0) return; + // + for (int y = fromLine; y <= toLine; y++) + tb[y].ClearFoldingMarkers(); + // + tb.Invalidate(); + } + + void OnSelectionChanged() + { + //clear cache + cachedTextVersion = -1; + cachedText = null; + cachedCharIndexToPlace = null; + // + if (tb.Selection == this) + if (updating == 0) + tb.OnSelectionChanged(); + } + + /// + /// Starts selection position updating + /// + public void BeginUpdate() + { + updating++; + } + + /// + /// Ends selection position updating + /// + public void EndUpdate() + { + updating--; + if (updating == 0) + OnSelectionChanged(); + } + + public override string ToString() + { + return "Start: " + Start + " End: " + End; + } + + /// + /// Exchanges Start and End if End appears before Start + /// + public void Normalize() + { + if (Start > End) + Inverse(); + } + + /// + /// Exchanges Start and End + /// + public void Inverse() + { + var temp = start; + start = end; + end = temp; + } + + /// + /// Expands range from first char of Start line to last char of End line + /// + public void Expand() + { + Normalize(); + start = new Place(0, start.iLine); + end = new Place(tb.GetLineLength(end.iLine), end.iLine); + } + + IEnumerator IEnumerable.GetEnumerator() + { + int fromLine = Math.Min(end.iLine, start.iLine); + int toLine = Math.Max(end.iLine, start.iLine); + int fromChar = FromX; + int toChar = ToX; + if (fromLine < 0) yield break; + // + for (int y = fromLine; y <= toLine; y++) + { + int fromX = y == fromLine ? fromChar : 0; + int toX = y == toLine ? toChar - 1 : tb[y].Count - 1; + for (int x = fromX; x <= toX; x++) + yield return new Place(x, y); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return (this as IEnumerable).GetEnumerator(); + } + + /// + /// Get fragment of text around Start place. Returns maximal mathed to pattern fragment. + /// + /// Allowed chars pattern for fragment + /// Range of found fragment + public Range GetFragment(string allowedSymbolsPattern) + { + return GetFragment(allowedSymbolsPattern, RegexOptions.None); + } + + /// + /// Get fragment of text around Start place. Returns maximal mathed to pattern fragment. + /// + /// Allowed chars pattern for fragment + /// Range of found fragment + public Range GetFragment(string allowedSymbolsPattern, RegexOptions options) + { + Range r = new Range(tb); + r.Start = Start; + Regex regex = new Regex(allowedSymbolsPattern, options); + //go left, check symbols + while (r.GoLeftThroughFolded()) + { + if (!regex.IsMatch(r.CharAfterStart.ToString())) + { + r.GoRightThroughFolded(); + break; + } + } + Place startFragment = r.Start; + + r.Start = Start; + //go right, check symbols + do + { + if (!regex.IsMatch(r.CharAfterStart.ToString())) + break; + } while (r.GoRightThroughFolded()) ; + Place endFragment = r.Start; + + return new Range(tb, startFragment, endFragment); + } + + bool IsIdentifierChar(char c) + { + return char.IsLetterOrDigit(c) || c == '_'; + } + + internal void GoWordLeft(bool shift) + { + Range range = this.Clone();//for OnSelectionChanged disable + + Place prev; + bool findIdentifier = IsIdentifierChar(range.CharBeforeStart); + + do{ + prev = range.Start; + if (IsIdentifierChar(range.CharBeforeStart) ^ findIdentifier) + break; + + //move left + range.GoLeft(shift); + } while (prev != range.Start); + + this.Start = range.Start; + this.End = range.End; + + if (tb.lineInfos[Start.iLine].VisibleState != VisibleState.Visible) + GoRight(shift); + } + + internal void GoWordRight(bool shift) + { + Range range = this.Clone();//for OnSelectionChanged disable + + Place prev; + bool findIdentifier = IsIdentifierChar(range.CharAfterStart); + + do + { + prev = range.Start; + if (IsIdentifierChar(range.CharAfterStart) ^ findIdentifier) + break; + + //move right + range.GoRight(shift); + } while (prev != range.Start); + + this.Start = range.Start; + this.End = range.End; + + if (tb.lineInfos[Start.iLine].VisibleState != VisibleState.Visible) + GoLeft(shift); + } + + internal void GoFirst(bool shift) + { + start = new Place(0, 0); + if (tb.lineInfos[Start.iLine].VisibleState != VisibleState.Visible) + GoRight(shift); + if(!shift) + end = start; + + OnSelectionChanged(); + } + + internal void GoLast(bool shift) + { + start = new Place(tb[tb.LinesCount - 1].Count, tb.LinesCount-1); + if (tb.lineInfos[Start.iLine].VisibleState != VisibleState.Visible) + GoLeft(shift); + if (!shift) + end = start; + + OnSelectionChanged(); + } + + public static StyleIndex ToStyleIndex(int i) + { + return (StyleIndex)(1 << i); + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/ReplaceForm.Designer.cs b/Irony.WinForms/FastColoredTextBox/ReplaceForm.Designer.cs new file mode 100644 index 0000000..422beb7 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/ReplaceForm.Designer.cs @@ -0,0 +1,198 @@ +namespace FastColoredTextBoxNS +{ + partial class ReplaceForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btClose = new System.Windows.Forms.Button(); + this.btFindNext = new System.Windows.Forms.Button(); + this.tbFind = new System.Windows.Forms.TextBox(); + this.cbRegex = new System.Windows.Forms.CheckBox(); + this.cbMatchCase = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.cbWholeWord = new System.Windows.Forms.CheckBox(); + this.btReplace = new System.Windows.Forms.Button(); + this.btReplaceAll = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.tbReplace = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btClose + // + this.btClose.Location = new System.Drawing.Point(273, 138); + this.btClose.Name = "btClose"; + this.btClose.Size = new System.Drawing.Size(75, 23); + this.btClose.TabIndex = 7; + this.btClose.Text = "Close"; + this.btClose.UseVisualStyleBackColor = true; + this.btClose.Click += new System.EventHandler(this.btClose_Click); + // + // btFindNext + // + this.btFindNext.Location = new System.Drawing.Point(111, 109); + this.btFindNext.Name = "btFindNext"; + this.btFindNext.Size = new System.Drawing.Size(75, 23); + this.btFindNext.TabIndex = 4; + this.btFindNext.Text = "Find next"; + this.btFindNext.UseVisualStyleBackColor = true; + this.btFindNext.Click += new System.EventHandler(this.btFindNext_Click); + // + // tbFind + // + this.tbFind.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.tbFind.Location = new System.Drawing.Point(62, 12); + this.tbFind.Name = "tbFind"; + this.tbFind.Size = new System.Drawing.Size(286, 20); + this.tbFind.TabIndex = 0; + this.tbFind.TextChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + this.tbFind.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.tbFind_KeyPress); + // + // cbRegex + // + this.cbRegex.AutoSize = true; + this.cbRegex.Location = new System.Drawing.Point(273, 38); + this.cbRegex.Name = "cbRegex"; + this.cbRegex.Size = new System.Drawing.Size(57, 17); + this.cbRegex.TabIndex = 3; + this.cbRegex.Text = "Regex"; + this.cbRegex.UseVisualStyleBackColor = true; + this.cbRegex.CheckedChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + // + // cbMatchCase + // + this.cbMatchCase.AutoSize = true; + this.cbMatchCase.Location = new System.Drawing.Point(66, 38); + this.cbMatchCase.Name = "cbMatchCase"; + this.cbMatchCase.Size = new System.Drawing.Size(82, 17); + this.cbMatchCase.TabIndex = 1; + this.cbMatchCase.Text = "Match case"; + this.cbMatchCase.UseVisualStyleBackColor = true; + this.cbMatchCase.CheckedChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(23, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(33, 13); + this.label1.TabIndex = 5; + this.label1.Text = "Find: "; + // + // cbWholeWord + // + this.cbWholeWord.AutoSize = true; + this.cbWholeWord.Location = new System.Drawing.Point(154, 38); + this.cbWholeWord.Name = "cbWholeWord"; + this.cbWholeWord.Size = new System.Drawing.Size(113, 17); + this.cbWholeWord.TabIndex = 2; + this.cbWholeWord.Text = "Match whole word"; + this.cbWholeWord.UseVisualStyleBackColor = true; + this.cbWholeWord.CheckedChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + // + // btReplace + // + this.btReplace.Location = new System.Drawing.Point(192, 109); + this.btReplace.Name = "btReplace"; + this.btReplace.Size = new System.Drawing.Size(75, 23); + this.btReplace.TabIndex = 5; + this.btReplace.Text = "Replace"; + this.btReplace.UseVisualStyleBackColor = true; + this.btReplace.Click += new System.EventHandler(this.btReplace_Click); + // + // btReplaceAll + // + this.btReplaceAll.Location = new System.Drawing.Point(273, 109); + this.btReplaceAll.Name = "btReplaceAll"; + this.btReplaceAll.Size = new System.Drawing.Size(75, 23); + this.btReplaceAll.TabIndex = 6; + this.btReplaceAll.Text = "Replace all"; + this.btReplaceAll.UseVisualStyleBackColor = true; + this.btReplaceAll.Click += new System.EventHandler(this.btReplaceAll_Click); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(6, 74); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(50, 13); + this.label2.TabIndex = 9; + this.label2.Text = "Replace:"; + // + // tbReplace + // + this.tbReplace.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.tbReplace.Location = new System.Drawing.Point(62, 71); + this.tbReplace.Name = "tbReplace"; + this.tbReplace.Size = new System.Drawing.Size(286, 20); + this.tbReplace.TabIndex = 0; + this.tbReplace.TextChanged += new System.EventHandler(this.cbMatchCase_CheckedChanged); + this.tbReplace.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.tbFind_KeyPress); + // + // ReplaceForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(360, 173); + this.Controls.Add(this.tbFind); + this.Controls.Add(this.label2); + this.Controls.Add(this.tbReplace); + this.Controls.Add(this.btReplaceAll); + this.Controls.Add(this.btReplace); + this.Controls.Add(this.cbWholeWord); + this.Controls.Add(this.label1); + this.Controls.Add(this.cbMatchCase); + this.Controls.Add(this.cbRegex); + this.Controls.Add(this.btFindNext); + this.Controls.Add(this.btClose); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "ReplaceForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Find and replace"; + this.TopMost = true; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FindForm_FormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btClose; + private System.Windows.Forms.Button btFindNext; + private System.Windows.Forms.CheckBox cbRegex; + private System.Windows.Forms.CheckBox cbMatchCase; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckBox cbWholeWord; + private System.Windows.Forms.Button btReplace; + private System.Windows.Forms.Button btReplaceAll; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox tbReplace; + public System.Windows.Forms.TextBox tbFind; + } +} \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/ReplaceForm.cs b/Irony.WinForms/FastColoredTextBox/ReplaceForm.cs new file mode 100644 index 0000000..dbc41c4 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/ReplaceForm.cs @@ -0,0 +1,171 @@ +using System; +using System.Windows.Forms; +using System.Text.RegularExpressions; +using System.Collections.Generic; + +namespace FastColoredTextBoxNS +{ + public partial class ReplaceForm : Form + { + FastColoredTextBox tb; + bool firstSearch = true; + Place startPlace; + + public ReplaceForm(FastColoredTextBox tb) + { + InitializeComponent(); + this.tb = tb; + } + + private void btClose_Click(object sender, EventArgs e) + { + Close(); + } + + private void btFindNext_Click(object sender, EventArgs e) + { + try + { + if(!Find()) + MessageBox.Show("Not found"); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + List FindAll() + { + string pattern = tbFind.Text; + RegexOptions opt = cbMatchCase.Checked ? RegexOptions.None : RegexOptions.IgnoreCase; + if (!cbRegex.Checked) + pattern = Regex.Replace(pattern, FastColoredTextBoxNS.FindForm.RegexSpecSymbolsPattern, "\\$0"); + if (cbWholeWord.Checked) + pattern = "\\b" + pattern + "\\b"; + // + Range range = tb.Selection.Clone(); + range.Normalize(); + range.Start = range.End; + range.End = new Place(tb.GetLineLength(tb.LinesCount - 1), tb.LinesCount - 1); + // + List list = new List(); + foreach (var r in range.GetRangesByLines(pattern, opt)) + list.Add(r); + + return list; + } + + bool Find() + { + string pattern = tbFind.Text; + RegexOptions opt = cbMatchCase.Checked ? RegexOptions.None : RegexOptions.IgnoreCase; + if (!cbRegex.Checked) + pattern = Regex.Replace(pattern, FastColoredTextBoxNS.FindForm.RegexSpecSymbolsPattern, "\\$0"); + if (cbWholeWord.Checked) + pattern = "\\b" + pattern + "\\b"; + // + Range range = tb.Selection.Clone(); + range.Normalize(); + // + if (firstSearch) + { + startPlace = range.Start; + firstSearch = false; + } + // + range.Start = range.End; + if (range.Start >= startPlace) + range.End = new Place(tb.GetLineLength(tb.LinesCount - 1), tb.LinesCount - 1); + else + range.End = startPlace; + // + foreach (var r in range.GetRangesByLines(pattern, opt)) + { + tb.Selection.Start = r.Start; + tb.Selection.End = r.End; + tb.DoSelectionVisible(); + tb.Invalidate(); + return true; + } + if (range.Start >= startPlace && startPlace > Place.Empty) + { + tb.Selection.Start = new Place(0, 0); + return Find(); + } + return false; + } + + private void tbFind_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == '\r') + btFindNext_Click(sender, null); + if (e.KeyChar == '\x1b') + Hide(); + } + + private void FindForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + e.Cancel = true; + Hide(); + } + } + + private void btReplace_Click(object sender, EventArgs e) + { + try + { + if (tb.SelectionLength != 0) + tb.InsertText(tbReplace.Text); + btFindNext_Click(sender, null); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + private void btReplaceAll_Click(object sender, EventArgs e) + { + try + { + tb.Selection.BeginUpdate(); + tb.Selection.Start = new Place(0, 0); + //search + var ranges = FindAll(); + //replace + if (ranges.Count > 0) + { + tb.TextSource.Manager.ExecuteCommand(new ReplaceTextCommand(tb.TextSource, ranges, tbReplace.Text)); + tb.Selection.Start = new Place(0, 0); + } + // + tb.Invalidate(); + MessageBox.Show(ranges.Count + " occurrence(s) replaced"); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + tb.Selection.EndUpdate(); + } + + protected override void OnActivated(EventArgs e) + { + tbFind.Focus(); + ResetSerach(); + } + + void ResetSerach() + { + firstSearch = true; + } + + private void cbMatchCase_CheckedChanged(object sender, EventArgs e) + { + ResetSerach(); + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/ReplaceForm.resx b/Irony.WinForms/FastColoredTextBox/ReplaceForm.resx new file mode 100644 index 0000000..5ea0895 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/ReplaceForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.WinForms/FastColoredTextBox/Style.cs b/Irony.WinForms/FastColoredTextBox/Style.cs new file mode 100644 index 0000000..04fe966 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/Style.cs @@ -0,0 +1,302 @@ +using System.Drawing; +using System; +using System.Drawing.Drawing2D; + +namespace FastColoredTextBoxNS +{ + /// + /// Style of chars + /// + /// This is base class for all text and design renderers + public abstract class Style : IDisposable + { + /// + /// This style is exported to outer formats (HTML for example) + /// + public virtual bool IsExportable { get; set; } + /// + /// Occurs when user click on StyleVisualMarker joined to this style + /// + public event EventHandler VisualMarkerClick; + + /// + /// Constructor + /// + public Style() + { + IsExportable = true; + } + + /// + /// Renders given range of text + /// + /// Graphics object + /// Position of the range in absolute control coordinates + /// Rendering range of text + public abstract void Draw(Graphics gr, Point position, Range range); + + /// + /// Occurs when user click on StyleVisualMarker joined to this style + /// + public virtual void OnVisualMarkerClick(FastColoredTextBox tb, VisualMarkerEventArgs args) + { + if (VisualMarkerClick != null) + VisualMarkerClick(tb, args); + } + + /// + /// Shows VisualMarker + /// Call this method in Draw method, when you need to show VisualMarker for your style + /// + protected virtual void AddVisualMarker(FastColoredTextBox tb, StyleVisualMarker marker) + { + tb.AddVisualMarker(marker); + } + + public static Size GetSizeOfRange(Range range) + { + return new Size((range.End.iChar - range.Start.iChar) * range.tb.CharWidth, range.tb.CharHeight); + } + + public static GraphicsPath GetRoundedRectangle(Rectangle rect, int d) + { + GraphicsPath gp = new GraphicsPath(); + + gp.AddArc(rect.X, rect.Y, d, d, 180, 90); + gp.AddArc(rect.X + rect.Width - d, rect.Y, d, d, 270, 90); + gp.AddArc(rect.X + rect.Width - d, rect.Y + rect.Height - d, d, d, 0, 90); + gp.AddArc(rect.X, rect.Y + rect.Height - d, d, d, 90, 90); + gp.AddLine(rect.X, rect.Y + rect.Height - d, rect.X, rect.Y + d / 2); + + return gp; + } + + public virtual void Dispose() + { + ; + } + } + + /// + /// Style for chars rendering + /// This renderer can draws chars, with defined fore and back colors + /// + public class TextStyle : Style + { + public Brush ForeBrush { get; set; } + public Brush BackgroundBrush { get; set; } + public FontStyle FontStyle { get; set; } + //public readonly Font Font; + public StringFormat stringFormat; + + public TextStyle(Brush foreBrush, Brush backgroundBrush, FontStyle fontStyle) + { + this.ForeBrush = foreBrush; + this.BackgroundBrush = backgroundBrush; + this.FontStyle = fontStyle; + stringFormat = new StringFormat(StringFormatFlags.MeasureTrailingSpaces); + } + + public override void Draw(Graphics gr, Point position, Range range) + { + //draw background + if (BackgroundBrush != null) + gr.FillRectangle(BackgroundBrush, position.X, position.Y, (range.End.iChar - range.Start.iChar) * range.tb.CharWidth, range.tb.CharHeight); + //draw chars + Font f = new Font(range.tb.Font, FontStyle); + //Font fHalfSize = new Font(range.tb.Font.FontFamily, f.SizeInPoints/2, FontStyle); + Line line = range.tb[range.Start.iLine]; + float dx = range.tb.CharWidth; + float y = position.Y + range.tb.LineInterval / 2; + float x = position.X - range.tb.CharWidth/3; + + if(ForeBrush == null) + ForeBrush = new SolidBrush(range.tb.ForeColor); + + //IME mode + if (range.tb.ImeAllowed) + for (int i = range.Start.iChar; i < range.End.iChar; i++) + { + SizeF size = FastColoredTextBox.GetCharSize(f, line[i].c); + + var gs = gr.Save(); + float k = size.Width>range.tb.CharWidth + 1?range.tb.CharWidth / size.Width:1; + gr.TranslateTransform(x, y+(1-k)*range.tb.CharHeight/2); + gr.ScaleTransform(k, (float)Math.Sqrt(k)); + gr.DrawString(line[i].c.ToString(), f, ForeBrush, 0, 0, stringFormat); + gr.Restore(gs); + /* + if(size.Width>range.tb.CharWidth*1.5f) + gr.DrawString(line[i].c.ToString(), fHalfSize, foreBrush, x, y+range.tb.CharHeight/4, stringFormat); + else + gr.DrawString(line[i].c.ToString(), f, foreBrush, x, y, stringFormat); + * */ + x += dx; + } + else + //classic mode + for (int i = range.Start.iChar; i < range.End.iChar; i++) + { + //draw char + gr.DrawString(line[i].c.ToString(), f, ForeBrush, x, y, stringFormat); + x += dx; + } + // + f.Dispose(); + } + + public override void Dispose() + { + base.Dispose(); + + if(ForeBrush!=null) + ForeBrush.Dispose(); + if(BackgroundBrush!=null) + BackgroundBrush.Dispose(); + } + } + + /// + /// Renderer for folded block + /// + public class FoldedBlockStyle : TextStyle + { + public FoldedBlockStyle(Brush foreBrush, Brush backgroundBrush, FontStyle fontStyle): + base(foreBrush, backgroundBrush, fontStyle) + { + } + + public override void Draw(Graphics gr, Point position, Range range) + { + if (range.End.iChar > range.Start.iChar) + { + base.Draw(gr, position, range); + + int firstNonSpaceSymbolX = position.X; + + //find first non space symbol + for (int i = range.Start.iChar; i < range.End.iChar; i++) + if (range.tb[range.Start.iLine][i].c != ' ') + break; + else + firstNonSpaceSymbolX += range.tb.CharWidth; + + //create marker + range.tb.AddVisualMarker(new FoldedAreaMarker(range.Start.iLine, new Rectangle(firstNonSpaceSymbolX, position.Y, position.X + (range.End.iChar - range.Start.iChar) * range.tb.CharWidth - firstNonSpaceSymbolX, range.tb.CharHeight))); + } + else + { + //draw '...' + using(Font f = new Font(range.tb.Font, FontStyle)) + gr.DrawString("...", f, ForeBrush, range.tb.LeftIndent, position.Y - 2); + //create marker + range.tb.AddVisualMarker(new FoldedAreaMarker(range.Start.iLine, new Rectangle(range.tb.LeftIndent + 2, position.Y, 2 * range.tb.CharHeight, range.tb.CharHeight))); + } + } + } + + /// + /// Renderer for selection area + /// + public class SelectionStyle : Style + { + public Brush BackgroundBrush{get;set;} + + public override bool IsExportable + { + get{return false;} set{} + } + + public SelectionStyle(Brush backgroundBrush) + { + this.BackgroundBrush = backgroundBrush; + } + + public override void Draw(Graphics gr, Point position, Range range) + { + //draw background + if (BackgroundBrush != null) + { + Rectangle rect = new Rectangle(position.X, position.Y, (range.End.iChar - range.Start.iChar) * range.tb.CharWidth, range.tb.CharHeight); + if (rect.Width == 0) + return; + gr.FillRectangle(BackgroundBrush, rect); + } + } + + public override void Dispose() + { + base.Dispose(); + + if (BackgroundBrush != null) + BackgroundBrush.Dispose(); + } + } + + /// + /// Marker style + /// Draws background color for text + /// + public class MarkerStyle : Style + { + public Brush BackgroundBrush{get;set;} + + public MarkerStyle(Brush backgroundBrush) + { + this.BackgroundBrush = backgroundBrush; + IsExportable = false; + } + + public override void Draw(Graphics gr, Point position, Range range) + { + //draw background + if (BackgroundBrush != null) + { + Rectangle rect = new Rectangle(position.X, position.Y, (range.End.iChar - range.Start.iChar) * range.tb.CharWidth, range.tb.CharHeight); + if (rect.Width == 0) + return; + //var path = GetRoundedRectangle(rect, 5); + //gr.FillPath(BackgroundBrush, path); + gr.FillRectangle(BackgroundBrush, rect); + } + } + + public override void Dispose() + { + base.Dispose(); + + if (BackgroundBrush != null) + BackgroundBrush.Dispose(); + } + } + + public class ShortcutStyle : Style + { + public Pen borderPen; + + public ShortcutStyle(Pen borderPen) + { + this.borderPen = borderPen; + } + + public override void Draw(Graphics gr, Point position, Range range) + { + //get last char coordinates + Point p = range.tb.PlaceToPoint(range.End); + //draw small square under char + Rectangle rect = new Rectangle(p.X - 5, p.Y + range.tb.CharHeight - 3, 4, 3); + gr.FillPath(Brushes.White, GetRoundedRectangle(rect, 1)); + gr.DrawPath(borderPen, GetRoundedRectangle(rect, 1)); + //add visual marker for handle mouse events + AddVisualMarker(range.tb, new StyleVisualMarker(new Rectangle(p.X-range.tb.CharWidth, p.Y, range.tb.CharWidth, range.tb.CharHeight), this)); + } + + public override void Dispose() + { + base.Dispose(); + + if (borderPen != null) + borderPen.Dispose(); + } + } +} diff --git a/Irony.WinForms/FastColoredTextBox/SyntaxDescriptor.cs b/Irony.WinForms/FastColoredTextBox/SyntaxDescriptor.cs new file mode 100644 index 0000000..0ca1fb1 --- /dev/null +++ b/Irony.WinForms/FastColoredTextBox/SyntaxDescriptor.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System; + +namespace FastColoredTextBoxNS +{ + public class SyntaxDescriptor: IDisposable + { + public char leftBracket = '('; + public char rightBracket = ')'; + public char leftBracket2 = '\x0'; + public char rightBracket2 = '\x0'; + public readonly List