using System; using System.Collections.Generic; using System.ComponentModel; 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; 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 StepRTBMouseWheelEvent(object sender, MouseEventArgs args); public partial class StepRTB : RichTextBox , IStepRTB { #region Events 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 // 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 RtfPrefix { get { return _RtfPrefix + @"\f1\fs" + this.Font.SizeInPoints * 2 + " "; } } private StepItem _MyStepItem; public StepItem MyStepItem { get { return _MyStepItem; } set { _MyStepItem = value; } } // _IsDirty compares the original rtf to the current rtf from the // richtextbox to see if a change was made. private 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 { _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 { return _origDisplayText.TextFont; } } public bool ViewRTB = true; private ItemInfo _MyItemInfo; public ItemInfo MyItemInfo { get { return _MyItemInfo; } set { _MyItemInfo = value; if (value != null) { RTBFillIn(!ViewRTB); //ViewRTB = MyStepItem.MyStepPanel.PanelViewEditMode == E_ViewMode.View; } } } private string _origRTF; public void RTBFillIn(bool edit) { _InitializingRTB = true; _SelectedRtfSB.Remove(0, _SelectedRtfSB.Length); DisplayText vlntxt = new DisplayText(_MyItemInfo, EpMode, VwMode, !edit, FieldToEdit); _origDisplayText = vlntxt; #if(DEBUG) // Use Times New Roman for Debugging //Font = new Font("Times New Roman", 14, FontStyle.Regular); Font = _origDisplayText.TextFont.WindowsFont; #elif(RELEASE) Font = _origDisplayText.TextFont.WindowsFont; // font defined in plant's format #else //DEMO // Comment this out for DEMO to customer // UN-Comment this for testing //Font = _origDisplayText.TextFont.WindowsFont; // font defined in plant's format #endif Text = ""; // Initialize text before add text // IMPORTANT: SetLineSpacing must be set before Links, otherwise it // was confusing the 'handle' of the rtf box. RTBAPI.SetLineSpacing(this, RTBAPI.ParaSpacing.PFS_EXACT); AddRtfText(vlntxt.StartText); AddRtfStyles(); // set readonly based on initial modes, however, these may change if // user selected view mode. ReadOnly = !(EpMode == E_EditPrintMode.Edit && VwMode == E_ViewMode.Edit); if (!ReadOnly && !edit) ReadOnly = true; ClearUndo(); RightMargin = Width; // figure out if needs outlined, depends on table/figure type if (!edit) { RemoveEventHandlers(); if (_MyItemInfo.IsTable || _MyItemInfo.IsFigure) { int typ = ((int)_MyItemInfo.MyContent.Type) % 10000; OutlineTable(_MyItemInfo.ActiveFormat.PlantFormat.FormatData.StepDataList[typ].Type.IndexOf(@"Borderless")<0); FindAllLinks(); AdjustSizeForContents(); // TODO: this is not quite right yet. } SelectAll(); SelectionHangingIndent = 0; int indchar = 0; string indentToken = _MyItemInfo.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.IndentToken; if (indentToken == null) indentToken = "\x5"; while ((indchar = Find(indentToken, indchar, RichTextBoxFinds.None)) >= 0) { Point indent = GetPositionFromCharIndex(indchar); SelectionHangingIndent = indent.X; indchar++; } AddEventHandlers(); } _origRTF = Rtf; _InitializingRTB = false; _MyItemInfo.MyConfig.PropertyChanged += new PropertyChangedEventHandler(MyConfig_PropertyChanged); } private bool _ProcessKeystrokes = true; public bool ProcessKeystrokes { get { return _ProcessKeystrokes; } set { _ProcessKeystrokes = value; } } //public EnterKeyHandler EnterKeyPressed; //protected override bool ProcessCmdKey(ref Message msg, Keys keyData) //{ // const int WM_KEYDOWN = 0x100; // const int WM_SYSKEYDOWN = 0x104; // if ((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN)) // { // switch (keyData) // { // case Keys.Control | Keys.V: // // check for valid data to be inserted: // return base.ProcessCmdKey(ref msg, keyData); // default: // return base.ProcessCmdKey(ref msg, keyData); // } // } // return base.ProcessCmdKey(ref msg, keyData); //} 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(); } } 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(); } } public System.Windows.Forms.AutoScaleMode AutoScaleMode; private DisplayText _origDisplayText; 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(_MyStepItem, _MyLinkText)); //} } } #endregion #region Constructors public StepRTB() { InitializeComponent(); SetUp(); AddEventHandlers(); } public StepRTB(IContainer container) { container.Add(this); InitializeComponent(); _Container = container; SetUp(); AddEventHandlers(); } protected override void OnMouseWheel(MouseEventArgs e) { _MyStepItem.MyStepPanel.MouseWheel(e); //base.OnMouseWheel(e); } private void RemoveEventHandlers() { 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); } private void AddEventHandlers() { BorderStyle = System.Windows.Forms.BorderStyle.None; this.DetectUrls = true; 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); } private void SetUp() { 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 = true; } // 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; } public bool inRoAdd = false; void StepRTB_SelectionChanged(object sender, EventArgs e) { if (_InitializingRTB) return; HandleSelectionChange(); } private bool _MouseDown = false; private void StepRTB_MouseDown(object sender, MouseEventArgs e) { _MouseDown = true; } void StepRTB_MouseUp(object sender, MouseEventArgs e) { _MouseDown = false; if(this.Focused) // Only HandleSelectionChange if this control has focus. HandleSelectionChange(); } #endregion #region ApplicationSupport public void ToggleEditView() { SaveText(); //ItemInfo tmp = MyItemInfo; //MyItemInfo = null; ReadOnly = !ReadOnly; EpMode = ReadOnly ? E_EditPrintMode.Print : E_EditPrintMode.Edit; VwMode = ReadOnly ? E_ViewMode.View : E_ViewMode.Edit; ViewRTB = ReadOnly; Clear(); RTBFillIn(!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 = _MyItemInfo.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.IndentToken; if (indentToken == null) indentToken = "\x5"; AddText(indentToken); } public void InsertText(string txt) { AddText(txt); } 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 SaveData public void SaveText() { if (ReadOnly) return; if (ViewRTB) return; if (!_IsDirty && Text.Contains("(Resolved Transition Text)") == false) return; bool success = _origDisplayText.Save((RichTextBox)this); if (success) { FindAllLinks(); _origRTF = Rtf; ClearUndo(); } } public void SaveConfig() { if (!_MyItemInfo.MyConfig.IsDirty) return; Item itm = _MyItemInfo.Get(); itm.MyContent.Config = _MyItemInfo.MyConfig.ToString(); itm.Save(); _MyItemInfo.MyConfig.IsDirty = false; } #endregion #region AddRtfTextAndStyles private void AddRtfText(string txt) { AddFontTable(); _RtfPrefix = _SelectedRtfSB.ToString(); _SelectedRtfSB.Append(txt); SelectedRtf = _SelectedRtfSB.ToString() + "}"; FindAllLinks(); } private StringBuilder _SelectedRtfSB = new StringBuilder(); private void AddFontTable() { StringBuilder sbbeg = new StringBuilder(); StringBuilder sbend = new StringBuilder(); if (Font.Bold) { sbbeg.Append(@"\b"); sbend.Append(@"\b0"); } if (Font.Underline) { sbbeg.Append(@"\ul"); sbend.Insert(0, @"\ulnone"); } if (Font.Italic) { sbbeg.Append(@"\i"); sbend.Insert(0, @"\i0"); } _SelectedRtfSB.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset2 " + this.Font.FontFamily.Name + @";}"); //}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}"; if (!FontIsFixed()) _SelectedRtfSB.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}{\colortbl ;\red255\green0\blue0;}"); else _SelectedRtfSB.Append(@"{\f1\fnil\fcharset0 VESymbFix;}}{\colortbl ;\red255\green0\blue0;}"); _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(this.Font.SizeInPoints * 2).ToString() + @" "); // \f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}"; } private bool FontIsFixed() { Graphics grph = Graphics.FromHwnd(this.Handle); SizeF sfW = grph.MeasureString("W", this.Font); SizeF sfE = grph.MeasureString("!", this.Font); 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); int position = SelectionStart; SelectedRtf = _RtfPrefix + @"\f1\fs" + this.Font.SizeInPoints * 2 + @" " + str + @"}"; Select(position, 1); RTBAPI.SetFontStyle(this, fs); Select(position + 1, 0); SelectionFont = _origDisplayText.TextFont.WindowsFont; } 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); } private void AddLink20(string linkValue, string linkUrl) { this.DetectUrls = false; int position = SelectionStart; SelectionLength = 0; SelectedRtf = @"{\rtf1\ansi{\colortbl ;\red255\green0\blue0;}\v \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); } if ((_origDisplayText.TextFont.Style & E_Style.Underline) > 0) RTBAPI.ToggleUnderline(true, this, RTBAPI.RTBSelection.SCF_ALL); if ((_origDisplayText.TextFont.Style & E_Style.Italics) > 0) RTBAPI.ToggleItalic(true, this, RTBAPI.RTBSelection.SCF_ALL); } #endregion #region HeightSupport public event StepRTBEvent HeightChanged; private void OnHeightChanged(object sender, EventArgs args) { if (HeightChanged != null) HeightChanged(sender, args); } private void AdjustSizeForContents() { Size offset = Size - ClientSize; int widthNew = ContentsSize.Width + offset.Width + AdjustSize.Width; int heightNew = ContentsSize.Height + offset.Height + AdjustSize.Height; Size szNew = new Size(widthNew,heightNew); if (this.Size != szNew) { this.Size = szNew; OnHeightChanged(this, new EventArgs()); } } 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 void AdjustWidthForContent() { 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) { Width = w; AdjustWidthForContent();// Try one more time } } else { Width = widthMaxWW; AdjustWidthForContent(); } } 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. FindAllLinks(); } void StepRTB_ContentsResized(object sender, ContentsResizedEventArgs e) { ContentsRectangle = e.NewRectangle; } #endregion #region Selection Handlers bool _AdjustingSelection = false; private bool _ProcessingDelete; private void HandleSelectionChange() { //vlnStackTrace.ShowStackLocal("HandleSelectionChangeStack", 1, 10); bool startingValue = _AdjustingSelection; if (_IdentifyingLinks || _ProcessingDelete) 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) { 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()); } 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; } 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"); 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; default: break; } } private bool IsControlChar = false; private bool _SendBackSpace = false; void StepRTB_KeyDown(object sender, KeyEventArgs e) { if (e.Control) { IsControlChar = true; switch (e.KeyCode) { case Keys.V: 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 { 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; } } 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; } HandleSelectionChange(); 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 { HandleSelectionChange(); } 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; } HandleSelectionChange(); 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); HandleSelectionChange(); e.Handled = true; return; } if (!e.Shift)HandleSelectionChange(); // 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; } } 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)_MyStepItem.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; //_MyStepItem.MyStepPanel.StepCursorKeys(this, keyargs); Replaced with an event OnCursorKeyPress(this, keyargs); } private void StepRTB_ArrowPressed(E_ArrowKeys key) { Point cp = PointToClient(Cursor.Position); //_MyStepItem.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) { // 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 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 } 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) { if (sel >= ll.Start && sel < ll.Start + ll.Length + 7) return true; } return false; } #endregion #endregion #region SelectionStack Stack _SelectionStack = new Stack(); public void PushSelection() { _SelectionStack.Push(new SelectionData(this)); } public void PopSelection() { SelectionData selection = _SelectionStack.Pop(); 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 "); 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; } 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' }; public static string Repeat(string str, int count) { StringBuilder lStr = new StringBuilder(); for (int i = 0; i < count; i++) lStr.Append(str); return lStr.ToString(); } public void OutlineTable(bool withBorder) { // Determine the number of characters per line int w = MaxCharacterWidth(); string horzLine = Repeat(withBorder ? @"\u9472?" : " ", w); // Determine the number of lines int l = Lines.Length; for (int row = 0; row < Lines.Length; row++) { //int spaces = w - line.Length; int offset = GetFirstCharIndexFromLine(row); Select(offset, 0); string cleanLine = RemoveLinkComments(Lines[row]); int w2 = Lines[row].Length; int w3 = cleanLine.Length; offset = SelectionStart + w2 + (w - w3) + 1; SelectedRtf = RtfPrefix + (withBorder ? @"\u9474?" : " ") + "}"; Select(SelectionStart + w2, 0); if (w3 < w) { SelectedText = "".PadRight(w - w3); Select(offset, 0); } SelectedRtf = RtfPrefix + (withBorder ? @"\u9474?" : " ") + "}"; } // Add the top line Select(0, 0); SelectedRtf = RtfPrefix + (withBorder ? @"\u9484?" : " ") + horzLine + (withBorder ? @"\u9488?\par " : @" \par ") + "}"; // Add the bottom line Select(TextLength, 0); SelectedRtf = RtfPrefix + (withBorder ? @"\par\u9492?" : @"\par ") + horzLine + (withBorder ? @"\u9496?" : @" ") + "}"; ReplaceLinesInTable(withBorder); } 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; } private string RemoveLinkComments(string line) { StringBuilder sb = new StringBuilder(); int lastIndex = 0; MatchCollection mc = Regex.Matches(line, @""); 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 - 1) + 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); 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); SelectedRtf = RtfPrefix + TableCharsU[tableCharIndx] + "?}"; } #endregion #region Debug private bool _ShowDebug = false; public bool ShowDebug { get { return _ShowDebug; } set { _ShowDebug = value; } } private void DebugPrint(string where, string format, params object[] myParams) { DebugPrint(where + string.Format(format, myParams)); } private void DebugPrint(string format, params object[] myParams) { if (_ShowDebug) Console.WriteLine(format, myParams); } private void DebugSelection(string where) { DebugPrint(where, ": {0} {1} '{2}'", SelectionStart, SelectionLength, SelectedText); } #endregion } public partial class StepRTBModeChangeEventArgs : EventArgs { private E_ViewMode _ViewMode; public E_ViewMode ViewMode { get { return _ViewMode; } set { _ViewMode = value; } } public StepRTBModeChangeEventArgs(E_ViewMode vmode) { _ViewMode = vmode; } } public class StepRTBCursorMovementEventArgs { private Point _CursorLocation; public Point CursorLocation { get { return _CursorLocation; } set { _CursorLocation = value; } } private E_ArrowKeys _Key; public E_ArrowKeys Key { get { return _Key; } set { _Key = value; } } public StepRTBCursorMovementEventArgs(Point pt, E_ArrowKeys key) { _CursorLocation = pt; _Key = key; } } #region LinkLocation Class public class LinkLocation { private int _Start; public int Start { get { return _Start; } set { _Start = value; } } private int _Length; public int Length { get { return _Length; } set { _Length = value; } } public int End { get { return _Length + _Start; } } private int _StartRtf; public int StartRtf { get { return _StartRtf; } set { _StartRtf = value; } } private int _LengthRtf; public int LengthRtf { get { return _LengthRtf; } set { _LengthRtf = value; } } public int EndRtf { get { return _LengthRtf + _StartRtf; } } private string _Text; public string Text { get { return _Text; } set { _Text = value; } } private LinkLocation _PreviousLink = null; public LinkLocation PreviousLink { get { return _PreviousLink; } set { _PreviousLink = value; value.NextLink = this; } } private LinkLocation _NextLink = null; public LinkLocation NextLink { get { return _NextLink; } set { _NextLink = value; } } public int PreviousEnd { get { return PreviousLink == null ? -1 : PreviousLink.End; } } public bool StartsBetween { get { return PreviousEnd == Start; } } public int NextStart { get { return NextLink == null ? -1 : NextLink.Start; } } public bool EndsBetween { get { return NextStart == End; } } public LinkLocation(int start, int length, string text, int startRtf, int lengthRtf, LinkLocation previousLink) { _Start = start; _Length = length; _Text = text; _StartRtf = startRtf; _LengthRtf = lengthRtf; if (previousLink != null) PreviousLink = previousLink; } public override string ToString() { return (string.Format("{0}, {1}", Start, End)); } public void Show(string str) { if (PreviousLink != null) Console.WriteLine("LinkLocation: {0}.PreviousLink {1}", str, PreviousLink); Console.WriteLine("LinkLocation: {0} {1}", str, this); if (NextLink != null) Console.WriteLine("LinkLocation: {0}.NextLink {1}", str, NextLink); } } #endregion }