// // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // License: GNU Lesser General Public License (LGPLv3) // // Email: pavel_torgashov@mail.ru. // // Copyright (C) Pavel Torgashov, 2011-2012. //#define debug using Microsoft.Win32; using System.ComponentModel; using System.Drawing.Design; using System.Drawing.Drawing2D; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms.Design; using Timer = System.Windows.Forms.Timer; namespace LB_VisionControls { /// /// Fast colored textbox /// public class FastColoredTextBox : UserControl { private const int minLeftIndent = 8; private const int maxBracketSearchIterations = 1000; private const int maxLinesForFolding = 3000; private const int minLinesForAccuracy = 100000; private const int WM_IME_SETCONTEXT = 0x0281; private const int WM_HSCROLL = 0x114; private const int WM_VSCROLL = 0x115; private const int SB_ENDSCROLL = 0x8; internal readonly List lineInfos = new List(); private Range selection; private readonly Timer timer = new Timer(); private readonly Timer timer2 = new Timer(); private readonly List visibleMarkers = new List(); private Color changedLineColor; private int charHeight; private Color currentLineColor; private bool caretVisible; private Range delayedTextChangedRange; private string descriptionFile; private int endFoldingLine = -1; private Color foldingIndicatorColor; private bool handledChar; private bool highlightFoldingIndicator; private Color indentBackColor; private Color paddingBackColor; private bool isChanged; private Language language; private Keys lastModifiers; private DateTime lastNavigatedDateTime; private Range leftBracketPosition; private Range leftBracketPosition2; private int leftPadding; private int lineInterval; private Color lineNumberColor; private uint lineNumberStartValue; private TextSource lines; private IntPtr m_hImc; private bool mouseIsDrag; private bool isLineSelect; private int lineSelectFrom; private bool multiline; private bool needRecalc; private bool needRiseSelectionChangedDelayed; private bool needRiseTextChangedDelayed; private bool needRiseVisibleRangeChangedDelayed; private int preferredLineWidth; private Range rightBracketPosition; private Range rightBracketPosition2; private bool scrollBars; private Color serviceLinesColor; private bool showLineNumbers; private bool showFoldingLines; private bool needRecalcFoldingLines; private FastColoredTextBox sourceTextBox; private int startFoldingLine = -1; private int updating; private Range updatingRange; private bool wordWrap; private int wordWrapLinesCount; private int maxLineLength = 0; private WordWrapMode wordWrapMode = WordWrapMode.WordWrapControlWidth; private Color selectionColor; private Brush backBrush; internal bool allowInsertRemoveLines = true; bool isReplaceMode = false; /// /// Constructor /// public FastColoredTextBox() { //type provider TypeDescriptor.AddProvider(new FCTBDescriptionProvider(GetType()), this); //drawing optimization SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.ResizeRedraw, true); //append monospace font //Font = new Font("Consolas", 9.75f, FontStyle.Regular, GraphicsUnit.Point); Font = new Font(FontFamily.GenericMonospace, 9.75f); //create one line InitTextSource(CreateTextSource()); if (lines.Count == 0) lines.InsertLine(0, lines.CreateLine()); selection = new Range(this) { Start = new Place(0, 0) }; //default settings Cursor = Cursors.IBeam; BackColor = Color.White; LineNumberColor = Color.Teal; IndentBackColor = Color.White; ServiceLinesColor = Color.Silver; FoldingIndicatorColor = Color.Green; CurrentLineColor = Color.Transparent; ChangedLineColor = Color.Transparent; HighlightFoldingIndicator = true; ShowLineNumbers = true; TabLength = 4; FoldedBlockStyle = new FoldedBlockStyle(Brushes.Gray, null, FontStyle.Regular); SelectionColor = Color.Blue; BracketsStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(80, Color.Lime))); BracketsStyle2 = new MarkerStyle(new SolidBrush(Color.FromArgb(60, Color.Red))); DelayedEventsInterval = 100; DelayedTextChangedInterval = 100; AllowSeveralTextStyleDrawing = false; LeftBracket = '\x0'; RightBracket = '\x0'; LeftBracket2 = '\x0'; RightBracket2 = '\x0'; SyntaxHighlighter = new SyntaxHighlighter(); language = Language.Custom; PreferredLineWidth = 0; needRecalc = true; lastNavigatedDateTime = DateTime.Now; AutoIndent = true; AutoIndentExistingLines = true; CommentPrefix = "//"; lineNumberStartValue = 1; multiline = true; scrollBars = true; AcceptsTab = true; AcceptsReturn = true; caretVisible = true; CaretColor = Color.Black; Paddings = new Padding(0, 0, 0, 0); PaddingBackColor = Color.Transparent; DisabledColor = Color.FromArgb(100, 180, 180, 180); needRecalcFoldingLines = true; AllowDrop = true; FindEndOfFoldingBlockStrategy = FindEndOfFoldingBlockStrategy.Strategy1; VirtualSpace = false; // base.AutoScroll = true; timer.Tick += timer_Tick; timer2.Tick += timer2_Tick; // } /// /// Enables virtual spaces /// [DefaultValue(false)] [Description("Enables virtual spaces.")] public bool VirtualSpace { get; set; } /// /// Strategy of search of end of folding block /// [DefaultValue(FindEndOfFoldingBlockStrategy.Strategy1)] [Description("Strategy of search of end of folding block.")] public FindEndOfFoldingBlockStrategy FindEndOfFoldingBlockStrategy { get; set; } /// /// Indicates if tab characters are accepted as input /// [DefaultValue(true)] [Description("Indicates if tab characters are accepted as input.")] public bool AcceptsTab { get; set; } /// /// Indicates if return characters are accepted as input /// [DefaultValue(true)] [Description("Indicates if return characters are accepted as input.")] public bool AcceptsReturn { get; set; } /// /// Shows or hides the caret /// [DefaultValue(true)] [Description("Shows or hides the caret")] public bool CaretVisible { get { return caretVisible; } set { caretVisible = value; Invalidate(); } } /// /// Background color for current line /// [DefaultValue(typeof(Color), "Transparent")] [Description("Background color for current line. Set to Color.Transparent to hide current line highlighting")] public Color CurrentLineColor { get { return currentLineColor; } set { currentLineColor = value; Invalidate(); } } /// /// Background color for highlighting of changed lines /// [DefaultValue(typeof(Color), "Transparent")] [Description( "Background color for highlighting of changed lines. Set to Color.Transparent to hide changed line highlighting" )] public Color ChangedLineColor { get { return changedLineColor; } set { changedLineColor = value; Invalidate(); } } /// /// Fore color (default style color) /// public override Color ForeColor { get { return base.ForeColor; } set { base.ForeColor = value; lines.InitDefaultStyle(); Invalidate(); } } /// /// Height of char in pixels /// [Description("Height of char in pixels")] public int CharHeight { get { return charHeight; } private set { charHeight = value; OnCharSizeChanged(); } } /// /// Interval between lines (in pixels) /// [Description("Interval between lines in pixels")] [DefaultValue(0)] public int LineInterval { get { return lineInterval; } set { lineInterval = value; Font = Font; Invalidate(); } } /// /// Width of char in pixels /// [Description("Width of char in pixels")] public int CharWidth { get; private set; } /// /// Spaces count for tab /// [DefaultValue(4)] [Description("Spaces count for tab")] public int TabLength { get; set; } /// /// Text was changed /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsChanged { get { return isChanged; } set { if (!value) //clear line's IsChanged property lines.ClearIsChanged(); isChanged = value; } } /// /// Text version /// /// This counter is incremented each time changes the text [Browsable(false)] public int TextVersion { get; private set; } /// /// Read only /// [DefaultValue(false)] public bool ReadOnly { get; set; } /// /// Shows line numbers. /// [DefaultValue(true)] [Description("Shows line numbers.")] public bool ShowLineNumbers { get { return showLineNumbers; } set { showLineNumbers = value; NeedRecalc(); Invalidate(); } } /// /// Shows vertical lines between folding start line and folding end line. /// [DefaultValue(false)] [Description("Shows vertical lines between folding start line and folding end line.")] public bool ShowFoldingLines { get { return showFoldingLines; } set { showFoldingLines = value; Invalidate(); } } /// /// Color of line numbers. /// [DefaultValue(typeof(Color), "Teal")] [Description("Color of line numbers.")] public Color LineNumberColor { get { return lineNumberColor; } set { lineNumberColor = value; Invalidate(); } } /// /// Start value of first line number. /// [DefaultValue(typeof(uint), "1")] [Description("Start value of first line number.")] public uint LineNumberStartValue { get { return lineNumberStartValue; } set { lineNumberStartValue = value; needRecalc = true; Invalidate(); } } /// /// Background color of indent area /// [DefaultValue(typeof(Color), "White")] [Description("Background color of indent area")] public Color IndentBackColor { get { return indentBackColor; } set { indentBackColor = value; Invalidate(); } } /// /// Background color of padding area /// [DefaultValue(typeof(Color), "Transparent")] [Description("Background color of padding area")] public Color PaddingBackColor { get { return paddingBackColor; } set { paddingBackColor = value; Invalidate(); } } /// /// Color of disabled component /// [DefaultValue(typeof(Color), "100;180;180;180")] [Description("Color of disabled component")] public Color DisabledColor { get; set; } /// /// Color of caret /// [DefaultValue(typeof(Color), "Black")] [Description("Color of caret.")] public Color CaretColor { get; set; } /// /// Color of service lines (folding lines, borders of blocks etc.) /// [DefaultValue(typeof(Color), "Silver")] [Description("Color of service lines (folding lines, borders of blocks etc.)")] public Color ServiceLinesColor { get { return serviceLinesColor; } set { serviceLinesColor = value; Invalidate(); } } /// /// Padings of text area /// [Browsable(true)] [Description("Paddings of text area.")] public Padding Paddings { get; set; } //hide parent padding [Browsable(false)] public new Padding Padding { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } //hide RTL [Browsable(false)] public new bool RightToLeft { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } /// /// Color of folding area indicator /// [DefaultValue(typeof(Color), "Green")] [Description("Color of folding area indicator.")] public Color FoldingIndicatorColor { get { return foldingIndicatorColor; } set { foldingIndicatorColor = value; Invalidate(); } } /// /// Enables folding indicator (left vertical line between folding bounds) /// [DefaultValue(true)] [Description("Enables folding indicator (left vertical line between folding bounds)")] public bool HighlightFoldingIndicator { get { return highlightFoldingIndicator; } set { highlightFoldingIndicator = value; Invalidate(); } } /// /// Left indent in pixels /// [Browsable(false)] [Description("Left indent in pixels")] public int LeftIndent { get; private set; } /// /// Left padding in pixels /// [DefaultValue(0)] [Description("Width of left service area (in pixels)")] public int LeftPadding { get { return leftPadding; } set { leftPadding = value; Invalidate(); } } /// /// This property draws vertical line after defined char position. /// Set to 0 for disable drawing of vertical line. /// [DefaultValue(0)] [Description( "This property draws vertical line after defined char position. Set to 0 for disable drawing of vertical line." )] public int PreferredLineWidth { get { return preferredLineWidth; } set { preferredLineWidth = value; Invalidate(); } } /// /// Styles /// Maximum style count is 16 /// [Browsable(false)] public Style[] Styles { get { return lines.Styles; } } /// /// Default text style /// This style is using when no one other TextStyle is not defined in Char.style /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public TextStyle DefaultStyle { get { return lines.DefaultStyle; } set { lines.DefaultStyle = value; } } /// /// Style for rendering Selection area /// [Browsable(false)] public SelectionStyle SelectionStyle { get; set; } /// /// Style for folded block rendering /// [Browsable(false)] public TextStyle FoldedBlockStyle { get; set; } /// /// Style for brackets highlighting /// [Browsable(false)] public MarkerStyle BracketsStyle { get; set; } /// /// Style for alternative brackets highlighting /// [Browsable(false)] public MarkerStyle BracketsStyle2 { get; set; } /// /// Opening bracket for brackets highlighting. /// Set to '\x0' for disable brackets highlighting. /// [DefaultValue('\x0')] [Description("Opening bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")] public char LeftBracket { get; set; } /// /// Closing bracket for brackets highlighting. /// Set to '\x0' for disable brackets highlighting. /// [DefaultValue('\x0')] [Description("Closing bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")] public char RightBracket { get; set; } /// /// Alternative opening bracket for brackets highlighting. /// Set to '\x0' for disable brackets highlighting. /// [DefaultValue('\x0')] [Description( "Alternative opening bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")] public char LeftBracket2 { get; set; } /// /// Alternative closing bracket for brackets highlighting. /// Set to '\x0' for disable brackets highlighting. /// [DefaultValue('\x0')] [Description( "Alternative closing bracket for brackets highlighting. Set to '\\x0' for disable brackets highlighting.")] public char RightBracket2 { get; set; } /// /// Comment line prefix. /// [DefaultValue("//")] [Description("Comment line prefix.")] public string CommentPrefix { get; set; } /// /// This property specifies which part of the text will be highlighted as you type (by built-in highlighter). /// /// When a user enters text, a component of rebuilding the highlight (because the text is changed). /// This property specifies exactly which section of the text will be re-highlighted. /// This can be useful to highlight multi-line comments, for example. [DefaultValue(typeof(HighlightingRangeType), "ChangedRange")] [Description("This property specifies which part of the text will be highlighted as you type.")] public HighlightingRangeType HighlightingRangeType { get; set; } /// /// Is keyboard in replace mode (wide caret) ? /// [Browsable(false)] public bool IsReplaceMode { get { return isReplaceMode && Selection.IsEmpty && Selection.Start.iChar < lines[Selection.Start.iLine].Count; } set { isReplaceMode = value; } } /// /// Allows text rendering several styles same time. /// [Browsable(true)] [DefaultValue(false)] [Description("Allows text rendering several styles same time.")] public bool AllowSeveralTextStyleDrawing { get; set; } /// /// Allows AutoIndent. Inserts spaces before new line. /// [DefaultValue(true)] [Description("Allows auto indent. Inserts spaces before line chars.")] public bool AutoIndent { get; set; } /// /// Does autoindenting in existing lines. It works only if AutoIndent is True. /// [DefaultValue(true)] [Description("Does autoindenting in existing lines. It works only if AutoIndent is True.")] public bool AutoIndentExistingLines { get; set; } /// /// Minimal delay(ms) for delayed events (except TextChangedDelayed). /// [Browsable(true)] [DefaultValue(100)] [Description("Minimal delay(ms) for delayed events (except TextChangedDelayed).")] public int DelayedEventsInterval { get { return timer.Interval; } set { timer.Interval = value; } } /// /// Minimal delay(ms) for TextChangedDelayed event. /// [Browsable(true)] [DefaultValue(100)] [Description("Minimal delay(ms) for TextChangedDelayed event.")] public int DelayedTextChangedInterval { get { return timer2.Interval; } set { timer2.Interval = value; } } /// /// Language for highlighting by built-in highlighter. /// [Browsable(true)] [DefaultValue(typeof(Language), "Custom")] [Description("Language for highlighting by built-in highlighter.")] public Language Language { get { return language; } set { language = value; Invalidate(); } } /// /// Syntax Highlighter /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SyntaxHighlighter SyntaxHighlighter { get; set; } /// /// XML file with description of syntax highlighting. /// This property works only with Language == Language.Custom. /// [Browsable(true)] [DefaultValue(null)] [Editor(typeof(FileNameEditor), typeof(UITypeEditor))] [Description( "XML file with description of syntax highlighting. This property works only with Language == Language.Custom." )] public string DescriptionFile { get { return descriptionFile; } set { descriptionFile = value; Invalidate(); } } /// /// Position of left highlighted bracket. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Range LeftBracketPosition { get { return leftBracketPosition; } } /// /// Position of right highlighted bracket. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Range RightBracketPosition { get { return rightBracketPosition; } } /// /// Position of left highlighted alternative bracket. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Range LeftBracketPosition2 { get { return leftBracketPosition2; } } /// /// Position of right highlighted alternative bracket. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Range RightBracketPosition2 { get { return rightBracketPosition2; } } /// /// Start line index of current highlighted folding area. Return -1 if start of area is not found. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int StartFoldingLine { get { return startFoldingLine; } } /// /// End line index of current highlighted folding area. Return -1 if end of area is not found. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int EndFoldingLine { get { return endFoldingLine; } } /// /// TextSource /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public TextSource TextSource { get { return lines; } set { InitTextSource(value); } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool HasSourceTextBox { get { return SourceTextBox != null; } } /// /// The source of the text. /// Allows to get text from other FastColoredTextBox. /// [Browsable(true)] [DefaultValue(null)] [Description("Allows to get text from other FastColoredTextBox.")] //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public FastColoredTextBox SourceTextBox { get { return sourceTextBox; } set { if (value == sourceTextBox) return; sourceTextBox = value; if (sourceTextBox == null) { InitTextSource(CreateTextSource()); lines.InsertLine(0, TextSource.CreateLine()); IsChanged = false; } else { InitTextSource(SourceTextBox.TextSource); isChanged = false; } Invalidate(); } } Range visibleRange = null; /// /// Returns current visible range of text /// [Browsable(false)] public Range VisibleRange { get { if (visibleRange != null) return visibleRange; return GetRange( PointToPlace(new Point(LeftIndent, 0)), PointToPlace(new Point(ClientSize.Width, ClientSize.Height)) ); } } /// /// Current selection range /// [Browsable(false)] public Range Selection { get { return selection; } set { selection.BeginUpdate(); selection.Start = value.Start; selection.End = value.End; selection.EndUpdate(); Invalidate(); } } /// /// Background color. /// It is used if BackBrush is null. /// [DefaultValue(typeof(Color), "White")] [Description("Background color.")] public override Color BackColor { get { return base.BackColor; } set { base.BackColor = value; } } /// /// Background brush. /// If Null then BackColor is used. /// [Browsable(false)] public Brush BackBrush { get { return backBrush; } set { backBrush = value; Invalidate(); } } [Browsable(true)] [DefaultValue(true)] [Description("Scollbars visibility.")] public bool ShowScrollBars { get { return scrollBars; } set { if (value == scrollBars) return; scrollBars = value; needRecalc = true; Invalidate(); } } /// /// Multiline /// [Browsable(true)] [DefaultValue(true)] [Description("Multiline mode.")] public bool Multiline { get { return multiline; } set { if (multiline == value) return; multiline = value; needRecalc = true; if (multiline) { base.AutoScroll = true; ShowScrollBars = true; } else { base.AutoScroll = false; ShowScrollBars = false; if (lines.Count > 1) lines.RemoveLine(1, lines.Count - 1); lines.Manager.ClearHistory(); } Invalidate(); } } /// /// WordWrap. /// [Browsable(true)] [DefaultValue(false)] [Description("WordWrap.")] public bool WordWrap { get { return wordWrap; } set { if (wordWrap == value) return; wordWrap = value; if (wordWrap) Selection.ColumnSelectionMode = false; RecalcWordWrap(0, LinesCount - 1); Invalidate(); } } /// /// WordWrap mode. /// [Browsable(true)] [DefaultValue(typeof(WordWrapMode), "WordWrapControlWidth")] [Description("WordWrap mode.")] public WordWrapMode WordWrapMode { get { return wordWrapMode; } set { if (wordWrapMode == value) return; wordWrapMode = value; RecalcWordWrap(0, LinesCount - 1); Invalidate(); } } /// /// Count of lines with wordwrap effect /// [Browsable(false)] public int WordWrapLinesCount { get { if (needRecalc) Recalc(); return wordWrapLinesCount; } } [Browsable(false)] public FindForm findForm { get; private set; } [Browsable(false)] public ReplaceForm replaceForm { get; private set; } /// /// Do not change this property /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override bool AutoScroll { get { return base.AutoScroll; } set {; } } /// /// Count of lines /// [Browsable(false)] public int LinesCount { get { return lines.Count; } } /// /// Gets or sets char and styleId for given place /// This property does not fire OnTextChanged event /// public Char this[Place place] { get { return lines[place.iLine][place.iChar]; } set { lines[place.iLine][place.iChar] = value; } } /// /// Gets Line /// public Line this[int iLine] { get { return lines[iLine]; } } /// /// Text of control /// [Browsable(true)] [Localizable(true)] [Editor( "System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" , typeof(UITypeEditor))] [SettingsBindable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] [Description("Text of the control.")] [Bindable(true)] public override string Text { get { var sel = new Range(this); sel.SelectAll(); return sel.Text; } set { SetAsCurrentTB(); Selection.ColumnSelectionMode = false; Selection.BeginUpdate(); try { Selection.SelectAll(); InsertText(value); GoHome(); } finally { Selection.EndUpdate(); } } } /// /// Text lines /// [Browsable(false)] public IList Lines { get { return lines.Lines; } } /// /// Gets colored text as HTML /// /// For more flexibility you can use ExportToHTML class also [Browsable(false)] public string Html { get { var exporter = new ExportToHTML(); exporter.UseNbsp = false; exporter.UseStyleTag = false; exporter.UseBr = false; return "
" + exporter.GetHtml(this) + "
"; } } /// /// Text of current selection /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string SelectedText { get { return Selection.Text; } set { InsertText(value); } } /// /// Start position of selection /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionStart { get { return Math.Min(PlaceToPosition(Selection.Start), PlaceToPosition(Selection.End)); } set { Selection.Start = PositionToPlace(value); } } /// /// Length of selected text /// [Browsable(false)] [DefaultValue(0)] public int SelectionLength { get { return Math.Abs(PlaceToPosition(Selection.Start) - PlaceToPosition(Selection.End)); } set { if (value > 0) Selection.End = PositionToPlace(SelectionStart + value); } } /// /// Font /// /// Use only monospaced font [DefaultValue(typeof(Font), "Courier New, 9.75")] public override Font Font { get { return base.Font; } set { base.Font = value; // 注释掉强制等宽字体检查 //SizeF sizeM = GetCharSize(base.Font, 'M'); //SizeF sizeDot = GetCharSize(base.Font, '.'); //if (sizeM != sizeDot) // base.Font = new Font("Courier New", base.Font.SizeInPoints, FontStyle.Regular, GraphicsUnit.Point); // 保持原有的字符宽度计算,但使用传入的字体 SizeF size = GetCharSize(base.Font, '.'); CharWidth = (int)Math.Round(size.Width * 1f) - 1; CharHeight = lineInterval + (int)Math.Round(size.Height * 1f) - 1; // NeedRecalc(); Invalidate(); } } private new Size AutoScrollMinSize { set { if (scrollBars) { if (!base.AutoScroll) base.AutoScroll = true; Size newSize = value; if (WordWrap) { int maxWidth = GetMaxLineWordWrapedWidth(); newSize = new Size(Math.Min(newSize.Width, maxWidth), newSize.Height); } base.AutoScrollMinSize = newSize; } else { if (base.AutoScroll) base.AutoScroll = false; base.AutoScrollMinSize = new Size(0, 0); VerticalScroll.Visible = false; HorizontalScroll.Visible = false; HorizontalScroll.Maximum = value.Width; VerticalScroll.Maximum = value.Height; } } get { if (scrollBars) return base.AutoScrollMinSize; else return new Size(HorizontalScroll.Maximum, VerticalScroll.Maximum); } } /// /// Indicates that IME is allowed (for CJK language entering) /// [Browsable(false)] public bool ImeAllowed { get { return ImeMode != ImeMode.Disable && ImeMode != ImeMode.Off && ImeMode != ImeMode.NoControl; } } /// /// Is undo enabled? /// [Browsable(false)] public bool UndoEnabled { get { return lines.Manager.UndoEnabled; } } /// /// Is redo enabled? /// [Browsable(false)] public bool RedoEnabled { get { return lines.Manager.RedoEnabled; } } private int LeftIndentLine { get { return LeftIndent - minLeftIndent / 2 - 3; } } /// /// Range of all text /// [Browsable(false)] public Range Range { get { return new Range(this, new Place(0, 0), new Place(lines[lines.Count - 1].Count, lines.Count - 1)); } } /// /// Occurs when VisibleRange is changed /// public virtual void OnVisibleRangeChanged() { needRecalcFoldingLines = true; needRiseVisibleRangeChangedDelayed = true; ResetTimer(timer); if (VisibleRangeChanged != null) VisibleRangeChanged(this, new EventArgs()); } /// /// Invalidates the entire surface of the control and causes the control to be redrawn. /// This method is thread safe and does not require Invoke. /// public new void Invalidate() { if (InvokeRequired) BeginInvoke(new MethodInvoker(Invalidate)); else base.Invalidate(); } protected virtual void OnCharSizeChanged() { VerticalScroll.SmallChange = charHeight; VerticalScroll.LargeChange = 10 * charHeight; HorizontalScroll.SmallChange = CharWidth; } /// /// TextChanged event. /// It occurs after insert, delete, clear, undo and redo operations. /// [Browsable(true)] [Description("It occurs after insert, delete, clear, undo and redo operations.")] public new event EventHandler TextChanged; /// /// Fake event for correct data binding /// [Browsable(false)] internal new event EventHandler BindingTextChanged; /// /// TextChanging event. /// It occurs before insert, delete, clear, undo and redo operations. /// [Browsable(true)] [Description("It occurs before insert, delete, clear, undo and redo operations.")] public event EventHandler TextChanging; /// /// SelectionChanged event. /// It occurs after changing of selection. /// [Browsable(true)] [Description("It occurs after changing of selection.")] public event EventHandler SelectionChanged; /// /// VisibleRangeChanged event. /// It occurs after changing of visible range. /// [Browsable(true)] [Description("It occurs after changing of visible range.")] public event EventHandler VisibleRangeChanged; /// /// TextChangedDelayed event. /// It occurs after insert, delete, clear, undo and redo operations. /// This event occurs with a delay relative to TextChanged, and fires only once. /// [Browsable(true)] [Description( "It occurs after insert, delete, clear, undo and redo operations. This event occurs with a delay relative to TextChanged, and fires only once." )] public event EventHandler TextChangedDelayed; /// /// SelectionChangedDelayed event. /// It occurs after changing of selection. /// This event occurs with a delay relative to SelectionChanged, and fires only once. /// [Browsable(true)] [Description( "It occurs after changing of selection. This event occurs with a delay relative to SelectionChanged, and fires only once." )] public event EventHandler SelectionChangedDelayed; /// /// VisibleRangeChangedDelayed event. /// It occurs after changing of visible range. /// This event occurs with a delay relative to VisibleRangeChanged, and fires only once. /// [Browsable(true)] [Description( "It occurs after changing of visible range. This event occurs with a delay relative to VisibleRangeChanged, and fires only once." )] public event EventHandler VisibleRangeChangedDelayed; /// /// It occurs when user click on VisualMarker. /// [Browsable(true)] [Description("It occurs when user click on VisualMarker.")] public event EventHandler VisualMarkerClick; /// /// It occurs when visible char is enetering (alphabetic, digit, punctuation, DEL, BACKSPACE) /// /// Set Handle to True for cancel key [Browsable(true)] [Description("It occurs when visible char is enetering (alphabetic, digit, punctuation, DEL, BACKSPACE).")] public event KeyPressEventHandler KeyPressing; /// /// It occurs when visible char is enetered (alphabetic, digit, punctuation, DEL, BACKSPACE) /// [Browsable(true)] [Description("It occurs when visible char is enetered (alphabetic, digit, punctuation, DEL, BACKSPACE).")] public event KeyPressEventHandler KeyPressed; /// /// It occurs when calculates AutoIndent for new line /// [Browsable(true)] [Description("It occurs when calculates AutoIndent for new line.")] public event EventHandler AutoIndentNeeded; /// /// It occurs when line background is painting /// [Browsable(true)] [Description("It occurs when line background is painting.")] public event EventHandler PaintLine; /// /// Occurs when line was inserted/added /// [Browsable(true)] [Description("Occurs when line was inserted/added.")] public event EventHandler LineInserted; /// /// Occurs when line was removed /// [Browsable(true)] [Description("Occurs when line was removed.")] public event EventHandler LineRemoved; /// /// Occurs when current highlighted folding area is changed. /// Current folding area see in StartFoldingLine and EndFoldingLine. /// /// [Browsable(true)] [Description("Occurs when current highlighted folding area is changed.")] public event EventHandler FoldingHighlightChanged; /// /// Occurs when undo/redo stack is changed /// /// [Browsable(true)] [Description("Occurs when undo/redo stack is changed.")] public event EventHandler UndoRedoStateChanged; private TextSource CreateTextSource() { return new TextSource(this); } private void SetAsCurrentTB() { TextSource.CurrentTB = this; } private void InitTextSource(TextSource ts) { if (lines != null) { ts.LineInserted -= ts_LineInserted; ts.LineRemoved -= ts_LineRemoved; ts.TextChanged -= ts_TextChanged; ts.RecalcNeeded -= ts_RecalcNeeded; ts.TextChanging -= ts_TextChanging; lines.Dispose(); } lineInfos.Clear(); lines = ts; if (ts != null) { ts.LineInserted += ts_LineInserted; ts.LineRemoved += ts_LineRemoved; ts.TextChanged += ts_TextChanged; ts.RecalcNeeded += ts_RecalcNeeded; ts.TextChanging += ts_TextChanging; while (lineInfos.Count < ts.Count) lineInfos.Add(new LineInfo(-1)); } isChanged = false; needRecalc = true; } private void ts_TextChanging(object sender, TextChangingEventArgs e) { if (TextSource.CurrentTB == this) { string text = e.InsertingText; OnTextChanging(ref text); e.InsertingText = text; } } private void ts_RecalcNeeded(object sender, TextSource.TextChangedEventArgs e) { if (e.iFromLine == e.iToLine && !WordWrap && lines.Count > minLinesForAccuracy) RecalcScrollByOneLine(e.iFromLine); else needRecalc = true; } /// /// Call this method if the recalc of the position of lines is needed. /// public void NeedRecalc() { needRecalc = true; } private void ts_TextChanged(object sender, TextSource.TextChangedEventArgs e) { if (e.iFromLine == e.iToLine && !WordWrap) RecalcScrollByOneLine(e.iFromLine); else needRecalc = true; Invalidate(); if (TextSource.CurrentTB == this) OnTextChanged(e.iFromLine, e.iToLine); } private void ts_LineRemoved(object sender, LineRemovedEventArgs e) { lineInfos.RemoveRange(e.Index, e.Count); OnLineRemoved(e.Index, e.Count, e.RemovedLineUniqueIds); } private void ts_LineInserted(object sender, LineInsertedEventArgs e) { VisibleState newState = VisibleState.Visible; if (e.Index >= 0 && e.Index < lineInfos.Count && lineInfos[e.Index].VisibleState == VisibleState.Hidden) newState = VisibleState.Hidden; var temp = new List(e.Count); for (int i = 0; i < e.Count; i++) temp.Add(new LineInfo(-1) { VisibleState = newState }); lineInfos.InsertRange(e.Index, temp); OnLineInserted(e.Index, e.Count); } /// /// Navigates forward (by Line.LastVisit property) /// public bool NavigateForward() { DateTime min = DateTime.Now; int iLine = -1; for (int i = 0; i < LinesCount; i++) if (lines.IsLineLoaded(i)) if (lines[i].LastVisit > lastNavigatedDateTime && lines[i].LastVisit < min) { min = lines[i].LastVisit; iLine = i; } if (iLine >= 0) { Navigate(iLine); return true; } else return false; } /// /// Navigates backward (by Line.LastVisit property) /// public bool NavigateBackward() { var max = new DateTime(); int iLine = -1; for (int i = 0; i < LinesCount; i++) if (lines.IsLineLoaded(i)) if (lines[i].LastVisit < lastNavigatedDateTime && lines[i].LastVisit > max) { max = lines[i].LastVisit; iLine = i; } if (iLine >= 0) { Navigate(iLine); return true; } else return false; } /// /// Navigates to defined line, without Line.LastVisit reseting /// public void Navigate(int iLine) { if (iLine >= LinesCount) return; lastNavigatedDateTime = lines[iLine].LastVisit; Selection.Start = new Place(0, iLine); DoSelectionVisible(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); m_hImc = ImmGetContext(Handle); } private void timer2_Tick(object sender, EventArgs e) { timer2.Enabled = false; if (needRiseTextChangedDelayed) { needRiseTextChangedDelayed = false; if (delayedTextChangedRange == null) return; delayedTextChangedRange = Range.GetIntersectionWith(delayedTextChangedRange); delayedTextChangedRange.Expand(); OnTextChangedDelayed(delayedTextChangedRange); delayedTextChangedRange = null; } } public void AddVisualMarker(VisualMarker marker) { visibleMarkers.Add(marker); } private void timer_Tick(object sender, EventArgs e) { timer.Enabled = false; if (needRiseSelectionChangedDelayed) { needRiseSelectionChangedDelayed = false; OnSelectionChangedDelayed(); } if (needRiseVisibleRangeChangedDelayed) { needRiseVisibleRangeChangedDelayed = false; OnVisibleRangeChangedDelayed(); } } public virtual void OnTextChangedDelayed(Range changedRange) { if (TextChangedDelayed != null) TextChangedDelayed(this, new TextChangedEventArgs(changedRange)); } public virtual void OnSelectionChangedDelayed() { RecalcScrollByOneLine(Selection.Start.iLine); //highlight brackets ClearBracketsPositions(); if (LeftBracket != '\x0' && RightBracket != '\x0') HighlightBrackets(LeftBracket, RightBracket, ref leftBracketPosition, ref rightBracketPosition); if (LeftBracket2 != '\x0' && RightBracket2 != '\x0') HighlightBrackets(LeftBracket2, RightBracket2, ref leftBracketPosition2, ref rightBracketPosition2); //remember last visit time if (Selection.IsEmpty && Selection.Start.iLine < LinesCount) { if (lastNavigatedDateTime != lines[Selection.Start.iLine].LastVisit) { lines[Selection.Start.iLine].LastVisit = DateTime.Now; lastNavigatedDateTime = lines[Selection.Start.iLine].LastVisit; } } if (SelectionChangedDelayed != null) SelectionChangedDelayed(this, new EventArgs()); } public virtual void OnVisibleRangeChangedDelayed() { if (VisibleRangeChangedDelayed != null) VisibleRangeChangedDelayed(this, new EventArgs()); } private void ResetTimer(Timer timer) { timer.Stop(); if (IsHandleCreated) timer.Start(); } /// /// Adds new style /// /// Layer index of this style public int AddStyle(Style style) { if (style == null) return -1; int i = GetStyleIndex(style); if (i >= 0) return i; for (i = Styles.Length - 1; i >= 0; i--) if (Styles[i] != null) break; i++; if (i >= Styles.Length) throw new Exception("Maximum count of Styles is exceeded"); Styles[i] = style; return i; } /// /// Shows find dialog /// public void ShowFindDialog() { ShowFindDialog(null); } /// /// Shows find dialog /// public void ShowFindDialog(string findText) { if (findForm == null) findForm = new FindForm(this); if (findText != null) findForm.tbFind.Text = findText; else if (!Selection.IsEmpty && Selection.Start.iLine == Selection.End.iLine) findForm.tbFind.Text = Selection.Text; findForm.tbFind.SelectAll(); findForm.Show(); } /// /// Shows replace dialog /// public void ShowReplaceDialog() { ShowReplaceDialog(null); } /// /// Shows replace dialog /// public void ShowReplaceDialog(string findText) { if (ReadOnly) return; if (replaceForm == null) replaceForm = new ReplaceForm(this); if (findText != null) replaceForm.tbFind.Text = findText; else if (!Selection.IsEmpty && Selection.Start.iLine == Selection.End.iLine) replaceForm.tbFind.Text = Selection.Text; replaceForm.tbFind.SelectAll(); replaceForm.Show(); } /// /// Gets length of given line /// /// Line index /// Length of line public int GetLineLength(int iLine) { if (iLine < 0 || iLine >= lines.Count) throw new ArgumentOutOfRangeException("Line index out of range"); return lines[iLine].Count; } /// /// Get range of line /// /// Line index public Range GetLine(int iLine) { if (iLine < 0 || iLine >= lines.Count) throw new ArgumentOutOfRangeException("Line index out of range"); var sel = new Range(this); sel.Start = new Place(0, iLine); sel.End = new Place(lines[iLine].Count, iLine); return sel; } /// /// Copy selected text into Clipboard /// public void Copy() { if (Selection.IsEmpty) Selection.Expand(); if (!Selection.IsEmpty) { var exp = new ExportToHTML(); exp.UseBr = false; exp.UseNbsp = false; exp.UseStyleTag = true; string html = "
" + exp.GetHtml(Selection.Clone()) + "
"; var data = new DataObject(); data.SetData(DataFormats.UnicodeText, true, Selection.Text); data.SetData(DataFormats.Html, PrepareHtmlForClipboard(html)); // var thread = new Thread(() => Clipboard.SetDataObject(data, true)); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); } } public static MemoryStream PrepareHtmlForClipboard(string html) { Encoding enc = Encoding.UTF8; string begin = "Version:0.9\r\nStartHTML:{0:000000}\r\nEndHTML:{1:000000}" + "\r\nStartFragment:{2:000000}\r\nEndFragment:{3:000000}\r\n"; string html_begin = "\r\n\r\n" + "\r\n" + "HTML clipboard\r\n\r\n\r\n" + ""; 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.IsEmpty) { Copy(); ClearSelected(); } else { Copy(); //remove current line if (Selection.Start.iLine >= 0 && Selection.Start.iLine < LinesCount) { var iLine = Selection.Start.iLine; RemoveLines(new List() { iLine }); Selection.Start = new Place(0, Math.Max(0, Math.Min(iLine, LinesCount - 1))); } } } /// /// 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.TrimStart()); } /// /// 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); DoCaretVisible(); //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, bool jumpToCaret = true) { if (text == null) return; lines.Manager.BeginAutoUndoCommands(); try { if (!Selection.IsEmpty) lines.Manager.ExecuteCommand(new ClearSelectedCommand(TextSource)); //insert virtual spaces if (Selection.IsEmpty && Selection.Start.iChar > GetLineLength(Selection.Start.iLine) && VirtualSpace) InsertVirtualSpaces(); lines.Manager.ExecuteCommand(new InsertTextCommand(TextSource, text)); if (updating <= 0 && jumpToCaret) DoCaretVisible(); } finally { lines.Manager.EndAutoUndoCommands(); } // Invalidate(); } /// /// Insert text into current selection position (with predefined style) /// /// public void InsertText(string text, Style style, bool jumpToCaret = true) { if (text == null) return; //remember last caret position Place last = Selection.Start; //insert text InsertText(text, jumpToCaret); //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; Selection.ColumnSelectionMode = false; 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(); // if (se.ScrollOrientation == ScrollOrientation.VerticalScroll) VerticalScroll.Value = se.NewValue; if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll) HorizontalScroll.Value = se.NewValue; UpdateScrollbars(); Invalidate(); } private void InsertChar(char c) { lines.Manager.BeginAutoUndoCommands(); try { if (!Selection.IsEmpty) lines.Manager.ExecuteCommand(new ClearSelectedCommand(TextSource)); //insert virtual spaces if (Selection.IsEmpty && Selection.Start.iChar > GetLineLength(Selection.Start.iLine) && VirtualSpace) InsertVirtualSpaces(); //insert char lines.Manager.ExecuteCommand(new InsertCharCommand(TextSource, c)); } finally { lines.Manager.EndAutoUndoCommands(); } Invalidate(); } private void InsertVirtualSpaces() { var lineLength = GetLineLength(Selection.Start.iLine); var count = Selection.Start.iChar - lineLength; Selection.BeginUpdate(); try { Selection.Start = new Place(lineLength, Selection.Start.iLine); lines.Manager.ExecuteCommand(new InsertTextCommand(TextSource, new string(' ', count))); } finally { Selection.EndUpdate(); } } /// /// Deletes selected chars /// public void ClearSelected() { if (!Selection.IsEmpty) { 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 wordWrapLinesCount = 0; maxLineLength = RecalcMaxLineLength(); //adjust AutoScrollMinSize int minWidth; CalcMinAutosizeWidth(out minWidth, ref maxLineLength); AutoScrollMinSize = new Size(minWidth, wordWrapLinesCount * CharHeight + Paddings.Top + Paddings.Bottom); #if debug sw.Stop(); Debug.WriteLine("Recalc: " + sw.ElapsedMilliseconds); #endif } void CalcMinAutosizeWidth(out int minWidth, ref int maxLineLength) { //adjust AutoScrollMinSize minWidth = LeftIndent + (maxLineLength) * CharWidth + 2 + Paddings.Left + Paddings.Right; if (wordWrap) switch (WordWrapMode) { case WordWrapMode.WordWrapControlWidth: case WordWrapMode.CharWrapControlWidth: maxLineLength = Math.Min(maxLineLength, (ClientSize.Width - LeftIndent - Paddings.Left - Paddings.Right) / CharWidth); minWidth = 0; break; case WordWrapMode.WordWrapPreferredWidth: case WordWrapMode.CharWrapPreferredWidth: maxLineLength = Math.Min(maxLineLength, PreferredLineWidth); minWidth = LeftIndent + PreferredLineWidth * CharWidth + 2 + Paddings.Left + Paddings.Right; break; } } private void RecalcScrollByOneLine(int iLine) { if (iLine >= lines.Count) return; int maxLineLength = lines[iLine].Count; if (this.maxLineLength < maxLineLength && !WordWrap) this.maxLineLength = maxLineLength; int minWidth; CalcMinAutosizeWidth(out minWidth, ref maxLineLength); 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 = Paddings.Top; 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 + Paddings.Left + Paddings.Right; } 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 - Paddings.Left - Paddings.Right) / CharWidth; break; case WordWrapMode.CharWrapControlWidth: maxCharsPerLine = (ClientSize.Width - LeftIndent - Paddings.Left - Paddings.Right) / 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; // v = Math.Max(0, v); h = Math.Max(0, h); // try { if (VerticalScroll.Visible || !ShowScrollBars) VerticalScroll.Value = v; if (HorizontalScroll.Visible || !ShowScrollBars) HorizontalScroll.Value = h; } catch (ArgumentOutOfRangeException) { ; } UpdateScrollbars(); // if (oldV != VerticalScroll.Value) OnVisibleRangeChanged(); } /// /// Updates scrollbar position after Value changed /// public void UpdateScrollbars() { if (ShowScrollBars) { //some magic for update scrolls base.AutoScrollMinSize -= new Size(1, 0); base.AutoScrollMinSize += new Size(1, 0); } } /// /// 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), ClientSize.Height)));//Math.Abs(car2.Y-car.Y) + 2 * CharHeight Invalidate(); } /// /// Scroll control for display given range /// public void DoRangeVisible(Range range) { range = range.Clone(); range.Normalize(); range.End = new Place(range.End.iChar, Math.Min(range.End.iLine, range.Start.iLine + ClientSize.Height / CharHeight)); if (lineInfos[range.End.iLine].VisibleState != VisibleState.Visible) ExpandBlock(range.End.iLine); if (lineInfos[range.Start.iLine].VisibleState != VisibleState.Visible) ExpandBlock(range.Start.iLine); Recalc(); DoVisibleRectangle(new Rectangle(PlaceToPoint(new Place(0, range.Start.iLine)), new Size(2 * CharWidth, (1 + range.End.iLine - range.Start.iLine) * CharHeight))); 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; } private const Keys AltShift = Keys.Alt | Keys.Shift; protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (Focused) 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.F3: if (e.Modifiers == Keys.None) if (findForm == null || findForm.tbFind.Text == "") ShowFindDialog(); else findForm.FindNext(findForm.tbFind.Text); 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 || e.Modifiers == Keys.Shift) { if (OnKeyPressing('\b')) //KeyPress event processed key break; if (!Selection.IsEmpty) ClearSelected(); else InsertChar('\b'); OnKeyPressed('\b'); } else if (e.Modifiers == Keys.Control) { if (OnKeyPressing('\b')) //KeyPress event processed key break; if (!Selection.IsEmpty) ClearSelected(); Selection.GoWordLeft(true); ClearSelected(); OnKeyPressed('\b'); } break; case Keys.Insert: if (e.Modifiers == Keys.None) { if (!ReadOnly) isReplaceMode = !isReplaceMode; } else if (e.Modifiers == Keys.Control) { Copy(); } else if (e.Modifiers == Keys.Shift) { if (!ReadOnly) Paste(); } break; case Keys.Delete: if (ReadOnly) break; if (e.Modifiers == Keys.None) { if (OnKeyPressing((char)0xff)) //KeyPress event processed key break; if (!Selection.IsEmpty) ClearSelected(); else { //if line contains only spaces then delete line if (this[Selection.Start.iLine].StartSpacesCount == this[Selection.Start.iLine].Count) RemoveSpacesAfterCaret(); if (Selection.GoRightThroughFolded()) { int iLine = Selection.Start.iLine; InsertChar('\b'); //if removed \n then trim spaces if (iLine != Selection.Start.iLine && AutoIndent) if (Selection.Start.iChar > 0) RemoveSpacesAfterCaret(); } } OnKeyPressed((char)0xff); } else if (e.Modifiers == Keys.Control) { if (OnKeyPressing((char)0xff)) //KeyPress event processed key break; if (!Selection.IsEmpty) ClearSelected(); else { Selection.GoWordRight(true); ClearSelected(); } OnKeyPressed((char)0xff); } else if (e.Modifiers == Keys.Shift) { if (OnKeyPressing((char)0xff)) //KeyPress event processed key break; Cut(); 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.IsEmpty) ClearSelected(); //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); if (e.Modifiers == AltShift) { CheckAndChangeSelectionType(); if (Selection.ColumnSelectionMode) Selection.GoLeft_ColumnSelectionMode(); } 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); if (e.Modifiers == AltShift) { CheckAndChangeSelectionType(); if (Selection.ColumnSelectionMode) Selection.GoRight_ColumnSelectionMode(); } break; case Keys.Up: if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) { Selection.GoUp(e.Shift); ScrollLeft(); } if (e.Modifiers == AltShift) { CheckAndChangeSelectionType(); if (Selection.ColumnSelectionMode) Selection.GoUp_ColumnSelectionMode(); } if (e.Modifiers == Keys.Alt) { if (!ReadOnly && !Selection.ColumnSelectionMode) MoveSelectedLinesUp(); } break; case Keys.Down: if (e.Modifiers == Keys.None || e.Modifiers == Keys.Shift) { Selection.GoDown(e.Shift); ScrollLeft(); } else if (e.Modifiers == AltShift) { CheckAndChangeSelectionType(); if (Selection.ColumnSelectionMode) Selection.GoDown_ColumnSelectionMode(); } if (e.Modifiers == Keys.Alt) { if (!ReadOnly && !Selection.ColumnSelectionMode) MoveSelectedLinesDown(); } 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; case Keys.Alt: return; default: if ((e.Modifiers & Keys.Control) != 0) return; if ((e.Modifiers & Keys.Alt) != 0) { if ((Control.MouseButtons & MouseButtons.Left) != 0) CheckAndChangeSelectionType(); return; } if (e.KeyCode == Keys.ShiftKey) return; break; } e.Handled = true; DoCaretVisible(); Invalidate(); } /// /// Moves selected lines down /// public virtual void MoveSelectedLinesDown() { var prevSelection = Selection.Clone(); Selection.Expand(); var iLine = Selection.Start.iLine; if (Selection.End.iLine >= LinesCount - 1) { Selection = prevSelection; return; } var text = SelectedText; var temp = new List(); for (int i = Selection.Start.iLine; i <= Selection.End.iLine; i++) temp.Add(i); RemoveLines(temp); Selection.Start = new Place(GetLineLength(iLine), iLine); SelectedText = "\n" + text; Selection.Start = new Place(prevSelection.Start.iChar, prevSelection.Start.iLine + 1); Selection.End = new Place(prevSelection.End.iChar, prevSelection.End.iLine + 1); } /// /// Moves selected lines up /// public virtual void MoveSelectedLinesUp() { var prevSelection = Selection.Clone(); Selection.Expand(); var iLine = Selection.Start.iLine; if (iLine == 0) { Selection = prevSelection; return; } var text = SelectedText; var temp = new List(); for (int i = Selection.Start.iLine; i <= Selection.End.iLine; i++) temp.Add(i); RemoveLines(temp); Selection.Start = new Place(0, iLine - 1); SelectedText = text + "\n"; Selection.Start = new Place(prevSelection.Start.iChar, prevSelection.Start.iLine - 1); Selection.End = new Place(prevSelection.End.iChar, prevSelection.End.iLine - 1); } 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 void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); ProcessKeyPress(e.KeyChar); }*/ 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.iLine == Selection.End.iLine) { ClearSelected(); //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 (c == '\n' || AutoIndentExistingLines) DoAutoIndentIfNeed(); } DoCaretVisible(); Invalidate(); OnKeyPressed(sourceC); return true; } private void DoAutoIndentIfNeed() { if (Selection.ColumnSelectionMode) return; 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; } } } private void RemoveSpacesAfterCaret() { if (!Selection.IsEmpty) 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) { if (Selection.ColumnSelectionMode) return; 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(); DoCaretVisible(); Invalidate(); } /// /// Redo /// public void Redo() { lines.Manager.Redo(); DoCaretVisible(); 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); protected override void OnPaintBackground(PaintEventArgs e) { if (BackBrush == null) base.OnPaintBackground(e); else e.Graphics.FillRectangle(BackBrush, ClientRectangle); } /// /// Draw control /// protected override void OnPaint(PaintEventArgs e) { if (needRecalc) Recalc(); if (needRecalcFoldingLines) RecalcFoldingLines(); #if debug var sw = Stopwatch.StartNew(); #endif visibleMarkers.Clear(); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; //FCTBRenderingHints.SetGridFitTextHint(e.Graphics); // var servicePen = new Pen(ServiceLinesColor); Brush changedLineBrush = new SolidBrush(ChangedLineColor); Brush indentBrush = new SolidBrush(IndentBackColor); Brush paddingBrush = new SolidBrush(PaddingBackColor); Brush currentLineBrush = new SolidBrush(Color.FromArgb(CurrentLineColor.A == 255 ? 50 : CurrentLineColor.A, CurrentLineColor)); //draw padding area //top e.Graphics.FillRectangle(paddingBrush, 0, -VerticalScroll.Value, ClientSize.Width, Math.Max(0, Paddings.Top - 1)); //bottom var bottomPaddingStartY = wordWrapLinesCount * charHeight + Paddings.Top; e.Graphics.FillRectangle(paddingBrush, 0, bottomPaddingStartY - VerticalScroll.Value, ClientSize.Width, ClientSize.Height); //right var rightPaddingStartX = LeftIndent + maxLineLength * CharWidth + Paddings.Left + 1; e.Graphics.FillRectangle(paddingBrush, rightPaddingStartX - HorizontalScroll.Value, 0, ClientSize.Width, ClientSize.Height); //left e.Graphics.FillRectangle(paddingBrush, LeftIndentLine, 0, LeftIndent - LeftIndentLine - 1, ClientSize.Height); if (HorizontalScroll.Value <= Paddings.Left) e.Graphics.FillRectangle(paddingBrush, LeftIndent - HorizontalScroll.Value - 2, 0, Math.Max(0, Paddings.Left - 1), ClientSize.Height); var leftTextIndent = Math.Max(LeftIndent, LeftIndent + Paddings.Left - HorizontalScroll.Value); var textWidth = rightPaddingStartX - HorizontalScroll.Value - leftTextIndent; //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 + Paddings.Left + PreferredLineWidth * CharWidth - HorizontalScroll.Value + 1, 0), new Point(LeftIndent + Paddings.Left + PreferredLineWidth * CharWidth - HorizontalScroll.Value + 1, Height)); // int firstChar = (Math.Max(0, HorizontalScroll.Value - Paddings.Left)) / CharWidth; int lastChar = (HorizontalScroll.Value + ClientSize.Width) / CharWidth; //draw chars int startLine = YtoLineIndex(VerticalScroll.Value); int iLine; for (iLine = startLine; 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(leftTextIndent, y, textWidth, CharHeight * lineInfo.WordWrapStringsCount)); //draw current line background if (CurrentLineColor != Color.Transparent && iLine == Selection.Start.iLine) if (Selection.IsEmpty) e.Graphics.FillRectangle(currentLineBrush, new Rectangle(leftTextIndent, y, textWidth, 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) using (var lineNumberBrush = new SolidBrush(LineNumberColor)) 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, LeftIndent + Paddings.Left - HorizontalScroll.Value, y); } } var endLine = iLine - 1; //draw folding lines if (ShowFoldingLines) DrawFoldingLines(e, startLine, endLine); //draw column selection if (Selection.ColumnSelectionMode) if (SelectionStyle.BackgroundBrush is SolidBrush) { var color = ((SolidBrush)SelectionStyle.BackgroundBrush).Color; var p1 = PlaceToPoint(Selection.Start); var p2 = PlaceToPoint(Selection.End); using (var pen = new Pen(color)) e.Graphics.DrawRectangle(pen, Rectangle.FromLTRB(Math.Min(p1.X, p2.X) - 1, Math.Min(p1.Y, p2.Y), Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y) + CharHeight)); } //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 || IsDragDrop) && car.X >= LeftIndent && CaretVisible) { int carWidth = IsReplaceMode ? CharWidth : 1; CreateCaret(Handle, 0, carWidth, CharHeight + 1); SetCaretPos(car.X, car.Y); ShowCaret(Handle); //using (Pen pen = new Pen(CaretColor)) // e.Graphics.DrawLine(pen, car.X, car.Y, car.X, car.Y + CharHeight); } else HideCaret(Handle); //draw disabled mask if (!Enabled) using (var brush = new SolidBrush(DisabledColor)) e.Graphics.FillRectangle(brush, ClientRectangle); //dispose resources servicePen.Dispose(); changedLineBrush.Dispose(); indentBrush.Dispose(); currentLineBrush.Dispose(); paddingBrush.Dispose(); // #if debug Debug.WriteLine("OnPaint: "+ sw.ElapsedMilliseconds); #endif // base.OnPaint(e); } protected virtual void DrawFoldingLines(PaintEventArgs e, int startLine, int endLine) { e.Graphics.SmoothingMode = SmoothingMode.None; using (var pen = new Pen(Color.FromArgb(200, ServiceLinesColor)) { DashStyle = DashStyle.Dot }) foreach (var iLine in foldingPairs) if (iLine.Key < endLine && iLine.Value > startLine) { var line = lines[iLine.Key]; var y = lineInfos[iLine.Key].startY - VerticalScroll.Value + CharHeight; y += y % 2; int y2; if (iLine.Value >= LinesCount) y2 = lineInfos[LinesCount - 1].startY + CharHeight - VerticalScroll.Value; else if (lineInfos[iLine.Value].VisibleState == VisibleState.Visible) { var d = 0; var spaceCount = line.StartSpacesCount; if (lines[iLine.Value].Count <= spaceCount || lines[iLine.Value][spaceCount].c == ' ') d = CharHeight; y2 = lineInfos[iLine.Value].startY - VerticalScroll.Value + d; } else continue; var x = LeftIndent + Paddings.Left + line.StartSpacesCount * CharWidth - HorizontalScroll.Value; if (x >= LeftIndent + Paddings.Left) e.Graphics.DrawLine(pen, x, y >= 0 ? y : 0, x, y2 < ClientSize.Height ? y2 : ClientSize.Height); } } /// /// Color of selected area /// [DefaultValue(typeof(Color), "Blue")] [Description("Color of selected area.")] public virtual Color SelectionColor { get { return selectionColor; } set { selectionColor = value; if (selectionColor.A == 255) selectionColor = Color.FromArgb(50, selectionColor); SelectionStyle = new SelectionStyle(new SolidBrush(selectionColor)); Invalidate(); } } private void DrawLineChars(PaintEventArgs e, int firstChar, int lastChar, int iLine, int iWordWrapLine, int x, int y) { Line line = lines[iLine]; LineInfo lineInfo = lineInfos[iLine]; int from = lineInfo.GetWordWrapStringStartPosition(iWordWrapLine); int to = lineInfo.GetWordWrapStringFinishPosition(iWordWrapLine, line); int startX = x; 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.IsEmpty && 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 OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); isLineSelect = 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; isLineSelect = e.Location.X < LeftIndentLine; // CheckAndChangeSelectionType(); // //click on text Place oldEnd = Selection.End; Selection.BeginUpdate(); if (isLineSelect) { //select whole line var iLine = PointToPlaceSimple(e.Location).iLine; lineSelectFrom = iLine; Selection.Start = new Place(0, iLine); Selection.End = new Place(GetLineLength(iLine), iLine); } else { if (Selection.ColumnSelectionMode) { Selection.Start = PointToPlaceSimple(e.Location); Selection.ColumnSelectionMode = true; } else { if (VirtualSpace) Selection.Start = PointToPlaceSimple(e.Location); else Selection.Start = PointToPlace(e.Location); } if ((lastModifiers & Keys.Shift) != 0) Selection.End = oldEnd; } Selection.EndUpdate(); Invalidate(); return; } } private void CheckAndChangeSelectionType() { //change selection type to ColumnSelectionMode if ((Control.ModifierKeys & Keys.Alt) != 0 && !WordWrap) { Selection.ColumnSelectionMode = true; } else //change selection type to Range { Selection.ColumnSelectionMode = false; } } protected override void OnMouseWheel(MouseEventArgs e) { Invalidate(); base.OnMouseWheel(e); OnVisibleRangeChanged(); } Cursor defaultCursor; public override Cursor Cursor { get { return base.Cursor; } set { defaultCursor = value; base.Cursor = value; } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (e.Button == MouseButtons.Left && mouseIsDrag) { Place place; if (Selection.ColumnSelectionMode || VirtualSpace) place = PointToPlaceSimple(e.Location); else place = PointToPlace(e.Location); if (isLineSelect) { Selection.BeginUpdate(); var iLine = place.iLine; if (iLine < lineSelectFrom) { Selection.Start = new Place(0, iLine); Selection.End = new Place(GetLineLength(lineSelectFrom), lineSelectFrom); } else { Selection.Start = new Place(GetLineLength(iLine), iLine); Selection.End = new Place(0, lineSelectFrom); } Selection.EndUpdate(); DoCaretVisible(); HorizontalScroll.Value = 0; UpdateScrollbars(); Invalidate(); } else if (place != Selection.Start) { Place oldEnd = Selection.End; Selection.BeginUpdate(); if (Selection.ColumnSelectionMode) { Selection.Start = place; Selection.ColumnSelectionMode = true; } else Selection.Start = place; Selection.End = oldEnd; Selection.EndUpdate(); DoCaretVisible(); Invalidate(); return; } } VisualMarker marker = FindVisualMarkerForPoint(e.Location); if (marker != null) base.Cursor = marker.Cursor; else { if (e.Location.X < LeftIndentLine || isLineSelect) base.Cursor = Cursors.Arrow; else base.Cursor = defaultCursor; } } 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 - Paddings.Left, 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 Debug.WriteLine("PointToPlace: " + sw.ElapsedMilliseconds); #endif return new Place(x, iLine); } private Place PointToPlaceSimple(Point point) { point.Offset(HorizontalScroll.Value, VerticalScroll.Value); point.Offset(-LeftIndent - Paddings.Left, 0); int iLine = YtoLineIndex(point.Y); var x = (int)Math.Round((float)point.X / CharWidth); if (x < 0) x = 0; 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; if (args.Cancel) text = string.Empty; } } 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 Debug.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 Debug.WriteLine("OnSelectionChanged: "+ sw.ElapsedMilliseconds); #endif } //find folding markers for highlighting private void HighlightFoldings() { if (LinesCount == 0) return; // int prevStartFoldingLine = startFoldingLine; int prevEndFoldingLine = endFoldingLine; // startFoldingLine = -1; endFoldingLine = -1; // string marker = null; int counter = 0; for (int i = Selection.Start.iLine; i >= Math.Max(Selection.Start.iLine - maxLinesForFolding, 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, maxLinesForFolding); 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 /// Point of 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 absolute char position from char position /// public Point PositionToPoint(int pos) { return PlaceToPoint(PositionToPlace(pos)); } /// /// 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 + Paddings.Left + 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 /// /// Start line 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) { return FindEndOfFoldingBlock(iStartLine, int.MaxValue); } protected virtual int FindEndOfFoldingBlock(int iStartLine, int maxLines) { //find end of block int counter = 0; int i; string marker = lines[iStartLine].FoldingStartMarker; Stack stack = new Stack(); switch (FindEndOfFoldingBlockStrategy) { case FindEndOfFoldingBlockStrategy.Strategy1: for (i = iStartLine /*+1*/; i < LinesCount; i++) { if (lines.LineHasFoldingStartMarker(i)) stack.Push(lines[i].FoldingStartMarker); if (lines.LineHasFoldingEndMarker(i)) { var m = lines[i].FoldingEndMarker; while (stack.Count > 0 && stack.Pop() != m) ; if (stack.Count == 0) return i; } maxLines--; if (maxLines < 0) return i; } break; case FindEndOfFoldingBlockStrategy.Strategy2: for (i = iStartLine /*+1*/; i < LinesCount; i++) { if (lines.LineHasFoldingEndMarker(i)) { var m = lines[i].FoldingEndMarker; while (stack.Count > 0 && stack.Pop() != m) ; if (stack.Count == 0) return i; } if (lines.LineHasFoldingStartMarker(i)) stack.Push(lines[i].FoldingStartMarker); maxLines--; if (maxLines < 0) return i; } break; } //return -1; return LinesCount - 1; } /// /// Start foilding marker for the line /// public string GetLineFoldingStartMarker(int iLine) { if (lines.LineHasFoldingStartMarker(iLine)) return lines[iLine].FoldingStartMarker; return null; } /// /// End foilding marker for the line /// public string GetLineFoldingEndMarker(int iLine) { if (lines.LineHasFoldingEndMarker(iLine)) return lines[iLine].FoldingEndMarker; return null; } protected Dictionary foldingPairs = new Dictionary(); protected virtual void RecalcFoldingLines() { if (!needRecalcFoldingLines) return; needRecalcFoldingLines = false; if (!ShowFoldingLines) return; foldingPairs.Clear(); // var range = VisibleRange; var startLine = Math.Max(range.Start.iLine - maxLinesForFolding, 0); var endLine = Math.Min(range.End.iLine + maxLinesForFolding, Math.Max(range.End.iLine, LinesCount - 1)); var stack = new Stack(); for (int i = startLine; i <= endLine; i++) { bool hasStartMarker = lines.LineHasFoldingStartMarker(i); bool hasEndMarker = lines.LineHasFoldingEndMarker(i); if (hasEndMarker && hasStartMarker) continue; if (hasStartMarker) { stack.Push(i); } if (hasEndMarker) { var m = lines[i].FoldingEndMarker; while (stack.Count > 0) { var iStartLine = stack.Pop(); foldingPairs[iStartLine] = i; if (m == lines[iStartLine].FoldingStartMarker) break; } } } while (stack.Count > 0) foldingPairs[stack.Pop()] = endLine + 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.IsEmpty) 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.IsEmpty) 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() { if (Selection.ColumnSelectionMode) return; 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.IsEmpty) return; if (LinesCount == 0) 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 Debug.WriteLine("OnSyntaxHighlight: "+ sw.ElapsedMilliseconds); #endif } private void InitializeComponent() { SuspendLayout(); // // FastColoredTextBox // Name = "FastColoredTextBox"; ResumeLayout(false); } /// /// Prints range of text /// public virtual void Print(Range range, PrintDialogSettings settings) { //prepare export with wordwrapping var exporter = new ExportToHTML(); exporter.UseBr = true; exporter.UseForwardNbsp = true; exporter.UseNbsp = true; exporter.UseStyleTag = false; exporter.IncludeLineNumbers = settings.IncludeLineNumbers; if (range == null) range = this.Range; if (range.Text == string.Empty) return; //change visible range visibleRange = range; try { //call handlers for VisibleRange if (VisibleRangeChanged != null) VisibleRangeChanged(this, new EventArgs()); if (VisibleRangeChangedDelayed != null) VisibleRangeChangedDelayed(this, new EventArgs()); } finally { //restore visible range visibleRange = null; } //generate HTML var HTML = exporter.GetHtml(range); HTML = "" + PrepareHtmlText(settings.Title) + "" + HTML + SelectHTMLRangeScript(); var tempFile = Path.GetTempPath() + "fctb.html"; File.WriteAllText(tempFile, HTML); //clear wb page setup settings SetPageSetupSettings(settings); //create wb var wb = new WebBrowser(); wb.Tag = settings; wb.Visible = false; wb.Location = new Point(-1000, -1000); wb.Parent = this; wb.StatusTextChanged += new EventHandler(wb_StatusTextChanged); wb.Navigate(tempFile); } protected virtual string PrepareHtmlText(string s) { return s.Replace("<", "<").Replace(">", ">").Replace("&", "&"); } void wb_StatusTextChanged(object sender, EventArgs e) { var wb = sender as WebBrowser; if (wb.StatusText.Contains("#print")) { var settings = wb.Tag as PrintDialogSettings; try { //show print dialog if (settings.ShowPrintPreviewDialog) wb.ShowPrintPreviewDialog(); else { if (settings.ShowPageSetupDialog) wb.ShowPageSetupDialog(); if (settings.ShowPrintDialog) wb.ShowPrintDialog(); else wb.Print(); } } finally { //destroy webbrowser wb.Parent = null; wb.Dispose(); } } } /// /// 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 string SelectHTMLRangeScript() { var sel = Selection.Clone(); sel.Normalize(); var start = PlaceToPosition(sel.Start) - sel.Start.iLine; var len = sel.Text.Length - (sel.End.iLine - sel.Start.iLine); return string.Format( @"", start, len); } private static void SetPageSetupSettings(PrintDialogSettings settings) { var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Internet Explorer\PageSetup", true); if (key != null) { key.SetValue("footer", settings.Footer); key.SetValue("header", settings.Header); } } 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); } /// /// Search lines by regex pattern /// public List FindLines(string searchPattern, RegexOptions options) { List iLines = new List(); foreach (var r in Range.GetRangesByLines(searchPattern, options)) iLines.Add(r.Start.iLine); return iLines; } /// /// Removes given lines /// public void RemoveLines(List iLines) { TextSource.Manager.ExecuteCommand(new RemoveLinesCommand(TextSource, iLines)); if (iLines.Count > 0) IsChanged = true; if (LinesCount == 0) Text = ""; NeedRecalc(); Invalidate(); } #region Drag and drop private bool IsDragDrop { get; set; } protected override void OnDragEnter(DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text)) { e.Effect = DragDropEffects.Copy; IsDragDrop = true; } base.OnDragEnter(e); } protected override void OnDragDrop(DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text)) { if (ParentForm != null) ParentForm.Activate(); Focus(); var p = PointToClient(new Point(e.X, e.Y)); Selection.Start = PointToPlace(p); InsertText(e.Data.GetData(DataFormats.Text).ToString()); IsDragDrop = false; } base.OnDragDrop(e); } protected override void OnDragOver(DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text)) { var p = PointToClient(new Point(e.X, e.Y)); Selection.Start = PointToPlace(p); Invalidate(); } base.OnDragOver(e); } protected override void OnDragLeave(EventArgs e) { IsDragDrop = false; base.OnDragLeave(e); } #endregion #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; } /// /// Set to true if you want to cancel text inserting /// public bool Cancel { get; set; } } 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 { get; set; } public bool ShowPrintDialog { get; set; } public bool ShowPrintPreviewDialog { get; set; } /// /// Title of page. If you want to print Title on the page, insert code &w in Footer or Header. /// public string Title { get; set; } /// /// Footer of page. /// Here you can use special codes: &w (Window title), &D, &d (Date), &t(), &4 (Time), &p (Current page number), &P (Total number of pages), && (A single ampersand), &b (Right justify text, Center text. If &b occurs once, then anything after the &b is right justified. If &b occurs twice, then anything between the two &b is centered, and anything after the second &b is right justified). /// More detailed see here /// public string Footer { get; set; } /// /// Header of page /// Here you can use special codes: &w (Window title), &D, &d (Date), &t(), &4 (Time), &p (Current page number), &P (Total number of pages), && (A single ampersand), &b (Right justify text, Center text. If &b occurs once, then anything after the &b is right justified. If &b occurs twice, then anything between the two &b is centered, and anything after the second &b is right justified). /// More detailed see here /// public string Header { get; set; } /// /// Prints line numbers /// public bool IncludeLineNumbers { get; set; } public PrintDialogSettings() { ShowPrintPreviewDialog = true; Title = ""; Footer = ""; Header = ""; Footer = ""; Header = ""; } } 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 } /// /// Strategy of search of end of folding block /// public enum FindEndOfFoldingBlockStrategy { Strategy1, Strategy2 } }