using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using VEPROMS.CSLA.Library;
using Volian.Base.Library;
namespace Volian.Controls.Library
{
	public delegate void StepRTBEvent(object sender, EventArgs args);
	public delegate void StepRTBCursorKeysEvent(object sender, KeyEventArgs args);
	public delegate void StepRTBCursorMovementEvent(object sender, StepRTBCursorMovementEventArgs args);
	public delegate void StepRTBModeChangeEvent(object sender, StepRTBModeChangeEventArgs args);
	public delegate void StepRTBMenuEvent(object sender, StepRTBMenuEventArgs args);
	public delegate void StepRTBTableWidthEvent(object sender, StepRTBTableWidthEventArgs args);
	public delegate bool StepRTBBooleanEvent(object sender, EventArgs args);
	public delegate void StepRTBLocationEvent(object sender, StepRTBLocationEventArgs args);
	public delegate void StepRTBMouseEvent(object sender, MouseEventArgs args);
	public delegate void StepRTBRoEvent(object sender, StepRTBRoEventArgs args);
	//public delegate void StepRTBMouseWheelEvent(object sender, MouseEventArgs args);
	public partial class StepRTB : RichTextBox // , IStepRTB
	{
		private static readonly log4net.ILog _MyLog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
		 private bool _Disposed = false;
	  private static int _CountCreated = 0;
		private static int _CountDisposed = 0;
		private static int _CountFinalized = 0;
		private static int IncrementCountCreated
		{ get {	return ++_CountCreated; } }
		private int _CountWhenCreated = IncrementCountCreated;
		public static int CountCreated
		{ get { return _CountCreated; } }
		public static int CountNotDisposed
		{ get { return _CountCreated - _CountDisposed; } }
		public static int CountNotFinalized
		{ get { return _CountCreated - _CountFinalized; } }
		private bool _Finalized = false;
		~StepRTB()
		{
			if (!_Finalized) _CountFinalized++;
			_Finalized = true;
		}
		#region Events
		public event StepRTBRoEvent RoInsert;
		public void OnRoInsert(object sender, StepRTBRoEventArgs args)
		{
			if (RoInsert != null) RoInsert(sender, args);
		}
		public event StepRTBEvent ReturnToEditor;
		public void OnReturnToEditor(object sender, EventArgs args)
		{
			if (ReturnToEditor != null) ReturnToEditor(sender, args);
		}
		public event StepRTBEvent EditModeChanged;
		public void OnEditModeChanged(object sender, EventArgs args)
		{
			if (EditModeChanged != null) EditModeChanged(sender, args);
		}
		public event StepRTBEvent DoSaveContents;
		public void OnDoSaveContents(object sender, EventArgs args)
		{
			if (DoSaveContents != null) DoSaveContents(sender, args);
		}
		public event StepRTBMouseEvent DoMouseWheel;
		public void OnDoMouseWheel(object sender, MouseEventArgs args)
		{
			if (DoMouseWheel != null) DoMouseWheel(sender, args);
		}
		public event StepRTBLocationEvent OpenContextMenu;
		public void OnOpenContextMenu(object sender, StepRTBLocationEventArgs args)
		{
			if (OpenContextMenu != null) OpenContextMenu(sender, args);
		}
		public event StepRTBEvent CopyStep;
		public void OnCopyStep(object sender, EventArgs args)
		{
			if (CopyStep != null) CopyStep(sender, args);
		}
		public event StepRTBBooleanEvent CheckClipboard;
		public bool OnCheckClipboard(object sender, EventArgs args)
		{
			if (CheckClipboard != null) return CheckClipboard(sender, args);
			return false;
		}
		public event StepRTBCursorKeysEvent EnterKeyPressed;
		public void OnEnterKeyPressed(object sender, KeyEventArgs args)
		{
			if (EnterKeyPressed != null) EnterKeyPressed(sender, args);
		}
		public event StepRTBEvent InsertPgBrk;
		public void OnInsertPgBrk(object sender, EventArgs args)
		{
			if (InsertPgBrk != null) InsertPgBrk(sender, args);
		}
		public event StepRTBEvent OpenAnnotations;
		public void OnOpenAnnotations(object sender, EventArgs args)
		{
			if (OpenAnnotations != null) OpenAnnotations(sender, args);
		}
		public event StepRTBBooleanEvent IsNotCurrentSelection;
		public bool OnIsNotCurrentSelection(object sender, EventArgs args)
		{
			if (IsNotCurrentSelection != null) return IsNotCurrentSelection(sender, args);
			return false;
		}
		public event StepRTBTableWidthEvent AdjustTableWidth;
		public void OnAdjustTableWidth(object sender, StepRTBTableWidthEventArgs args)
		{
			if (AdjustTableWidth != null) AdjustTableWidth(sender, args);
		}
		public event StepRTBMenuEvent SetMenu;
		private void OnSetMenu(object sender, StepRTBMenuEventArgs args)
		{
			if (SetMenu != null) SetMenu(sender, args);
		}
		public event StepRTBEvent RTBSelectionChanged;
		private void OnRTBSelectionChanged(object sender, EventArgs args)
		{
			if (RTBSelectionChanged != null) RTBSelectionChanged(sender, args);
		}
		public event StepRTBEvent LinkLocationsChanged;
		private void OnLinkLocationChanged(object sender, EventArgs args)
		{
			if (LinkLocationsChanged != null) LinkLocationsChanged(sender, args);
		}
		public event StepRTBEvent RTBRangeStatusChanged;
		private void OnRTBRangeStatusChanged(object sender, EventArgs args)
		{
			if (RTBRangeStatusChanged != null) RTBRangeStatusChanged(sender, args);
		}
		public event StepRTBCursorKeysEvent CursorKeyPress;
		private void OnCursorKeyPress(object sender, KeyEventArgs args)
		{
			if (CursorKeyPress != null) CursorKeyPress(sender, args);
		}
		public event StepRTBCursorMovementEvent CursorMovement;
		private void OnCursorMovement(object sender, StepRTBCursorMovementEventArgs args)
		{
			if (CursorMovement != null) CursorMovement(sender, args);
		}
		//public event StepRTBModeChangeEvent ModeChange;
		//private void OnModeChange(object sender, StepRTBModeChangeEventArgs args)
		//{
		//    //_MyModeChangeEventArgs = args;
		//    if (ModeChange != null) ModeChange(sender, args);
		//    else MessageBox.Show("StepRTB - no mode change defined");
		//}
		//public event StepRTBMouseWheelEvent MouseWheel;
		//private void OnMouseWheel(object sender, MouseEventArgs args)
		//{
		//  if (MouseWheel != null) MouseWheel(sender, args);
		//}
		/// 
		/// This event  is not raised during all the in-between changes for link deletions
		/// 
		public event StepRTBEvent RTBTextChanged;
		private void OnRTBTextChanged(object sender, EventArgs args)
		{
			if (RTBTextChanged != null) RTBTextChanged(sender, args);
		}
		#endregion
		#region Properties and Variables
		
