Click here to Skip to main content
15,892,005 members
Articles / Desktop Programming / Windows Forms

Storm - the world's best IDE framework for .NET

Rate me:
Please Sign up or sign in to vote.
4.96/5 (82 votes)
4 Feb 2010LGPL311 min read 276.7K   6.5K   340  
Create fast, flexible, and extensible IDE applications easily with Storm - it takes nearly no code at all!
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Storm.TextEditor.Core;
using Storm.TextEditor.Core.Splitting;
using Storm.TextEditor.Drawing;
using Storm.TextEditor.Editor.CodeCompletion;
using Storm.TextEditor.Editor.Forms;
using Storm.TextEditor.Editor.Interacting;
using Storm.TextEditor.Editor.Text;
using Storm.TextEditor.Editor.Undo;
using Storm.TextEditor.Interacting;
using Storm.TextEditor.Interacting.Keyboard;
using Storm.TextEditor.Interacting.Scrolling;
using Storm.TextEditor.Parser.Objects;
using Storm.TextEditor.Parser.Objects.Collections;
using Storm.TextEditor.Parser.XML;
using Storm.TextEditor.Win32;
using Storm.TextEditor.Win32.Enums;

using ScrollEventArgs = Storm.TextEditor.Interacting.Scrolling.ScrollEventArgs;
using ScrollEventHandler = Storm.TextEditor.Interacting.Scrolling.ScrollEventHandler;

