context: //  Copyright (c) 2008, Michael unfried
  //  Email:  serbius3@gmail.com
  //  All rights reserved.
  
  //  Redistribution and use in source and binary forms, with or without modification, 
  //  are permitted provided that the following conditions are met:
  
  //  Redistributions of source code must retain the above copyright notice, 
  //  this list of conditions and the following disclaimer. 
  //  Redistributions in binary form must reproduce the above copyright notice, 
  //  this list of conditions and the following disclaimer in the documentation 
  //  and/or other materials provided with the distribution. 
  
  //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  //  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
  //  REMAINS UNCHANGED.
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Runtime.InteropServices;
  using System.Text;
  using System.Drawing;
  using System.Windows.Forms;
  using RainstormStudios.Unmanaged;
  using RainstormStudios.Collections;
  
  namespace RainstormStudios.Controls
  {
      [System.Drawing.ToolboxBitmap(typeof(System.Windows.Forms.RichTextBox)), DesignerCategoryAttribute("UserControl")]
      public class AdvRichTextBox : System.Windows.Forms.RichTextBox
      {
          #region Nested Types
          //***************************************************************************
          // Private Types
          // 
          protected class POINT
          {
              public int
                  x, y;
  
              public POINT(int x, int y)
              { this.x = x; this.y = y; }
  
              public static implicit operator System.Drawing.Point(POINT p)
              { return new System.Drawing.Point(p.x, p.y); }
              public static implicit operator POINT(System.Drawing.Point p)
              { return new POINT(p.X, p.Y); }
          }
          [StructLayout(LayoutKind.Sequential)]
          protected struct CharFormat2
          {
              public Int32
                  cbSize,
                  dwMask,
                  dwEffects,
                  yHeight,
                  yOffset,
                  crTextColor;
              public Byte
                  bCharSet,
                  pPitchAndFamily;
              [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
              public string
                  szFaceName;
              public Int16
                  wweight,
                  sSpacing;
              public Int32
                  crBackColor,
                  lcid,
                  dwReserved;
              public Int16
                  sStyle,
                  wKerning;
              public Byte
                  bUnderlineType,
                  bAnimation,
                  bRevAuthor,
                  bReserved1;
          }
          public struct AdvRichTextBoxPadding
          {
              public int
                  Top, Left, Right, Bottom;
  
              public AdvRichTextBoxPadding(int t, int r, int b, int l)
              {
                  this.Top = t;
                  this.Right = r;
                  this.Bottom = b;
                  this.Left = l;
              }
          }
          //***************************************************************************
          // Private Enums
          // 
          protected enum SelectDirection
          {
              None,
              Forward,
              Backword
          }
          #endregion
  
          #region Declarations
          //***************************************************************************
          // Constants
          // 
          const Int32
              LF_FACESIZE = 32,
              CFM_BACKCOLOR = 0x40000000,
              CFE_AUTOBACKCOLOR = CFM_BACKCOLOR,
              WM_USER = 0x400,
              EM_SETCHARFORMAT = (WM_USER + 68),
              EM_SETBKGNDCOLOR = (WM_USER + 67),
              EM_GETCHARFORMAT = (WM_USER + 58),
              WM_SETTEXT = 0xC,
              SCF_SELECTION = 0x1;
          //***************************************************************************
          // Private Fields
          // 
          UndoStateCollection
              _undoCol,
              _redoCol;
          string
              _lastVer,
              _clearedText;
          Keys
              _lastKeyPress,
              _lastModifierPress;
          int
              _lastCharPos,
              _lastVScPos,
              _lastHScPos,
              _printCurPg;
          IntPtr
              _evntMask;
          //protected AdvRichTextBoxPadding
          //    _intrPadding;
          NCCALCSIZE_PARAMS 
              nccsp;
          RainstormStudios.Forms.frmFindText
              _frmFindText;
  #if DEBUG
          private int
              paintCount = 0,
              redrawCount = 0;
  #endif
          protected bool
              _internalDrag = false,
              _hideCrt = false,
              _noScroll = false,
              _lockRedraw = false;
          protected System.Drawing.Printing.PrintDocument
              _printDoc;
          internal string
              DocumentName;
          protected AdvRichTextBox.SelectDirection
              _selDir = AdvRichTextBox.SelectDirection.None;
          //***************************************************************************
          // Public Events
          // 
          [Category("Action"), Description("Occurs whenever a 'cut' command is issued to the control.")]
          public event EventHandler TextCut;
          [Category("Action"), Description("Occurs whenever a 'paste' command is issued to the control.")]
          public event EventHandler TextPaste;
          [Category("Action"), Description("Occurs whenever an 'undo' command is issued to the control.")]
          public event EventHandler TextUndo;
          [Category("Property Changed"), Description("Occurs whenever current number of undo levels has changed.")]
          public event EventHandler TextUndoLevelChanged;
          [Category("Action"), Description("Occurs whenever a 'redo' command is issued to the control.")]
          public event EventHandler TextRedo;
          [Category("Behavior"), Description("Occurs whenever the contents of the control are printed.")]
          public event System.Drawing.Printing.PrintEventHandler TextPrint;
          [Category("Property Changed"), Description("Occurs whenever the vertical scroll position is changed.")]
          public new event ScrollEventHandler VScroll;
          [Category("Property Changed"), Description("Occurs whenever the horizontal scroll position is changed.")]
          public new event ScrollEventHandler HScroll;
          //***************************************************************************
          // External Aliases
          // 
          [DllImport("user32.dll")]
          protected static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, ref CharFormat2 lParam);
          [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = false)]
          public static extern IntPtr SendMessage(IntPtr hWnd, int uMsg, int wParam, IntPtr lParam);
          [DllImport("user32.dll", CharSet = CharSet.Auto)]
          protected static extern int GetScrollPos(IntPtr hWnd, int nBar);
          [DllImport("user32.dll", CharSet = CharSet.Auto)]
          protected static extern bool SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
          [DllImport("user32.dll")]
          protected static extern bool LockWindowUpdate(IntPtr hWndLock);
          [DllImport("user32.dll")]
          protected static extern bool GetScrollRange(IntPtr hWnd, int nBar, ref int lpMinPos, ref int lpMaxPos);
          [DllImport("user32.dll")]
          protected static extern IntPtr GetWindowDC(IntPtr hWnd);
          [DllImport("user32.dll")]
          protected static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
          [DllImport("user32.dll")]
          protected static extern bool HideCaret(IntPtr hWnd);
          #endregion
  
          #region Properties
          //***************************************************************************
          // Public Properties
          // 
          public new Color SelectionBackColor
          {
              get
              {
                  // We need to ask the RTB for the backcolor of the current selection.
                  //   This is done using SendMessage with a format structure which
                  //   the RTB will fill in for us.
  
                  // First, we force the creation of the window handle.
                  IntPtr HWND = this.Handle;
                  CharFormat2 Format = new CharFormat2();
                  Format.dwMask = CFM_BACKCOLOR;
                  Format.cbSize = Marshal.SizeOf(Format);
                  SendMessage(HWND, EM_GETCHARFORMAT, SCF_SELECTION, ref Format);
                  return ColorTranslator.FromOle(Format.crBackColor);
              }
              set
              {
                  // Here, we do relatively the same thing as in the Get accessor, but
                  //   we are telling the RTB to set the color this time instead of
                  //   returning it to us.
                  IntPtr HWND = this.Handle;
                  CharFormat2 Format = new CharFormat2();
                  Format.crBackColor = ColorTranslator.ToOle(value);
                  Format.dwMask = CFM_BACKCOLOR;
                  Format.cbSize = Marshal.SizeOf(Format);
                  SendMessage(HWND, EM_SETCHARFORMAT, SCF_SELECTION, ref Format);
              }
          }
          public int HorizontalScrollPos
          {
              get { return GetScrollPos(this.Handle, (int)ScrollBarTypes.SB_HORZ); }
              set { SetScrollPos(this.Handle, (int)ScrollBarTypes.SB_HORZ, value, false); }
          }
          public int VerticalScrollPos
          {
              get { return GetScrollPos(this.Handle, (int)ScrollBarTypes.SB_VERT); }
              set { SetScrollPos(this.Handle, (int)ScrollBarTypes.SB_VERT, value, false); }
          }
          public int CurrentLine
          { get { return this.GetLineFromCharIndex(this.SelectionStart + this.SelectionLength); } }
          public int UndoLevelCount
          { get { return this._undoCol.Count; } }
          public int MaxUndoLevel
          {
              get { return this._undoCol.MaxUndoLevel; }
              set { this._undoCol.MaxUndoLevel = (value >= 0) ? value : 0; }
          }
          //public AdvRichTextBoxPadding Padding
          //{
          //    get { return this._intrPadding; }
          //}
          public bool HideCaretWhenReadOnly
          {
              get { return this._hideCrt; }
              set { this._hideCrt = value; }
          }
          private bool PreventScrolling
          {
              get { return this._noScroll; }
              set
              {
                  if (value)
                      this.ResumeScroll();
                  else
                      this.SuspendScroll();
              }
          }
          #endregion
  
          #region Class Constructors
          //***************************************************************************
          // Class Constructors
          // 
          public AdvRichTextBox()
              : base()
          {
              this._redoCol = new UndoStateCollection();
              this._undoCol = new UndoStateCollection();
              this._undoCol.Inserted += new CollectionEventHandler(this.undoCol_onInserted);
              this._undoCol.Removed+=new CollectionEventHandler(this.undoCol_onRemoved);
              this._undoCol.Cleared += new EventHandler(this.undoCol_onCleared);
              //this._intrPadding = new AdvRichTextBoxPadding(0, 0, 0, 0);
          }
          #endregion
  
          #region Public Methods
          //***************************************************************************
          // Public Methods
          // 
          public void Dispose()
          {
              this.Dispose(true);
          }
          public void ScrollToBottom()
          {
              int min = -1, max = -1;
              GetScrollRange(this.Handle, (int)ScrollBarFlags.SBS_VERT, ref min, ref max);
              this.VerticalScrollPos = max - this.Height;
          }
          public void SetForeColor(string findWhat, Color highlight, bool matchCase, bool matchWholeWord)
          {
              //LockWindowUpdate(this.Handle); // Prevent the control from redrawing itself.
              this.SuspendRefresh();
              this.SuspendScroll();
              this.SuspendLayout();
              int ScrollPosVert = this.VerticalScrollPos;
              int ScrollPosHoriz = this.HorizontalScrollPos;
              int selStart = this.SelectionStart;
              int selLength = this.SelectionLength;
  
              int startFrom = 0;
              int length = findWhat.Length;
              RichTextBoxFinds finds = RichTextBoxFinds.None;
  
              // Setup the flags for searching.
              if (matchCase) finds |= RichTextBoxFinds.MatchCase;
              if (matchWholeWord) finds |= RichTextBoxFinds.WholeWord;
  
              try
              {
                  // Do the search.
                  while (this.Find(findWhat, startFrom, finds) > -1)
                  {
                      this.SelectionBackColor = highlight;
                      startFrom = this.SelectionStart + this.SelectionLength;
                  }
              }
              finally
              {
                  // Return the previous values.
                  this.SelectionStart = selStart;
                  this.SelectionLength = selLength;
                  this.HorizontalScrollPos = ScrollPosHoriz;
                  this.VerticalScrollPos = ScrollPosVert;
                  this.ResumeLayout();
                  //LockWindowUpdate(IntPtr.Zero); // Unlock drawing
                  this.ResumeScroll();
                  this.ResumeRefresh();
              }
          }
          public void SetBackColor(string findWhat, Color highlight, bool matchCase, bool matchWholeWord)
          {
              //LockWindowUpdate(this.Handle); // Prevent the control from redrawing itself.
              this.SuspendRefresh();
              this.SuspendScroll();
              this.SuspendLayout();
              int ScrollPosVert = this.VerticalScrollPos;
              int ScrollPosHoriz = this.HorizontalScrollPos;
              int selStart = this.SelectionStart;
              int selLength = this.SelectionLength;
  
              int startFrom = 0;
              int length = findWhat.Length;
              RichTextBoxFinds finds = RichTextBoxFinds.None;
  
              // Setup the flags for searching.
              if (matchCase) finds |= RichTextBoxFinds.MatchCase;
  
gt:             if (matchWholeWord) finds |= RichTextBoxFinds.WholeWord;