/// <summary>
        /// Determines whether a location within the document is valid.
        /// </summary>
        /// <param name="Loc">The location to validate.</param>
        /// <returns>True if the supplied location is within the bounds of the document.</returns>
        public bool IsValidTextLocation(TextLocation Loc)
        {
            bool bResult = false;

            if(Loc.Line >= 0 && Loc.Line < mLines.Count && Loc.Column >= 0 && Loc.Column <= GetLineLength(Loc.Line))
            {
                bResult = true;
            }

            return bResult;
        }
		/// <summary>
		/// Event handler for when the mouse cursor has been moved.
		/// </summary>
		/// <param name="e">Information about the event.</param>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);

			if(mIsSelecting)
			{
				Rectangle TextArea = GetTextAreaRect();

				if(TextArea.Contains(e.Location))
				{
					mScrollSelectedTextTimer.Enabled = false;
					TextLocation NewCaretPos = GetCharacterPosFromCoord(e.X, e.Y);

					if(mCaretPosition != NewCaretPos)
					{
						mCaretPosition = NewCaretPos;
						UpdateCaret();
						Invalidate();
					}
				}
				else
				{
					mScrollSelectedTextTimer.Enabled = true;
				}
			}

			mLastMousePos = e.Location;
		}
 /// <summary>
 /// Finds the first occurence of the specified string within the document if it exists.
 /// </summary>
 /// <param name="Txt">The string to find.</param>
 /// <param name="StartLoc">The position within the document to begin searching.</param>
 /// <param name="EndLoc">The position within the document to end searching.</param>
 /// <param name="Flags">Flags telling the document how to conduct its search.</param>
 /// <param name="Result">The location within the document of the supplied text.</param>
 /// <returns>True if the text was found.</returns>
 public bool Find(string Txt, TextLocation StartLoc, TextLocation EndLoc, RichTextBoxFinds Flags, out FindResult Result)
 {
     if((Flags & RichTextBoxFinds.Reverse) == RichTextBoxFinds.Reverse)
     {
         return FindReverse(Txt, ref StartLoc, ref EndLoc, Flags, out Result);
     }
     else
     {
         return FindForward(Txt, ref StartLoc, ref EndLoc, Flags, out Result);
     }
 }
		/// <summary>
		/// Event handler or when the text in the "find" text box has changed or the state of the "match case" checkbox has changed.
		/// </summary>
		/// <param name="sender">The object that initiated the event.</param>
		/// <param name="e">Additional information about the event.</param>
		private void mToolStripTextBox_Find_TextChanged(object sender, EventArgs e)
		{
			if(mToolStripTextBox_Find.Text.Trim().Length == 0)
			{
				mCaretPosition = mSelectionStart;
				UpdateFindBoxColor(true);
				UpdateCaret();
				Invalidate();
			}
			else
			{
				TextLocation StartLocation = mCaretPosition <= mSelectionStart ? mCaretPosition : mSelectionStart;
				FindNext(StartLocation);
			}
		}
		/// <summary>
		/// Event handler for when a mouse button has been pressed.
		/// </summary>
		/// <param name="e">Information about the event.</param>
		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);

			if((e.Button & MouseButtons.Left) == MouseButtons.Left || (e.Button & MouseButtons.Right) == MouseButtons.Right)
			{
				bool bHadSelection = this.HasSelectedText;
				bool bUpdateCaret = true;
				TextLocation CharacterPosition = GetCharacterPosFromCoord(e.X, e.Y);
				mIsSelecting = e.Button == MouseButtons.Left;

				if(!mIsSelecting)
				{
					int SelectionStart;
					int SelectionLength;

					if(GetSelectionForLine(CharacterPosition.Line, out SelectionStart, out SelectionLength) && CharacterPosition.Column >= SelectionStart && CharacterPosition.Column < SelectionStart + SelectionLength)
					{
						bUpdateCaret = false;
					}
				}

				if(bUpdateCaret && (mCaretPosition != CharacterPosition || bHadSelection))
				{
#if !__MonoCS__
					mCaretPosition = CharacterPosition;

					ushort KeyState = GetAsyncKeyState(VK_SHIFT);

					// if shift key is not pressed
					if((KeyState & 0x8000) == 0)
					{
						mSelectionStart = mCaretPosition;

						if(bHadSelection)
						{
							OnSelectionChanged(new EventArgs());
						}
					}
					else
					{
						// if shift is pressed then we must be changing the selection
						bHadSelection = true;
						OnSelectionChanged(new EventArgs());
					}
#endif
				}

				// NOTE: This is very important. If you do not set the ActiveControl to null then
				// child controls such as the "find" toolstrip container won't give focus back!
				// This means the caret may never come back and babies will cry.
				this.ActiveControl = null;
				UpdateCaret();

				if(bHadSelection)
				{
					Invalidate();
				}
			}
		}
		/// <summary>
		/// Moves the caret up in the document.
		/// </summary>
		/// <param name="NumLines">The number of lines to move the caret up.</param>
		/// <param name="bMoveSelectionStart">Pass true if the selection start is moved with the caret.</param>
		private void MoveCaretUpLines(int NumLines, bool bMoveSelectionStart)
		{
			if(NumLines < 0)
			{
				throw new ArgumentOutOfRangeException("NumLines", "NumLines must be greater than 0.");
			}

			if(mDocument == null)
			{
				return;
			}

			mCaretPosition.Line = Math.Max(mCaretPosition.Line - NumLines, 0);

			if(mCaretPosition.Column > mDocument.GetLineLength(mCaretPosition.Line))
			{
				mCaretPosition.Column = mDocument.GetLineLength(mCaretPosition.Line);
			}

			if(bMoveSelectionStart)
			{
				mSelectionStart = mCaretPosition;
			}
		}
		/// <summary>
		/// Searches up within the text box for the specified search string.
		/// </summary>
		/// <param name="UpSearchStart">The location within the document to begin searching.
		void FindPrevious(TextLocation? UpSearchStart)
		{
			if(this.mDocument != null)
			{
				FindResult Result;
				RichTextBoxFinds Flags = (mCheckBox_MatchCase.Checked ? RichTextBoxFinds.MatchCase : RichTextBoxFinds.None) | RichTextBoxFinds.Reverse;

				if(!UpSearchStart.HasValue)
				{
					UpSearchStart = this.SelectionStart <= this.SelectionEnd ? this.SelectionStart : this.SelectionEnd;
				}

				bool bFound = true;
				string TxtToSearchFor = mToolStripTextBox_Find.Text.Trim();

				if(!this.Find(TxtToSearchFor, UpSearchStart.Value, this.mDocument.BeginningOfDocument, Flags, out Result))
				{
					if(!this.Find(TxtToSearchFor, this.mDocument.EndOfDocument, new TextLocation(UpSearchStart.Value.Line, 0), Flags, out Result))
					{
						bFound = false;
					}
				}

				UpdateFindBoxColor(bFound);
			}
		}
		/// <summary>
		/// Scrolls the caret to the beginning of the document.
		/// </summary>
		public void ScrollToHome()
		{
			if(mDocument != null)
			{
				bool bHadSelection = this.HasSelectedText;

				mSelectionStart = mCaretPosition = mDocument.BeginningOfDocument;

				if(bHadSelection)
				{
					OnSelectionChanged(new EventArgs());
				}

				ScrollToCaretOrInvalidate();
			}
		}
		/// <summary>
		/// Event handler for when the document has been modified.
		/// </summary>
		/// <param name="sender">The object that initiated the event.</param>
		/// <param name="e">Additional information about the event.</param>
		void mDocument_Modified(object sender, EventArgs e)
		{
			TextLocation PrevDocSize = mCurrentDocumentSize;
			mCurrentDocumentSize.Line = mDocument.LineCount;
			mCurrentDocumentSize.Column = mDocument.GetLineLength(mCurrentDocumentSize.Line - 1);

			bool bInvalidate = true;
			bool bHadSelection = this.HasSelectedText;

			if(mCurrentDocumentSize > PrevDocSize)
			{
				// we have to do these checks before we update he scrollbars
				if(mAutoScroll && !bHadSelection && mCaretPosition.Column == PrevDocSize.Column && mCaretPosition.Line == Math.Max(0, PrevDocSize.Line - 1)
					&& mVScrollBar.Value == Math.Max(0, GetMaximumVScrollValue()))
				{
					// ScrollToCaret() is dependent on scrollbar location so update that now instead of later
					UpdateScrollbars();

					mCaretPosition.Line = Math.Max(0, mDocument.LineCount - 1);
					mCaretPosition.Column = mDocument.GetLineLength(mCaretPosition.Line);
					mSelectionStart = mCaretPosition;

					bInvalidate = !ScrollToCaret();
				}
				else
				{
					UpdateScrollbars();
				}
			}
			else
			{
				// if doc size shrunk we need to make sure the caret is still within range
				if(mCaretPosition > mCurrentDocumentSize)
				{
					mCaretPosition = mCurrentDocumentSize;

					if(bHadSelection)
					{
						if(mCaretPosition > mSelectionStart)
						{
							mSelectionStart = mCaretPosition;
						}

						OnSelectionChanged(new EventArgs());
					}
					else
					{
						mSelectionStart = mCaretPosition;
					}
				}
				else if(bHadSelection && mSelectionStart > mCurrentDocumentSize)
				{
					mSelectionStart = mCurrentDocumentSize;
					OnSelectionChanged(new EventArgs());
				}

				UpdateScrollbars();
			}

			if(bInvalidate)
			{
				UpdateCaret();
				Invalidate();
			}
		}
		/// <summary>
		/// Selects a range of text and brings the selection into view.
		/// </summary>
		/// <param name="Range">The location of the selection and its length.</param>
		public void Select(FindResult Range)
		{
			if(mDocument != null)
			{
				TextLocation StartLoc = new TextLocation(Range.Line, Range.Column);

				if(!IsValidTextLocation(StartLoc))
				{
					throw new ArgumentOutOfRangeException("Range");
				}

				TextLocation EndLoc = new TextLocation(Range.Line, Math.Min(Range.Column + Range.Length, mDocument.GetLineLength(Range.Line)));

				Select(StartLoc, EndLoc);
			}
		}
		/// <summary>
		/// Finds the first occurence of the specified string within the document if it exists.
		/// </summary>
		/// <param name="Txt">The string to find.</param>
		/// <param name="StartLoc">The position within the document to begin searching.</param>
		/// <param name="EndLoc">The position within the document to end searching.</param>
		/// <param name="Flags">Flags telling the document how to conduct its search.</param>
		/// <param name="Result">The location within the document of the supplied text.</param>
		/// <returns>True if the text was found.</returns>
		public bool Find(string Txt, TextLocation StartLoc, TextLocation EndLoc, RichTextBoxFinds Flags, out FindResult Result)
		{
			bool bResult = false;

			if(mDocument == null)
			{
				Result = FindResult.Empty;
			}
			else
			{
				bResult = mDocument.Find(Txt, StartLoc, EndLoc, Flags, out Result);
			}

			if(bResult && (Flags & RichTextBoxFinds.NoHighlight) != RichTextBoxFinds.NoHighlight)
			{
				Select(Result);
			}

			return bResult;
		}
		/// <summary>
		/// Selects a range of text and brings the selection into view.
		/// </summary>
		/// <param name="StartLoc">The start location of the selection.</param>
		/// <param name="EndLoc">The end location (and caret location) of the selection.</param>
		public void Select(TextLocation StartLoc, TextLocation EndLoc)
		{
			if(mDocument != null)
			{
				if(!IsValidTextLocation(StartLoc))
				{
					throw new ArgumentOutOfRangeException("StartLoc");
				}

				if(!IsValidTextLocation(EndLoc))
				{
					throw new ArgumentOutOfRangeException("EndLoc");
				}

				mSelectionStart = StartLoc;
				mCaretPosition = EndLoc;

				OnSelectionChanged(new EventArgs());

				ScrollToCaretOrInvalidate();
			}
		}
		/// <summary>
		/// Determines whether a location within the current document is valid.
		/// </summary>
		/// <param name="Loc">The location to validate.</param>
		/// <returns>True if the supplied location is within the bounds of the document.</returns>
		bool IsValidTextLocation(TextLocation Loc)
		{
			bool bResult = false;

			if(mDocument != null)
			{
				bResult = mDocument.IsValidTextLocation(Loc);
			}

			return bResult;
		}
		/// <summary>
		/// Selects all text and scrolls to the caret.
		/// </summary>
		public void SelectAll()
		{
			mSelectionStart = mDocument.BeginningOfDocument;
			mCaretPosition = mDocument.EndOfDocument;

			OnSelectionChanged(new EventArgs());

			if(!ScrollToCaret())
			{
				UpdateCaret();
				Invalidate();
			}
		}
        /// <summary>
        /// Searches for a string in the reverse direction of <see cref="FindForward"/>.
        /// </summary>
        /// <param name="Txt">The text to search for.</param>
        /// <param name="StartLoc">The starting location of the search.</param>
        /// <param name="EndLoc">The ending location of the search.</param>
        /// <param name="Flags">Flags controlling how the searching is conducted.</param>
        /// <param name="Result">Receives the results of the search.</param>
        /// <returns>True if a match was found.</returns>
        private bool FindReverse(string Txt, ref TextLocation StartLoc, ref TextLocation EndLoc, RichTextBoxFinds Flags, out FindResult Result)
        {
            bool bFound = false;
            bool bMatchWord;
            bool bIsWord;
            StringComparison ComparisonFlags;

            SetupFindState(Txt, ref StartLoc, ref EndLoc, Flags, out Result, out bIsWord, out ComparisonFlags, out bMatchWord);

            for(int CurLineIndex = StartLoc.Line; CurLineIndex >= EndLoc.Line && !bFound; --CurLineIndex)
            {
                if(GetLineLength(CurLineIndex) == 0)
                {
                    continue;
                }

                DocumentLine CurLineBldr = mLines[CurLineIndex];
                string LineTxt;
                int ColumnIndex = 0;

                if(CurLineIndex == StartLoc.Line && StartLoc.Column < GetLineLength(CurLineIndex))
                {
                    LineTxt = CurLineBldr.ToString(0, StartLoc.Column);
                }
                else if(CurLineIndex == EndLoc.Line && EndLoc.Column > 0)
                {
                    LineTxt = CurLineBldr.ToString(EndLoc.Column, CurLineBldr.Length - EndLoc.Column);
                    ColumnIndex = EndLoc.Column;
                }
                else
                {
                    LineTxt = CurLineBldr.ToString();
                }

                int Index = LineTxt.LastIndexOf(Txt, ComparisonFlags);

                if(Index != -1)
                {
                    ColumnIndex += Index;
                    CheckForWholeWord(Txt, ref Result, bMatchWord, ref bFound, bIsWord, CurLineIndex, CurLineBldr, ColumnIndex, Index);
                }
            }

            return bFound;
        }
		/// <summary>
		/// Searches down within the text box for the specified search string.
		/// </summary>
		/// <param name="DownSearchStart">The location within the document to begin searching.</param>
		void FindNext(TextLocation? DownSearchStart)
		{
			if(this.mDocument != null)
			{
				FindResult Result;
				RichTextBoxFinds Flags = mCheckBox_MatchCase.Checked ? RichTextBoxFinds.MatchCase : RichTextBoxFinds.None;

				if(!DownSearchStart.HasValue)
				{
					DownSearchStart = this.SelectionStart >= this.SelectionEnd ? this.SelectionStart : this.SelectionEnd;
				}

				bool bFound = true;
				string TxtToSearchFor = mToolStripTextBox_Find.Text.Trim();

				if(!this.Find(TxtToSearchFor, DownSearchStart.Value, this.mDocument.EndOfDocument, Flags, out Result))
				{
					if(!this.Find(TxtToSearchFor, this.mDocument.BeginningOfDocument, new TextLocation(DownSearchStart.Value.Line, mDocument.GetLineLength(DownSearchStart.Value.Line)), Flags, out Result))
					{
						//MessageBox.Show(this, "The specified search string does not exist!", mToolStripTextBox_Find.Text);
						bFound = false;
					}
				}

				UpdateFindBoxColor(bFound);
			}
		}
        /// <summary>
        /// Performs general housekeeping for setting up a search.
        /// </summary>
        /// <param name="Txt">The text to search for.</param>
        /// <param name="StartLoc">The location to begin searching from.</param>
        /// <param name="EndLoc">The location to stop searching at.</param>
        /// <param name="Flags">Flags controlling how the search is performed.</param>
        /// <param name="Result">Receives the resulting location if a match is found.</param>
        /// <param name="bIsWord">Set to true if <paramref name="Txt"/> is a valid word.</param>
        /// <param name="ComparisonFlags">Receives flags controlling how strings are compared.</param>
        /// <param name="bMatchWord">Is set to true if only full words are to be matched.</param>
        private void SetupFindState(string Txt, ref TextLocation StartLoc, ref TextLocation EndLoc, RichTextBoxFinds Flags, out FindResult Result, out bool bIsWord, out StringComparison ComparisonFlags, out bool bMatchWord)
        {
            Result = FindResult.Empty;

            if(!IsValidTextLocation(StartLoc))
            {
                throw new ArgumentException("StartLoc is an invalid text location!");
            }

            if(!IsValidTextLocation(EndLoc))
            {
                throw new ArgumentException("EndLoc is an invalid text location!");
            }

            if((Flags & RichTextBoxFinds.Reverse) == RichTextBoxFinds.Reverse)
            {
                if(StartLoc < EndLoc)
                {
                    throw new ArgumentException("StartLoc must be greater than EndLoc when doing a reverse search!");
                }
            }
            else
            {
                if(StartLoc > EndLoc)
                {
                    throw new ArgumentException("StartLoc must be less than EndLoc when doing a forward search!");
                }
            }

            bMatchWord = (Flags & RichTextBoxFinds.WholeWord) == RichTextBoxFinds.WholeWord;
            bIsWord = IsWord(0, Txt);
            ComparisonFlags = StringComparison.OrdinalIgnoreCase;

            if((Flags & RichTextBoxFinds.MatchCase) == RichTextBoxFinds.MatchCase)
            {
                ComparisonFlags = StringComparison.Ordinal;
            }
        }
		/// <summary>
		/// Event handler for when a key has been pressed.
		/// </summary>
		/// <param name="e">Information about the event.</param>
		protected override void OnKeyDown(KeyEventArgs e)
		{
			base.OnKeyDown(e);

			if(mDocument != null)
			{
				bool bHadSelection = (mCaretPosition != mSelectionStart);
				TextLocation OldCaretPosition = !bHadSelection ? mCaretPosition : mSelectionStart;
				bool bHandleShift = false;

				if((e.KeyCode == Keys.Insert || e.KeyCode == Keys.C) && e.Control)
				{
					CopySelectedText();
				}
				else if(e.KeyCode == Keys.A && e.Control)
				{
					e.Handled = true;

					SelectAll();
				}
				else if(e.KeyCode == Keys.F && e.Control)
				{
					EnterFindMode();
				}
				else if(e.KeyCode == Keys.F3)
				{
					if(e.Shift)
					{
						FindPrevious();
					}
					else
					{
						FindNext();
					}
				}
				else if(e.KeyCode == Keys.End)
				{
					if(e.Control)
					{
						mCaretPosition.Line = Math.Max(0, mDocument.LineCount - 1);
					}

					mCaretPosition.Column = mDocument.GetLineLength(mCaretPosition.Line);

					bHandleShift = true;

					ScrollToCaret();
				}
				else if(e.KeyCode == Keys.Home)
				{
					mCaretPosition.Column = 0;

					if(e.Control)
					{
						mCaretPosition.Line = 0;
					}

					bHandleShift = true;

					ScrollToCaret();
				}
				else if(e.KeyCode == Keys.Up)
				{
					MoveCaretUpLines(1, false);

					bHandleShift = true;

					ScrollToCaret();
				}
				else if(e.KeyCode == Keys.Down)
				{
					MoveCaretDownLines(1, false);

					bHandleShift = true;

					ScrollToCaret();
				}
				else if(e.KeyCode == Keys.Right)
				{
					++mCaretPosition.Column;

					if(mCaretPosition.Column >= mDocument.GetLineLength(mCaretPosition.Line))
					{
						++mCaretPosition.Line;

						if(mCaretPosition.Line >= mDocument.LineCount)
						{
							--mCaretPosition.Line;
							--mCaretPosition.Column;
						}
						else
						{
							mCaretPosition.Column = 0;
						}
					}

					bHandleShift = true;

					ScrollToCaret();
				}
				else if(e.KeyCode == Keys.Left)
				{
					--mCaretPosition.Column;

					if(mCaretPosition.Column < 0)
					{
						--mCaretPosition.Line;

						if(mCaretPosition.Line < 0)
						{
							mCaretPosition.Line = 0;
							mCaretPosition.Column = 0;
						}
						else
						{
							mCaretPosition.Column = mDocument.GetLineLength(mCaretPosition.Line);
						}
					}

					bHandleShift = true;
				}
				else if(e.KeyCode == Keys.PageUp)
				{
					mCaretPosition.Line = Math.Max(0, mCaretPosition.Line - mVScrollBar.LargeChange);

					int LineLength = mDocument.GetLineLength(mCaretPosition.Line);
					if(mCaretPosition.Column > LineLength)
					{
						mCaretPosition.Column = LineLength;
					}

					bHandleShift = true;
				}
				else if(e.KeyCode == Keys.PageDown)
				{
					mCaretPosition.Line = Math.Min(mDocument.LineCount - 1, mCaretPosition.Line + mVScrollBar.LargeChange);

					int LineLength = mDocument.GetLineLength(mCaretPosition.Line);
					if(mCaretPosition.Column > LineLength)
					{
						mCaretPosition.Column = LineLength;
					}

					bHandleShift = true;
				}

				if(bHandleShift)
				{
					// prevent the key from propagating to children
					e.Handled = true;

					if(e.Shift)
					{
						mSelectionStart = OldCaretPosition;
					}
					else
					{
						mSelectionStart = mCaretPosition;
					}

					if(mSelectionStart == mCaretPosition)
					{
						if(bHadSelection)
						{
							OnSelectionChanged(new EventArgs());
						}
					}
					else
					{
						OnSelectionChanged(new EventArgs());
					}

					ScrollToCaretOrInvalidate();
				}
			}
		}