namespace Storm.TextEditor.Editor
{
	/// <summary>
	/// Represents the TextEditor's child control that takes care of all the actual input from the user.
	/// </summary>
	[ToolboxItem(false)]
	public sealed class TextEditorBase
		: SplitViewChildControl
	{
		#region Fields

		private int mouseX = 0;
		private int mouseY = 0;

		private int maxCharWidth	= 8;
		private int tabSpaces		= 4;

		private double scrollHandlerCurrentPosition = 0;
		private string currentFileName				= null;

		private bool keepTabs		= false;
		private bool overWrite		= false;
		private bool keyDownHandled = false;

		private Selection	selection	= null;
		private Caret		caret		= null;

		private MouseButtons mouseButton	= MouseButtons.None;
		private TextDrawType textDrawType	= TextDrawType.StarBorder;

		private CodeCompletionWindow codeCompletionWindow = null;

		private WeakTimer		caretTimer			= null;
		private FindReplace		findReplaceDialog	= null;
		private ScrollHandler	scrollHandler		= null;

		private PictureBox	filler	= null;
		private ToolTip		tooltip = null;

		private IMEWindow imeWindow	= null;

		private IContainer components;

		private IPainter  painter   = null;
		private ViewPoint viewPoint = new ViewPoint();

		#region Events

		/// <summary>
		/// Occurs when the clipboard is updated.
		/// </summary>
		public event CopyHandler ClipboardUpdated = null;

		/// <summary>
		/// Occurs when the caret has changed.
		/// </summary>
		public event EventHandler CaretChange = null;

		/// <summary>
		/// Occurs when the selection has changed.
		/// </summary>
		public event EventHandler SelectionChange = null;

		/// <summary>
		/// Occurs when the mouse pointer is over a row and a mouse button is pressed.
		/// </summary>
		public event RowMouseHandler RowMouseDown = null;

		/// <summary>
		/// Occurs when the mouse pointer is moved over a row.
		/// </summary>
		public event RowMouseHandler RowMouseMove = null;

		/// <summary>
		/// Occurs when a mouse button has been released.
		/// </summary>
		public event RowMouseHandler RowMouseUp = null;

		/// <summary>
		/// Occurs when a row has been clicked.
		/// </summary>
		public event RowMouseHandler RowClick = null;

		/// <summary>
		/// Occurs when when a row has been double clicked.
		/// </summary>
		public event RowMouseHandler RowDoubleClick = null;

		/// <summary>
		/// Occurs when a key is pressed while the control has focus.
		/// </summary>
		public new event KeyEventHandler KeyDown = null;

		#endregion

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the painter of the TextEditorBase.
		/// </summary>
		public IPainter Painter
		{
			get { return painter; }
			set { painter = value; }
		}

		/// <summary>
		/// Gets or sets the view point of the TextEditorBase.
		/// </summary>
		public ViewPoint ViewPoint
		{
			get { return viewPoint; }
			set { viewPoint = value; }
		}

		/// <summary>
		/// Gets or sets whether to keep tabs when indenting or replace them with spaces.
		/// </summary>
		public bool KeepTabs
		{
			get { return keepTabs; }
			set { keepTabs = value; }
		}

		/// <summary>
		/// Gets or sets the number of spaces a tab contains.
		/// </summary>
		public int TabSpaces
		{
			get { return TextEditor.TabSpaces; }
			set { TextEditor.TabSpaces = value; }
		}

		/// <summary>
		/// Gets or sets the FindReplace dialog of the TextEditorBase.
		/// </summary>
		public FindReplace FindReplaceDialog
		{
			get
			{
				this.CreateFindForm();
				return findReplaceDialog;
			}
			set { findReplaceDialog = value; }
		}

		/// <summary>
		/// Gets or sets the IMEWindow of the TextEditorBase.
		/// </summary>
		public IMEWindow IMEWindow
		{
			get { return imeWindow; }
			set { imeWindow = value; }
		}

		/// <summary>
		/// Gets or sets the current filename.
		/// </summary>
		[Browsable(false)]
		[ReadOnly(true)]
		public string FileName
		{
			get { return currentFileName; }
			set
			{
				if (currentFileName != value)
					currentFileName = value;
			}
		}

		/// <summary>
		/// Gets or sets the size of tabs in pixels.
		/// </summary>
		public int PixelTabSize
		{
			get { return this.TextEditor.TabSpaces * this.ViewPoint.CharWidth; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase is in Overwrite mode.
		/// </summary>
		[Browsable(false)]
		public bool OverWrite
		{
			get { return overWrite; }
		}

		/// <summary>
		/// Gets whether the user would be able to do a copy action.
		/// </summary>
		[Browsable(false)]
		public bool CanCopy
		{
			get { return this.Selection.IsValid; }
		}

		/// <summary>
		/// Gets whether the user would be able to do a paste action.
		/// </summary>
		[Browsable(false)]
		public bool CanPaste
		{
			get
			{
				string clipboardData = "";

				try
				{
					IDataObject dataObject = Clipboard.GetDataObject();

					if (dataObject.GetDataPresent(DataFormats.Text))
						clipboardData = dataObject.GetData(DataFormats.Text) as string;

					if (clipboardData != null)
						return true;
				}
				catch
				{
				}

				return false;
			}
		}

		/// <summary>
		/// Gets whether the user would be able to do an undo action.
		/// </summary>
		[Browsable(false)]
		public bool CanUndo
		{
			get { return this.Document.UndoStep > 0; }
		}

		/// <summary>
		/// Gets whether the user would be able to do a redo action.
		/// </summary>
		[Browsable(false)]
		public bool CanRedo
		{
			get
			{
				if (this.Document.UndoStep >= this.Document.UndoBuffer.Count)
					return false;
				else
					return true;
			}
		}

		/// <summary>
		/// Gets the size (in pixels) of the font to use when rendering the content.
		/// </summary>
		public float FontSize
		{
			get { return this.TextEditor.FontSize; }
		}

		/// <summary>
		/// Gets the indention style to use when inserting new lines.
		/// </summary>
		public IndentStyle Indent
		{
			get { return this.TextEditor.Indent; }
		}

		/// <summary>
		/// Gets the SyntaxDocument the TextEditorBase is currently attatched to.
		/// </summary>
		public SyntaxDocument Document
		{
			get { return this.TextEditor.Document; }
		}

		/// <summary>
		/// Gets the delay in milliseconds before the tooltip is displayed when hovering a collapsed section.
		/// </summary>
		public int TooltipDelay
		{
			get { return this.TextEditor.TooltipDelay; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase is readonly.
		/// </summary>
		public bool ReadOnly
		{
			get { return this.TextEditor.ReadOnly; }
		}

		/// <summary>
		/// Gets or sets the name of the font to use when rendering the TextEditorBase.
		/// </summary>
		public string FontName
		{
			get { return this.TextEditor.FontName; }
		}

		/// <summary>
		/// Gets the style to use when painting with Alt + (Arrow) keys.
		/// </summary>
		public TextDrawType TextDrawStyle
		{
			get { return textDrawType; }
			set { textDrawType = value; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render bracket matching.
		/// </summary>
		public bool BracketMatching
		{
			get { return this.TextEditor.BracketMatching; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become bold when matched.
		/// </summary>
		public bool BracketBold
		{
			get { return this.TextEditor.BracketBold; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become italic when matched.
		/// </summary>
		public bool BracketItalic
		{
			get { return this.TextEditor.BracketItalic; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become underlined when matched.
		/// </summary>
		public bool BracketUnderline
		{
			get { return this.TextEditor.BracketUnderline; }
		}

		/// <summary>
		/// Gets whether the matching bracket should become strikethroughed when matched.
		/// </summary>
		public bool BracketStrikethrough
		{
			get { return this.TextEditor.BracketStrikethrough; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render whitespace chars.
		/// </summary>
		public bool VirtualWhitespace
		{
			get { return this.TextEditor.VirtualWhitespace; }
		}

		/// <summary>
		/// Gets the Color of the horizontal separators (a'la Visual Basic 6).
		/// </summary>
		public Color SeparatorColor
		{
			get { return this.TextEditor.SeparatorColor; }
		}

		/// <summary>
		/// Gets the text color to use when rendering bracket matching.
		/// </summary>
		public Color BracketForeColor
		{
			get { return this.TextEditor.BracketForeColor; }
		}

		/// <summary>
		/// Gets or sets the border color of active selection areas.
		/// </summary>
		public Color SelectionBorderColor
		{
			get { return this.TextEditor.SelectionBorderColor; }
		}

		/// <summary>
		/// Gets or sets the border color of inactive selection areas.
		/// </summary>
		public Color InactiveSelectionBorderColor
		{
			get { return this.TextEditor.InactiveSelectionBorderColor; }
		}

		/// <summary>
		/// Gets the background color to use when rendering bracket matches.
		/// </summary>
		public Color BracketBackColor
		{
			get { return this.TextEditor.BracketBackColor; }
		}

		/// <summary>
		/// Gets the background color to use when rendering selected text.
		/// </summary>
		public Color SelectionBackColor
		{
			get { return this.TextEditor.SelectionBackColor; }
		}

		/// <summary>
		/// Gets the background color to use when rendering inactive selected text.
		/// </summary>
		public Color InactiveSelectionBackColor
		{
			get { return this.TextEditor.InactiveSelectionBackColor; }
		}

		/// <summary>
		/// Gets the color of the border between the line number area and the folding area.
		/// </summary>
		public Color LineNumberBorderColor
		{
			get { return this.TextEditor.LineNumberBorderColor; }
		}

		/// <summary>
		/// Gets the text color to use when rendering breakpoints.
		/// </summary>
		public Color BreakpointForeColor
		{
			get { return this.TextEditor.BreakpointForeColor; }
		}

		/// <summary>
		/// Gets the back color to use when rendering breakpoints.
		/// </summary>
		public Color BreakpointBackColor
		{
			get { return this.TextEditor.BreakpointBackColor; }
		}

		/// <summary>
		/// Gets the text color to use when rendering line numbers.
		/// </summary>
		public Color LineNumberForeColor
		{
			get { return this.TextEditor.LineNumberForeColor; }
		}

		/// <summary>
		/// Gets the back color to use when rendering the line number area.
		/// </summary>
		public Color LineNumberBackColor
		{
			get { return this.TextEditor.LineNumberBackColor; }
		}

		/// <summary>
		/// Gets the color of the gutter margin.
		/// </summary>
		public Color GutterMarginColor
		{
			get { return this.TextEditor.GutterMarginColor; }
		}

		/// <summary>
		/// <summary>
		/// Gets or sets the color to use when rendering an expansion's background color.
		/// </summary>
		public Color ExpansionBackgroundColor
		{
			get { return this.TextEditor.ExpansionBackgroundColor; }
		}

		/// <summary>
		/// Gets or sets the background color of the client area.
		/// </summary>
		public override Color BackColor
		{
			get { return this.TextEditor.BackColor; }
			set { this.TextEditor.BackColor = value; }
		}

		/// <summary>
		/// Gets the background color to use when rendering the active line.
		/// </summary>
		public Color HighlightedLineColor
		{
			get { return this.TextEditor.HighlightedLineColor; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should highlight the active line.
		/// </summary>
		public bool HighlightActiveLine
		{
			get { return this.TextEditor.HighlightActiveLine; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render whitespace chars.
		/// </summary>
		public bool ShowWhitespace
		{
			get { return this.TextEditor.ShowWhitespace; }
		}

		/// <summary>
		/// Gets whether the line number margin should be visible or not.
		/// </summary>
		public bool ShowLineNumbers
		{
			get { return this.TextEditor.ShowLineNumbers; }
		}

		/// <summary>
		/// Gets whether the gutter margin should be visible or not.
		/// </summary>
		public bool ShowGutterMargin
		{
			get { return this.TextEditor.ShowGutterMargin; }
		}

		/// <summary>
		/// Gets the width of the gutter margin. (In pixels)
		/// </summary>
		public int GutterMarginWidth
		{
			get { return this.TextEditor.GutterMarginWidth; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should render Tab Guides.
		/// </summary>
		public bool ShowTabGuides
		{
			get { return this.TextEditor.ShowTabGuides; }
		}

		/// <summary>
		/// Gets the color to use when rendering whitespace chars.
		/// </summary>
		public Color WhitespaceColor
		{
			get { return this.TextEditor.WhitespaceColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering tab guides.
		/// </summary>
		public Color TabGuideColor
		{
			get { return this.TextEditor.TabGuideColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering bracket matching borders.
		/// </summary>
		public Color BracketBorderColor
		{
			get { return this.TextEditor.BracketBorderColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering a hovered row.
		/// </summary>
		public Color RowHoverBackColor
		{
			get { return this.TextEditor.RowHoverBackColor; }
		}

		/// <summary>
		/// Gets or sets the color to use when rendering a row's expansion's inner symbol. (-/+)
		/// </summary>
		public Color ExpansionSymbolColor
		{
			get { return this.TextEditor.ExpansionSymbolColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering Outline symbols.
		/// </summary>
		public Color OutlineColor
		{
			get { return this.TextEditor.OutlineColor; }
		}

		/// <summary>
		/// Gets the color to use when rendering a collapsed expansion's background.
		/// </summary>
		public Color CollapsedBackgroundColor
		{
			get { return this.TextEditor.CollapsedBackgroundColor; }
		}

		/// <summary>
		/// Gets or sets whether the TextEditorBase should use smooth scrolling.
		/// </summary>
		public bool SmoothScroll
		{
			get { return this.TextEditor.SmoothScroll; }
		}

		/// <summary>
		/// Gets the number of pixels the screen should be scrolled per scroll when smooth scrolling is enabled.
		/// </summary>
		public int SmoothScrollSpeed
		{
			get { return this.TextEditor.SmoothScrollSpeed; }
		}

		/// <summary>
		/// Gets whether the TextEditorBase should parse all of its contents when more content is drag dropped/pasted into it.
		/// </summary>
		public bool ParseOnPaste
		{
			get { return this.TextEditor.ParseOnPaste; }
		}

		/// <summary>
		/// Gets a value indicating whether the TextEditorBase should use a dotted margin border.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if the TextEditorBase should use a dotted margin border; otherwise, <c>false</c>.
		/// </value>
		public bool UseDottedMarginBorder
		{
			get { return this.TextEditor.UseDottedMarginBorder; }
		}

		/// <summary>
		/// Gets the Caret object.
		/// </summary>
		public Caret Caret
		{
			get { return caret; }
		}

		/// <summary>
		/// Gets the Selection object.
		/// </summary>
		public Selection Selection
		{
			get { return selection; }
		}

		/// <summary>
		/// Gets the code completion window.
		/// </summary>
		/// <value>The code completion window.</value>
		public CodeCompletionWindow CodeCompletionWindow
		{
			get { return codeCompletionWindow; }
		}

		#endregion

		#region Methods

		#region Private

		/// <summary>
		/// Finds the matched word.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="wordToSearch">The word to search.</param>
		/// <returns>The found word.</returns>
		private string FindMatchedWord(ArrayList list, string wordToSearch)
		{
			int binaryMatch = 0;

			string nextMatch = "";
			string lastMatch = "";

			ArrayList nextList = null;
			list.Sort();

			binaryMatch = list.BinarySearch(wordToSearch);
			if (binaryMatch < 0)
				binaryMatch = ~binaryMatch;

			if (binaryMatch > 0)
			{
				try
				{
					if (binaryMatch < list.Count)
						nextList = list.GetRange(binaryMatch + 1, 1);
				}
				catch
				{
					nextList = list.GetRange(binaryMatch, 1);
					lastMatch = nextList[0].ToString();
				}
			}

			if (nextList != null)
				nextMatch = nextList[0].ToString();

			if (nextMatch.IndexOf(wordToSearch, StringComparison.OrdinalIgnoreCase) < 0)
			{
				if (binaryMatch < list.Count)
					lastMatch = list.GetRange(binaryMatch, 1)[0].ToString();
			}

			return lastMatch;
		}

		/// <summary>
		/// Creates the find form.
		/// </summary>
		private void CreateFindForm()
		{
			if (this.TextEditor.DisableFindForm == false && findReplaceDialog == null)
				this.FindReplaceDialog = new FindReplace(this);
		}

		/// <summary>
		/// Updates the sizes and visibility of the SplitViewChildControl's child Controls,
		/// including the left and top SplitViewThumbControl and the ScrollBars. Also updates margins.
		/// </summary>
		private void UpdateContent()
		{
			this.SuspendLayout();
			if (this.Visible == true && this.Width > 0 && this.Height > 0 && this.IsHandleCreated == true)
			{
				if (filler == null)
					return;

				this.TopThumb.Width = SystemInformation.VerticalScrollBarWidth;
				this.LeftThumb.Height = SystemInformation.HorizontalScrollBarHeight;

				vScroll.Width = SystemInformation.VerticalScrollBarWidth;
				hScroll.Height = SystemInformation.HorizontalScrollBarHeight;

				if (this.TopThumbVisible == true)
				{
					vScroll.Top = this.TopThumb.Height;
					if (hScroll.Visible == true)
						vScroll.Height = this.ClientHeight - hScroll.Height - this.TopThumb.Height;
					else
						vScroll.Height = this.ClientHeight - this.TopThumb.Height;
				}
				else
				{
					if (hScroll.Visible == true)
						vScroll.Height = this.ClientHeight - hScroll.Height;
					else
						vScroll.Height = this.ClientHeight;

					vScroll.Top = 0;
				}

				if (this.LeftThumbVisible == true)
				{
					hScroll.Left = this.LeftThumb.Width;
					if (vScroll.Visible == true)
						hScroll.Width = this.ClientWidth - vScroll.Width - this.LeftThumb.Width;
					else
						hScroll.Width = this.ClientWidth - this.LeftThumb.Width;
				}
				else
				{
					if (vScroll.Visible == true)
						hScroll.Width = this.ClientWidth - vScroll.Width;
					else
						hScroll.Width = this.ClientWidth;

					hScroll.Left = 0;
				}

				if (this.Painter != null)
					this.Painter.Resize();

				vScroll.Left = this.ClientWidth - vScroll.Width;
				hScroll.Top = this.ClientHeight - hScroll.Height;

				this.LeftThumb.Left = 0;
				this.LeftThumb.Top = hScroll.Top;

				this.TopThumb.Left = vScroll.Left;
				this.TopThumb.Top = 0;

				filler.Left = vScroll.Left;
				filler.Top = hScroll.Top;
				filler.Width = vScroll.Width;
				filler.Height = hScroll.Height;
			}

			this.ResumeLayout(false);
		}

		/// <summary>
		/// Inserts a new line.
		/// </summary>
		private void InsertEnter()
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
			{
				this.Selection.DeleteSelection();
				this.InsertEnter();
			}
			else
			{
				Row currentRow = this.Caret.CurrentRow;
				string indentationLevel = "";
				switch (this.Indent)
				{
					case IndentStyle.None:
						this.Document.InsertText("\n", this.Caret.Position.X, this.Caret.Position.Y);

						this.Caret.CurrentRow.Parse();
						this.Caret.MoveDown(false);
						this.Caret.CurrentRow.Parse(true);

						this.Caret.Position.X = 0;
						this.Caret.SetPosition(this.Caret.Position);

						break;
					case IndentStyle.LastRow:
						indentationLevel = currentRow.GetLeadingWhitespace();

						int maximumLength = Math.Min(indentationLevel.Length, this.Caret.Position.X);
						string split = "\n" + indentationLevel.Substring(0, maximumLength);

						this.Document.InsertText(split, this.Caret.Position.X, this.Caret.Position.Y);
						this.Document.ResetVisibleRows();

						this.Caret.CurrentRow.Parse(true);
						this.Caret.MoveDown(false);
						this.Caret.CurrentRow.Parse(true);

						this.Caret.Position.X = indentationLevel.Length;
						this.Caret.SetPosition(this.Caret.Position);

						currentRow.Parse(true);
						currentRow.NextRow.Parse(true);

						break;
					case IndentStyle.Scope:
						currentRow.Parse(true);
						if (currentRow.ShouldOutdent == true)
							this.OutdentEndRow();

						this.Document.InsertText("\n", this.Caret.Position.X, this.Caret.Position.Y);
						this.Caret.CurrentRow.Parse();
						this.Caret.MoveDown(false);
						this.Caret.CurrentRow.Parse(false);

						if (keepTabs == true)
							indentationLevel = new string('\t', this.Caret.CurrentRow.Depth);
						else
							indentationLevel = new string(' ', tabSpaces * this.Caret.CurrentRow.Depth);

						this.Document.InsertText(indentationLevel, 0, this.Caret.Position.Y);
						this.Caret.CurrentRow.Parse(true);

						this.Caret.Position.X = indentationLevel.Length;
						this.Caret.SetPosition(this.Caret.Position);
						this.Caret.CropPosition();
						this.Selection.ClearSelection();

						currentRow.Parse(true);
						currentRow.NextRow.Parse(true);

						break;
				}

				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// Outdents the end row.
		/// </summary>
		private void OutdentEndRow()
		{
			if (this.Indent == IndentStyle.Scope)
			{
				Row currentRow = this.Caret.CurrentRow;

				string indentation    = currentRow.Text.Substring(0, currentRow.GetLeadingWhitespace().Length);
				string newIndentation = "";

				if (keepTabs == true)
					newIndentation = new string('\t', this.Caret.CurrentRow.Depth);
				else
					newIndentation = new string(' ', tabSpaces * this.Caret.CurrentRow.Depth);

				TextRange textRange = new TextRange();
				textRange.FirstColumn = 0;
				textRange.LastColumn  = currentRow.GetLeadingWhitespace().Length;
				textRange.FirstRow    = currentRow.Index;
				textRange.LastRow     = currentRow.Index;

				this.Document.DeleteRange(textRange);
				this.Document.InsertText(newIndentation, 0, currentRow.Index, true);

				this.Caret.Position.X += newIndentation.Length;
				this.Caret.SetPosition(this.Caret.Position);
				this.Caret.CropPosition();

				this.Selection.ClearSelection();
				this.Caret.CurrentRow.Parse(true);
			}
		}

		/// <summary>
		/// Performs a delete action one step forward from the current caret position.
		/// </summary>
		private void DeleteForward()
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
				this.Selection.DeleteSelection();
			else
			{
				Row currentRow = this.Caret.CurrentRow;
				if (this.Caret.Position.X == currentRow.Text.Length)
				{
					if (this.Caret.Position.Y <= Document.Count - 2)
					{
						TextRange textRange   = new TextRange();
						textRange.FirstColumn = this.Caret.Position.X;
						textRange.FirstRow    = this.Caret.Position.Y;
						textRange.LastRow     = textRange.FirstRow + 1;
						textRange.LastColumn  = 0;

						this.Document.DeleteRange(textRange);
						this.Document.ResetVisibleRows();
					}
				}
				else
				{
					TextRange textRange   = new TextRange();
					textRange.FirstColumn = this.Caret.Position.X;
					textRange.FirstRow    = this.Caret.Position.Y;
					textRange.LastRow     = textRange.FirstRow;
					textRange.LastColumn  = textRange.FirstColumn + 1;

					this.Document.DeleteRange(textRange);
					this.Document.ResetVisibleRows();

					this.Caret.CurrentRow.Parse(true);
				}
			}
		}

		/// <summary>
		/// Performs a delete action one step backwards from the current caret position.
		/// </summary>
		private void DeleteBackwards()
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
				this.Selection.DeleteSelection();
			else
			{
				Row currentRow = this.Caret.CurrentRow;
				if (this.Caret.Position.X == 0)
				{
					if (this.Caret.Position.Y > 0)
					{
						this.Caret.Position.Y--;
						this.Caret.MoveEnd(false);

						this.DeleteForward();
						this.Document.ResetVisibleRows();
					}
				}
				else
				{
					if (this.Caret.Position.X >= currentRow.Text.Length)
					{
						TextRange textRange   = new TextRange();
						textRange.FirstColumn = this.Caret.Position.X - 1;
						textRange.FirstRow    = this.Caret.Position.Y;
						textRange.LastRow     = textRange.FirstRow;
						textRange.LastColumn  = textRange.FirstColumn + 1;

						if (this.Caret.Position.X - tabSpaces > -1)
						{
							if (this.Caret.CurrentRow.Text.Substring(this.Caret.Position.X - tabSpaces, tabSpaces) == new string(' ', tabSpaces))
							{
								textRange.FirstColumn = this.Caret.Position.X - tabSpaces;
								textRange.LastColumn  = this.Caret.Position.X;
							}
						}

						this.Document.DeleteRange(textRange);
						this.Document.ResetVisibleRows();

						this.Caret.MoveEnd(false);
						this.Caret.CurrentRow.Parse();
					}
					else
					{
						bool tabDeleted = false;

						TextRange textRange   = new TextRange();
						textRange.FirstColumn = this.Caret.Position.X - 1;
						textRange.FirstRow    = this.Caret.Position.Y;
						textRange.LastRow     = textRange.FirstRow;
						textRange.LastColumn  = textRange.FirstColumn + 1;

						if (this.Caret.Position.X - tabSpaces > -1)
						{
							if (this.Caret.CurrentRow.Text.Substring(this.Caret.Position.X - tabSpaces, tabSpaces) == new string(' ', tabSpaces))
							{
								textRange.FirstColumn = this.Caret.Position.X - tabSpaces;
								textRange.LastColumn  = this.Caret.Position.X;

								tabDeleted = true;
							}
						}

						this.Document.DeleteRange(textRange);
						this.Document.ResetVisibleRows();

						if (tabDeleted == false)
							this.Caret.MoveLeft(false);
						else
						{
							this.Caret.MoveEnd(false);
							this.Caret.MoveLeft(false);
						}

						this.Caret.CurrentRow.Parse();
					}
				}
			}
		}

		/// <summary>
		/// Scrolls the screen.
		/// </summary>
		/// <param name="scrollAmount">The scroll amount.</param>
		private void ScrollScreen(int scrollAmount)
		{
			this.ScrollScreen(scrollAmount, 2);
		}

		/// <summary>
		/// Scrolls the screen.
		/// </summary>
		/// <param name="scrollAmount">The scroll amount.</param>
		/// <param name="speed">The speed.</param>
		private void ScrollScreen(int scrollAmount, int speed)
		{
			tooltip.RemoveAll();
			int newScrollValue = vScroll.Value + scrollAmount;

			newScrollValue = Math.Max(newScrollValue, vScroll.Minimum);
			newScrollValue = Math.Min(newScrollValue, vScroll.Maximum);

			if (newScrollValue >= vScroll.Maximum - 2)
				newScrollValue = vScroll.Maximum - 2;

			vScroll.Value = newScrollValue;
			this.Redraw();
		}

		/// <summary>
		/// Pastes the text on the user's clipboard.
		/// </summary>
		private void PasteText()
		{
			IDataObject dataObject    = Clipboard.GetDataObject();
			string      clipboardText = "";

			if (dataObject.GetDataPresent(DataFormats.UnicodeText) == true)
				clipboardText = dataObject.GetData(DataFormats.UnicodeText) as string;
			else if (dataObject.GetDataPresent(DataFormats.Text) == true)
				clipboardText = dataObject.GetData(DataFormats.Text) as string;

			this.InsertText(clipboardText);
			if (this.ParseOnPaste == true)
				this.Document.ParseAll(true);
		}

		/// <summary>
		/// Begins a drag drop operation.
		/// </summary>
		private void BeginDragDrop()
		{
			this.DoDragDrop(this.Selection.Text, DragDropEffects.All);
		}

		/// <summary>
		/// Redraws this instance.
		/// </summary>
		private void Redraw()
		{
			this.Invalidate();
		}

		/// <summary>
		/// Redraws the caret.
		/// </summary>
		private void RedrawCaret()
		{
			using (Graphics gfx = this.CreateGraphics())
				this.Painter.RenderCaret(gfx);
		}

		/// <summary>
		/// Sets the mouse cursor.
		/// </summary>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		private void SetMouseCursor(int x, int y)
		{
			if (this.TextEditor.LockCursorUpdate == true)
			{
				this.Cursor = this.TextEditor.Cursor;
				return;
			}

			if (this.ViewPoint.Action == TextAction.DragText)
				this.Cursor = Cursors.Hand;
			else
			{
				if (x < this.ViewPoint.TotalMarginWidth)
				{
					if (x < this.ViewPoint.GutterMarginWidth)
						this.Cursor = Cursors.Arrow;
					else
					{
						Assembly assembly = this.GetType().Assembly;

						Stream cursorStream = assembly.GetManifestResourceStream("Storm.TextEditor.Resources.FlippedCursor.cur");
						this.Cursor = new Cursor(cursorStream);
					}
				}
				else
				{
					if (x > this.ViewPoint.TextMargin - 8)
					{
						if (this.IsOverSelection(x, y) == true && this.ViewPoint.Action != TextAction.Select)
							this.Cursor = Cursors.Arrow;
						else
						{
							TextPoint textPoint = this.Painter.GetTextPointAtPixel(x, y);
							Word textPointWord = this.Document.GetWordFromPos(textPoint);
							if (textPointWord != null)
							{
								WordMouseEventArgs wordMouseEventArgs = new WordMouseEventArgs();
								wordMouseEventArgs.Pattern = textPointWord.Pattern;
								wordMouseEventArgs.Button  = MouseButtons.None;
								wordMouseEventArgs.Cursor  = Cursors.IBeam;
								wordMouseEventArgs.Word    = textPointWord;

								this.TextEditor.OnWordMouseHover(ref wordMouseEventArgs);
								this.Cursor = wordMouseEventArgs.Cursor;
							}
							else
								this.Cursor = Cursors.IBeam;
						}
					}
					else
						this.Cursor = Cursors.Arrow;
				}
			}
		}

		/// <summary>
		/// Copies the selected text to the clipboard.
		/// </summary>
		private void CopyText()
		{
			if (this.Selection.IsValid == false)
				return;

			if (this.TextEditor.CopyAsRTF == true)
				this.CopyAsRtf();
			else
			{
				try
				{
					if (this.Selection != null)
					{
						Clipboard.SetDataObject(this.Selection.Text, true);
						CopyEventArgs copyEventArgs = new CopyEventArgs();

						copyEventArgs.Text = this.Selection.Text;
						this.OnClipboardUpdated(copyEventArgs);
					}
				}
				catch
				{
				}
			}
		}

		/// <summary>
		/// Gets the type of the char.
		/// </summary>
		/// <param name="characterString">The character string.</param>
		/// <returns>The type of the char.</returns>
		private int GetCharType(string characterString)
		{
			string firstType  = " \t";
			string secondType = ".,-+'?´=)(/&%¤#!\"\\<>[]$£@*:;{}";

			if (firstType.IndexOf(characterString) >= 0)
				return 1;

			if (secondType.IndexOf(characterString) >= 0)
				return 2;

			return 3;
		}

		/// <summary>
		/// Selects the pattern.
		/// </summary>
		/// <param name="rowIndex">Index of the row.</param>
		/// <param name="column">The column.</param>
		/// <param name="length">The length.</param>
		private void SelectPattern(int rowIndex, int column, int length)
		{
			this.Selection.Bounds.FirstColumn = column;
			this.Selection.Bounds.FirstRow    = rowIndex;
			this.Selection.Bounds.LastColumn  = column + length;
			this.Selection.Bounds.LastRow     = rowIndex;

			this.Caret.Position.X = column + length;
			this.Caret.Position.Y = rowIndex;
			this.Caret.CurrentRow.EnsureVisible();

			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new Container();
			ResourceManager resources = new ResourceManager(typeof(TextEditorBase));
			filler = new PictureBox();
			caretTimer = new WeakTimer(components);
			tooltip = new ToolTip(components);

			this.SuspendLayout();
			scrollHandler = new ScrollHandler();
			scrollHandler.Image = ((Bitmap)(resources.GetObject("ScrollHandler.Image")));
			scrollHandler.Location = new Point(197, 157);
			scrollHandler.Name = "scrollHandler";
			scrollHandler.Size = new Size(28, 28);
			scrollHandler.TabIndex = 4;
			scrollHandler.TransparencyKey = Color.FromArgb(255, 0, 255);
			scrollHandler.Visible = false;
			scrollHandler.EndScroll += this.OnScrollHandlerEndScroll;
			scrollHandler.BeginScroll += this.OnScrollHandlerBeginScroll;
			scrollHandler.Scroll += this.OnScrollHandlerScroll;

			hScroll.Cursor = Cursors.Default;
			hScroll.Scroll += this.OnHoritonzalScrollBarScroll;
			vScroll.Visible = false;

			vScroll.Cursor = Cursors.Default;
			vScroll.Scroll += this.OnVerticalScrollBarScroll;
			vScroll.Visible = false;

			caretTimer.Enabled = true;
			caretTimer.Interval = 500;
			caretTimer.Tick += this.OnCaretTimerTick;

			tooltip.AutoPopDelay = 50000;
			tooltip.InitialDelay = 0;
			tooltip.ReshowDelay = 1000;
			tooltip.ShowAlways = true;

			codeCompletionWindow = new CodeCompletionWindow(this.TextEditor);
			codeCompletionWindow.Visible = false;
			this.Controls.Add(codeCompletionWindow);

			this.TopThumb.BackColor = SystemColors.Control;
			this.TopThumb.Cursor = Cursors.HSplit;
			this.TopThumb.Location = new Point(101, 17);
			this.TopThumb.Name = "TopThumb";
			this.TopThumb.Size = new Size(16, 8);
			this.TopThumb.TabIndex = 3;
			this.TopThumb.Visible = false;

			this.LeftThumb.BackColor = SystemColors.Control;
			this.LeftThumb.Cursor = System.Windows.Forms.Cursors.VSplit;
			this.LeftThumb.Location = new Point(423, 17);
			this.LeftThumb.Name = "LeftThumb";
			this.LeftThumb.Size = new Size(8, 16);
			this.LeftThumb.TabIndex = 3;
			this.LeftThumb.Visible = false;

			this.AllowDrop = true;
			this.Controls.Add(scrollHandler);

			this.Size = new Size(240, 216);
			this.LostFocus += this.OnLeave;
			this.GotFocus += this.OnEnter;
			this.ResumeLayout(false);
		}

		/// <summary>
		/// Moves the caret to the next word.
		/// </summary>
		/// <param name="select">if set to <c>true</c> [select].</param>
		private void MoveCaretToNextWord(bool select)
		{
			int x = this.Caret.Position.X;
			int y = this.Caret.Position.Y;

			string currentCharacter     = "";
			int    currentCharacterType = 0;

			bool found = false;

			if (x == Caret.CurrentRow.Text.Length)
				currentCharacterType = 1;
			else
			{
				currentCharacter     = this.Document[y].Text.Substring(this.Caret.Position.X, 1);
				currentCharacterType = this.GetCharType(currentCharacter);
			}

			while (y < this.Document.Count)
			{
				while (x < this.Document[y].Text.Length)
				{
					string currentLocalCharacter     = this.Document[y].Text.Substring(x, 1);
					int    currentLocalCharacterType = this.GetCharType(currentLocalCharacter);

					if (currentLocalCharacterType != currentCharacterType)
					{
						if (currentLocalCharacterType == 1)
							currentCharacterType = 1;
						else
						{
							found = true;
							break;
						}
					}

					x++;
				}

				if (found == true)
					break;

				x = 0;
				y++;
			}

			if (y >= this.Document.Count - 1)
			{
				y = this.Document.Count - 1;

				if (x >= this.Document[y].Text.Length)
					x = this.Document[y].Text.Length - 1;

				if (x == -1)
					x = 0;
			}

			this.Caret.SetPosition(new TextPoint(x, y));
			if (select == false)
				this.Selection.ClearSelection();

			if (select == true)
				this.Selection.MakeSelection();

			this.ScrollIntoView();
		}

		/// <summary>
		/// Moves the caret to the previous word.
		/// </summary>
		/// <param name="select">if set to <c>true</c> [select].</param>
		private void MoveCaretToPreviousWord(bool select)
		{
			int x = this.Caret.Position.X;
			int y = this.Caret.Position.Y;

			string currentCharacter     = "";
			int    currentCharacterType = 0;

			bool found = false;

			if (x == this.Caret.CurrentRow.Text.Length)
			{
				currentCharacterType = 1;
				x = this.Caret.CurrentRow.Text.Length - 1;
			}
			else
			{
				currentCharacter     = this.Document[y].Text.Substring(this.Caret.Position.X, 1);
				currentCharacterType = this.GetCharType(currentCharacter);
			}

			while (y >= 0)
			{
				while (x >= 0 && x < this.Document[y].Text.Length)
				{
					string currentLocalCharacter     = this.Document[y].Text.Substring(x, 1);
					int    currentLocalCharacterType = this.GetCharType(currentLocalCharacter);

					if (currentLocalCharacterType != currentCharacterType)
					{
						found = true;

						string currentLoopCharacter     = this.Document[y].Text.Substring(x, 1);
						int    currentLoopCharacterType = currentLocalCharacterType;

						while (x > 0)
						{
							currentLoopCharacter     = this.Document[y].Text.Substring(x, 1);
							currentLoopCharacterType = this.GetCharType(currentLoopCharacter);
							if (currentLoopCharacterType != currentLocalCharacterType)
							{
								x++;
								break;
							}

							x--;
						}

						break;
					}

					x--;
				}

				if (found == true)
					break;

				if (y == 0)
				{
					x = 0;
					break;
				}

				y--;
				x = this.Document[y].Text.Length - 1;
			}

			this.Caret.SetPosition(new TextPoint(x, y));
			if (select == false)
				this.Selection.ClearSelection();

			if (select == true)
				this.Selection.MakeSelection();

			this.ScrollIntoView();
		}

		/// <summary>
		/// Copies the current selection as Rtf format.
		/// </summary>
		private void CopyAsRtf()
		{
			TextStyle[] textStyles = this.Document.Parser.Language.Styles;
			this.Document.ParseAll(true);

			int firstRowIndex = Selection.LogicalBounds.FirstRow;
			int lastRowIndex  = Selection.LogicalBounds.LastRow;

			int firstColumnIndex = Selection.LogicalBounds.FirstColumn;
			int lastColumnIndex  = Selection.LogicalBounds.LastColumn;

			StringBuilder rtfBuilder = new StringBuilder();
			rtfBuilder.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1053{\fonttbl{\f0\fmodern\fprq1\fcharset0 " + this.FontName + @";}}");
			rtfBuilder.Append(@"{\colortbl ;");

			foreach (TextStyle textStyle in textStyles)
			{
				rtfBuilder.AppendFormat("\\red{0}\\green{1}\\blue{2};", textStyle.ForeColor.R, textStyle.ForeColor.G, textStyle.ForeColor.B);
				rtfBuilder.AppendFormat("\\red{0}\\green{1}\\blue{2};", textStyle.BackColor.R, textStyle.BackColor.G, textStyle.BackColor.B);
			}

			rtfBuilder.Append(@";}");
			rtfBuilder.Append(@"\viewkind4\uc1\pard\f0\fs20");

			bool doneLooping = false;
			for (int rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++)
			{
				Row currentRow = this.Document[rowIndex];

				foreach (Word currentWord in currentRow)
				{
					if (rowIndex == firstRowIndex && currentWord.Column + currentWord.Text.Length < firstColumnIndex)
						continue;

					bool rowIsFirst = (rowIndex == firstRowIndex && currentWord.Column <= firstColumnIndex && currentWord.Column + currentWord.Text.Length > firstColumnIndex);
					bool rowIsLast  = (rowIndex == lastRowIndex  && currentWord.Column < lastColumnIndex   && currentWord.Column + currentWord.Text.Length > lastColumnIndex);

					if (currentWord.Type == WordType.Word && currentWord.Style != null)
					{
						int currentTextStyleIndex = Array.IndexOf(textStyles, currentWord.Style);
						currentTextStyleIndex *= 2;
						currentTextStyleIndex++;

						rtfBuilder.Append("{\\cf" + currentTextStyleIndex.ToString());
						if (currentWord.Style.Transparent == false)
							rtfBuilder.Append("\\highlight" + (currentTextStyleIndex + 1).ToString());

						rtfBuilder.Append(" ");
					}

					if (currentWord.Style != null)
					{
						if (currentWord.Style.Bold == true)
							rtfBuilder.Append(@"\b ");

						if (currentWord.Style.Underline == true)
							rtfBuilder.Append(@"\ul ");

						if (currentWord.Style.Italic == true)
							rtfBuilder.Append(@"\i ");
					}

					string currentWordText = currentWord.Text;

					if (rowIsFirst == true)
						currentWordText = currentWordText.Substring(firstColumnIndex - currentWord.Column);

					if (rowIsLast == true)
						currentWordText = currentWordText.Substring(0, lastColumnIndex - currentWord.Column);

					currentWordText = currentWordText.Replace(@"\", @"\\").Replace(@"}", @"\}").Replace(@"{", @"\{");

					rtfBuilder.Append(currentWordText);
					if (currentWord.Style != null)
					{
						if (currentWord.Style.Bold == true)
							rtfBuilder.Append(@"\b0 ");

						if (currentWord.Style.Underline == true)
							rtfBuilder.Append(@"\ul0 ");

						if (currentWord.Style.Italic == true)
							rtfBuilder.Append(@"\i0 ");
					}

					if (currentWord.Type == WordType.Word && currentWord.Style != null)
						rtfBuilder.Append("}");

					if (rowIsLast == true)
					{
						doneLooping = true;
						break;
					}
				}

				if (doneLooping == true)
					break;

				rtfBuilder.Append(@"\par");
			}

			DataObject dataObject = new DataObject();
			dataObject.SetData(DataFormats.Rtf, rtfBuilder.ToString());

			string selectionText = this.Selection.Text;
			dataObject.SetData(DataFormats.Text, selectionText);
			Clipboard.SetDataObject(dataObject);

			CopyEventArgs copyEventArgs = new CopyEventArgs();
			copyEventArgs.Text = selectionText;
			this.OnClipboardUpdated(copyEventArgs);
		}

		/// <summary>
		/// Selects the current word.
		/// </summary>
		private void SelectCurrentWord()
		{
			Row currentRow = this.Caret.CurrentRow;
			if (currentRow.Text == "")
				return;

			if (this.Caret.Position.X >= currentRow.Text.Length)
				return;

			string currentCaretCharacter = currentRow.Text.Substring(this.Caret.Position.X, 1);
			int characterType = this.GetCharType(currentCaretCharacter);

			int leftPosition = this.Caret.Position.X;
			int rightPosition = this.Caret.Position.X;

			while (leftPosition >= 0 && this.GetCharType(currentRow.Text.Substring(leftPosition, 1)) == characterType)
				leftPosition--;

			while (rightPosition <= currentRow.Text.Length - 1 && this.GetCharType(currentRow.Text.Substring(rightPosition, 1)) == characterType)
				rightPosition++;

			this.Selection.Bounds.FirstRow = this.Selection.Bounds.LastRow = currentRow.Index;
			this.Selection.Bounds.FirstColumn = leftPosition + 1;
			this.Selection.Bounds.LastColumn = rightPosition;

			this.Caret.Position.X = rightPosition;
			this.Caret.SetPosition(Caret.Position);

			this.Redraw();
		}

		#endregion

		#region Public

		/// <summary>
		/// Removes the focus.
		/// </summary>
		public void RemoveFocus()
		{
			if (this.ContainsFocus == false)
			{
				caretTimer.Enabled = false;
				this.Caret.Blink = false;
			}

			this.Redraw();
		}

		/// <summary>
		/// Setups the view point members of the TextEditorBase.
		/// </summary>
		public void SetupViewPoint()
		{
			if (this.ViewPoint.RowHeight == 0)
				this.ViewPoint.RowHeight = 48;

			if (this.ViewPoint.CharWidth == 0)
				this.ViewPoint.CharWidth = 16;

			this.ViewPoint.FirstVisibleColumn = hScroll.Value;
			this.ViewPoint.FirstVisibleRow = vScroll.Value;

			this.ViewPoint.VisibleRowCount = 0;
			if (hScroll.Visible == true)
				this.ViewPoint.VisibleRowCount = (this.Height - hScroll.Height) / this.ViewPoint.RowHeight + 1;
			else
				this.ViewPoint.VisibleRowCount = (this.Height - hScroll.Height) / this.ViewPoint.RowHeight + 2;

			if (this.ShowGutterMargin == true)
				this.ViewPoint.GutterMarginWidth = this.GutterMarginWidth;
			else
				this.ViewPoint.GutterMarginWidth = 0;

			if (this.ShowLineNumbers == true)
			{
				int    rowCount         = (this.Document.Count).ToString().Length;
				string lineNumberString = new string('9', rowCount);

				this.ViewPoint.LineNumberMarginWidth = 25 + this.Painter.MeasureString(lineNumberString).Width;
			}
			else
				this.ViewPoint.LineNumberMarginWidth = 0;

			this.ViewPoint.TotalMarginWidth = this.ViewPoint.GutterMarginWidth + this.ViewPoint.LineNumberMarginWidth;
			if (this.Document.Folding == true)
				this.ViewPoint.TextMargin = this.ViewPoint.TotalMarginWidth + 20;
			else
				this.ViewPoint.TextMargin = this.ViewPoint.TotalMarginWidth + 7;

			this.ViewPoint.ClientAreaWidth = this.Width - vScroll.Width - this.ViewPoint.TextMargin;
			this.ViewPoint.ClientAreaStart = this.ViewPoint.FirstVisibleColumn * this.ViewPoint.CharWidth;
		}

		/// <summary>
		/// Gets the pixel at the text point.
		/// </summary>
		/// <param name="textPoint">The text point.</param>
		/// <returns>The found point.</returns>
		public Point GetPixelAtTextPoint(TextPoint textPoint)
		{
			return this.Painter.GetPixelAtTextPoint(textPoint);
		}

		/// <summary>
		/// Calculates the maximum character width.
		/// </summary>
		public void CalculateMaxCharWidth()
		{
			maxCharWidth = this.Painter.GetMaxCharWidth();
		}

		/// <summary>
		/// Sets the maximum amount of "scrolling space" available to the horizontal ScrollBar.
		/// </summary>
		public void SetMaxHorizontalScroll()
		{
			this.CalculateMaxCharWidth();

			int charWidth = this.ViewPoint.CharWidth;
			if (charWidth == 0)
				charWidth = 1;

			if (this.ViewPoint.ClientAreaWidth / charWidth < 0)
			{
				hScroll.Maximum = 1000;
				return;
			}

			hScroll.LargeChange = this.ViewPoint.ClientAreaWidth / charWidth;

			try
			{
				int maximumLength = 0;
				for (int rowCount = this.ViewPoint.FirstVisibleRow; rowCount < this.Document.VisibleRows.Count; rowCount++)
				{
					if (rowCount >= this.ViewPoint.VisibleRowCount + this.ViewPoint.FirstVisibleRow)
						break;

					string rowText = "";
					if (this.Document.VisibleRows[rowCount].IsCollapsed == true)
						rowText = this.Document.VisibleRows[rowCount].VirtualCollapsedRow.Text;
					else
						rowText = this.Document.VisibleRows[rowCount].Text;

					rowText = rowText.Replace("\t", new string(' ', this.TabSpaces));
					if (rowText.Length > maximumLength)
						maximumLength = rowText.Length;
				}

				int pixels = maximumLength * maxCharWidth;
				int chars = pixels / charWidth;

				if (hScroll.Value <= chars)
					hScroll.Maximum = chars;
			}
			catch
			{
				hScroll.Maximum = 1000;
			}

			if (hScroll.Maximum > hScroll.LargeChange)
				hScroll.Show();
			else
				hScroll.Hide();
		}

		/// <summary>
		/// Sets up the ScrollBars.
		/// </summary>
		public void InitScrollbars()
		{
			if (this.Document.VisibleRows.Count > 0)
			{
				vScroll.Maximum = Document.VisibleRows.Count + 1;
				vScroll.LargeChange = ViewPoint.VisibleRowCount;
				this.SetMaxHorizontalScroll();
			}
			else
				vScroll.Maximum = 1;

			if (vScroll.Maximum > vScroll.LargeChange)
				vScroll.Show();
			else
				vScroll.Hide();
		}

		/// <summary>
		/// Initializes the TextEditorBase's Painter's graphics.
		/// </summary>
		public void InitGraphics()
		{
			this.Painter.InitGraphics();
		}

		/// <summary>
		/// Inserts the specified text at the caret's current position.
		/// </summary>
		/// <param name="text">Text to insert.</param>
		public void InsertText(string text)
		{
			this.Caret.CropPosition();
			if (this.Selection.IsValid == true)
			{
				this.Selection.DeleteSelection();
				this.InsertText(text);
			}
			else
			{
				if (overWrite == false || text.Length > 1)
				{
					TextPoint textPoint = this.Document.InsertText(text, this.Caret.Position.X, this.Caret.Position.Y);
					this.Caret.CurrentRow.Parse(true);

					if (text.Length == 1)
					{
						this.Caret.SetPosition(textPoint);
						this.Caret.CaretMoved(false);
					}
					else
					{
						this.Document.ResetVisibleRows();
						this.Caret.SetPosition(textPoint);
						this.Caret.CaretMoved(false);
					}
				}
				else
				{
					TextRange textRange   = new TextRange();
					textRange.FirstColumn = this.Caret.Position.X;
					textRange.FirstRow    = this.Caret.Position.Y;
					textRange.LastColumn  = this.Caret.Position.X + 1;
					textRange.LastRow     = this.Caret.Position.Y;

					UndoBlockCollection undoBlockCollection = new UndoBlockCollection();
					UndoBlock           undoBlock           = new UndoBlock();

					undoBlock.Action   = UndoAction.DeleteRange;
					undoBlock.Text     = this.Document.GetRange(textRange);
					undoBlock.Position = this.Caret.Position;

					undoBlockCollection.Add(undoBlock);
					this.Document.DeleteRange(textRange, false);

					undoBlock        = new UndoBlock();
					undoBlock.Action = UndoAction.InsertRange;

					undoBlock.Text     = text;
					undoBlock.Position = this.Caret.Position;
					undoBlockCollection.Add(undoBlock);

					this.Document.AddToUndoList(undoBlockCollection);
					this.Document.InsertText(text, this.Caret.Position.X, this.Caret.Position.Y, false);

					this.Caret.CurrentRow.Parse(true);
					this.Caret.MoveRight(false);
				}
			}
		}

		/// <summary>
		/// Gets all keywords of the current language and returns them in a list.
		/// </summary>
		/// <returns>List containing all found keywords.</returns>
		public ArrayList ExtractKeywords()
		{
			ArrayList keywordList = new ArrayList();
			Language  language    = this.Document.Parser.Language;

			for (int i = 0; i <= language.Blocks.Length - 1; i++)
			{
				Block block = language.Blocks[i];
				if (block.KeywordsList.Count > 0)
				{
					for (int keywordIndex = 0; keywordIndex <= block.KeywordsList.Count - 1; keywordIndex++)
					{
						PatternList patternList = block.KeywordsList[keywordIndex];
						foreach (string key in patternList.SimplePatterns.Keys)
						{
							if (keywordList.Contains(key) == false)
								keywordList.Add(key);
						}

					}
				}
			}

			return keywordList;
		}

		/// <summary>
		/// Shows the Goto Line dialog.
		/// </summary>
		public void ShowGotoLine()
		{
			GotoLine gotoLineDialog = new GotoLine(this, this.Document.Count);
			gotoLineDialog.ShowDialog(this.TopLevelControl);
		}

		/// <summary>
		/// Selects the specified row and scrolls to it.
		/// </summary>
		/// <param name="rowIndex">Row to select and scroll to.</param>
		public void GotoLine(int rowIndex)
		{
			if (rowIndex >= this.Document.Count)
				rowIndex = this.Document.Count - 1;

			if (rowIndex < 0)
				rowIndex = 0;

			this.Caret.Position.Y = rowIndex;
			this.Caret.Position.X = 0;
			this.Caret.CurrentRow.EnsureVisible();

			this.ClearSelection();
			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Clears the selection.
		/// </summary>
		public void ClearSelection()
		{
			this.Selection.ClearSelection();
			this.Redraw();
		}

		/// <summary>
		/// Returns whether the X and Y coordinates is over the current selection.
		/// </summary>
		/// <param name="x">The X coordinate.</param>
		/// <param name="y">The Y coordinate.</param>
		/// <returns>Whether the X and Y coordinates is the over the current selection.</returns>
		public bool IsOverSelection(int x, int y)
		{
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(x, y);
			if (textPoint.Y >= this.Selection.LogicalBounds.FirstRow && textPoint.Y <= this.Selection.LogicalBounds.LastRow && this.Selection.IsValid == true)
			{
				if (textPoint.Y > this.Selection.LogicalBounds.FirstRow && textPoint.Y < this.Selection.LogicalBounds.LastRow && this.Selection.IsValid == true)
					return true;
				else
				{
					if (textPoint.Y == this.Selection.LogicalBounds.FirstRow && this.Selection.LogicalBounds.FirstRow == this.Selection.LogicalBounds.LastRow)
					{
						if (textPoint.X >= this.Selection.LogicalBounds.FirstColumn && textPoint.X <= this.Selection.LogicalBounds.LastColumn)
							return true;
						else
							return false;
					}
					else if (textPoint.X >= this.Selection.LogicalBounds.FirstColumn && textPoint.Y == this.Selection.LogicalBounds.FirstRow)
						return true;
					else if (textPoint.X <= this.Selection.LogicalBounds.LastColumn && textPoint.Y == this.Selection.LogicalBounds.LastRow)
						return true;
					else
						return false;
				}
			}
			else
				return false;
		}

		/// <summary>
		/// If the specified TextPoint isn't visible, scrolls to it.
		/// </summary>
		/// <param name="textPoint">TextPoint to scroll to.</param>
		public void ScrollIntoView(TextPoint textPoint)
		{
			TextPoint tempTextPoint = this.Caret.Position;
			this.Caret.Position = textPoint;

			if (this.Caret.CurrentRow != null)
				this.Caret.CurrentRow.EnsureVisible();

			this.ScrollIntoView();
			this.Caret.Position = tempTextPoint;
			this.Invalidate();
		}

		/// <summary>
		/// If the specified Row isn't visible, scrolls to it.
		/// </summary>
		/// <param name="rowIndex">Row to scroll to.</param>
		public void ScrollIntoView(int rowIndex)
		{
			Row row = this.Document[rowIndex];
			row.EnsureVisible();

			vScroll.Value = row.VisibleIndex;
			this.Invalidate();
		}

		/// <summary>
		/// If the current caret position is out of view, scrolls to it.
		/// </summary>
		public void ScrollIntoView()
		{
			this.InitScrollbars();
			this.Caret.CropPosition();
			try
			{
				Row currentRow = this.Caret.CurrentRow;
				if (currentRow.VisibleIndex >= this.ViewPoint.FirstVisibleRow + this.ViewPoint.VisibleRowCount - 2)
				{
					int difference = this.Caret.CurrentRow.VisibleIndex - (this.ViewPoint.FirstVisibleRow + this.ViewPoint.VisibleRowCount - 2) + 
						this.ViewPoint.FirstVisibleRow;

					if (difference > this.Document.VisibleRows.Count - 1)
						difference = this.Document.VisibleRows.Count - 1;

					Row visibleRow = this.Document.VisibleRows[difference];
					int index = visibleRow.VisibleIndex;
					if (index != -1)
						vScroll.Value = index;
				}
			}
			catch
			{
			}

			if (this.Caret.CurrentRow.VisibleIndex < this.ViewPoint.FirstVisibleRow)
			{
				Row currentRow = this.Caret.CurrentRow;
				int index = currentRow.VisibleIndex;
				if (index != -1)
					vScroll.Value = index;
			}

			int x = 0;
			Row row = this.Caret.CurrentRow;
			if (Caret.CurrentRow.IsCollapsedEndPart)
			{
				x  = this.Painter.MeasureRow(row, this.Caret.Position.X).Width + this.Caret.CurrentRow.ExpansionPixelStart;
				x -= this.Painter.MeasureRow(row, row.ExpansionStartChar).Width;

				if (x >= this.ViewPoint.ClientAreaWidth + this.ViewPoint.ClientAreaStart)
					hScroll.Value = Math.Min(hScroll.Maximum, ((x - this.ViewPoint.ClientAreaWidth) / this.ViewPoint.CharWidth) + 15);

				if (x < this.ViewPoint.ClientAreaStart + 10)
					hScroll.Value = Math.Max(hScroll.Minimum, ((x) / this.ViewPoint.CharWidth) - 15);
			}
			else
			{
				x = this.Painter.MeasureRow(row, this.Caret.Position.X).Width;

				if (x >= this.ViewPoint.ClientAreaWidth + this.ViewPoint.ClientAreaStart)
					hScroll.Value = Math.Min(hScroll.Maximum, ((x - this.ViewPoint.ClientAreaWidth) / this.ViewPoint.CharWidth) + 15);

				if (x < this.ViewPoint.ClientAreaStart)
					hScroll.Value = Math.Max(hScroll.Minimum, (x / this.ViewPoint.CharWidth) - 15);
			}
		}

		/// <summary>
		/// Scrolls to the next bookmark.
		/// </summary>
		public void GotoNextBookmark()
		{
			int index = 0;
			index = this.Document.GetNextBookmark(this.Caret.Position.Y);

			this.Caret.SetPosition(new TextPoint(0, index));
			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Scrolls to the previous bookmark.
		/// </summary>
		public void GotoPreviousBookmark()
		{
			int index = 0;
			index = this.Document.GetPreviousBookmark(this.Caret.Position.Y);

			this.Caret.SetPosition(new TextPoint(0, index));
			this.ScrollIntoView();
			this.Redraw();
		}

		/// <summary>
		/// Selects the next match depending on the specified conditions.
		/// </summary>
		/// <param name="Pattern">Pattern to search for.</param>
		/// <param name="matchCase">Whether the pattern should match case.</param>
		/// <param name="wholeWords">Whether the pattern should be a whole word.</param>
		/// <param name="useRegularExpressions">Whether to use regular expressions when searching.</param>
		/// <returns>Found match.</returns>
		public bool SelectNext(string pattern, bool matchCase, bool wholeWords, bool useRegularExpressions)
		{
			string newPattern = pattern;
			int startColumn = this.Caret.Position.X;
			int startRow    = this.Caret.Position.Y;

			for (int rowIndex = this.Caret.Position.Y; rowIndex < this.Document.Count; rowIndex++)
			{
				Row row = this.Document[rowIndex];
				if (useRegularExpressions == true)
				{
					Regex regex = new Regex(pattern);
					int matchColumn = startColumn;
					if (rowIndex == startRow)
					{
						if (startColumn < row.Text.Length)
							continue;
					}
					else
						matchColumn = 0;

					Match match = regex.Match(row.Text, matchColumn);
					if (match.Success == true)
					{
						this.SelectPattern(rowIndex, match.Index, match.Length);
						return true;
					}
				}
				else
				{
					int column = -1;
					string spacedText = "";
					string rowText = row.Text;
					if (wholeWords == true)
					{
						spacedText = " " + row.Text + " ";
						rowText = "";
						newPattern = " " + pattern + " ";
						foreach (char character in spacedText)
						{
							if (".,+-*^\\/()[]{}@:;'?£$#%& \t=<>".IndexOf(character) >= 0)
								rowText += " ";
							else
								rowText += character;
						}
					}

					if (matchCase == false)
					{
						rowText = rowText.ToLower();
						newPattern = newPattern.ToLower();
					}

					column = rowText.IndexOf(newPattern);
					if (column >= startColumn || (rowIndex > startRow && column >= 0))
					{
						this.SelectPattern(rowIndex, column, pattern.Length);
						return true;
					}
				}
			}

			return false;
		}

		/// <summary>
		/// Replaces the current selection with the specified text.
		/// </summary>
		/// <param name="text">Text to replace selection with.</param>
		/// <returns>Whether the replacing was successful.</returns>
		public bool ReplaceSelection(string text)
		{
			if (this.Selection.IsValid == false)
				return false;

			int x = this.Selection.LogicalBounds.FirstColumn;
			int y = this.Selection.LogicalBounds.FirstRow;

			TextRange textRange = new TextRange(this.Selection.Bounds.FirstColumn, this.Selection.Bounds.FirstRow,
				this.Selection.Bounds.LastColumn, this.Selection.Bounds.LastRow);

			TextRange newTextRange = this.Document.ReplaceRange(textRange, text, true);
			this.Selection.Bounds = newTextRange;
			this.Caret.Position.X = newTextRange.LastColumn;
			this.Caret.Position.Y = newTextRange.LastRow;

			return true;
		}

		/// <summary>
		/// Toggles a bookmark at the line the user is currently on.
		/// </summary>
		public void ToggleBookmark()
		{
			this.Document[this.Caret.Position.Y].Bookmarked = this.Document[this.Caret.Position.Y].Bookmarked == false;
			this.Redraw();
		}

		/// <summary>
		/// Deletes the letter after the user's current selection.
		/// </summary>
		public void Delete()
		{
			this.DeleteForward();
			this.Refresh();
		}

		/// <summary>
		/// Selects all the content of the TextEditorBase.
		/// </summary>
		public void SelectAll()
		{
			this.Selection.SelectAll();
			this.Redraw();
		}

		/// <summary>
		/// Pastes the user's clipboard to the user's current selection. (If the clipboard content is valid)
		/// </summary>
		public void Paste()
		{
			this.PasteText();
			this.Refresh();
		}

		/// <summary>
		/// Copies the user's current selection to his/her clipboard.
		/// </summary>
		public void Copy()
		{
			this.CopyText();
		}

		/// <summary>
		/// Copies the user's current selection to his/her clipboard and deletes the selection.
		/// </summary>
		public void Cut()
		{
			this.CopyText();
			this.Selection.DeleteSelection();
		}

		/// <summary>
		/// Removes the row the user is currently on.
		/// </summary>
		public void RemoveCurrentRow()
		{
			if (this.Caret.CurrentRow != null && this.Document.Count > 1)
			{
				this.Document.Remove(this.Caret.CurrentRow.Index, true);
				this.Document.ResetVisibleRows();

				this.Caret.CropPosition();
				this.Caret.CurrentRow.Text = Caret.CurrentRow.Text;
				this.Caret.CurrentRow.Parse(true);

				this.Document.ResetVisibleRows();
				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// If the current selection is valid, does a Cut action, else, removes the current row.
		/// </summary>
		public void CutClear()
		{
			if (this.Selection.IsValid == true)
				this.Cut();
			else
				this.RemoveCurrentRow();
		}

		/// <summary>
		/// Redoes the last undone action by the user.
		/// </summary>
		public void Redo()
		{
			TextPoint textPoint = this.Document.Redo();
			if (textPoint.X != -1 && textPoint.Y != -1)
			{
				this.Caret.Position = textPoint;
				this.Selection.ClearSelection();
				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// Undoes the last action by the user.
		/// </summary>
		public void Undo()
		{
			TextPoint textPoint = this.Document.Undo();
			if (textPoint.X != -1 && textPoint.Y != -1)
			{
				this.Caret.Position = textPoint;
				this.Selection.ClearSelection();
				this.ScrollIntoView();
			}
		}

		/// <summary>
		/// Gets the text point at the given coordinates.
		/// </summary>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		/// <returns>The found text point.</returns>
		public TextPoint GetTextPointAtPixel(int x, int y)
		{
			return this.Painter.GetTextPointAtPixel(x, y);
		}

		/// <summary>
		/// Shows the Find form.
		/// </summary>
		public void ShowFind()
		{
			if (this.FindReplaceDialog != null)
			{
				this.FindReplaceDialog.TopLevel = true;
				if (this.TopLevelControl is Form)
					this.FindReplaceDialog.Owner = this.TopLevelControl as Form;

				this.FindReplaceDialog.ShowFind();
			}
		}

		/// <summary>
		/// Shows the Replace form.
		/// </summary>
		public void ShowReplace()
		{
			if (this.FindReplaceDialog != null)
			{
				this.FindReplaceDialog.TopLevel = true;
				if (this.TopLevelControl is Form)
					this.FindReplaceDialog.Owner = this.TopLevelControl as Form;

				this.FindReplaceDialog.ShowReplace();
			}
		}

		/// <summary>
		/// Finds the next match.
		/// </summary>
		public void FindNext()
		{
			this.FindReplaceDialog.FindNext();
		}

		/// <summary>
		/// Scrolls to the specified row and centers it in the TextEditorBase.
		/// </summary>
		/// <param name="rowIndex">Row to scroll to and center.</param>
		public void CenterInView(int rowIndex)
		{
			if (this.Document == null && rowIndex > this.Document.Count && rowIndex < 0)
				return;

			Row row = this.Document[rowIndex];
			int topRow = 0;
			int visibleLines = 0;

			if (row != null)
			{
				this.InitScrollbars();
				row.EnsureVisible();

				visibleLines = this.ViewPoint.VisibleRowCount / 2;
				if (row.VisibleIndex < visibleLines)
					topRow = 0;
				else
					topRow = row.VisibleIndex - visibleLines;

				vScroll.Value = topRow;
				this.Invalidate();
			}
		}

		#endregion

		#region Protected

		/// <summary>
		/// Processes Windows messages from the OS.
		/// </summary>
		/// <param name="m">Windows message to process.</param>
		protected override void WndProc(ref Message m)
		{
			if (m.Msg == (int)WindowMessage.WM_DESTROY)
			{
				try
				{
					if (this.FindReplaceDialog != null)
						this.FindReplaceDialog.Close();
				}
				catch
				{
				}
			}

			base.WndProc(ref m);
		}

		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		protected override void Dispose(bool disposing)
		{
			this.RemoveFocus();
			if (disposing == true)
			{
				if (components != null)
					components.Dispose();

				if (this.Painter != null)
					this.Painter.Dispose();
			}

			base.Dispose(disposing);
		}

		#endregion

		#region EventHandlers

		/// <summary>
		/// Called when [scroll handler begin scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnScrollHandlerBeginScroll(object sender, EventArgs e) {
			scrollHandlerCurrentPosition = 0;
			this.ViewPoint.YOffset = 0;
		}

		/// <summary>
		/// Called when [scroll handler end scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnScrollHandlerEndScroll(object sender, EventArgs e) {
			this.ViewPoint.YOffset = 0;
			this.Redraw();
		}

		/// <summary>
		/// Called when [scroll handler scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="Storm.TextEditor.Interacting.Scrolling.ScrollEventArgs"/> instance containing the event data.</param>
		private void OnScrollHandlerScroll(object sender, ScrollEventArgs e) {
			if (e.DeltaY < 0 && vScroll.Value == 0) {
				this.ViewPoint.YOffset = 0;
				this.Redraw();

				return;
			}

			if (e.DeltaY > 0 && vScroll.Value >= vScroll.Maximum - this.ViewPoint.VisibleRowCount + 1) {
				this.ViewPoint.YOffset = 0;
				this.Redraw();

				return;
			}

			scrollHandlerCurrentPosition += (double)e.DeltaY / (double)8;

			int rowScrollCount = (int)scrollHandlerCurrentPosition / this.ViewPoint.RowHeight;
			if (rowScrollCount != 0)
				scrollHandlerCurrentPosition -= rowScrollCount * this.ViewPoint.RowHeight;

			this.ViewPoint.YOffset = -(int)scrollHandlerCurrentPosition;
			this.ScrollScreen(rowScrollCount);
		}

		/// <summary>
		/// Raises the <see cref="E:ClipboardUpdated"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.CopyEventArgs"/> instance containing the event data.</param>
		private void OnClipboardUpdated(CopyEventArgs e)
		{
			if (this.ClipboardUpdated != null)
				this.ClipboardUpdated(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowMouseDown"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowMouseDown(RowMouseEventArgs e)
		{
			if (this.RowMouseDown != null)
				this.RowMouseDown(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowMouseMove"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowMouseMove(RowMouseEventArgs e)
		{
			if (this.RowMouseMove != null)
				this.RowMouseMove(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowMouseUp"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowMouseUp(RowMouseEventArgs e)
		{
			if (this.RowMouseUp != null)
				this.RowMouseUp(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowClick"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowClick(RowMouseEventArgs e)
		{
			if (this.RowClick != null)
				this.RowClick(this, e);
		}

		/// <summary>
		/// Raises the <see cref="E:RowDoubleClick"/> event.
		/// </summary>
		/// <param name="e">The <see cref="Storm.TextEditor.Editor.RowMouseEventArgs"/> instance containing the event data.</param>
		private void OnRowDoubleClick(RowMouseEventArgs e)
		{
			if (this.RowDoubleClick != null)
				this.RowDoubleClick(this, e);
		}

		/// <summary>
		/// Raises the Load event.
		/// </summary>
		/// <param name="e">EventArgs.</param>
		protected override void OnLoad(EventArgs e)
		{
			this.UpdateContent();
			this.Refresh();
		}

		/// <summary>
		/// Called when [parse].
		/// </summary>
		internal void OnParse()
		{
			this.Redraw();
		}

		/// <summary>
		/// Called when [change].
		/// </summary>
		internal void OnChange()
		{
			if (this.Caret.Position.Y > this.Document.Count - 1)
			{
				this.Caret.Position.Y = this.Document.Count - 1;
				this.ScrollIntoView();
			}

			if (this.VirtualWhitespace == false && this.Caret.CurrentRow != null && this.Caret.Position.X > this.Caret.CurrentRow.Text.Length)
			{
				this.Caret.Position.X = this.Caret.CurrentRow.Text.Length;
				this.Redraw();
			}

			if (this.ContainsFocus == false)
				this.Selection.ClearSelection();

			if (this.Selection.LogicalBounds.FirstRow > this.Document.Count)
			{
				this.Selection.Bounds.FirstColumn = Caret.Position.X;
				this.Selection.Bounds.LastColumn  = Caret.Position.X;
				this.Selection.Bounds.FirstRow    = Caret.Position.Y;
				this.Selection.Bounds.LastRow     = Caret.Position.Y;
			}

			if (this.Selection.LogicalBounds.LastRow > this.Document.Count)
			{
				this.Selection.Bounds.FirstColumn = this.Caret.Position.X;
				this.Selection.Bounds.LastColumn  = this.Caret.Position.X;
				this.Selection.Bounds.FirstRow    = this.Caret.Position.Y;
				this.Selection.Bounds.LastRow     = this.Caret.Position.Y;
			}

			this.Redraw();
		}

		/// <summary>
		/// Determines whether the specified key is a regular input key or a special key that requires preprocessing.
		/// </summary>
		/// <param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys"/> values.</param>
		/// <returns>
		/// true if the specified key is a regular input key; otherwise, false.
		/// </returns>
		protected override bool IsInputKey(Keys keyData)
		{
			switch (keyData & Keys.KeyCode)
			{
				case Keys.Up:
					return true;
				case Keys.Down:
					return true;
				case Keys.Right:
					return true;
				case Keys.Left:
					return true;
				case Keys.Tab:
					return true;
				default:
					return base.IsInputKey(keyData);
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.KeyDown"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs"/> that contains the event data.</param>
		protected override void OnKeyDown(KeyEventArgs e)
		{
			keyDownHandled = e.Handled;

			if (this.KeyDown != null)
				this.KeyDown(this, e);

			if (e.Handled == false)
			{
				foreach (KeyboardAction keyboardAction in this.TextEditor.KeyboardActions)
				{
					if (this.ReadOnly == false || keyboardAction.AllowReadOnly == true)
					{
						if (keyboardAction.Key == e.KeyCode && keyboardAction.Alt == e.Alt && keyboardAction.Shift == e.Shift
							&& keyboardAction.Control == e.Control)
						{
							keyboardAction.Action();
						}
					}
				}

				switch (e.KeyCode)
				{
					case Keys.ShiftKey:
					case Keys.ControlKey:
					case Keys.Alt:
						break;
					case Keys.Down:
						if (e.Control == true)
							this.ScrollScreen(1);
						else
						{
							if (codeCompletionWindow.Visible == true)
								codeCompletionWindow.UpdateInput((char)Keys.Down, e.Control, e.Alt, e.Shift);
							else
								this.Caret.MoveDown(e.Shift);
						}

						this.Redraw();
						break;
					case Keys.Up:
						if (e.Control == true)
							this.ScrollScreen(-1);
						else
						{
							if (codeCompletionWindow.Visible == true)
								codeCompletionWindow.UpdateInput((char)Keys.Up, e.Control, e.Alt, e.Shift);
							else
								this.Caret.MoveUp(e.Shift);
						}

						this.Redraw();
						break;
					case Keys.Left:
						if (e.Control == true)
							this.MoveCaretToPreviousWord(e.Shift);
						else
							this.Caret.MoveLeft(e.Shift);

						this.Redraw();
						break;
					case Keys.Right:
						if (e.Control == true)
							this.MoveCaretToNextWord(e.Shift);
						else
							this.Caret.MoveRight(e.Shift);

						this.Redraw();
						break;
					case Keys.End:
						if (e.Control == true)
							this.Caret.MoveAbsoluteEnd(e.Shift);
						else
							this.Caret.MoveEnd(e.Shift);

						this.Redraw();
						break;
					case Keys.Home:
						if (e.Control == true)
							this.Caret.MoveAbsoluteHome(e.Shift);
						else
							this.Caret.MoveHome(e.Shift);

						this.Redraw();
						break;
					case Keys.PageDown:
						this.Caret.MoveDown(this.ViewPoint.VisibleRowCount - 2, e.Shift);

						this.Redraw();
						break;
					case Keys.PageUp:
						this.Caret.MoveUp(this.ViewPoint.VisibleRowCount - 2, e.Shift);

						this.Redraw();
						break;
					default:
						break;
				}

				if (this.ReadOnly == false)
				{
					switch (e.KeyCode)
					{
						case Keys.Enter:
							if (e.Control == true)
							{
								if (this.Caret.CurrentRow.CanFold == true)
								{
									this.Caret.MoveHome(false);
									this.Document.ToggleRowExpansion(Caret.CurrentRow);
									this.Redraw();
								}
							}
							else
							{
								if (codeCompletionWindow.Visible == true)
									codeCompletionWindow.UpdateInput((char)Keys.Enter, e.Control, e.Alt, e.Shift);
								else
									this.InsertEnter();
							}

							break;
						case Keys.Back:
							if (e.Control == false)
							{
								this.DeleteBackwards();
								codeCompletionWindow.UpdateInput((char)Keys.Back, e.Control, e.Alt, e.Shift);
							}
							else
							{
								if (this.Selection.IsValid == true)
									this.Selection.DeleteSelection();
								else
								{
									this.Selection.ClearSelection();
									this.MoveCaretToPreviousWord(true);
									this.Selection.DeleteSelection();
								}

								this.Caret.CurrentRow.Parse(true);
							}

							break;
						case Keys.Delete:
							if (e.Control == false && e.Alt == false && e.Shift == false)
							{
								this.Delete();
								codeCompletionWindow.UpdateInput((char)Keys.Delete, e.Control, e.Alt, e.Shift);
							}
							else if (e.Control == true && e.Alt == false && e.Shift == false)
							{
								if (this.Selection.IsValid == true)
									this.Cut();
								else
								{
									this.Selection.ClearSelection();
									this.MoveCaretToNextWord(true);
									this.Selection.DeleteSelection();
								}

								this.Caret.CurrentRow.Parse(true);
							}

							break;
						case Keys.Insert:
							if (e.Control == false && e.Alt == false && e.Shift == false)
								overWrite = overWrite == false;

							break;
						case Keys.Tab:
							if (this.Selection.IsValid == false)
							{
								if (keepTabs == false)
								{
									for (int i = 0; i < tabSpaces; i++)
										this.InsertText(" ");
								}
								else
									this.InsertText("\t");

								codeCompletionWindow.UpdateInput((char)Keys.Tab, e.Control, e.Alt, e.Shift);
							}
							
							break;
						default:
							break;
					}
				}

				this.Caret.Blink = true;
			}

			base.OnKeyDown(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.KeyPress"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyPressEventArgs"/> that contains the event data.</param>
		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			base.OnKeyPress(e);

			if (e.Handled == false && keyDownHandled == false && e.KeyChar != (char)127)
			{
				if (((int)e.KeyChar) < 32)
					return;

				if (this.ReadOnly == false)
				{
					this.InsertText(e.KeyChar.ToString());

					bool shift = e.KeyChar == '(' || e.KeyChar == ')';
					codeCompletionWindow.UpdateInput(e.KeyChar, false, false, shift);
					if (this.Indent == IndentStyle.Scope)
					{
						if (this.Caret.CurrentRow.ShouldOutdent == true)
							this.OutdentEndRow();
					}
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseDown(MouseEventArgs e)
		{
			mouseX = e.X;
			mouseY = e.Y;
			mouseButton = e.Button;

			this.Focus();

			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(e.X, e.Y);
			if (textPoint.Y >= 0 && textPoint.Y < this.Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = e.Button;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (e.X >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (e.X < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (e.X < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (e.X < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowMouseDown(rowMouseEventArgs);

			row = this.Document[textPoint.Y];
			if (row != null)
			{
				if (e.X >= row.ExpansionPixelEnd && row.IsCollapsed == true)
				{
					if (row.ExpansionStartSegment != null)
					{
						if (row.ExpansionStartSegment.StartRow != null && row.ExpansionStartSegment.EndRow != null && 
							row.ExpansionStartSegment.Expanded == false)
						{
							if (this.IsOverSelection(e.X, e.Y) == false)
							{
								this.Caret.Position.X = textPoint.X;
								this.Caret.Position.Y = textPoint.Y;
								this.Selection.ClearSelection();

								Row expansionEndRow = row.ExpansionEndRow;
								int expansionStartCharIndex = expansionEndRow.ExpansionStartChar;

								this.Caret.Position.X = expansionStartCharIndex;
								this.Caret.Position.Y = expansionEndRow.Index;
								this.Selection.MakeSelection();

								this.Redraw();
								this.ViewPoint.Action = TextAction.Select;

								return;
							}
						}
					}
				}
			}

			bool shift = NativeUser32Api.IsKeyPressed(Keys.ShiftKey);

			if (e.X > this.ViewPoint.TotalMarginWidth)
			{
				if (e.X > this.ViewPoint.TextMargin - 8)
				{
					if (this.IsOverSelection(e.X, e.Y) == false)
					{
						if (e.Button == MouseButtons.Left)
						{
							if (shift == false)
							{
								Word textPointWord = this.Document.GetWordFromPos(textPoint);
								if (textPointWord != null && textPointWord.Pattern != null && textPointWord.Pattern.Category != "")
								{
									WordMouseEventArgs wordMouseEventArgs = new WordMouseEventArgs();
									wordMouseEventArgs.Pattern = textPointWord.Pattern;
									wordMouseEventArgs.Button  = e.Button;
									wordMouseEventArgs.Word    = textPointWord;

									this.TextEditor.OnWordMouseDown(ref wordMouseEventArgs);
								}

								this.ViewPoint.Action = TextAction.Select;
								this.Caret.SetPosition(textPoint);
								this.Selection.ClearSelection();

								this.Caret.Blink = true;
								this.Redraw();
							}
							else
							{
								this.ViewPoint.Action = TextAction.Select;
								this.Caret.SetPosition(textPoint);
								this.Selection.MakeSelection();

								this.Caret.Blink = true;
								this.Redraw();
							}
						}
					}
				}
				else
				{
					if (row.ExpansionStartSegment != null)
					{
						this.Caret.SetPosition(new TextPoint(0, textPoint.Y));
						this.Selection.ClearSelection();

						this.Document.ToggleRowExpansion(row);
						this.Redraw();
					}
				}
			}
			else
			{
				if (e.X < this.ViewPoint.GutterMarginWidth)
				{
					if (this.TextEditor.AllowBreakPoints == true)
					{
						row = this.Document[this.Painter.GetTextPointAtPixel(e.X, e.Y).Y];
						row.Breakpoint = row.Breakpoint == false;
						this.Redraw();
					}
					else
					{
						row = this.Document[this.Painter.GetTextPointAtPixel(e.X, e.Y).Y];
						row.Breakpoint = false;
						this.Redraw();
					}
				}
				else
				{
					this.ViewPoint.Action = TextAction.Select;
					this.Caret.SetPosition(this.Painter.GetTextPointAtPixel(0, e.Y));
					this.Selection.ClearSelection();

					this.Caret.MoveDown(true);
					this.Redraw();
				}
			}

			this.SetMouseCursor(e.X, e.Y);
			base.OnMouseDown(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			mouseX = e.X;
			mouseY = e.Y;
			mouseButton = e.Button;

			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(e.X, e.Y);
			if (textPoint.Y >= 0 && textPoint.Y < Document.Count)
				row = Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = e.Button;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (e.X >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (e.X < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (e.X < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (e.X < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowMouseMove(rowMouseEventArgs);

			if (this.Document != null)
			{
				if (e.Button == MouseButtons.Left)
				{
					if (this.ViewPoint.Action == TextAction.Select)
					{
						this.Caret.Blink = true;
						this.Caret.SetPosition(textPoint);
						if (e.X <= ViewPoint.TotalMarginWidth)
							this.Caret.MoveDown(true);

						this.Caret.CropPosition();
						this.Selection.MakeSelection();
						this.ScrollIntoView();
						this.Redraw();
					}
					else if (this.ViewPoint.Action == TextAction.None)
					{
						if (this.IsOverSelection(e.X, e.Y) == true)
							this.BeginDragDrop();
					}
				}
				else
				{
					row = this.Document[textPoint.Y];
					bool showTooltip = false;

					if (row != null)
					{
						if (e.X < this.ViewPoint.TextMargin - 8 && e.X > this.ViewPoint.TotalMarginWidth)
						{
							bool shouldContinue = true;

							int startRowIndex = textPoint.Y;
							Row endRow = row.ExpansionEndRow;

							if (row.CanFold == false)
							{
								int index = row.StartSegment != null && row.StartSegment.StartRow != null ? this.Document.IndexOf(row.StartSegment.StartRow) : -1;

								if (index < 0)
									shouldContinue = false;

								startRowIndex = index;
								endRow = row.StartSegment != null ? row.StartSegment.EndRow : null;
							}

							if (shouldContinue == true)
							{
								if (this.Document[startRowIndex].Expanded == true)
								{
									for (int rowIndex = 0; rowIndex < this.Document.Count; rowIndex++)
									{
										this.Document[rowIndex].BackColor = Color.Transparent;
										this.Document[rowIndex].Hovered = false;
									}

									for (int rowIndex = startRowIndex; rowIndex <= this.Document.IndexOf(endRow); rowIndex++)
									{
										this.Document[rowIndex].BackColor = this.RowHoverBackColor;
										this.Document[rowIndex].Hovered = true;
									}
								}

								this.Redraw();
							}
						}
						else
						{
							for (int rowIndex = 0; rowIndex < this.Document.Count; rowIndex++)
							{
								this.Document[rowIndex].BackColor = Color.Transparent;
								this.Document[rowIndex].Hovered = false;
							}

							this.Redraw();
						}

						if (e.X >= row.ExpansionPixelEnd && row.IsCollapsed)
						{
							if (row.ExpansionStartSegment != null)
							{
								if (row.ExpansionStartSegment.StartRow != null && row.ExpansionStartSegment.EndRow != null &&
									row.ExpansionStartSegment.Expanded == false)
								{
									string tabbedText = "";
									for (int rowIndex = this.Document.IndexOf(row.ExpansionStartSegment.StartRow); rowIndex <= 
										row.ExpansionStartSegment.EndRow.Index; rowIndex++)
									{
										if (rowIndex > 0)
											tabbedText += "\n";

										Row currentRow = this.Document[rowIndex];
										string removedTabsText = currentRow.Text.Replace("\t", "    ");
										tabbedText += removedTabsText;
										if (rowIndex > 20)
										{
											tabbedText += "...";
											break;
										}

										rowIndex++;
									}

									tooltip.InitialDelay = TooltipDelay;
									if (tooltip.GetToolTip(this) != tabbedText)
										tooltip.SetToolTip(this, tabbedText);

									tooltip.Active = true;
									showTooltip = true;
								}
							}
						}
					}

					if (tooltip != null)
					{
						if (showTooltip == false)
							tooltip.SetToolTip(this, "");
					}
				}

				this.SetMouseCursor(e.X, e.Y);
				base.OnMouseMove(e);
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseUp(MouseEventArgs e)
		{
			mouseX = e.X;
			mouseY = e.Y;
			mouseButton = e.Button;

			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(e.X, e.Y);
			if (textPoint.Y >= 0 && textPoint.Y < Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = e.Button;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (e.X >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (e.X < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (e.X < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (e.X < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowMouseUp(rowMouseEventArgs);

			if (this.ViewPoint.Action == TextAction.None)
			{
				if (e.X > ViewPoint.TotalMarginWidth)
				{
					if (this.IsOverSelection(e.X, e.Y) == true && e.Button == MouseButtons.Left)
					{
						this.ViewPoint.Action = TextAction.Select;
						this.Caret.SetPosition(this.Painter.GetTextPointAtPixel(e.X, e.Y));

						this.Selection.ClearSelection();
						this.Redraw();
					}
				}
			}

			this.ViewPoint.Action = TextAction.None;
			base.OnMouseUp(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseWheel"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
		protected override void OnMouseWheel(MouseEventArgs e)
		{
			int scrollLines = SystemInformation.MouseWheelScrollLines;
			this.ScrollScreen(-(e.Delta / 120) * scrollLines, 2);

			base.OnMouseWheel(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
		protected override void OnPaint(PaintEventArgs e)
		{
			if (this.Document != null && Width > 0 && Height > 0)
				this.Painter.RenderAll(e.Graphics);
		}

		/// <summary>
		/// Raises the <see cref="E:Resize"/> event.
		/// </summary>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected override void OnResize(EventArgs e)
		{
			this.UpdateContent();
			base.OnResize(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DragOver"/> event.
		/// </summary>
		/// <param name="drgevent">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
		protected override void OnDragOver(DragEventArgs drgevent)
		{
			if (this.ReadOnly == false)
			{
				if (this.Document != null)
				{
					this.ViewPoint.Action = TextAction.DragText;

					Point point = this.PointToClient(new Point(drgevent.X, drgevent.Y));

					int x = point.X;
					int y = point.Y;

					if ((drgevent.KeyState & 8) == 8)
						drgevent.Effect = DragDropEffects.Copy;
					else
						drgevent.Effect = DragDropEffects.Move;

					this.Caret.SetPosition(this.Painter.GetTextPointAtPixel(x, y));
					this.Redraw();
				}
			}
			else
				drgevent.Effect = DragDropEffects.None;

			base.OnDragOver(drgevent);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DragDrop"/> event.
		/// </summary>
		/// <param name="drgevent">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
		protected override void OnDragDrop(DragEventArgs drgevent)
		{
			if (this.ReadOnly == false)
			{
				if (this.Document != null)
				{
					this.ViewPoint.Action = TextAction.None;

					int selectionStart = this.Selection.LogicalSelStart;
					int dropPosition = this.Document.PointToIntPos(this.Caret.Position);

					string dropText = drgevent.Data.GetData(typeof(string)).ToString();
					int dropTextLength = dropText.Length;

					if (dropPosition >= selectionStart && dropPosition <= selectionStart + Math.Abs(this.Selection.SelLength))
						dropPosition = selectionStart;
					else if (dropPosition >= selectionStart + dropTextLength)
						dropPosition -= dropTextLength;

					this.Document.StartUndoCapture();
					if ((drgevent.KeyState & 8) == 0)
					{
						this.TextEditor.Selection.DeleteSelection();
						this.Caret.Position = this.Document.IntPosToPoint(dropPosition);
					}

					TextPoint textPoint = this.Document.InsertText(dropText, Caret.Position.X, Caret.Position.Y);
					this.Document.EndUndoCapture();

					this.Selection.SelStart  = this.Document.PointToIntPos(this.Caret.Position);
					this.Selection.SelLength = dropTextLength;

					this.Document.ResetVisibleRows();
					this.ScrollIntoView();
					this.Redraw();

					drgevent.Effect = DragDropEffects.All;

					if (this.ParseOnPaste == true)
						this.Document.ParseAll(true);

					this.ViewPoint.Action = TextAction.None;
				}
			}

			base.OnDragDrop(drgevent);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DragLeave"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnDragLeave(EventArgs e)
		{
			this.ViewPoint.Action = TextAction.None;
			base.OnDragLeave(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.DoubleClick"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnDoubleClick(EventArgs e)
		{
			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(mouseX, mouseY);
			if (textPoint.Y >= 0 && textPoint.Y < Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = MouseButtons.None;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (mouseX >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (mouseX < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (mouseX < this.ViewPoint.LineNumberMarginWidth + ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (mouseX < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowDoubleClick(rowMouseEventArgs);

			row = this.Document[textPoint.Y];
			if (row != null)
			{
				if (mouseX >= row.ExpansionPixelEnd && row.IsCollapsed == true)
				{
					if (row.ExpansionStartSegment != null)
					{
						if (row.ExpansionStartSegment.StartRow != null && row.ExpansionStartSegment.EndRow != null &&
							row.ExpansionStartSegment.Expanded == false)
						{
							row.Expanded = true;

							this.Document.ResetVisibleRows();
							this.Redraw();

							return;
						}
					}
				}
			}

			if (mouseX > this.ViewPoint.TotalMarginWidth)
				this.SelectCurrentWord();
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.Click"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnClick(EventArgs e)
		{
			Row row = null;
			TextPoint textPoint = this.Painter.GetTextPointAtPixel(mouseX, mouseY);
			if (textPoint.Y >= 0 && textPoint.Y < this.Document.Count)
				row = this.Document[textPoint.Y];

			RowMouseEventArgs rowMouseEventArgs = new RowMouseEventArgs();
			rowMouseEventArgs.Row    = row;
			rowMouseEventArgs.Button = MouseButtons.None;
			rowMouseEventArgs.MouseX = mouseX;
			rowMouseEventArgs.MouseY = mouseY;

			if (mouseX >= this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.TextArea;
			else if (mouseX < this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.GutterArea;
			else if (mouseX < this.ViewPoint.LineNumberMarginWidth + this.ViewPoint.GutterMarginWidth)
				rowMouseEventArgs.Area = RowArea.LineNumberArea;
			else if (mouseX < this.ViewPoint.TextMargin - 7)
				rowMouseEventArgs.Area = RowArea.FoldingArea;
			else
				rowMouseEventArgs.Area = RowArea.Other;

			this.OnRowClick(rowMouseEventArgs);
		}

		/// <summary>
		/// Called when [vertical scroll bar scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.ScrollEventArgs"/> instance containing the event data.</param>
		private void OnVerticalScrollBarScroll(object sender, System.Windows.Forms.ScrollEventArgs e)
		{
			this.SetMaxHorizontalScroll();
			this.Focus();

			int difference = e.NewValue - vScroll.Value;
			if ((difference == -1 || difference == 1) && (e.Type == ScrollEventType.SmallDecrement || e.Type == ScrollEventType.SmallIncrement))
				this.ScrollScreen(difference);
			else
				this.Invalidate();
		}

		/// <summary>
		/// Called when [horitonzal scroll bar scroll].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.ScrollEventArgs"/> instance containing the event data.</param>
		private void OnHoritonzalScrollBarScroll(object sender, System.Windows.Forms.ScrollEventArgs e)
		{
			this.Focus();
			this.Invalidate();
		}

		/// <summary>
		/// Called when [caret timer tick].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnCaretTimerTick(object sender, EventArgs e)
		{
			this.Caret.Blink = this.Caret.Blink == false;
			this.RedrawCaret();
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseLeave"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnMouseLeave(EventArgs e)
		{
			base.OnMouseLeave(e);
			if (tooltip != null)
				tooltip.RemoveAll();
		}

		/// <summary>
		/// Called when [caret changed].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnCaretChanged(object sender, EventArgs e)
		{
			this.OnCaretChange();
		}

		/// <summary>
		/// Called when [leave].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnLeave(object sender, EventArgs e)
		{
			this.RemoveFocus();
		}

		/// <summary>
		/// Called when [enter].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnEnter(object sender, EventArgs e)
		{
			caretTimer.Enabled = true;
		}

		/// <summary>
		/// Called when [selection changed].
		/// </summary>
		/// <param name="s">The s.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnSelectionChanged(object s, EventArgs e)
		{
			this.OnSelectionChange();
		}

		/// <summary>
		/// Called when [caret change].
		/// </summary>
		private void OnCaretChange()
		{
			if (this.CaretChange != null)
				this.CaretChange(this, null);
		}

		/// <summary>
		/// Called when [selection change].
		/// </summary>
		private void OnSelectionChange()
		{
			if (this.SelectionChange != null)
				this.SelectionChange(this, null);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.VisibleChanged"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
		protected override void OnVisibleChanged(EventArgs e)
		{
			if (this.Visible == false)
				this.RemoveFocus();

			base.OnVisibleChanged(e);
			this.UpdateContent();
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of TextEditorBase.
		/// </summary>
		public TextEditorBase(TextEditor parent)
			: base(parent)
		{
			painter   = new DefaultPainter(this);
			selection = new Selection(this);
			caret     = new Caret(this);

			caret.Change     += this.OnCaretChanged;
			selection.Change += this.OnSelectionChanged;

			this.InitializeComponent();

			this.SetStyle(ControlStyles.AllPaintingInWmPaint, false);
			this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
			this.SetStyle(ControlStyles.Selectable, true);
			this.SetStyle(ControlStyles.ResizeRedraw, true);
			this.SetStyle(ControlStyles.Opaque, true);
			this.SetStyle(ControlStyles.UserPaint, true);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)



Comments and Discussions