		public bool IsRoTable
		{
			get { return (Parent is VlnFlexGrid && (Parent as VlnFlexGrid).IsRoTable); }
		}
		public bool HasVScroll
		{
			get
			{
				return RTBAPI.HasVertScroll(this);
			}
		}
		public bool HasHScroll
		{
			get
			{
				return RTBAPI.HasHorzScroll(this);
			}
		}
		private bool _EditMode = true;
		/// 
		/// Allows insert of links. If false, don't allow selection of links.
		/// 
		public bool EditMode
		{
			get { return _EditMode; }
			set 
			{ 
				_EditMode = value;
				OnEditModeChanged(this, new EventArgs());
			}
		}
		private static string _MySymbolFontName;
		public static string MySymbolFontName
		{
			get { return StepRTB._MySymbolFontName; }
			set { StepRTB._MySymbolFontName = value; }
		}
		private static FontFamily _MyFontFamily = null;
		public static FontFamily MyFontFamily
		{
			get { return StepRTB._MyFontFamily; }
			set
			{
				_MyFontFamily = value;
				if (value != null)
				{
					Font font;
					if (value.IsStyleAvailable(FontStyle.Regular))
						font = new Font(value, 10);
					else if (value.IsStyleAvailable(FontStyle.Bold))
						font = new Font(value, 10, FontStyle.Bold);
					else // (value.IsStyleAvailable(FontStyle.Italic))
						font = new Font(value, 10, FontStyle.Italic);
					using (StepRTB srtb = new StepRTB())
					{
						if (srtb.FontIsFixed(font)) MySymbolFontName = "FreeMono"; // FreeMono is now used for the edit screen only.  VESymbFix and Consolas are used for printing
						else MySymbolFontName = "Arial Unicode MS";
					}
				}
				else
				{
					MySymbolFontName = null;
				}
			}
		}
		// use newer rich text box....
		//[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
		//static extern IntPtr LoadLibrary(string lpFileName);
		//protected override CreateParams CreateParams
		//{
		//    get
		//    {
		//        CreateParams prams = base.CreateParams;
		//        if (LoadLibrary("msftedit.dll") != IntPtr.Zero)
		//        {
		//            //prams.ExStyle |= 0x020; // transparent
		//            prams.ClassName = "RICHEDIT50W";
		//        }
		//        return prams;
		//    }
		//}
		private E_FieldToEdit _FieldToEdit = E_FieldToEdit.StepText;
		public E_FieldToEdit FieldToEdit
		{
			get { return _FieldToEdit; }
			set { _FieldToEdit = value; }
		}
		private string _RtfPrefix;	// contains Font table and styles (bold/underline/italics) for rtb from step style
		public string RtfPrefixForSymbols
		{
			get
			{
				if (_RtfPrefix == null)
				{
					StringBuilder selectedRtfSB = new StringBuilder();
					AddFontTable(selectedRtfSB, FormatFont, FontIsFixed(FormatFont));
					_RtfPrefix = selectedRtfSB.ToString();
				}
				return _RtfPrefix + @"\f1\fs" + FormatFont.SizeInPoints * 2 + " ";
			}
		}
		public string RtfPrefix
		{
			get
			{
				if (_RtfPrefix == null)
				{
					StringBuilder selectedRtfSB = new StringBuilder();
					AddFontTable(selectedRtfSB, FormatFont, FontIsFixed(FormatFont));
					_RtfPrefix = selectedRtfSB.ToString();
				}
				return _RtfPrefix;// +@"{\colortbl ;\red255\green0\blue0;}";
			}
		}
		// August 5, 2009 - KBR & RHM:
		// Insert/Overwrite will be developed later if needed.  various issues
		// were found during initial development that made its scope larger than
		// expected.  Problems were related to having overstrike on in the following
		// cases:
		//	1) left arrow when on link - positions after link
		//	2) left arrow and after link - does not move
		//	3) shift left arrow does not move past links correctly and also, first
		//		shift left arrow looks more like insert mode.
		// private bool _OverWrite;
		// _IsDirty compares the original rtf to the current rtf from the
		// richtextbox to see if a change was made.
		public bool IsDirty
		{
			get { return OrigRTF != Rtf; }
		}
		private bool _InitializingRTB;
		private IContainer _Container = null;
		private string _MyClassName=string.Empty;
		public string MyClassName
		{
			get { if (_MyClassName == string.Empty)_MyClassName = CreateParams.ClassName; return _MyClassName; }
			set { _MyClassName = value; }
		}
		//private E_EditPrintMode _epMode = E_EditPrintMode.Edit;
		//public E_EditPrintMode EpMode
		//{
		//    get { return _epMode; }
		//    set {
		//        if (value == E_EditPrintMode.Print) Console.WriteLine("");
		//        _epMode = value; }
		//}
		private E_ViewMode _vwMode = E_ViewMode.Edit;
		public E_ViewMode VwMode
		{
			get { return _vwMode; }
			set { _vwMode = value; }
		}
		private VE_Font _MyStyleFont;
		public VE_Font MyStyleFont
		{
			get 
			{
				if (_MyStyleFont == null) _MyStyleFont = MyItemInfo.GetItemFont();
				return _MyStyleFont; 
			}
		}
		private bool _ActiveMode = false;
		public bool ActiveMode
		{
			get { return _ActiveMode; }
			set { _ActiveMode = value; }
		}
		private ItemInfo _MyItemInfo;
		public ItemInfo MyItemInfo
		{
			get 
			{
				if (_MyItemInfo == null && Parent is EditItem)
					_MyItemInfo = (Parent as EditItem).MyItemInfo;
				else if (_MyItemInfo == null && Parent != null && Parent.Parent is EditItem)
					_MyItemInfo = (Parent.Parent as EditItem).MyItemInfo;
				return _MyItemInfo;
			}
			set	{ _MyItemInfo = value; }
		}
		private string _OrigRTF;
		public string OrigRTF
		{
			get { return _OrigRTF; }
			set { _OrigRTF = value; }
		}
		private Font _FormatFont;
		public Font FormatFont
		{
			get 
			{
				if (_FormatFont == null)
				{
					Font formatFont;
					if (MyItemInfo != null)
						formatFont = MyItemInfo.GetItemFont().WindowsFont; // OrigDisplayText.TextFont.WindowsFont;
					else
						formatFont = Font;
					if (MyItemInfo != null && (MyItemInfo.IsTable || MyItemInfo.IsFigure))
						_FormatFont = formatFont;
					else
					{
						//if (VlnSettings.DebugMode || VlnSettings.ProductionMode)
							_FormatFont = new Font(MyFontFamily == null ? formatFont.FontFamily : MyFontFamily, formatFont.Size, formatFont.Style);
						//else
						//  _FormatFont = new Font("Bookman Old Style", formatFont.Size, formatFont.Style);
						// TODO: Release Mode
						//Font = _origDisplayText.TextFont.WindowsFont; // font defined in plant's format
					}
				}
				// We found that the characters in the Letter Gothic font do not all use the same spacing.
				// Also, the character spacing is even more different between screen resolutions.
				// But the Letter Gothic font will print just fine.
				// We also found that the Letter Gothic Tall font works just fine on the screen, the only difference
				// is that the characters are a little bit taller.
				// So we decided to use the Letter Gothic Tall font for the screen display any time that Letter Gothic is used.
				if (_FormatFont.Name.ToUpper().Equals("LETTER GOTHIC"))
					_FormatFont = new Font("Letter Gothic Tall", _FormatFont.Size, _FormatFont.Style);
				return _FormatFont; 
			}
			set { _FormatFont = value; }
		}
		public void SetupRichText(string rtf, VE_Font vFont)
		{
			//FormatFont = vFont.WindowsFont;
			DisplayText vlnText = new DisplayText(rtf, vFont, true);
			AddRtfText(vlnText.StartText);
			ReadOnly = true;
		}
		public void RefreshDisplay()
		{
			if (Closed) return;
			try
			{
				RefreshDisplay(ActiveMode);
			}
			catch (Exception ex)
			{
				_MyLog.Error(string.Format("MyItemInfo:  {0} - {1} Problem doing RefreshDisplay",MyItemInfo.ItemID,MyItemInfo.ShortPath), ex);
			}
		}
		// RefreshDisplay is used to update the rtb for an entire Item as defined by MyItemInfo.  
		public void RefreshDisplay(bool activeMode)
		{
			ActiveMode = activeMode;
			OnAdjustTableWidth(this, new StepRTBTableWidthEventArgs(true));
			_InitializingRTB = true;
			DisplayText vlntxt = new DisplayText(MyItemInfo, E_EditPrintMode.Edit, VwMode, !ActiveMode, FieldToEdit, true,null, null);
			//if (_origDisplayText != null && vlntxt.StartText == _origDisplayText.StartText)
			//{
			//  ReadOnly = !(EpMode == E_EditPrintMode.Edit && VwMode == E_ViewMode.Edit);
			//  if (!ReadOnly && !edit) ReadOnly = true;
			//  return;
			//}
			OrigDisplayText = vlntxt;
			// RHM 20101201 - Don't reset the text.  Calculate the text and compare it with the existing text in AddRTFText
			//Text = ""; // Initialize text before add text
			// IMPORTANT: SetLineSpacing must be set before Links, otherwise it
			// was confusing the 'handle' of the rtf box.
			//Console.WriteLine("'font',{0}", Font);
			//if(Text == "")SelectionFont = Font; // Initialize SelectionFont
			if (FieldToEdit == E_FieldToEdit.StepText)
			{
				if (MyItemInfo != null)
				{
					if (MyItemInfo.IsStep) Font = MyFontFamily == null ? MyItemInfo.FormatStepData.Font.WindowsFont : new Font(MyFontFamily, MyItemInfo.FormatStepData.Font.WindowsFont.Size, MyItemInfo.FormatStepData.Font.WindowsFont.Style);
					else Font = Font = MyFontFamily == null ? MyItemInfo.ActiveFormat.PlantFormat.FormatData.Font.WindowsFont : new Font(MyFontFamily, MyItemInfo.ActiveFormat.PlantFormat.FormatData.Font.WindowsFont.Size, MyItemInfo.ActiveFormat.PlantFormat.FormatData.Font.WindowsFont.Style);
					LastRtf = Rtf;
				}
			}
			RTBAPI.SetLineSpacing(this, RTBAPI.ParaSpacing.PFS_EXACT);
			bool readOnlyStep = MyItemInfo == null || MyItemInfo.FormatStepData == null ? false : MyItemInfo.FormatStepData.ReadOnly;
			ReadOnly = readOnlyStep || VwMode == E_ViewMode.View || ActiveMode == false;
			AddRtfText(vlntxt.StartText);
			//AddRtfStyles();
			// set readonly based on initial modes, however, these may change if
			// user selected view mode.
			ClearUndo();
			// RHM: 20101122 - Not sure why the RightMargin is set.  The following line was added in Rev 23 
			// in May of 2008.
			//
			// RightMargin = Width;
			//
			// Normally a the Right Margin is handled by the RichTextBox it defaults to a value of 0.
			//
			// If the RightMargin is equal to the width of the RichTextBox the word-wrap occurs just beyond 
			// the visible extent (right edge) of the RichTextBox when it is in view mode.  In edit mode it 
			// wraps within the visible extent. An example of this is Step 24 of ES02 in HLP EOPs.
			//
			// If the RightMargin is greater than the width of the RichTextBox, the word-wrap occurs beyond 
			// the visible extent of the RichTextBox for both view and edit modes. 
			//
			// Setting the RightMargin to the Width minus one, accounts for the slight indent (1 pixel) of 
			// the text within the RichTextBox.
			RightMargin = Width > 0 ? Width - 1 : 0;
			// figure out if needs outlined, depends on table/figure type
			if (!ActiveMode)
			{
				RemoveEventHandlers();
				OnAdjustTableWidth(this, new StepRTBTableWidthEventArgs(false));// View Mode
				SelectAll();
				//if (SelectionHangingIndent !=0) SelectionHangingIndent = 0;
				int indchar = 0;
				string indentToken = MyItemInfo.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.IndentToken;
				if (indentToken == null || indentToken=="0") indentToken = "\x5";
				// this line of code was moved before the find for the indent so that
				// the window gets resized if the indent changes the height, i.e. more line(s) in window.
				AddEventHandlers();
				//if (MyItemInfo.InList(186722)) Console.WriteLine("here");
				if ((indchar = Find(indentToken, indchar, RichTextBoxFinds.None)) >= 0)
				{
					int nindents = 0;
					while ((indchar = Find(indentToken, indchar, RichTextBoxFinds.None)) >= 0)
					{
						nindents++;
						Point indent = GetPositionFromCharIndex(indchar);
						SelectionHangingIndent = indent.X;
						// RHM 02/20/2013
						// The following line attempts to remove the indent character for display purposes.
						// However, if the indent is followed immediately by an RO or Transition the
						// RichTextBox will not allow the character to be removed this way and the RichTextBox
						// will beep.  So, to keep from going in an infintie loop, the value of indchar is incremented.
						SelectedRtf = SelectedRtf.Replace(@"\'05", "");
						indchar++;// Don't so the same one twice.
					}
					if (MyItemInfo != null && MyItemInfo.ActiveFormat != null
						&& MyItemInfo.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.DontResetIndentOnNewline)
					{
						if (nindents == 1) Rtf = Rtf.Replace("\\par\r\n", @"\line ");
						Rtf = Rtf.Replace(@"\line\pard", @"\line");
					}
				}
			}
			OrigRTF = Rtf;
			_InitializingRTB = false;
			
			AdjustSizeForContents(!ActiveMode);
		}
		private bool _ProcessKeystrokes = true;
		public bool ProcessKeystrokes
		{
			get { return _ProcessKeystrokes; }
			set { _ProcessKeystrokes = value; }
		}
		private Point ScrollPos
		{
			get { return RTBAPI.GetScrollLocation(this); }
			set { RTBAPI.SetScrollLocation(this, value); }
		}
		private Rectangle _ContentsRectangle = new Rectangle(0,0,0,0);
		public Rectangle ContentsRectangle
		{
			get 
			{
				if (_ContentsRectangle.X == 0 && _ContentsRectangle.Y == 0 && _ContentsRectangle.Width == 0 && _ContentsRectangle.Height == 0)
					_ContentsRectangle = this.ClientRectangle;
				return _ContentsRectangle; 
			}
			set
			{
				_ContentsRectangle = value;
				AdjustSizeForContents(false);
			}
		}
		public Size ContentsSize
		{
			get { return _ContentsRectangle.Size; }
		}
		private Size _AdjustSize = new Size(0,0);  // if 0,0 puts text right next to bottom of box.
		public Size AdjustSize
		{
			get { return _AdjustSize; }
			set
			{
				_AdjustSize = value;
				AdjustSizeForContents(false);
			}
		}
		public System.Windows.Forms.AutoScaleMode AutoScaleMode;
		private DisplayText _OrigDisplayText;
		public DisplayText OrigDisplayText
		{
			get { return _OrigDisplayText; }
			set { _OrigDisplayText = value; }
		}
		private RichTextBox _rtbTemp = new RichTextBox();
		private string _MyLinkText;
		public string MyLinkText
		{
			get { return _MyLinkText; }
			set 
			{
				//if (value != _MyLinkText)
				//{
				// updates to the info panel were not always occurring when the previous two
				// lines were active
					_MyLinkText = value;
					OnLinkChanged(this, new StepPanelLinkEventArgs(_MyLinkText));
				//}
			}
		}
		#endregion
		#region Constructors
		public StepRTB()
		{
			InitializeComponent();
			SetUpStepRTB();
			AddEventHandlers();
			this.BorderStyleChanged += new EventHandler(StepRTB_BorderStyleChanged);
		}
		public StepRTB(IContainer container)
		{
			container.Add(this);
			InitializeComponent();
			_Container = container;
			SetUpStepRTB();
			AddEventHandlers();
			this.BorderStyleChanged += new EventHandler(StepRTB_BorderStyleChanged);
		}
		protected override void OnMouseWheel(MouseEventArgs e)
		{
			OnDoMouseWheel(this, e);
			//MyRTBItem.MyStepPanel.MouseWheel(e);
			//base.OnMouseWheel(e);
		}
		// When a border style is changed, the richtextbox's handle is 'destroyed', so that the handleDestroyed
		// event is done. This was causing the event handlers to be removed (RemoveEventHandler) so that the
		// keypress event handler was not run.  
		private void RemoveEventHandlers()
		{
			if (_EventHandlersForKeyPress==0) return;
			ContentsResized -= new ContentsResizedEventHandler(StepRTB_ContentsResized);
			this.Click -= new EventHandler(StepRTB_Click);
			this.KeyPress -= new KeyPressEventHandler(StepRTB_KeyPress);
			this.KeyDown -= new KeyEventHandler(StepRTB_KeyDown);
			this.KeyUp -= new KeyEventHandler(StepRTB_KeyUp);
			this.TextChanged -= new EventHandler(StepRTB_TextChanged);
			this.MouseUp -= new MouseEventHandler(StepRTB_MouseUp);
			this.MouseDown -= new MouseEventHandler(StepRTB_MouseDown);
			this.MouseLeave -= new EventHandler(StepRTB_MouseLeave);
			this.SelectionChanged -= new EventHandler(StepRTB_SelectionChanged);
			this.ContextMenuStripChanged -= new EventHandler(StepRTB_ContextMenuStripChanged);
			this.RTBSelectionChanged -= new StepRTBEvent(StepRTB_RTBSelectionChanged);
			this.HandleCreated -= new EventHandler(StepRTB_HandleCreated);
			this.HandleDestroyed -= new EventHandler(StepRTB_HandleDestroyed);
			_EventHandlersForKeyPress = _EventHandlersForKeyPress - 1;
		}
		private int _EventHandlersForKeyPress = 0;
		// When a border style is changed, the richtextbox's handle is 'destroyed', so that the handleDestroyed
		// event is done. This was causing the event handlers to be removed (RemoveEventHandler) so that the
		// keypress event handler was not run.  
		private void AddEventHandlers()
		{
			if (_EventHandlersForKeyPress>0) return;
			// Always be sure to add the same event handlers to RemoveEventHandlers
			BorderStyle = System.Windows.Forms.BorderStyle.None;
			this.DetectUrls = false;
			ContentsResized += new ContentsResizedEventHandler(StepRTB_ContentsResized);
			this.Click +=new EventHandler(StepRTB_Click);
			this.KeyPress += new KeyPressEventHandler(StepRTB_KeyPress);
			this.KeyDown += new KeyEventHandler(StepRTB_KeyDown);
			this.KeyUp += new KeyEventHandler(StepRTB_KeyUp);
			this.TextChanged += new EventHandler(StepRTB_TextChanged);
			this.MouseUp += new MouseEventHandler(StepRTB_MouseUp);
			this.MouseDown += new MouseEventHandler(StepRTB_MouseDown);
			this.MouseLeave += new EventHandler(StepRTB_MouseLeave);
			this.SelectionChanged +=new EventHandler(StepRTB_SelectionChanged);
			this.ContextMenuStripChanged += new EventHandler(StepRTB_ContextMenuStripChanged);
			this.RTBSelectionChanged += new StepRTBEvent(StepRTB_RTBSelectionChanged);
			this.HandleCreated += new EventHandler(StepRTB_HandleCreated);
			this.HandleDestroyed += new EventHandler(StepRTB_HandleDestroyed);
			_EventHandlersForKeyPress = _EventHandlersForKeyPress+1;
		}
		private int _HandleCount = 0;
		void StepRTB_HandleCreated(object sender, EventArgs e)
		{
			_HandleCount++;
		}
		// When a border style is changed, the richtextbox's handle is 'destroyed', so that the handleDestroyed
		// event is done. This was causing the event handlers to be removed (RemoveEventHandler) so that the
		// keypress event handler was not run.  The following was added so that the keypress event is restored
		// after the border style was changed.  This is specifically for the steprtb's on property pages.
		void StepRTB_BorderStyleChanged(object sender, EventArgs e)
		{
			if (_EventHandlersForKeyPress==0)
				this.KeyPress += new KeyPressEventHandler(StepRTB_KeyPress);
			_EventHandlersForKeyPress++;
		}
		private bool _Closed = false;
		public bool Closed
		{
			get { return _Closed; }
			set { _Closed = value; }
		}
		void StepRTB_HandleDestroyed(object sender, EventArgs e)
		{
			if (_HandleCount == 0)
			{
				Closed = true;
				RemoveEventHandlers();
			}
			_HandleCount--;
		}
		void StepRTB_RTBSelectionChanged(object sender, EventArgs args)
		{
			//Console.WriteLine("RTBSelectionChanged id= {0}", MyItemInfo.ItemID);
			if (!Focused) Focus();
		}
		void StepRTB_ContextMenuStripChanged(object sender, EventArgs e)
		{
			//Console.WriteLine("********** StepRTB_ContextMenuStripChanged");
			int sublocation = 0;
			try
			{
				//Console.WriteLine("{0}", ContextMenuStrip.GetType().FullName);
				OnSetMenu(this, new StepRTBMenuEventArgs(null));
				sublocation = 1;
				_ContextMenuStripChanged = true;
				sublocation = 2;
				_ContextMenuStepRTB = this;
				sublocation = 3;
				_ContextMenuStrip = ContextMenuStrip;
				sublocation = 4;
			}
			catch(Exception ex)
			{
				Console.WriteLine("StepRTB_ContextMenuStripChanged {0} - {1} [{2}]", ex.GetType().Name, ex.Message, sublocation);
			}
		}
		private void SetUpStepRTB()
		{
			DetectUrls = false;
			if(DoSpellCheck) 
				C1SpellChecker2.SetActiveSpellChecking(this, true);
			this.Height = 10; // initialize the height to 10, the default height was too big for the cells in grid tables
			BorderStyle = System.Windows.Forms.BorderStyle.None;
			this.ScrollBars = RichTextBoxScrollBars.None;
			this.DetectUrls = false;
		}
		// An event is needed to set MouseDown to false on mouse leave, because additional rtb's may
		// have been exposed based on entering a step, which causes the underlying item/rtb for which
		// the mouse event occurs to not be the current rtb.  RTB gets selected on MouseDown, MouseEnter 
		// and MouseUp are 
		void StepRTB_MouseLeave(object sender, EventArgs e)
		{
			_MouseDown = false;
		}
		//void MyConfig_PropertyChanged(object sender, PropertyChangedEventArgs e)
		//{
		//    SaveConfig();
		//}
		private void StepRTB_Click(object sender, EventArgs e)
		{
			if (ReadOnly) return;
		}
		void StepRTB_SelectionChanged(object sender, EventArgs e)
		{
			if (_InitializingRTB) return;
			HandleSelectionChange();
		}
		private bool _MouseDown = false;
		private bool _ContextMenuStripChanged = false;
		private void StepRTB_MouseDown(object sender, MouseEventArgs e)
		{
			_MouseDown = true;
			//Console.WriteLine("vvvvvvvvvv StepRTB Mouse Down id= {0}",MyItemInfo.ItemID);
			bool inPsi = this.Parent.FindForm().Name.Contains("frmPSI");
			if (!_ContextMenuStripChanged)
				OnSetMenu(this, new StepRTBMenuEventArgs(inPsi?"PSI":"OpenContextMenu"));
			_ContextMenuStripChanged = false;
			CorrectSelectionAtEndOfLine();
		}
		void StepRTB_MouseUp(object sender, MouseEventArgs e)
		{
			_MouseDown = false;
			//Console.WriteLine("^^^^^^^^^^ StepRTB Mouse Up id= {0}", MyItemInfo.ItemID);
			if(this.Focused) // Only HandleSelectionChange if this control has focus.
			{
				HandleSelectionChange();
				// save our context menu to add to the spell checker's context menu
				ThisContextMenuStrip = this.ContextMenuStrip;
			}
		}
		#endregion
		#region ApplicationSupport
		// Used in StepTabRibbon:
		//public void ToggleEditView(E_ViewMode vwMode)
		//{
		//    OnDoSaveContents(this, new EventArgs()); // SaveText();
		//    //ItemInfo tmp = MyItemInfo;
		//    //MyItemInfo = null;
		//    //ReadOnly = !ReadOnly;
		//    //EpMode = ReadOnly ? E_EditPrintMode.Print : E_EditPrintMode.Edit;
		//    VwMode = vwMode;
		//    ReadOnly = VwMode == E_ViewMode.View;
		//    ViewRTB = ReadOnly;
		//    Clear();
		//    RefreshDisplay(!ViewRTB);
		//    //MyItemInfo = tmp;
		//    SelectionStart = 0;
		//    SelectionLength = 0;
		//    //OnModeChange(this, new StepRTBModeChangeEventArgs(ViewRTB?E_ViewMode.View:E_ViewMode.Edit));
		//}
		public void InsertRO(string value, string link)
		{
			AddRtfLink(value, link);
		}
		public void InsertTran(string value, string link)
		{
			AddRtfLink(value, link);
		}
		public void InsertSymbol(int symbcode)
		{
			string sym = string.Format(symbcode < 256 ? "\'{0:X2}" : @"\u{0}", symbcode);
			if (symbcode < 256)
				AddText(((char)symbcode).ToString());
			else
				AddSymbol(sym);		// Adds font commands around symbol, needed for higher codes
		}
		public void InsertSymbol(string symbol)
		{
			AddSymbol(symbol);
		}
		public void InsertIndent(string indentToken)
		{
			if (indentToken == null || indentToken == "0") indentToken = "\x5";
			AddText(indentToken);
		}
		public void InsertText(string txt)
		{
			AddText(txt);
		}
		private void ToggleCase()
		{
			char type = 'l';
			// do not change case on linked text
			RangeStatus rs = FindRangeStatus();
			string tmp = null;
			if (rs != RangeStatus.NoContainedLinks)
			{
				int start = SelectionStart;
				int ostart = SelectionStart;
				int end = SelectionStart + SelectionLength;
				bool processed = false;
				while (!processed && start <= end)
				{
					foreach (LinkLocation ll in LinkLocations)
					{
						if (ll.Start >= start && ll.End <= end)
						{
							processed = true;
							if (start < ll.Start)
							{
								SelectionStart = start;
								SelectionLength = ll.Start - start;
							}
							start = ll.End + 1;
							break;
						}
					}
				}
			}
			string ostring = SelectedText;
			if (ostring.Length == 0) return; // nothing selected
			if (ostring.Length == 1)
				type = char.IsUpper(ostring, 0) ? 'l' : 'U';
			else if ((char.IsUpper(ostring, 0) && char.IsUpper(ostring, 1)) ||
				(char.IsLower(ostring, 0) && char.IsUpper(ostring, 1))) type = 'l'; // lower case
			else if ((char.IsUpper(ostring, 0) && char.IsLower(ostring, 1))) type = 'U'; // upper case
			else type = 'T'; // Title case
			SetSelectedCase(type);
		}
		public void SetSelectedCase(char type)
		{
			// do not change case on linked text
			RangeStatus rs = FindRangeStatus();
			string tmp = null;
			if (rs == RangeStatus.NoContainedLinks)
				SetCase(type);
			else
			{
				int start = SelectionStart;
				int ostart = SelectionStart;
				int end = SelectionStart + SelectionLength;
				StringBuilder sb = new StringBuilder();
				while (start <= end)
				{
					bool processed = false;
					foreach (LinkLocation ll in LinkLocations)
					{
						if (ll.Start >= start && ll.End <= end)
						{
							processed = true;
							if (start < ll.Start)
							{
								SelectionStart = start;
								SelectionLength = ll.Start - start;
								SetCase(type);
							}
							start = ll.End + 1;
							break;
						}
					}
					// if none were processed, no more links, copy over any remaining text.
					if (!processed)
					{
						SelectionStart = start;
						SelectionLength = end-start;
						SetCase(type);
						start = end + 1;
					}
				}
				SelectionStart = ostart;
				SelectionLength = end - ostart;
			}
		}
		private void SetCase(char type)
		{
			int ostart = SelectionStart;
			int olen = SelectionLength;
			string ostring = SelectedText;
			string tmp = null;
			bool docap = true;
			// go character by character.  Because of symbols, setting entire
			// to upper or lower set symbols incorrectly some of time (depending
			// on symbol) .
			for (int i = 0; i < olen; i++)
			{
				SelectionStart = ostart + i;
				SelectionLength = 1;
				switch (type)
				{
					case 'l':
						if (SelectedText[0] >= 'A' && SelectedText[0] <= 'Z')
							SelectedText = SelectedText.ToLower();
						break;
					case 'U':
						if (SelectedText[0] >= 'a' && SelectedText[0] <= 'z')
							SelectedText = SelectedText.ToUpper();
						break;
					case 'T':
						if (docap && SelectedText[0] >= 'a' && SelectedText[0] <= 'z')
							SelectedText = SelectedText.ToUpper();
						else if (!docap && SelectedText[0] >= 'A' && SelectedText[0] <= 'Z')
							SelectedText = SelectedText.ToLower();
						docap = ostring[i] == ' '; 
						break;
				}
			}
			SelectionStart = ostart;
			SelectionLength = olen;
		}
		#endregion
		#region AddRtfTextAndStyles
		private string _LastRtf = "";
		public string LastRtf
		{
			get { return _LastRtf; }
			set { _LastRtf = value; }
		}
		private bool _lastReadOnly = false;
		private void AddRtfText(string txt)
		{
			//Console.WriteLine("ItemID:{0}", MyItemInfo.ItemID);
			//if(MyItemInfo.ItemID==10256)
			//	Volian.Base.Library.vlnStackTrace.ShowStackLocal("ItemID:{0}", MyItemInfo.ItemID.ToString());
			StringBuilder selectedRtfSB = new StringBuilder();
			AddFontTable(selectedRtfSB, FormatFont, FontIsFixed(FormatFont));
			_RtfPrefix = selectedRtfSB.ToString();
			selectedRtfSB.Append(txt);
			string newRtf = selectedRtfSB.ToString() + "}";
			if(newRtf != _LastRtf || ReadOnly != _lastReadOnly)
			{
				//Console.WriteLine("ItemID:{0}\r\nOld:'{1}'\r\nNew:'{2}'\r\n", MyItemInfo.ItemID, Rtf, newRtf);
				this.ContentsResized -= new ContentsResizedEventHandler(StepRTB_ContentsResized);
				Text = "";
				this.ContentsResized += new ContentsResizedEventHandler(StepRTB_ContentsResized);
				// indents went from being handled by code support of an indent character (usually \05) to 
				// using rtf commands to fix various indent bugs including B2015-103).  The following code
				// puts the rtf indent characters that are stored at beginning of string into correct location
 				// of rtf header (have to be after the \pard).  Otherwise the indents did not show up.
				Match match = Regex.Match(txt,@"\\fi([-0-9]*) ?\\li([0-9]*)");
				if (match.Success)
				{
					string indentStr = @"\fi" + match.Groups[1].Value + @"\li" + match.Groups[2].Value;
					Rtf = Rtf.Replace(@"\pard", @"\pard" + indentStr);
				}
				SelectedRtf = _LastRtf = newRtf;
				_lastReadOnly = ReadOnly;
			}
			FindAllLinks();
			if (txt == "") SelectionFont = FormatFont;
		}
		public void SetTableGridCellRTFPrefix(Font myfont)
		{
			if (_FormatFont == null)
				_FormatFont = myfont;
			if (_RtfPrefix == null)
			{
				StringBuilder selectedRtfSB = new StringBuilder();
				AddFontTable(selectedRtfSB, myfont, FontIsFixed(myfont));
				_RtfPrefix = selectedRtfSB.ToString();
			}
		}
		// The following code is used to display the little 'tic' marks to show indenting:
		private const int WM_PAINT = 15;
		protected override void WndProc(ref Message m)
		{
			if (m.Msg == WM_PAINT)
			{
				this.Invalidate();
				base.WndProc(ref m);
				if ((FieldToEdit == E_FieldToEdit.StepText) && (SelectionHangingIndent > 0 || SelectionIndent > 0) && (ActiveMode || MyItemInfo.IsTable))
				{
					using (Graphics g = Graphics.FromHwnd(this.Handle))
					{
						int size = 2;
						g.DrawLine(Pens.DarkBlue, SelectionIndent, 0, SelectionIndent + size, 0);
						g.DrawLine(Pens.DarkBlue, SelectionIndent, 2 * size, SelectionIndent, 0);
						g.DrawLine(Pens.DarkBlue, SelectionHangingIndent, Height - 1, SelectionHangingIndent + size, Height - 1);
						g.DrawLine(Pens.DarkBlue, SelectionHangingIndent, Height - 1 - 2 * size, SelectionHangingIndent, Height - 1);
					}
				}
			}
			else
			{
				base.WndProc(ref m);
			}
		}
		private static void AddFontTable(StringBuilder selectedRtfSB, Font myFont, bool isFixed)
		{
			StringBuilder sbbeg = new StringBuilder();
			StringBuilder sbend = new StringBuilder();
			if (myFont.Bold)
			{
				sbbeg.Append(@"\b");
				sbend.Append(@"\b0");
			}
			if (myFont.Underline)
			{
				sbbeg.Append(@"\ul");
				sbend.Insert(0, @"\ulnone");
			}
			if (myFont.Italic)
			{
				sbbeg.Append(@"\i");
				sbend.Insert(0, @"\i0");
			}
			selectedRtfSB.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset2 " + myFont.FontFamily.Name + @";}"); //}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}";
			if (!isFixed)
				selectedRtfSB.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}{\colortbl ;\red255\green0\blue0;}");
			else
				selectedRtfSB.Append(@"{\f1\fnil\fcharset0 FreeMono;}}{\colortbl ;\red255\green0\blue0;}");  // FreeMono is now used for the edit screen only.  VESymbFix and Consolas are used for printing
			selectedRtfSB.Append("\r\n");
			// use styles to construct rtf commands to insert into next line (where \b, etc is)
			selectedRtfSB.Append(@"\viewkind4\uc1\pard\sl-240\slmult0" + sbbeg.ToString() + @"\fs" + Convert.ToInt32(myFont.SizeInPoints * 2).ToString() + @" "); // \f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}";
		}
		private bool FontIsFixed(Font myFont)
		{
			Graphics grph = Graphics.FromHwnd(this.Handle);
			SizeF sfW = grph.MeasureString("W", myFont);
			SizeF sfE = grph.MeasureString("!", myFont);
			if (sfW.Width == sfE.Width) return true;
			return false;
		}
		private void AddRtf(string str)
		{
			// Because we're inserting rtf with { }, the surrounding styles are not included.  Get the font
			// style of current position & use it after the insert to set the style.
			RTBAPI.E_FontStyle fs = RTBAPI.GetFontStyle(this);
			int positionStart = SelectionStart;
			SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset2 " + this.Font.FontFamily.Name + @";}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + str + @"}}";
			// Note that SelectedRtf does not contain the inserted text after the previous line.  We need
			// to determine how long the inserted string is in order to set its style. SelectionStart contains
			// the location after the insertion.
			int positionAfter = SelectionStart;
			Select(positionStart, positionAfter - positionStart);
			RTBAPI.SetFontStyle(this, fs);
			Select(positionAfter, 0);
		}
		private void AddText(string str)
		{
			// See comments in AddRtf(string str) to explain the font style setting
			RTBAPI.E_FontStyle fs = RTBAPI.GetFontStyle(this);
			int positionStart = SelectionStart;
			SelectedText = str;
			int positionAfter = SelectionStart;
			Select(positionStart, positionAfter - positionStart);
			RTBAPI.SetFontStyle(this, fs);
			Select(positionAfter, 0);
		}
		private void AddSymbol(string str)
		{
			// See comments in AddRtf(string str) to explain the font style setting
			RTBAPI.E_FontStyle fs = RTBAPI.GetFontStyle(this);
			Font locfont = this.Font; 
			int position = SelectionStart;
			SelectedRtf = RtfPrefixForSymbols + str + @"}";
			Select(position, 1);
			RTBAPI.SetFontStyle(this, fs);
			Select(position + 1, 0);
			if (FieldToEdit == E_FieldToEdit.StepText)
				SelectionFont = MyItemInfo.GetItemFont().WindowsFont;
			else
				// if doing properties for folder, docversion, proc or sect, don't get item's font.
				SelectionFont = locfont;  
		}
		private string GetAddSymbolText(string symtxt)
		{
			 return (@"{\f0\fs" + this.Font.SizeInPoints * 2 + @" " + symtxt + @"}");
		}
		public void AddRtfLink(string linkUrl, string linkValue)
		{
			if (CreateParams.ClassName == "RICHEDIT50W")
				AddLink50(linkUrl, linkValue);
			else
				AddLink20(linkUrl, linkValue);
		}
		public string FontTable
		{
			get
			{
				StringBuilder sb = new StringBuilder();
				sb.Append(@"{\fonttbl{\f0\fnil\fcharset2 " + this.Font.FontFamily.Name + @";}");
				if (!FontIsFixed(this.Font))
					sb.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}");
				else
					sb.Append(@"{\f1\fnil\fcharset0 FreeMono;}}"); // FreeMono is now used for the edit screen only.  VESymbFix and Consolas are used for printing
				return sb.ToString();
			}
		}
		private string FontSize
		{
			get
			{
				Match match = Regex.Match(Rtf, @"\\fs[0-9]*");
				return match.Value;
			}
		}
		private void AddLink20(string linkValue, string linkUrl)
		{
			string fonttab = FontTable;
			string fontsize = FontSize;
			//if (this.DetectUrls) this.DetectUrls = false;
			if (SelectionLength > 0)HandleDeleteKeyWithSelectedText(new KeyEventArgs(Keys.None), null);
			int position = SelectionStart;
			SelectionLength = 0;
			linkValue = linkValue.Replace("\\u8209?", "\\f1\\u8209?\\f0 ");
			linkValue = linkValue.Replace("\\u916?", "\\f1\\u916?\\f0 ");
			linkValue = linkValue.Replace(@"{", @"\{");
			linkValue = linkValue.Replace(@"}", @"\}");
			SelectedRtf = @"{\rtf1\ansi"+FontTable+@"{\colortbl ;\red255\green0\blue0;}\v"+FontSize+@" \v0 }";
			this.SelectionLength = 0;
			this.SelectionStart = position;
			FindAllLinks();
		}
		private void AddLink50(string linkValue, string linkUrl)
		{
			//this.DetectUrls = false;
			int position = SelectionStart;
			SelectionLength = 0;
			SelectedRtf = string.Format(@"{{\rtf\field{{\*\fldinst{{HYPERLINK ""www.volian.com #{0}"" }}}}{{\fldrslt{{\cf2\ul {1}}}}}}}", linkUrl, linkValue);
			this.SelectionStart = this.TextLength;
			this.SelectionLength = 0;
		}
		private void AddRtfStyles()
		{
			if ((OrigDisplayText.TextFont.Style & E_Style.Bold) > 0)
			{
				this.SelectAll();
				RTBAPI.ToggleBold(true, this, RTBAPI.RTBSelection.SCF_SELECTION);
				this.Select(0, 0);
			}
			// Bug Fix
			// The code below was changed to select all of the text and then
			// apply the underlining to the selected text, because if the
			// the underlining was applied to RTBAPI.RTBSelection.SCF_ALL
			// the Links were changed so that they were no longer hidden.
			if ((OrigDisplayText.TextFont.Style & E_Style.Underline) > 0)
			//	RTBAPI.ToggleUnderline(true, this, RTBAPI.RTBSelection.SCF_ALL);
			{
				this.SelectAll();
				RTBAPI.ToggleUnderline(true, this, RTBAPI.RTBSelection.SCF_SELECTION);
				this.Select(0, 0);
			}
			// Bug Fix
			// The code below was changed to select all of the text and then
			// apply the italics to the selected text, because if the
			// the italics was applied to RTBAPI.RTBSelection.SCF_ALL
			// the Links were changed so that they were no longer hidden.
			if ((OrigDisplayText.TextFont.Style & E_Style.Italics) > 0)
			//	RTBAPI.ToggleItalic(true, this, RTBAPI.RTBSelection.SCF_ALL);
			{
				this.SelectAll();
				RTBAPI.ToggleItalic(true, this, RTBAPI.RTBSelection.SCF_SELECTION);
				this.Select(0, 0);
			}
		}
		#endregion
		#region HeightSupport
		public event StepRTBEvent HeightChanged;
		private void OnHeightChanged(object sender, EventArgs args)
		{
			if (HeightChanged != null) HeightChanged(sender, args);
		}
		private void AdjustSizeForContents(bool adjustWidth)
		{
			if (!_InitializingRTB)
			{
				Size offset = Size - ClientSize;
				int widthNew = (this is TableCellEditor) ? Width : (ContentsSize.Width + offset.Width + AdjustSize.Width);
				//int widthNew = Width;
				//if (!(this is TableCellEditor))
				//    widthNew = ContentsSize.Width + offset.Width + AdjustSize.Width;
				int heightNew = ContentsSize.Height + offset.Height + AdjustSize.Height;
				// Don't make the window narrower unless it has text - RHM - 20100107
				Size szNew = new Size(((Text != "" &&  adjustWidth) ? widthNew : (widthNew > Width ? widthNew : Width)), heightNew);
				if (this.Size != szNew)
				{
					int heightOld = Height;
					//Console.WriteLine("before {0}, {1}, {2}, {3}, {4}", Size, szNew, widthNew, Width, this.GetType().Name);
					this.Size = szNew;
					//Console.WriteLine("after {0}, {1}, {2}, {3}", Size, szNew, widthNew, Width);
					if (heightOld != Height)
					{
						OnHeightChanged(this, new EventArgs());
						if (ScrollPos.Y != 0) // Adjust ScrollPosition if it isn't Zero
							ScrollPos = new Point(0, 0);
					}
				}
			}
		}
		private float GetStringWidth(string strMeasureString)
		{
			using (Graphics g = Graphics.FromHwnd(Handle))
			{
				return g.MeasureString(strMeasureString, Font).Width;
			}
		}
		private int Ceiling(float f)
		{
			return (int)(Math.Ceiling(f));
		}
		public int MaxTextWidth
		{
			get
			{
				int maxWidth = 0;
				for (int i = 0; i < TextLength; i++)
				{
					int w = GetPositionFromCharIndex(i).X;
					maxWidth = Math.Max(maxWidth, w);
				}
				return maxWidth+10; // add 10 to account for the last character
			}
		}
    public void AdjustWidthForContent()
    {
      AdjustWidthForContent(0);
    }
    public void AdjustWidthForContent(int count)
		{
			int widthNL = Ceiling(GetStringWidth("\n"));
			int widthMax = 0;
			int widthMaxWW = 0;
			int indexStart = 0;
			int lineCountFromLines = Lines.Length;
			int lineCountFromGet = GetLineFromCharIndex(TextLength)+1;
			for (int i = 0; i < Lines.Length; i++)
			{
				int lineStart = GetLineFromCharIndex(indexStart);
				int indexEnd = indexStart + Lines[i].Length;
				int lineEnd = GetLineFromCharIndex(indexEnd);
				Point pointStart = GetPositionFromCharIndex(indexStart);
				Point pointEnd = GetPositionFromCharIndex(indexEnd);
				int indexEndPos = GetCharIndexFromPosition(pointEnd);
				if (indexEndPos + 1 < indexEnd) // This indicates that the text is wider than the Rich Text Box
				{
					int w = pointEnd.X + (indexEnd - indexEndPos) * widthNL;
					if (w > widthMaxWW)
						widthMaxWW = w;
				}
				if (lineEnd > lineStart)// this indicates that there was word-wrap on this line.
				{
					int w = pointEnd.X + Width * (lineEnd - lineStart);
          if (w > widthMaxWW)
            widthMaxWW = w;
				}
				else
				{
					if (pointEnd.X > widthMax)
						widthMax = pointEnd.X;
				}
				indexStart = indexEnd + 1;
			}
			if (widthMaxWW == 0)
			{
				int widthBorder =	Width - ClientSize.Width;
				int w = widthMax + widthNL + widthBorder;
				if (Width != w)
				{
          if (count < 5 || Width < w)
          {
            Width = w;
            AdjustWidthForContent(count + 1);// Try one more time
          }
				}
			}
			else
			{
        if (count < 5 || Width < widthMaxWW)
        {
          Width = widthMaxWW;
          AdjustWidthForContent(count + 1);
        }
			}
		}
		public int CalculateHeight()
		{
			if (this.CreateParams.ClassName == "RICHEDIT50W")
				return CalculateHeight50();
			else
				return CalculateHeight20();
		}
		private int CalculateHeight20()
		{
			Application.DoEvents();
			int yBottom = GetPositionFromCharIndex(TextLength).Y;
			int yTop = GetPositionFromCharIndex(0).Y;
			int heightFont = SelectionFont.Height;
			int borderSize = this.Height - this.ClientSize.Height;
			int heightNext = (yBottom - yTop) + heightFont + borderSize + 2;// 2 pixels - 1 at the top and 1 at the bottom
			if (heightNext != Height)
			{
				Height = heightNext;
				OnHeightChanged(this, new EventArgs());
				ScrollPos = new Point(0, 0); // Scroll to make sure that the first line is displayed as the first line
			}
			return heightNext;
		}
		private int CalculateHeight50()
		{
			Application.DoEvents();
			int heightFont = SelectionFont.Height;
			int borderSize = this.Height - this.ClientSize.Height;
			int heightNext = (1 + GetLineFromCharIndex(TextLength)) * heightFont + borderSize + 2;// 2 pixels - 1 at the top and 1 at the bottom
			return heightNext;
		}
		#endregion
		#region ColorSupport - Not currently used.
		//private void SetBackGroundColor(ItemInfo itemInfo)
		//{
		//    string backcolor = null;
		//    int type = (int)itemInfo.MyContent.Type;
		//    FormatInfo formatinfo = itemInfo.ActiveFormat;
		//    if (type == (int)E_FromType.Procedure)
		//        backcolor = formatinfo.PlantFormat.FormatData.ProcData.BackColor;
		//    else if (type == (int)E_FromType.Section)
		//        backcolor = formatinfo.PlantFormat.FormatData.SectData.BackColor;
		//    else
		//    {
		//        int typindx = (int)itemInfo.MyContent.Type - 20000;  // what to do for other types rather than steps
		//        backcolor = formatinfo.PlantFormat.FormatData.StepDataList[typindx].StepLayoutData.BackColor;
		//    }
		//    BackColor = Color.FromName(backcolor);
		//}
		#endregion
		#region EventSupport
		#region LinkEvents
		private StepPanelLinkEventArgs _MyLinkClickedEventArgs;
		public event StepRTBLinkEvent LinkChanged;
		private void OnLinkChanged(object sender, StepPanelLinkEventArgs args)
		{
			_MyLinkClickedEventArgs = args;
			if (LinkChanged != null) LinkChanged(sender, args);
		}
		public event StepRTBLinkEvent LinkGoTo;
		private void OnLinkGoTo(object sender, StepPanelLinkEventArgs args)
		{
			_MyLinkClickedEventArgs = args;
			if (LinkGoTo != null) LinkGoTo(sender, args);
		}
		public event StepRTBLinkEvent LinkModifyTran;
		private void OnLinkModifyTran(object sender, StepPanelLinkEventArgs args)
		{
			_MyLinkClickedEventArgs = args;
			if (LinkModifyTran != null) LinkModifyTran(sender, args);
		}
		public event StepRTBLinkEvent LinkModifyRO;
		private void OnLinkModifyRO(object sender, StepPanelLinkEventArgs args)
		{
			_MyLinkClickedEventArgs = args;
			if (LinkModifyRO != null) LinkModifyRO(sender, args);
		}
		#endregion
		#region TextAndContentsEvents
		void StepRTB_TextChanged(object sender, EventArgs e)
		{
			if (_InitializingRTB) return;
			// Was setting _IsDirty to true here, but this was getting called from
			// 'dotnetbar' when text was NOT Changed. So _IsDirty was made into 
			// a property and compared original rtf versus current richtextbox's
			// rtf.
			// the below 2 lines were added to solve the problem with the change manager detail not showing the text of an audit when the same audit
			//was selected twice
			if (Text == string.Empty)
				LastRtf = string.Empty;
			FindAllLinks();
		}
		private void ShowMyParentsFonts()
		{
			Console.WriteLine("\r\n--- Parents Fonts ---- For ----{0}", Text);
			ShowMyParentsFonts(this);
			Console.WriteLine("Selection Font".PadRight(31) + SelectionFont);
		}
		private void ShowMyParentsFonts(Control ctrl)
		{
			if (ctrl.Parent is Control)
				ShowMyParentsFonts(ctrl.Parent);
			Console.WriteLine("{0} {1}", ctrl.GetType().Name.PadRight(30), ctrl.Font);
		}
		public void StepRTB_ContentsResized(object sender, ContentsResizedEventArgs e)
		{
			ContentsRectangle = e.NewRectangle;
		}
		#endregion
		#region Selection Handlers
		bool _AdjustingSelection = false;
		private bool _ProcessingDelete;
		private bool _HandlingCtrlA = false;
		private void HandleLocalSelectionChange()
		{
			//if (MyRTBItem != null && MyRTBItem.MyStepPanel.SelectedStepRTB != this)
			if (OnIsNotCurrentSelection(this, new EventArgs()))
				return;
			HandleSelectionChange();
		}
		private void HandleSelectionChange()
		{
			if (_HandlingCtrlA) return;
			if (!_HandlingCtrlA && this.TextLength == this.SelectionLength && this.SelectedRtf.EndsWith("\\par\r\n}\r\n"))
			{
				_HandlingCtrlA = true;
				SelectAll();
				_HandlingCtrlA = false;
			}
			//HandleOverWrite();
			//vlnStackTrace.ShowStackLocal("HandleSelectionChangeStack", 1, 10);
			bool startingValue = _AdjustingSelection;
			if (_IdentifyingLinks || _ProcessingDelete) return;
			// If the cursor is beyond the end of the line then move the cursor back one character
			if (CorrectSelectionAtEndOfLine()) 
				return;
			if (ProcessKeystrokes)
			{
				if (!_MouseDown && !_AdjustingSelection)
				{
					if (_LinkLocations != null)
					{
						DebugPrint("HSC===================>Beginning: SelectionStart {0}, SelectionLength {1}", SelectionStart, SelectionLength);
						_AdjustingSelection = true;
						LinkLocation ll = FindLinkLocation();
						LinkLocation llend = null;
						if (SelectionLength != 0)
							llend = FindLinkLocation(SelectionStart + SelectionLength - 1);
						if (ll != null)
						{
							if (SelectionStart == ll.Start && SelectionLength == 0)
							{
								if (SelectionStart >= 7)
								{
									// If the cursor is at the beginning of the line and a link is at the beginning of the line
									// then select the link rather than postitioning the cursor at the end of the previous line
									if (!_LastWasLeftArrow && GetFirstCharIndexOfCurrentLine() == SelectionStart)
										SetSelection(ll);
									else
									{
										DebugPrint("HSC===================>ll Insert: Sel {0}, Length {1}, Link Start {2}, Link Length {3}", SelectionStart, SelectionLength, ll.Start, ll.Length);
										SelectionStart = ll.Start - 7;
									}
								}
							}
							else if (SelectionStart + SelectionLength > ll.End) // Beyond the end of the starting link
							{
								int end = SelectionStart + SelectionLength;
								if (llend != null) end = llend.End; // If it extends into another link extend the end to the end of that link
								DebugPrint("HSC===================>ll After: Sel {0}, Length {1}, Link Start {2}, Link Length {3}", SelectionStart, SelectionLength, ll.Start, ll.Length);
								SetSelection(ll.Start, end - ll.Start);
							}
							else if (SelectionStart >= ll.Start || SelectionLength > 0)// Within the starting link
							{
								DebugPrint("HSC===================>ll: Sel {0}, Length {1}, Link Start {2}, Link Length {3}", SelectionStart, SelectionLength, ll.Start, ll.Length);
								SetSelection(ll);
							}
						}
						else if (llend != null)
						{
							DebugPrint("HSC===================>llend: Sel {0}, Length {1}, Link Start {2}, Link Length {3}", SelectionStart, SelectionLength, llend.Start, llend.Length);
							// Check to see if the beginning starts before the Removing ending Removing starting  _AdjustingSelection problem");
			DebugPrint("RS------ SelectionChange > {0}", FindRangeStatus());
			if (SelectionLength > 0 && IsSelectionLinked(SelectionStart, SelectionLength))
			{
				if (SelectedText.IndexOf(@"[END>") > 0) MyLinkText = SelectedText.Substring(0, SelectedText.IndexOf(@"[END>"));
				else MyLinkText = SelectedText;
			}
			else
				MyLinkText = null;
			OnRTBSelectionChanged(this, new EventArgs());
			_LastWasLeftArrow = false;
		}
		/// 
		/// If the Selection is beyond the end of the line, move the selection back one character
		/// 
		/// 
		private bool CorrectSelectionAtEndOfLine()
		{
			if (SelectionStart > 0 && SelectionLength == 0 &&
				GetFirstCharIndexFromLine(GetLineFromCharIndex(SelectionStart)) >
				GetFirstCharIndexOfCurrentLine())
			{
				SelectionStart--; // Move the cursor back one character
				return true;
			}
			return false;
		}
		//private void HandleOverWrite()
		//{
		//    if (!_OverWrite) return;
		//    if (SelectionLength > 0) return;
		//    if (SelectionStart == TextLength) return;
		//    Console.WriteLine("Handle Overwrite, SelectionStart = {0}, SelectionLength = {1}", SelectionStart, SelectionLength);
		//    SelectionLength = 1;
		//}
		private bool _CheckSelection = false;
		#endregion
		#region Delete Handlers
		private void HandleDeleteKeyWithSelectedText(KeyEventArgs e, string keychars)
		{
			_ProcessingDelete = true;
			FindRangeStatus();
			//DebugPrint("RS---------------- Delete > {0}", _RTBRangeStatus);
			switch (_RTBRangeStatus)
			{
				case RangeStatus.NoContainedLinks:
				case RangeStatus.Before_After:
				case RangeStatus.Before_EndLink:
				case RangeStatus.Before_EndBox:
				default:
					DeleteCurrentSelection(keychars);
					e.SuppressKeyPress = true;
					break;
				case RangeStatus.Before_Between: //myRTB1.SelectedText.EndsWith(@"[END>
		/// This inserts a space in between two links.  
		/// It actually inserts "\v0 {space}\v " between the END tag and the START tag
		/// 
		/// 
		/// 
		/// 
		private void InsertCharBetweenLinks(LinkLocation ll, char charToAdd, bool setSelect)
		{
			//_InsertingSpaceBetweenLinks = true;
			DebugPrint("ICBLvvvvvvvvvvvvvvv>>>");
			Rtf = Rtf.Substring(0, ll.StartRtf) + @"\v0 " + charToAdd.ToString() + @"\v " + Rtf.Substring(ll.StartRtf);
			//_InsertingSpaceBetweenLinks = false;
			if (setSelect)
				SelectionStart = ll.Start - 6;   // account for >>");
		}
		private void ExpandSelectionToIncludeStart()
		{
			//_AdjustingSelection = true;
			SetSelection(SelectionStart - 7, SelectionLength + 7);  // Expand selection to include start
			//_AdjustingSelection = false;
		}
		public string GetSelectionForClipBoard()
		{
			string selTxtRTF = SelectedRtf;
			LinkLocation llstart = FindLinkLocation();
			if (llstart != null)
			{
				selTxtRTF = Rtf.Substring(llstart.StartRtf, llstart.EndRtf - llstart.StartRtf);
				string lookfor = selTxtRTF.Substring(selTxtRTF.IndexOf(' '));
				return SelectedRtf.Replace(lookfor, @"\v " + selTxtRTF);
			}
			return (selTxtRTF);
		}
		private void DeleteBetweenBetweenLinks(string keychars)
		{
			
			DebugSelection("DeleteBetweenBetweenLinks");
			int selStart = SelectionStart - 7;
			int selLength = SelectionLength + 2; // Include the two added spaces
			InsertCharBetweenLinks(_RangeEndLink.NextLink); // Add a space at the end link
			InsertCharBetweenLinks(_RangeStartLink); // Add a space a the start link
			SetSelection(selStart, selLength);// Select everything including the spaces
			DeleteCurrentSelection(keychars);// Delete Selection
		}
		/// 
		/// This is added to handle a glitch in richtextbox.  Depending on
		/// which direction that selection is made (left -> right or right -> left)
		/// a replacement or delete may not work, you'll just get a 'beep'.
		/// This approach consistently works.
		/// 
		public bool WasXDelete = false;
		private void DeleteCurrentSelection(string keys)
		{
			DebugPrint("vvvvvvvvvvvvxxxxxxxxxxxx>");
			DebugSelection("Before X");
			DebugPrint(keys == null ? "NULL" : "NOT NULL");
			SelectedText = keys==null?"X":keys;			// replace text with X
			WasXDelete = (keys == null);
			DebugSelection("After X");
			DebugPrint("------------xxxxxxxxxxxx>");
			if (keys == null)
			{
				_SendBackSpace = true;
				RtbSendKeys("{BS}");			// remove X
				//this.ClearUndo();				// undo was redisplay 'X' and then deleted text
				Application.DoEvents();
				DebugSelection("After BS");
			}
			DebugPrint("^^^^^^^^^^^^xxxxxxxxxxxx>");
		}
		private void DeleteSelection(int start, int length, string keychars)
		{
			SetSelection(start, length);
			DeleteCurrentSelection(keychars);
		}
		private void DeleteEndBetweenLinks(string keychars)
		{
			_ProcessingKeys++;
			DebugSelection("DeleteEndBetweenLinks");
			int sstart = SelectionStart;
			int slen = SelectionLength + 1 - 7;
			// This puts a space at the link that starts at the end of the selection
			InsertCharBetweenLinks(_RangeEndLink.NextLink);
			//_AdjustingSelection = true;
			DeleteSelection(sstart, slen, keychars);
			//_AdjustingSelection = false;
			_ProcessingKeys--;
		}
		private void DeleteStartBetweenLinks(string keychars)
		{
			_ProcessingKeys++;
			DebugSelection("DeleteStartBetweenLinks");
			int slen = SelectionLength + 8;
			int sstart = SelectionStart - 7;
			//LinkLocation ll = FindBetweenLinks(SelectionStart);
			InsertCharBetweenLinks(_RangeStartLink);
			DeleteSelection(sstart, slen, keychars);
			_ProcessingKeys--;
		}
		private void DeleteFromStartOfBox(string keychars)
		{
			_ProcessingKeys++;
			DebugSelection("DeleteFromStartOfBox");
			int slen = SelectionLength;
			SetSelection(0, 0);
			//RtbSendKeys(" ");		// open for space between links which separates END/START tokens
			SelectedText = " ";
			DeleteSelection(0, slen + 8, keychars);
			_ProcessingKeys--;
		}
		private void DeleteFromStartOfBoxEndBetweenLinks(string keychars)
		{
			_ProcessingKeys++;
			DebugSelection("DeleteFromStartOfBoxEndBetweenLinks");
			// This puts a space at the link that starts at the end of the selection
			int sLen = SelectionStart + SelectionLength - 7 + 2;// -7 for  0) SetSelection(SelectionStart, newend - SelectionStart);
					break;
				case Keys.Space:
					if (e.Control) // Hardspace - Ctrl+Shift+Space
						InsertSymbol(@"\u160?");
					break;
				case Keys.F3: // shift F3
					e.Handled = true;
					ToggleCase(); // toggle through Upper, Lower, and Title case
					break;
				default:
					break;
			}
		}
		private string GetSelectedDisplayableText()
		{
			using (StepRTB srtb = new StepRTB())
			{
				srtb.Rtf = this.SelectedRtf.Replace(@"\u8209?", "-");
				string rtnstr = "";
				string ctxt = srtb.Text;//this.SelectedText;
				if (ctxt.EndsWith("");
				if ((strtidx == -1 || strtidx > lnkidx) && lnkidx > -1 && endidx > -1)
				{
					rtnstr += ctxt.Substring(idx, lnkidx);
					idx = endidx + 5;
					strtidx = ctxt.IndexOf("", idx);
				}
				while (strtidx > -1)
				{
					rtnstr += ctxt.Substring(idx, strtidx - idx);
					idx = strtidx + 7;
					rtnstr += ctxt.Substring(idx, lnkidx - idx);
					idx = endidx + 5;
					strtidx = ctxt.IndexOf("", idx);
				}
				if (idx < ctxt.Length)
					rtnstr += ctxt.Substring(idx);
				return rtnstr;
			}
		}
		private bool IsControlChar = false;
		private bool _SendBackSpace = false;
		void StepRTB_KeyDown(object sender, KeyEventArgs e)
		{
			// added jcb 20130103 to support set ro from word doc with annotation when right click menu is opened by keyboard
			if (e.KeyCode == Keys.Apps)
				OnSetMenu(this, new StepRTBMenuEventArgs("OpenContextMenu"));
			if (e.Control)
			{
				IsControlChar = true;
				if (e.Shift)
				{
					//Console.WriteLine("keycode =  {0}  key value = {1}",e.KeyCode,e.KeyValue);
					switch (e.KeyCode)
					{
						// The following keys are supported in underlying RTB (with ctrl+shift key), 
						// but PROMS should not do anything for that functionality:
						case Keys.Add: // the plus on the key pad
						case Keys.Subtract:
						case Keys.L:
						case Keys.Oemplus: // the plus on the keyboard (not the key pad)
							e.Handled = true;
							break;
					}
				}
				else
				{
					switch (e.KeyCode)
					{
						case Keys.X: //ctrl-X
						case Keys.C: //ctrl-C
							// handle the clipboard copy and cut when a Transition or RO is selected
							// For now, we are coping/cutting just the displayed text to the clipboard (like 16-bit VE-PROMS)
							string ts = GetSelectedDisplayableText();
							if (ts != null && ts != "") Clipboard.SetText(GetSelectedDisplayableText());
							e.Handled = true;
							e.SuppressKeyPress = true;
							if (ts != null && ts != "" && e.KeyCode == Keys.X) // cut to clipboard - delete the selected text
								HandleDeleteKeyWithSelectedText(e, null);
							break;
						case Keys.V:
							// NOTE that if in a Properties Dialog StepRTB (for numbers and titles) or in a Procedure Specific
							// Information (PSI) Dialog, i.e. any non-EditItem/Step editor field, the following code is run 
							// for the ctrl-V.  For EditItems & Grid cells, the code in StepTabRibbon's btnPaste_Click is
							// run.  The way this occurs is that in the designer for StepTabRibbon (StepTabRibbon.designer.cs),
							// the line of code:
							//		this.btnPaste.Shortcuts.Add(DevComponents.DotNetBar.eShortcut.CtrlV);
							// maps the Ctrl-V to btnPaste for those StepRTB's that are associated with the StepTabRibbon, i.e.
							// EditItems & Grid cells.
							IDataObject iData = Clipboard.GetDataObject();
							if (!iData.GetDataPresent(DataFormats.Text) && !iData.GetDataPresent(DataFormats.Rtf))
							{
								MessageBox.Show("Cannot paste, text has special characters or symbols that will not paste correctly.");
							}
							else
							{
								// if contains bad rtf (from Word), paste as text, otherwise, do the paste here.
								if (!PasteRtfAsText(true)) Paste();
								if (SelectionLength == 0) SelectionFont = MyStyleFont.WindowsFont;
							}
							e.Handled = true;
							return;
						case Keys.Home:
							StepRTB_HomeEndPressed(e);
							e.Handled = true;
							break;
						case Keys.End:
							StepRTB_HomeEndPressed(e);
							e.Handled = true;
							break;
						// 16bit Proms used 'ctrl-enter' for Hard returns.  32bit Proms uses 'shift-enter'
						// because the richtextbox does this by default.  Also Word uses 'shift-enter'
						// for Hard return.  And Word uses 'ctrl-enter' for inserting of page break.  So
						// 'ctrl-enter' for 32bit will insert page break
						case Keys.Enter:
							OnInsertPgBrk(this, new EventArgs());
							e.Handled = true;
							break;
						// The following keys are supported in underlying RTB (with ctrl key), but PROMS should not
						// do anything for that functionality:
						case Keys.R:	// Right Align text
						case Keys.L:	// Left Align text
						case Keys.E:	// Center Align text
						case Keys.D1:   // Single space text (D1 is the '1' key)
						case Keys.D2:	// Double space text
						case Keys.D5:	// 1.5 space text
						case Keys.H:	// Find  (proms uses Ctrl+F for find)
						case Keys.Oemplus: // plus on the keyboard (not the key pad)
							e.Handled = true;
							break;
					}
				}
			}
			if (((int)e.KeyCode) == 220) e.Handled = true;
			switch (e.KeyCode)
			{
				case Keys.Left:
					if (e.Shift)
					{
						int newstart = FindStart();		// find start of link ending on.
						// if not link, don't do special processing
						if (newstart == SelectionStart - 1) return;
						int len = SelectionLength + SelectionStart - newstart;
						SetSelection(newstart, len);
						e.Handled = true;
						return;
					}
					if (e.Control || SelectionStart == 0)
					{
						StepRTB_ArrowPressed(e.Control ? E_ArrowKeys.CtrlLeft : E_ArrowKeys.Left);
						e.Handled = true;
					}
					HandleLocalSelectionChange();
					break;
				case Keys.Up:
					int ln = GetLineFromCharIndex(SelectionStart);
					if (e.Control || ln == 0)
					{
						StepRTB_ArrowPressed(e.Control ? E_ArrowKeys.CtrlUp : E_ArrowKeys.Up);
						e.Handled = true;
					}
					// if shift-up & at selection had a link, handle this as special case.
					if (e.Shift && SelectionLength > 0)
					{
						RangeStatus rs = FindRangeStatus();
						if (rs != RangeStatus.NoContainedLinks)
						{
							int curend = SelectionStart + SelectionLength;
							SelectionLength = 0;
							RtbSendKeys("{Up}");
							Application.DoEvents();
							Select(SelectionStart, curend - SelectionStart);
							e.Handled = true;
						}
					}
					else
					{
						HandleLocalSelectionChange();
					}
					break;
				case Keys.Right:
					// If at beginning of box that starts with a link, don't worry about shift or not,
					// because selection is first character or link without any other selection.  Don't 
					// need to write code to handle link at beginning of box in Shift Right code because
					// it's handled in HandleSelectionChange.
					if (e.Shift && ((SelectionStart > 0) || (SelectionStart == 0 && _LinkLocations.Count > 0 && _LinkLocations[0].Start != 7)))
					{
						int newlen = FindEnd();
						int len = SelectionLength + newlen;
						SetSelection(SelectionStart, len);
						e.Handled = true;
						return;
					}
					if (e.Control || SelectionStart == this.Text.Length)
					{
						StepRTB_ArrowPressed(e.Control ? E_ArrowKeys.CtrlRight : E_ArrowKeys.Right);
						e.Handled = true;
					}
					HandleLocalSelectionChange();
					break;
				case Keys.Down:
					int l = GetLineFromCharIndex(SelectionStart);
					Point pos = new Point();
					pos.X = ClientRectangle.Width;
					pos.Y = ClientRectangle.Height;
					int lastIndex = this.GetCharIndexFromPosition(pos);
					int lastLine = this.GetLineFromCharIndex(lastIndex);
					if (e.Control || l >= lastLine)
					{
						StepRTB_ArrowPressed(e.Control ? E_ArrowKeys.CtrlDown : E_ArrowKeys.Down);
						HandleLocalSelectionChange();
						e.Handled = true;
						return;
					}
					if (!e.Shift) HandleLocalSelectionChange();
					// if shift-down & on link at beginning of box - do special processing (regular processing
					// didn't handle it correctly.
					if (e.Shift && lastLine > 0 && SelectionStart == 0 && _LinkLocations.Count > 0 && _LinkLocations[0].Start == 7)
					{
						Point cpos = GetPositionFromCharIndex(SelectionStart);
						int addon = ClientRectangle.Height / (lastLine + 1);
						cpos.Y = cpos.Y + addon;
						int selend = GetCharIndexFromPosition(cpos);
						Select(7, selend - 7);
						e.Handled = true;
					}
					break;
				case Keys.PageUp:
					StepRTB_PageKeyPressed(e);
					e.Handled = true;
					break;
				case Keys.PageDown:
					StepRTB_PageKeyPressed(e);
					e.Handled = true;
					break;
				case Keys.Delete:
					if (SelectionLength == 0)
					{
						foreach (LinkLocation lls in _LinkLocations)
						{
							if (SelectionStart >= lls.Start - 7 && SelectionStart < lls.End)
							{
								SetSelection(lls);// Select the link to the right
								HandleDeleteKeyWithSelectedText(e, null);
								e.SuppressKeyPress = true;
								return;
							}
						}
						if (SelectionStart != TextLength)
						{
							SelectionStart++;// A Delete can be accomplished with a right arrow followed by a backspace.
							RtbSendKeys("{BS}"); // This is done due to a glitch in the RichTextBox that sometimes causes a beep rather than a delete
							e.SuppressKeyPress = true;
						}
					}
					else
						HandleDeleteKeyWithSelectedText(e, null);
					break;
				case Keys.Back:
					if (_SendBackSpace)
					{
						_SendBackSpace = false;
						return;
					}
					_CheckSelection = true;
					if (SelectionLength == 0)
					{
						foreach (LinkLocation lls in _LinkLocations)
						{
							if (SelectionStart > lls.Start - 7 && SelectionStart <= lls.End)
							{
								SetSelection(lls);
								break;
							}
						}
					}
					if (SelectionLength > 0) HandleDeleteKeyWithSelectedText(e, null);
					break;
				case Keys.Insert:
					//_OverWrite = !_OverWrite;
					//if (_OverWrite)
					//{
					//    HandleSelectionChange();
					//}
					//else
					//{
					//    SelectionLength = 0;
					//    HandleSelectionChange();
					//}
					// For now, don't allow for toggling between insert/overwrite mode - see
					// comment on _OverWrite
					e.Handled = true;
					break;
				case Keys.F5:
					if (e.Shift)
					{
						e.Handled = true;
            ItemInfo myCopyStep = (this.Parent.Parent.Parent as StepTabPanel).MyDisplayTabControl.MyCopyStep;
            if (myCopyStep != null)
            {
              if (this.MyItemInfo.IsSection && myCopyStep.IsSection)
                OnSetMenu(this, new StepRTBMenuEventArgs("StepPaste"));
              if (this.MyItemInfo.IsStep && myCopyStep.IsStep)
              {
                if ((this.MyItemInfo.IsHigh && myCopyStep.IsHigh) || (!this.MyItemInfo.IsHigh && !myCopyStep.IsHigh))
                  OnSetMenu(this, new StepRTBMenuEventArgs("StepPaste"));
              }
            }
            //if (!OnCheckClipboard(this, new EventArgs())) return;  // check if 'clipboard' contains a step.
            //OnSetMenu(this, new StepRTBMenuEventArgs("StepPaste"));
					}
					else if (!e.Control && !e.Alt)
					{
						e.Handled = true;
						OnCopyStep(this, new EventArgs());
					}
					break;
				case Keys.F6:
					e.Handled = true;
					SendKeys.Send("%H{ESC}");
					break;
				case Keys.Tab:
					VlnFlexGrid myGrid = Parent as VlnFlexGrid;
					if (myGrid == null)
					{
						e.SuppressKeyPress = true;
						e.Handled = true;
						Form frm = ParentForm(this);
						if (frm != null)
							frm.SelectNextControl(this, true, true, true, true);
					}
					else
					{
						if (e.Shift)
						{
							e.SuppressKeyPress = true;
							e.Handled = true;
							if (!myGrid.SelectPrevCell())
							{
								// don't do anything if at top (for now)
							}
						}
						else
						{
							e.SuppressKeyPress = true;
							e.Handled = true;
							if (!myGrid.SelectNextCell())
							{
								myGrid.MyBorders.InsertRow(myGrid.Rows.Count - 1);
								myGrid.Rows.Add(1);
								myGrid.SelectNextCell();
							}
						}
					}
					break;
				case Keys.Enter:
					if (!e.Control && !e.Shift && !e.Alt)
					{
						OnEnterKeyPressed(sender, e);
					}
					break;
			}
		}
		private static Form ParentForm(Control ctrl)
		{
			while (!(ctrl.Parent is Form || ctrl.Parent is EditItem)) 
				ctrl = ctrl.Parent;
			return ctrl.Parent as Form;
		}
		private void StepRTB_HomeEndPressed(KeyEventArgs keyargs)
		{
			if (MyItemInfo.IsProcedure || MyItemInfo.IsSection) return;
			// Cursor moves out of box only if control is pressed too - otherwise key is
			// handled in rtb.
			//if (keyargs.Control)MyRTBItem.MyStepPanel.StepCursorKeys(this, keyargs); // Replaced with an event
			if (keyargs.Control) OnCursorKeyPress(this, keyargs);
		}
		private void StepRTB_PageKeyPressed(KeyEventArgs keyargs)
		{
			if (MyItemInfo.IsProcedure || MyItemInfo.IsSection) return;
			//MyRTBItem.MyStepPanel.StepCursorKeys(this, keyargs); Replaced with an event
			OnCursorKeyPress(this, keyargs);
		}
		public void StepRTB_ArrowPressed(E_ArrowKeys key)
		{
			Point cp = PointToClient(Cursor.Position);
			//MyRTBItem.MyStepPanel.CursorMovement(this, cp, key); Replaced with an event
			OnCursorMovement(this, new StepRTBCursorMovementEventArgs(cp, key));
		}
		private void StepRTB_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
		{
			if (!ReadOnly)
			{
				if (Text == "" && Parent != null && Parent is VlnFlexGrid)
				{
					//ShowMyParentsFonts();
					SelectionFont = Parent.Font;
				}
				// add the character with its font depending on the char....
				if (!IsControlChar)
				{
					string strpressed = null;
					if (e.KeyChar == '-')
						strpressed = GetAddSymbolText(@"\u8209?");
					else
						strpressed = e.KeyChar.ToString();
					if (e.KeyChar >= ' ')
					{
						LinkLocation ll = FindBetweenLinks();
						if (ll != null && SelectionLength == 0) // SelectionLength = 0 means insert
						{
							if (e.KeyChar == '}')
								strpressed = @"\}";
							else if (e.KeyChar == '{')
								strpressed = @"\{";
							InsertCharBetweenLinks(ll, strpressed); // e.KeyChar);
							e.Handled = true;
						}
						else if (SelectionLength != 0)
						{
							HandleDeleteKeyWithSelectedText(new KeyEventArgs(Keys.None), strpressed);
							e.Handled = true;
						}
					}
					if (!e.Handled)
					{
						if (e.KeyChar == '-')
							AddSymbol(@"\u8209?");
						else if (e.KeyChar == '{')
							AddRtf(@"\{");
						else if (e.KeyChar == '}')
							AddRtf(@"\}");
						else if (e.KeyChar == '\\')
							AddRtf(@"\\");
						else
							return;
						e.Handled = true; // flag that it's been handled, otherwise, will get 2 chars.
					}
					if (e.Handled && SelectionLength != 0) SelectionFont = MyStyleFont.WindowsFont;
				}
				IsControlChar = false;
			}
		}
		private void RtbSendKeys(string keys)
		{
			Focus();
			SendKeys.Send(keys);  // With .Net Framework 3.0 this can be replaced with EditingCommands
			// http://msdn.microsoft.com/en-us/library/ms771634.aspx
		}
		public bool PasteRtfAsText(bool PasteNoReturnsSetting)
		{
			// check if rtf is coming from Word, i.e. if it has 'SCHEMAS.MICROSOFT.COM/OFFICE/WORD'.
			// If it is coming from Word, it will be pasted as text so as not to get any rtf commands
			// that PROMS does not support such as odd Fonts, other symbols, etc. This isn't a complete 
			// solution, clipboard data may come from other 3rd-party tools and unsupported Rtf may get 
			// pasted in.  But the hope is that this will happen less often than getting it from MS Word.
			IDataObject myDO = Clipboard.GetDataObject();
			if (myDO.GetDataPresent(DataFormats.Text)) return false;
			string tmpForLink = myDO.GetData(DataFormats.Rtf).ToString();
			if (tmpForLink.ToUpper().Contains(@"SCHEMAS.MICROSOFT.COM/OFFICE/WORD"))
			{
				SelectedText = GetPasteText(PasteNoReturnsSetting, myDO);
				return true;
			}
			return false;
		}
		public string GetPasteText(bool PasteNoReturnsSetting, IDataObject myDO)
		{
			// allowable symbols are those symbols between 127-256 that the proms editor supports that
			// can be pasted from word as is.  These symbols are (in the order they are in the following 
			// string): degree, plus/minus, exponent 2, exponent 3, micro, quarter, half, divide:
			char[] allowableSymbols = { '\xb0', '\xb1', '\xb2', '\xb3', '\xb5', '\xbc', '\xbd', '\xf7' };
			string ptext = myDO.GetData(DataFormats.Text).ToString();
			ptext = ptext.TrimEnd("\r\n\t ".ToCharArray());
			if (PasteNoReturnsSetting) ptext = ptext.Replace("\r\n", " ");
			StringBuilder sb = new StringBuilder(ptext);
			bool didCharReplace = false;
			for (int i = 0; i < ptext.Length; i++)
			{
				// if allowable, allow for it to pasted.  Otherwise, replace that character with a '?'.
				if ((sb[i] > 0x7e))
				if ((ptext.Substring(i, 1).IndexOfAny(allowableSymbols) < 0))
				{
					sb[i] = '?';
					didCharReplace = true;
				}
			}
			ptext = sb.ToString();
			ptext = ptext.Replace("\u2013", "-"); // Replace EN Dash with hyphen
			ptext = ptext.Replace("\u2014", "-"); // Replace EM Dash with hyphen
			ptext = ptext.Replace("\u2011", "-"); // Replace non-breaking hyphen with hyphen
			if (didCharReplace)
				MessageBox.Show("Replacing pasted characters that are not supported by Proms with a '?'.");
			return ptext;
		}
		private void DoDeleteEndBetweenLinks()
		{
			DebugSelection("Beginning");
			int sstart = SelectionStart;
			RtbSendKeys("{RIGHT} ");		// open for space between links which separates END/START tokens
			SetSelection(sstart, SelectionStart - sstart - 1);  // 1 is accounting for typed space
			DebugSelection("SetSelection");
			RtbSendKeys("{DELETE}");		// deletes text including link
			RtbSendKeys("{RIGHT}{BS}");		// deletes space that was added
		}
		private void DoDeleteStartBetweenLinks()
		{
			int slen = SelectionLength;
			RtbSendKeys("{LEFT} ");
			SetSelection(SelectionStart, slen + 7);
			RtbSendKeys("{DELETE}");
			RtbSendKeys("{BS}");
		}
		#endregion
		#region LinkSelectionAndHandling
		public bool IsSelectionLinked(int index, int len)
		{
			if (_LinkLocations == null)return false;
			int sel = index;
			int selLength = len;
			foreach (LinkLocation ll in _LinkLocations)
			{
				// When the selector moved to the right of a link, the GOTO button was still lit.
				// Removing the "+7" seems to have fixed this. - jsj 1/08/2010
				//if (sel >= ll.Start && sel < ll.Start + ll.Length + 7) return true;
				if (sel >= ll.Start && sel < ll.Start + ll.Length) return true;
			}
			return false;
		}
		/// 
		/// For use in Find/Replace.
		/// If text is found is part of the link information and not the link value,
		/// then we want to continue searching.
		/// 
		/// 
		/// 
		/// 
		public bool SkipLinkInfo(int index, int len)
		{
			if (_LinkLocations == null)return false;
			int sel = index;
			int selLength = len;
			foreach (LinkLocation ll in _LinkLocations)
			{
				if (sel >= ll.Start - 7 && sel < ll.Start + ll.Length)
				{
					int pos  = ll.Text.IndexOf("#Link:");
					if (sel < ll.Start || sel >= ll.Start-7+pos)
					return (true);			
				}
			}
			return false;
		}
		#endregion
		#endregion
		#region SelectionStack
		Stack _SelectionStack = null;
		public Stack SelectionStack
		{
			get 
			{
				if (_SelectionStack == null)
					_SelectionStack = new Stack();
				return _SelectionStack; 
			}
		}
		public void PushSelection()
		{
			SelectionStack.Push(new SelectionData(this));
		}
		public void PopSelection()
		{
			SelectionData selection = SelectionStack.Pop();
			if (SelectionStack.Count == 0)
				_SelectionStack = null;
			Select(selection.SelectionStart, selection.SelectionLength);
		}
		public class SelectionData
		{
			int _SelectionStart;
			public int SelectionStart
			{
				get { return _SelectionStart; }
				set { _SelectionStart = value; }
			}
			int _SelectionLength;
			public int SelectionLength
			{
				get { return _SelectionLength; }
				set { _SelectionLength = value; }
			}
			public SelectionData(RichTextBox richTextBox)
			{
				_SelectionStart = richTextBox.SelectionStart;
				_SelectionLength = richTextBox.SelectionLength;
			}
		}
		#endregion
		#region Link Support
		int _ProcessingKeys = 0;
		List _LinkLocations;
		public List LinkLocations
		{
			get { return _LinkLocations; }
		}
		private int _FALLevel = 0;
		private bool _IdentifyingLinks = false;
		public void FindAllLinks()
		{
			if (_IdentifyingLinks || _ProcessingDelete) return;
			//DebugPrint("FAL{0}vvvvvvvvvvvvvvvvvvvvvvvvv>>", ++_FALLevel);
			_AdjustingSelection = true;
			PushSelection();
			FindLinks();
			//IdentifyLinks();
			PopSelection();
			LinkLocation llx = FindLinkLocation();
			if (_CheckSelection)
			{
				if (llx != null) SetSelection(llx.End, 0);
				_CheckSelection = false;
			}
			_AdjustingSelection = false;
			if (!_ProcessingDelete)
				OnLinkLocationChanged(this, new EventArgs());
			//DebugPrint("FAL{0}^^^^^^^^^^^^^^^^^^^^^^^^^>>", _FALLevel--);
		}
		private void FindLinks()
		{
			string str = Text;
			_LinkLocations = new List();
			MatchCollection matches = Regex.Matches(str, @"", RegexOptions.Singleline);
			MatchCollection matchesRtf = Regex.Matches(Rtf, "", RegexOptions.Singleline);
			LinkLocation thisLink = null;
			for (int i = 0; i < matches.Count; i++) //each (Match match in matches)
			{
				Match match = matches[i];
				Match matchrtf = matchesRtf[i];
				thisLink = new LinkLocation(match.Index + 7, // If the [END> is immediately followed by ");
			if (_LinkLocations == null) return null;
			foreach (LinkLocation ll in _LinkLocations)
				if (ll.Start == start && ll.StartsBetween)
					return ll;
			return null;
		}
		private LinkLocation FindLinkLocation()
		{
			return FindLinkLocation(SelectionStart);
		}
		private LinkLocation FindLinkLocation(int sel)
		{
			//DebugPrint("FL----------------Location>");
			if (_LinkLocations == null) return null;
			foreach (LinkLocation ll in _LinkLocations)
			{
				// Moving right:
				//  if less than, allows stopping between two links
				if (ll.Start < sel && ll.End > sel)
				{
					DebugPrint("Greater Than {0} {1} {2}", sel, ll.Start, ll.End);
					return ll;
				}
				//  if less than or equal, does not stop between two links
				if ((!ll.StartsBetween) && ll.Start <= sel && ll.End > sel)
				{
					DebugPrint("Greater Than or Equal {0} {1} {2}", sel, ll.Start, ll.End);
					return ll;
				}
			}
			return null;
		}
		public DialogResult ReplaceText(string rpltxt, string fndstr, bool caseSensitive, bool matchWholeWord, bool reverse, bool prompt, IWin32Window fndrpldlg)
		{
			DialogResult dlgrslt = DialogResult.Yes;
			if (SelectionLength > 0)
			{
				if (IsSelectionLinked(SelectionStart, SelectionLength))
				{
					MessageBox.Show(fndrpldlg,"Cannot replace linked text!", "Find/Replace");
					dlgrslt = DialogResult.No;
				}
				else
				{
					if (prompt)
						dlgrslt = MessageBox.Show(fndrpldlg,"Replace This Occurence?", "Replace Text", MessageBoxButtons.YesNoCancel);
					if (dlgrslt == DialogResult.Yes)
						SelectedText = rpltxt;
				}
			}
			return dlgrslt;
		}
		private RichTextBoxFinds _FindOptions = RichTextBoxFinds.None;
		public bool FindText(string str, bool caseSensitive, bool matchWholeWord, bool reverse)
		{
			using (StepRTB savRTF = new StepRTB())
			{
				int startpos = SelectionStart + SelectionLength;
				savRTF.Text = this.Text;
				_FindOptions = RichTextBoxFinds.None;
				if (caseSensitive)
					_FindOptions |= RichTextBoxFinds.MatchCase;
				if (matchWholeWord)
					_FindOptions |= RichTextBoxFinds.WholeWord;
				if (reverse)
				{
					_FindOptions |= RichTextBoxFinds.Reverse;
					savRTF.Text = this.Text.Substring(0, SelectionStart);
					startpos = savRTF.Text.Length;
					if (startpos == 0)
						return false; // at beginning of rtfbox during a reverse find
				}
				else
				{
					if (startpos >= savRTF.Text.Length)
						return false; // at end of rtfbox during a forward find
				}
				// look for the find string in the temporary rtfbox
				// then set the cursor selection in the real rtfbox
				bool keepgoing = true;
				while (keepgoing)
				{
					int pos = savRTF.Find(str, startpos, _FindOptions);
					keepgoing = false;
					if (pos >= 0)
					{
						if (this.SkipLinkInfo(pos, str.Length))
						{
							if (reverse)
							{
								startpos--;
								savRTF.Text = savRTF.Text.Substring(0, startpos);
							}
							else
								startpos++;
							if (startpos > 0 && startpos < savRTF.Text.Length)
								keepgoing = true;
						}
						else
						{
							SelectionStart = pos;
							SelectionLength = str.Length;
						}
					}
					else
						return false;
				}
				return true;
			}
		}
		private int FindStart()
		{
			foreach (LinkLocation ll in _LinkLocations)
			{
				if (SelectionStart == ll.End) return ll.Start;
				if (SelectionStart == ll.Start)
				{
					if (ll.Start == 7) return 7;
					return ll.Start - 8;
				}
			}
			if (SelectionStart > 0) return SelectionStart - 1;
			return 0;
		}
		private int FindEnd()
		{
			foreach (LinkLocation ll in _LinkLocations)
			{
				if (SelectionStart + SelectionLength + 7 == ll.Start) return ll.Length + 7;
				// this is for in-between links
				if (SelectionStart + SelectionLength == ll.Start) return ll.Length;		
			}
			if (SelectionStart + SelectionLength < TextLength) return 1;
			return 0;
		}
		private int FindEndDown()
		{
			foreach (LinkLocation ll in _LinkLocations)
			{
				if (SelectionStart + SelectionLength >= ll.Start && SelectionStart + SelectionLength <= ll.End) return ll.End;
			}
			return 0;
		}
		private int FindStartUp()
		{
			foreach (LinkLocation ll in _LinkLocations)
			{
				if ((SelectionStart >= ll.Start) && (SelectionStart <= ll.End)) return ll.Start;
			}
			return -1;
		}
		public void SetSelection(LinkLocation ll)
		{
			SetSelection(ll.Start, ll.Length);
		}
		public void SetSelection(int locStart, int locLength)
		{
			//Application.DoEvents();  // Not needed since SendKeys is always done last.
			if (_IdentifyingLinks)
				DebugPrint("SS------------------------> {0} {1}", locStart, locLength);
			else
				DebugPrint("SS========================> {0} {1}", locStart, locLength);
			Select(locStart, locLength);
		}
		#endregion
		//#region FontAndStylesSupport
		//private void ToggleFontStyle(FontStyle style, bool att_on)
		//{
		//    int start = SelectionStart;
		//    int len = SelectionLength;
		//    System.Drawing.Font currentFont;
		//    FontStyle fs;
		//    for (int i = 0; i < len; ++i)
		//    {
		//        Select(start + i, 1);
		//        currentFont = SelectionFont;
		//        fs = currentFont.Style;
		//        //add or remove style
		//        if (!att_on)fs = fs | style;
		//        else fs = fs & ~style;
				
		//        SelectionFont = new Font(
		//            currentFont.FontFamily,
		//            currentFont.Size,
		//            fs
		//            );
		//    }
		//}
		/// 
		///     Returns a Font with:
		///     1) The font applying to the entire selection, if none is the default font. 
		///     2) The font size applying to the entire selection, if none is the size of the default font.
		///     3) A style containing the attributes that are common to the entire selection, default regular.
		/// 		
		/// 
		//public Font GetFontDetails()
		//{
		//    //This method should handle cases that occur when multiple fonts/styles are selected
		//    int start = SelectionStart;
		//    int len = SelectionLength;
		//    int TempStart = 0;
		//    if (len <= 1)
		//    {
		//        // Return the selection or default font
		//        if (SelectionFont != null)
		//            return SelectionFont;
		//        else
		//            return Font;		// should be default from format.
		//    }
		//    // Step through the selected text one char at a time	
		//    // after setting defaults from first char
		//    _rtbTemp.Rtf = SelectedRtf;
		//    //Turn everything on so we can turn it off one by one
		//    FontStyle replystyle =
		//        FontStyle.Bold | FontStyle.Italic | FontStyle.Underline;
		//    // Set reply font, size and style to that of first char in selection.
		//    _rtbTemp.Select(TempStart, 1);
		//    string replyfont = _rtbTemp.SelectionFont.Name;
		//    float replyfontsize = _rtbTemp.SelectionFont.Size;
		//    replystyle = replystyle & _rtbTemp.SelectionFont.Style;
		//    // Search the rest of the selection
		//    for (int i = 1; i < len; ++i)
		//    {
		//        _rtbTemp.Select(TempStart + i, 1);
		//        // Check reply for different style
		//        replystyle = replystyle & _rtbTemp.SelectionFont.Style;
		//        // Check font
		//        if (replyfont != _rtbTemp.SelectionFont.FontFamily.Name)
		//            replyfont = "";
		//        // Check font size
		//        if (replyfontsize != _rtbTemp.SelectionFont.Size)
		//            replyfontsize = (float)0.0;
		//    }
		//    // Now set font and size if more than one font or font size was selected
		//    if (replyfont == "")
		//        replyfont = _rtbTemp.Font.FontFamily.Name;
		//    if (replyfontsize == 0.0)
		//        replyfontsize = _rtbTemp.Font.Size;
		//    // generate reply font
		//    Font reply
		//        = new Font(replyfont, replyfontsize, replystyle);
		//    return reply;
		//}
		//#endregion
		#region EnumsSelectionRange
		private enum StartStatus : int
		{
			Before = 100,
			Between = 200,
			StartLink = 300,
			StartBox = 400
		};
		private enum EndStatus : int
		{
			After = 1,
			Between = 2,
			EndLink = 3,
			EndBox = 4,
			StartLink = 5
		};
		public enum RangeStatus : int
		{
			NoContainedLinks = 0,
			Before_After = StartStatus.Before + EndStatus.After,
			Before_Between = StartStatus.Before + EndStatus.Between,
			Before_EndLink = StartStatus.Before + EndStatus.EndLink,
			Before_EndBox = StartStatus.Before + EndStatus.EndBox,
			Before_StartLink = StartStatus.Before + EndStatus.StartLink,
			Between_After = StartStatus.Between + EndStatus.After,
			Between_Between = StartStatus.Between + EndStatus.Between,
			Between_EndLink = StartStatus.Between + EndStatus.EndLink,
			Between_EndBox = StartStatus.Between + EndStatus.EndBox,
			Between_StartLink = StartStatus.Between + EndStatus.StartLink,
			StartLink_After = StartStatus.StartLink + EndStatus.After,
			StartLink_Between = StartStatus.StartLink + EndStatus.Between,
			StartLink_EndLink = StartStatus.StartLink + EndStatus.EndLink,
			StartLink_EndBox = StartStatus.StartLink + EndStatus.EndBox,
			StartLink_StartLink = StartStatus.StartLink + EndStatus.StartLink,
			StartBox_After = StartStatus.StartBox + EndStatus.After,
			StartBox_Between = StartStatus.StartBox + EndStatus.Between,
			StartBox_EndLink = StartStatus.StartBox + EndStatus.EndLink,
			StartBox_EndBox = StartStatus.StartBox + EndStatus.EndBox,
			StartBox_StartLink = StartStatus.StartBox + EndStatus.StartLink
		};
		private RangeStatus _RTBRangeStatus;
		public RangeStatus RTBRangeStatus
		{
			get { return _RTBRangeStatus; }
			set
			{
				_RTBRangeStatus = value;
				if (!_ProcessingDelete) OnRTBRangeStatusChanged(this, new EventArgs());
			}
		}
		private LinkLocation _RangeStartLink = null;
		private LinkLocation _RangeEndLink = null;
		public RangeStatus FindRangeStatus()
		{
			_RangeStartLink = null;
			_RangeEndLink = null;
			if (_LinkLocations == null || _LinkLocations.Count == 0)
			{
				return RTBRangeStatus = RangeStatus.NoContainedLinks;
			}
			LinkLocation foundLink = null;
			int start = SelectionStart;
			int end = start + SelectionLength;
			foreach (LinkLocation ll in _LinkLocations)
			{
				if (foundLink == null && ((ll.Start >= start && ll.Start < end) || (ll.End >= start && ll.End < end)))
					foundLink = ll;
				if (_RangeStartLink == null && start >= ll.Start - 7 && start < ll.End)
					_RangeStartLink = ll;
				if (_RangeEndLink == null && end >= ll.Start && end <= ll.End)
					_RangeEndLink = ll;
			}
			DebugPrint("SelectionStart {0}, SelectionEnd {1}, TextLength {2}",
			  SelectionStart, SelectionStart + SelectionLength,
			  TextLength);
			//if (_RangeStartLink != null)
			//    _RangeStartLink.Show("startLink");
			//if (_RangeEndLink != null)
			//    _RangeEndLink.Show("endLink");
			//if (foundLink != null)
			//	foundLink.Show("foundLink");
			if (foundLink == null)
				return RTBRangeStatus = RangeStatus.NoContainedLinks;
			StartStatus myStartStatus = StartStatus.Before;
			EndStatus myEndStatus = EndStatus.After;
			if (_RangeStartLink != null)
			{
				if (_RangeStartLink.Start == 7)
					myStartStatus = StartStatus.StartBox;
				else if (_RangeStartLink.StartsBetween)
					myStartStatus = StartStatus.Between;
				else
					myStartStatus = StartStatus.StartLink;
			}
			if (_RangeEndLink != null)
			{
				if (_RangeEndLink.End == TextLength)
					myEndStatus = EndStatus.EndBox;
				else if (_RangeEndLink.NextStart == _RangeEndLink.End)
					myEndStatus = EndStatus.Between;
				else if (end == _RangeEndLink.Start)
					myEndStatus = EndStatus.StartLink; // Should not happen because of code in HandleSelectionChange
				else //if (end == endLink.End)
					myEndStatus = EndStatus.EndLink;
			}
			return RTBRangeStatus = (RangeStatus)((int)myStartStatus + (int)myEndStatus);
		}
		#endregion
		#region OutlineTable
		private string _CheckRight = "-\u2500\u2011";
		public string CheckRight
		{
			get { return _CheckRight; }
			set { _CheckRight = value; }
		}
		private string _CheckAbove = "|\u2502\u2514\u252c\u253c\u251c\u250c\u2534\u2510\u2524";
		public string CheckAbove
		{
			get { return _CheckAbove; }
			set { _CheckAbove = value; }
		}
		private string _CheckLeft = "-\u2500\u2524\u252c\u251c\u253c\u250c\u2510\u2514\u2011";
		public string CheckLeft
		{
			get { return _CheckLeft; }
			set { _CheckLeft = value; }
		}
		private string _CheckBelow = "|\u2502";
		public string CheckBelow
		{
			get { return _CheckBelow; }
			set { _CheckBelow = value; }
		}
		// This is a table of graphics characters
		// The index into this table (0-15) is a bitmask with each bit representing
		// a different direction from the current character.
		// Right is Bit 0 (0 or 1)
		// Above is Bit 1 (0 or 2)
		// Left is Bit 2 (0 or 4)
		// Below is Bit 3 (0 or 8)
		// The index is contolled in the following way:
		// If there is a graphics character to the right, then you add 1
		// If there is a graphics character above, then you add 2
		// If there is a graphics character left, then you add 4
		// If there is a graphics character below, then you add 8
		// The total results in an index into this array and gives the appropriate character
		// combining horizontal and vertical lines.
		static private string [] TableCharsU = {
			"\x0",			// HEX"\x0",   //    No character	
			@"\u9472",		// HEX@"\u2500",//	-  Horizontal line		- 16-bit char: '\xC4'
			@"\u9474",		// HEX@"\u2502",// |  Vertical line			- 16-bit char: '\xB3'
			@"\u9492",		// HEX@"\u2514",// L  Bottom Left corner	- 16-bit char: '\xC0'
			@"\u9472",		// HEX@"\u2500",//	-  Horizontal line		- 16-bit char: '\xC4'
			@"\u9472",		// HEX@"\u2500",//	-  Horizontal line		- 16-bit char: '\xC4'
			@"\u9496",		// HEX@"\u2518",//    Bottom Right Corner	- 16-bit char: '\xD9'
			@"\u9524",		// HEX@"\u2534",//    Bottom Tee			- 16-bit char: '\xC1'
			@"\u9474",		// HEX@"\u2502",// |  Vertical Bar			- 16-bit char: '\xB3'
			@"\u9484",		// HEX@"\u250c",//    Upper Left corner		- 16-bit char: '\xDA'
			@"\u9474",		// HEX@"\u2502",// |  Vertical Bar			- 16-bit char: '\xB3'
			@"\u9500",		// HEX@"\u251c",//    Left Tee				- 16-bit char: '\xC3'
			@"\u9488",		// HEX@"\u2510",//    Upper Right corner	- 16-bit char: '\xBF'
			@"\u9516",		// HEX@"\u252c",// T  Top Tee				- 16-bit char: '\xC2'
			@"\u9508",		// HEX@"\u2524",//    Right Tee				- 16-bit char: '\xB4'
			@"\u9532",		// HEX@"\u253c" //  +  Plus					- 16-bit char: '\xC5'
		};
		private int MaxCharacterWidth()
		{
			// loop through lines and get the width in characters
			int w = 0;
			foreach (string line in Lines)
			{
				string cleanLine = RemoveLinkComments(line);
				if (w < cleanLine.Length)
					w = cleanLine.Length;
			}
			return w;
		}
		internal static string RemoveLinkComments(string line)
		{
			StringBuilder sb = new StringBuilder();
			int lastIndex = 0;
			MatchCollection mc = Regex.Matches(line, @"", RegexOptions.Singleline);
			foreach (Match m in mc)
			{
				sb.Append(line.Substring(lastIndex, m.Index - lastIndex)); // Append text before the link
				sb.Append(m.Groups[1].Value);	// Append the text portion of the link
				lastIndex = m.Index + m.Length; // Calculate the beginning of the remaining text
			}
			sb.Append(line.Substring(lastIndex)); // Append the text following the last link
			string result = sb.ToString();
			result = result.Replace(@"");
			if (mcEnd.Count > 0)
			{
				result = result.Substring(0, mcEnd[0].Index) + result.Substring(mcEnd[0].Index + mcEnd[0].Length);
			}
			return result;
		}
		private void ReplaceLinesInTable(bool withBorder)
		{
			int rowWidth = Lines[0].Length;
			for (int row=1;row", RegexOptions.Singleline);// this last parameter was commented out in 7/24/09 but it appears to be valid
				if (matchCollection.Count == 0) matchCollection = Regex.Matches(line, @"");
				Match match = matchCollection.Count > 0 ? matchCollection[0] : null;
				int matchOffset = 0;
				for (int col = 1; col < rowWidth - 1; col++)
				{
					if (match != null && match.Index == matchOffset + col)
					{
						matchOffset += match.Length - match.Groups[1].Length; // Increment the offset by the link comment length
						col += match.Groups[1].Length; // increment the column by the link value length
						if (col >= rowWidth - 1) break;// Don't continue if beyond the contents
						match = match.NextMatch(); // Watch for the next match
					}
					int coll = matchOffset + col;
					char chr = line[coll];
					char chrLast = line[coll - 1];
					char chrNext = line[coll + 1];
					char chrAbove = lineAbove[col];
					char chrBelow = lineBelow[col];
					// The following is all using symbol font (either unicode for proportional or
					// VeSymbFix for fixed font):
					// if this character is a table line draw character, i.e. a dash or vertical bar, or
					// graphics characters for those, look around it to see what table character it is
					// replaced with
					// Look for -||-- (last three are graphics chars or unicode dash)
					if ("-|\u2500\u2502\u2011".IndexOf(chr) > -1)	 
					{
						bool horizontalCharacter = "-\u2500\u2011".IndexOf(chr) > -1;
						int lineDrawRight = (CheckRight.IndexOf(chrNext) > -1 || (horizontalCharacter && "\u2502".IndexOf(chrNext) > -1)) ? 1 : 0;
						int lineDrawAbove = (CheckAbove.IndexOf(chrAbove) > -1 || (!horizontalCharacter && "\u2500\u2011".IndexOf(chrAbove) > -1)) ? 2 : 0;
						int lineDrawLeft = (CheckLeft.IndexOf(chrLast) > -1 || (horizontalCharacter && "\u2502".IndexOf(chrLast) > -1)) ? 4 : 0;
						int lineDrawBelow = (CheckBelow.IndexOf(chrBelow) > -1 || (!horizontalCharacter && "-\u2500\u2011".IndexOf(chrBelow) > -1)) ? 8 : 0;
						int tableCharIndx = lineDrawRight + lineDrawAbove + lineDrawLeft + lineDrawBelow;
						if (tableCharIndx > 0)
						{
							SetTableChar(row, coll, tableCharIndx);
							if (withBorder) // Adjust the border as it intersects with lines within the box
							{
								if (row == 1 && !horizontalCharacter ) SetTableChar(row - 1, col, 13);									// Top Row
								if (row == Lines.Length - 2 && !horizontalCharacter) SetTableChar(row + 1, col, 7);			// Bottom Row
								if (col == 1 && horizontalCharacter && ((tableCharIndx & 4)==4)) SetTableChar(row, col - 1, 11);								// First Column
								if (col == rowWidth - 2 && horizontalCharacter && ((tableCharIndx & 1) == 1)) SetTableChar(row, coll + 1, 14);		// Last Column
							}
						}
					}
				}
			}
		}
		private void SetTableChar(int row, int col, int tableCharIndx)
		{
			int rowOffset = GetFirstCharIndexFromLine(row);
			Select(rowOffset + col, 1);
			string charToBeReplaced = SelectedText;
			if (Text.Length > (rowOffset + col + 7) && Text.Substring(rowOffset + col + 1, 6) != "#Link:")
				SelectedRtf = RtfPrefixForSymbols + TableCharsU[tableCharIndx] + "?}";
			else
			{
				SelectionStart++;
				int lenComment = 1 + SelectionStart - (rowOffset + col);
				//Console.WriteLine("{0},{1},{2}", rowOffset + col, SelectionStart, SelectionLength);
				Select(rowOffset + col, 0);
				SelectedRtf = RtfPrefixForSymbols + TableCharsU[tableCharIndx] + "?}";
				Select(rowOffset + col, lenComment);
				//Console.WriteLine("'{0}',{1},{2},{3}", SelectedText, lenComment, SelectionLength, SelectionStart);
				//Console.WriteLine("'{0}'", SelectedRtf);
				SelectedRtf = SelectedRtf.Replace(" " + charToBeReplaced, "");
			}
		}
		#endregion
		#region SpellChecker
		private static bool _DoSpellCheck = true;
		public static bool DoSpellCheck
		{
			get { return StepRTB._DoSpellCheck; }
			set { StepRTB._DoSpellCheck = value; }
		}
		// We made the Spell Checker method a static so that all of the StepRTB boxes will bare the same instance of the dictionary.
		// This allow all the StepRTB's to automatically update when a new word is added.
		private static C1.Win.C1SpellChecker.C1SpellChecker _C1SpellChecker2;
		private static C1.Win.C1SpellChecker.C1SpellChecker C1SpellChecker2
		{
			get 
			{
				if (_C1SpellChecker2 == null)
				{
					_C1SpellChecker2 = new C1.Win.C1SpellChecker.C1SpellChecker();
					_C1SpellChecker2.ContextMenuCreated += new C1.Win.C1SpellChecker.ContextMenuEventHandler(_C1SpellChecker2_ContextMenuCreated);
					// The veproms.dct file is the main spell checker dictionary.
					// We edited the C1 dictionary (C:\Program Files\ComponentOne\Dictionaries\C1Spell_en-US.dct) using ComponentOne's 
					// dictionary editor (C:\Program Files\ComponentOne\Studio for WinForms\C1SpellChecker\C1DictionaryEditor.exe)
					// and added the veproms.words list as another word list in the dictionary.  We saved the resulting file as veproms.dct.
					string dictpath = string.Format(@"{0}\veproms.dct", Application.StartupPath);
					// Only use the veproms.dct if it is found in the BIN folder. Otherwise, let it default to use the standard dictionary.
					if (File.Exists(dictpath))
						_C1SpellChecker2.MainDictionary.FileName = dictpath; // looks in the bin folder by default
					// NOTE: By default, a "Custom.dct" file (UserDictionary) will be created in the BIN folder when a user adds words to the 
					//       spell checker dictionary.  The words that the user adds are not placed in the Volian.dct file.
					//_C1SpellChecker2.UserDictionary.FileName  = @"C:\Development\proms\VEPROMS User Interface\bin\Debug\MYDictionary.dct";
				}
				return StepRTB._C1SpellChecker2; 
				}
		}
		// the grid uses this to reference the same instance of the spell checker
		public C1.Win.C1SpellChecker.C1SpellChecker SpellCheckerInstance
		{
			get { return C1SpellChecker2; }
		}
		// Get or Save our context menu
		// This is added to the the spell checker's context menu for a mis-spelled word
		private static ContextMenuStrip _ThisContextMenuStrip;
		public ContextMenuStrip ThisContextMenuStrip
		{
			get { return _ThisContextMenuStrip; }
			set { _ThisContextMenuStrip = value; }
		}
		private static StepRTB _ContextMenuStepRTB;
		private static object _ContextMenuStrip;
		static void _C1SpellChecker2_ContextMenuCreated(object sender, C1.Win.C1SpellChecker.ContextMenuEventArgs e)
		{
			// Adjust their bad word context menu
			//Console.WriteLine("==========> spell check context menu {0}",e.Menu.Items[0].Text);
			if (e.Menu.Equals(_ContextMenuStrip))
			{
				// Add an item for our "Edit Menu" context menu on their spell check context menu
				// This gives the user access to our regular context menu when on a mis-spelled word
				ToolStripItem tsi = e.Menu.Items.Add("Edit Menu");
				tsi.Click += new EventHandler(tsi_Click);
				//Now replace their "Spell" item with ours, so that we can display our custom spell check dialog
				for (int i = 0; i < e.Menu.Items.Count; i++)
				{
					ToolStripItem tsi1 = e.Menu.Items[i];
					if (tsi1 is ToolStripMenuItem && tsi1.Text == "Spell")
					{
						e.Menu.Items.RemoveAt(i); // remove their spell check dialog item
						ToolStripMenuItem tsi2 = new ToolStripMenuItem("Spell");
						tsi2.Click += new EventHandler(tsi2_Click);
						e.Menu.Items.Insert(i, tsi2); // add our spell check dialog item
					}
				}
			}
		}
		static void tsi2_Click(object sender, EventArgs e)
		{
			C1SpellChecker2.CheckControl(_ContextMenuStepRTB, true, MySpellCheckDlg);
		}
		// Click event to display our "Edit Menu" (StepRTB context menu)
		static void tsi_Click(object sender, EventArgs e)
		{
			ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
			//_ContextMenuStepRTB.MyRTBItem.MyStepPanel.MyStepTabPanel.MyStepTabRibbon.OpenContextMenu(tsmi.Owner.Location);
			_ContextMenuStepRTB.OnOpenContextMenu(sender, new StepRTBLocationEventArgs(tsmi.Owner.Location));
		}
		// This is our customized Spell Check dialog
		// This is display when the use clicks the Spell button from the Ribbon 
		// or when the "Spell" item is selected from the context menu when on a mis-spelled word
		static VlnSpellCheckDlg _MySpellCheckDlg;
		public static VlnSpellCheckDlg MySpellCheckDlg
		{
			get 
			{
				if (_MySpellCheckDlg == null)
					_MySpellCheckDlg = new VlnSpellCheckDlg();
				return _MySpellCheckDlg; 
			}
		}
		// This is used when walking through the section doing a spell check
		// (from the Spell button on the ribbon)
		public bool SpellCheckNext()
		{
			int nBad = C1SpellChecker2.CheckControl(this, false, MySpellCheckDlg);
			return (nBad >= 0); // nBad = -1 means user pressed Cancel button
		}
		
		// This allows us to turn off/on the specll check context menu when we toggle in and out of View Mode.
		// see btnToggleEditView_Click() in StepTabRibbon.cs
		public void SpellCheckContextMenuOn(bool turnOnMenu)
		{
			C1SpellChecker2.Options.ShowSuggestionsInContextMenu = turnOnMenu;
		}
		#endregion
		#region
		public string DoNewLinkInGridCell()
		{
			// first find the new link and determine whether it's RO or transition.
			int indx = Rtf.IndexOf(@"#Link:ReferencedObject:");
			if (indx > 0)
			{
				Match mro = Regex.Match(Rtf.Substring(indx, Rtf.Length - indx), @"([A-Za-z]*):(.*?)\[END>");
				string linkstr = mro.Groups[2].Value;
				string[] roparts = linkstr.Split(" ".ToCharArray());
				ContentRoUsage rousg = null;
				int oldid = -1;
				using (Item itm = MyItemInfo.Get())
				{
					using (RODb rodb = RODb.GetJustRoDb(Convert.ToInt32(roparts[2])))
					{
						rousg = itm.MyContent.ContentRoUsages.Add(roparts[1], rodb);
					}
					//get temporary rousageid
					oldid = rousg.ROUsageID;
					Rtf = Rtf.Replace("", string.Format("", rousg.ROUsageID));
					//save temporary rousageid to get real rousageid
					itm.Save();
					//replace temprorary rousageid with real rousageid
					Rtf = Rtf.Replace(string.Format("", oldid), rousg.ROUsageID.ToString());
					//save real rousageid
					itm.Save();
					MyItemInfo.MyContent.RefreshContentRoUsages();
				}
			}
			// Need to look for any 'New' transitions, don't include existing ones.  
			Match mt = Regex.Match(Rtf, @"#Link:Transition:([0-9]+)  ");
			if (mt.Length <= 0) mt = Regex.Match(Rtf, @"#Link:TransitionRange:([0-9]+)  ");
			if (mt.Length > 0)
			{
				indx = mt.Index+6;  // get past '#Link:"
				Match m = Regex.Match(Rtf.Substring(indx, Rtf.Length - indx), @"([A-Za-z]*):(.*?)\[END>");
				bool isSingleTran = true;
				if (Rtf.Substring(indx, 16).IndexOf("TransitionRange") >= 0) isSingleTran = false;
				string linkstr = m.Groups[2].Value;
				string[] tparts = linkstr.Split(" ".ToCharArray());
				int type = System.Convert.ToInt32(tparts[0]);
				int tr1 = System.Convert.ToInt32(tparts[2]);		// tparts[2] is token for tranid
				int tr2 = tr1;
				if (tparts.Length > 3)
					tr2 = System.Convert.ToInt32(tparts[3]);		// tparts[3] is token for rangeid
				bool dispose1 = false;
				Item itm1 = null;
				bool dispose2 = false;
				Item itm2 = null;
				using (Item itm = MyItemInfo.Get())
				{
					if (itm.ItemID == tr1)
						itm1 = itm;  // a transition that points to itself should not dispose
					else
					{
						dispose1 = true;
						itm1 = Item.Get(tr1);
					}
					if (itm.ItemID == tr2)
						itm2 = itm;  // a transition that points to itself should not dispose
					else if (tr1 == tr2)
						itm2 = itm1; // if both destinations are the same only dispose the first one
					else
					{
						dispose2 = true;
						itm2 = Item.Get(tr2);
					}
					ContentTransition ct = itm.MyContent.ContentTransitions.Add(itm1, itm2);
					//Console.WriteLine("CT {0},{1},{2},{3}", ct.TransitionID, itm.ItemID, itm.MyContent.MyContentUnique, itm.MyContent.ContentTransitions.Count);
					ct.TranType = type;
					if (isSingleTran)
						ct.IsRange = 0;
					else if (tr1 != tr2)
						ct.IsRange = 1;
					else
						ct.IsRange = 2;
					int oldidt = ct.TransitionID;
					Rtf = Rtf.Replace("", string.Format("", ct.TransitionID));
					itm.Save();
					Rtf = Rtf.Replace(string.Format("", oldidt), ct.TransitionID.ToString());
					itm.Save();
					MyItemInfo.MyContent.RefreshContentTransitions();
				}
				if (dispose2) itm2.Dispose();
				if (dispose1) itm1.Dispose();
			}
			return Rtf;
		}
		#endregion
		#region RoInsertInterface
		public void UpdateStepRtb(string linktxt, string valtxt)
		{
			int ss = SelectionStart; // Remember where the link is being added
			int sl = SelectionLength;
			OnReturnToEditor(this, new EventArgs());
			Select(ss, sl);
			InsertRO(valtxt, linktxt); // Insert the LINK
			OnDoSaveContents(this, new EventArgs()); //  .SaveText(); // Save the text with the LINK - This also moves the cursor to the end of the text
			// By selecting a starting position within a link, StepRTB (HandleSelectionChange) will select the link
			Select(ss + 7 + valtxt.Length, 0);// Select the link, Try 7 for "