diff --git a/PROMS/Volian.Controls.Library/StepRTB.cs b/PROMS/Volian.Controls.Library/StepRTB.cs index 7b41d6ef..ca6ef7db 100644 --- a/PROMS/Volian.Controls.Library/StepRTB.cs +++ b/PROMS/Volian.Controls.Library/StepRTB.cs @@ -7,15 +7,41 @@ 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 partial class StepRTB : RichTextBox , IStepRTB + 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); + } + /// + /// 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.... + // use newer rich text box.... //[DllImport("kernel32.dll", CharSet = CharSet.Auto)] //static extern IntPtr LoadLibrary(string lpFileName); //protected override CreateParams CreateParams @@ -39,7 +65,7 @@ namespace Volian.Controls.Library set { _MyStepItem = value; } } private bool _IsDirty = false; - //private bool _InitializingRTB; + private bool _InitializingRTB; private IContainer _Container = null; private string _MyClassName=string.Empty; public string MyClassName @@ -73,7 +99,7 @@ namespace Volian.Controls.Library _MyItemInfo = value; if (value != null) { - //_InitializingRTB = true; + _InitializingRTB = true; DisplayText vlntxt = new DisplayText(_MyItemInfo, EpMode, VwMode); _origDisplayText = vlntxt; #if(DEBUG) @@ -85,32 +111,25 @@ namespace Volian.Controls.Library #endif Text = ""; // Initialize text before add text - //if (_MyStepItem.MyID == 168) - // IMPORTANT: SetLineSpacing must be set before Link/protect, otherwise it + // IMPORTANT: SetLineSpacing must be set before Links, otherwise it // was confusing the 'handle' of the rtf box. RTBAPI.SetLineSpacing(this, RTBAPI.ParaSpacing.PFS_EXACT); - - //SelectionStart = 1; AddRtfText(vlntxt); - //AddRtfStyles(); + AddRtfStyles(); ReadOnly = !(EpMode == E_EditPrintMode.Edit && VwMode == E_ViewMode.Edit); - if (_MyStepItem.MyID == 168) - RTBAPI.SetLineSpacing(this, RTBAPI.ParaSpacing.PFS_EXACT); - if (_MyStepItem.MyID == 168) - Console.WriteLine("After Linespacing\r\n {0}", Rtf); - //if (((_origDisplayText.TextFont.Style & E_Style.Bold) != 0) || ((_origDisplayText.TextFont.Style & E_Style.MmBold) != 0)) - // RTBAPI.ToggleBold(true, this, RTBAPI.RTBSelection.SCF_ALL); - //if ((_origDisplayText.TextFont.Style & E_Style.Italics) != 0) - // RTBAPI.ToggleItalic(true, this, RTBAPI.RTBSelection.SCF_ALL); - //if ((_origDisplayText.TextFont.Style & E_Style.Underline) != 0) - // RTBAPI.ToggleUnderline(true, this, RTBAPI.RTBSelection.SCF_ALL); - //_InitializingRTB = false; + _InitializingRTB = false; _IsDirty = false; ClearUndo(); _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) @@ -214,9 +233,11 @@ namespace Volian.Controls.Library this.LinkClicked += new LinkClickedEventHandler(StepRTB_LinkClicked); this.Click +=new EventHandler(StepRTB_Click); this.KeyPress += new KeyPressEventHandler(StepRTB_KeyPress); - this.KeyUp += new KeyEventHandler(StepRTB_KeyUp); 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.SelectionChanged +=new EventHandler(StepRTB_SelectionChanged); } @@ -224,23 +245,27 @@ namespace Volian.Controls.Library { SaveConfig(); } - private void StepRTB_Click(object sender, EventArgs e) { if (ReadOnly) return; - - if (!SelectionProtected) - { - MyLinkText = null; - } } public bool inRoAdd = false; void StepRTB_SelectionChanged(object sender, EventArgs e) { - if (inRoAdd) - Console.WriteLine("SelectionStart {0}, SelectionLength {1}", SelectionStart, SelectionLength); - if (!SelectionProtected && MyLinkText != null) MyLinkText = null; - + if (_InitializingRTB) return; + //DebugSelection("SelectionChanged Event "); + if (SelectionStart == 40 && SelectionLength == 1) vlnStackTrace.ShowStack("SELECTIONCHANGED EVENT"); + HandleSelectionChange(); + } + private bool _MouseDown = false; + private void StepRTB_MouseDown(object sender, MouseEventArgs e) + { + _MouseDown = true; + } + void StepRTB_MouseUp(object sender, MouseEventArgs e) + { + _MouseDown = false; + HandleSelectionChange(); } #endregion #region ApplicationSupport @@ -256,6 +281,7 @@ namespace Volian.Controls.Library } public void InsertRO(string value, string link) { + Console.WriteLine("Before Insert Called: {0}", Rtf); AddRtfLink(value, link); } public void InsertTran(string value, string link) @@ -292,6 +318,7 @@ namespace Volian.Controls.Library bool success = _origDisplayText.Save((RichTextBox)this); if (success) { + FindAllLinks(); _IsDirty = false; ClearUndo(); } @@ -319,36 +346,10 @@ namespace Volian.Controls.Library else AddRtfLink((displayLinkElement)vte); } - if (_MyStepItem.MyID == 168) - Console.WriteLine("_SelectedRtbSB\r\n {0}", _SelectedRtfSB.ToString()); SelectedRtf = _SelectedRtfSB.ToString() + "}"; - if (_MyStepItem.MyID == 168) - Console.WriteLine("Rtf\r\n {0}", Rtf); - //if (_LinkPosition > -1) - FormatLinks(); - } - - private void FormatLinks() - { - if (_LinkLocations != null) - { - foreach (LinkLocation ll in _LinkLocations) - { - Select(ll.Position, ll.Length); - - RTBAPI.CharFormatTwo charFormat = RTBAPI.GetCharFormat(this, RTBAPI.RTBSelection.SCF_SELECTION); - // Protect the link text to avoid manual changes - charFormat.dwMask = RTBAPI.CharFormatMasks.CFM_LINK | RTBAPI.CharFormatMasks.CFM_PROTECTED; - charFormat.dwEffects = RTBAPI.CharFormatEffects.CFE_LINK | RTBAPI.CharFormatEffects.CFE_PROTECTED; - RTBAPI.SetCharFormat((RichTextBox)this, RTBAPI.RTBSelection.SCF_SELECTION, charFormat); - //charFormat.dwMask = /* RTBAPI.CharFormatMasks.CFM_LINK | */ RTBAPI.CharFormatMasks.CFM_PROTECTED; - //charFormat.dwEffects = /* RTBAPI.CharFormatEffects.CFE_LINK | */ RTBAPI.CharFormatEffects.CFE_PROTECTED; - //RTBAPI.SetCharFormat((RichTextBox)this, RTBAPI.RTBSelection.SCF_SELECTION, charFormat); - } - } + FindAllLinks(); } private StringBuilder _SelectedRtfSB = new StringBuilder(); - private void AddFontTable() { StringBuilder sbbeg = new StringBuilder(); @@ -371,65 +372,57 @@ namespace Volian.Controls.Library _SelectedRtfSB.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset2 " + this.Font.FontFamily.Name + @";}"); //}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}"; _SelectedRtfSB.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}"); _SelectedRtfSB.Append("\r\n"); - // use styles to construct rtf commands to insert into next line (where \b is) + // 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" + this.Font.SizeInPoints * 2 + @" "); // \f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}"; - //SelectedRtf = _SelectedRtfSB.ToString(); } private void AddRtf(displayTextElement myDisplayTextElement) { - // try later, i.e. adding bold to font name: SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset2 " + this.Font.FontFamily.Name + @",Bold;}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}"; - //SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset2 " + this.Font.FontFamily.Name + @";}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}"; - //_SelectedRtfSB.Append(@"\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text); // + @"}"); - _SelectedRtfSB.Append(@"\f0 " + myDisplayTextElement.Text); // + @"}"); + _SelectedRtfSB.Append(@"\f0 " + myDisplayTextElement.Text); } 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; -// SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset2 " + this.Font.FontFamily.Name + @";}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + str + @"}}"; + int positionAfter = SelectionStart; + Select(positionStart, positionAfter - positionStart); + RTBAPI.SetFontStyle(this, fs); + Select(positionAfter, 0); } private void AddSymbol(displayTextElement myDisplayTextElement) { - //_SelectedRtfSB.Append(@"\f1\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text); _SelectedRtfSB.Append(@"\f1 " + myDisplayTextElement.Text); - //SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset0 Arial Unicode MS;}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}"; } private void AddSymbol(string str) { - //MessageBox.Show(SelectedRtf); - Font selfont = this.SelectionFont; - StringBuilder sbbeg = new StringBuilder(); - StringBuilder sbend = new StringBuilder(); - if (selfont.Bold) - { - sbbeg.Append(@"\b"); - sbend.Append(@"\b0"); - } - if (selfont.Underline) - { - sbbeg.Append(@"\ul"); - sbend.Insert(0,@"\ulnone"); - } - if (selfont.Italic) - { - sbbeg.Append(@"\i"); - sbend.Insert(0, @"\i0"); - } - if (sbend.Length > 0) sbbeg.Append(" "); - - // sub/superscript??? - SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset0 Arial Unicode MS;}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + sbbeg.ToString() + str + sbend.ToString() + @"}"; + // See comments in AddRtf(string str) to explain the font style setting + RTBAPI.E_FontStyle fs = RTBAPI.GetFontStyle(this); + int position = SelectionStart; + SelectedRtf = @"{\rtf1{\fonttbl{\f0\fcharset0 Arial Unicode MS;}}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + str + @"}"; + Select(position, 1); + RTBAPI.SetFontStyle(this, fs); + Select(position + 1, 0); } - private List _LinkLocations = null; - private void AddLinkLocation(string location, string url) + private string GetAddSymbolText(string symtxt) { - SelectedRtf = _SelectedRtfSB.ToString() + "}"; - int linkPosition = this.TextLength; - this.Text = ""; - if (_LinkLocations == null) _LinkLocations = new List(); - _LinkLocations.Add(new LinkLocation(linkPosition, location.Length + url.Length)); + return (@"{\f0\fs" + this.Font.SizeInPoints * 2 + @" " + symtxt + @"}"); } - private int _LinkPosition = -1; - private int _LinkLength; private void AddRtfLink(displayLinkElement myDisplayLinkElement) { if (CreateParams.ClassName == "RICHEDIT50W") @@ -438,40 +431,27 @@ namespace Volian.Controls.Library else { AddLinkLocation(myDisplayLinkElement.Text, myDisplayLinkElement.Link); - _SelectedRtfSB.Append(@"\f0 "); - _SelectedRtfSB.Append(myDisplayLinkElement.Text); - _SelectedRtfSB.Append(@"\v "); - _SelectedRtfSB.Append(myDisplayLinkElement.Link); - _SelectedRtfSB.Append(@"\v0 "); - //_SelectedRtfSB.Append(@"\sl-240\slmult0\f0\fs20 0POP05-EO-ES00, REDIAGNOSIS, COVER\v #Link:Transition:5 7 198\v0"); - //AddLink20(myDisplayLinkElement.Text, myDisplayLinkElement.Link); + _SelectedRtfSB.Append(@"\f0\fs20 "); + _SelectedRtfSB.Append(myDisplayLinkElement.TextAndLink); } } public void AddRtfLink(string linkUrl, string linkValue) { - //if (CreateParams.ClassName == "RICHEDIT50W") + if (CreateParams.ClassName == "RICHEDIT50W") - //AddLink50(linkUrl, linkValue); - //else - - //AddLink20(linkUrl, linkValue); + AddLink50(linkUrl, linkValue); + else + AddLink20(linkUrl, linkValue); } private void AddLink20(string linkValue, string linkUrl) { this.DetectUrls = false; - RTBAPI.CharFormatTwo charFormat = RTBAPI.GetCharFormat(this, RTBAPI.RTBSelection.SCF_SELECTION); int position = SelectionStart; // before inserttran = this.TextLength; SelectionLength = 0; - //SelectedRtf = @"{\rtf1\ansi " + linkValue + @"\v " + linkUrl + @"\v0}"; - SelectedRtf = linkValue + @"\v " + linkUrl + @"\v0"; - Select(position, linkValue.Length + linkUrl.Length); - // Protect the link text to avoid manual changes - charFormat.dwMask = RTBAPI.CharFormatMasks.CFM_LINK | RTBAPI.CharFormatMasks.CFM_PROTECTED; - charFormat.dwEffects = RTBAPI.CharFormatEffects.CFE_LINK | RTBAPI.CharFormatEffects.CFE_PROTECTED; - RTBAPI.SetCharFormat((RichTextBox)this, RTBAPI.RTBSelection.SCF_SELECTION, charFormat); - this.SelectionStart = this.TextLength; -// \protect\fs20 0POP05-EO-ES00, REDIAGNOSIS, COVER\v #Link:Transition:5 7 198\protect0\v0 + SelectedRtf = @"{\rtf1\ansi\v \v0 }"; this.SelectionLength = 0; + this.SelectionStart = position; + FindAllLinks(); } private void AddLink50(string linkValue, string linkUrl) { @@ -539,10 +519,6 @@ namespace Volian.Controls.Library Application.DoEvents(); int heightFont = SelectionFont.Height; int borderSize = this.Height - this.ClientSize.Height; - //for (int i = 235; i < TextLength; i++) - //{ - // Console.WriteLine("{0}\t{1}\t{2}", i, GetLineFromCharIndex(i), GetPositionFromCharIndex(i)); - //} int heightNext = (1 + GetLineFromCharIndex(TextLength)) * heightFont + borderSize + 2;// 2 pixels - 1 at the top and 1 at the bottom return heightNext; } @@ -566,7 +542,6 @@ namespace Volian.Controls.Library } #endregion #region EventSupport - #region LinkEvents private StepPanelLinkEventArgs _MyLinkClickedEventArgs; public event StepRTBLinkEvent LinkChanged; @@ -608,14 +583,290 @@ namespace Volian.Controls.Library void StepRTB_TextChanged(object sender, EventArgs e) { _IsDirty = true; + FindAllLinks(); } void StepRTB_ContentsResized(object sender, ContentsResizedEventArgs e) { ContentsRectangle = e.NewRectangle; } #endregion + #region Selection Handlers + bool _AdjustingSelection = false; + private bool _ProcessingDelete; + private void HandleSelectionChange() + { + 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()); + OnRTBSelectionChanged(this, new EventArgs()); + } + private bool _CheckSelection = false; + #endregion + #region Delete Handlers + private void HandleDeleteKeyWithSelectedText(KeyEventArgs e, char keychar) + { + _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(keychar); + 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(char keychar) + { + + 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(keychar);// 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. + /// + private void DeleteCurrentSelection(char key) + { + DebugPrint("vvvvvvvvvvvvxxxxxxxxxxxx>"); + DebugSelection("Before X"); + SelectedText = key==0?"X":key.ToString(); // replace text with X + DebugSelection("After X"); + DebugPrint("------------xxxxxxxxxxxx>"); + if (key == 0) + { + _SendBackSpace = true; + RtbSendKeys("{BS}"); // remove X + Application.DoEvents(); + DebugSelection("After BS"); + } + DebugPrint("^^^^^^^^^^^^xxxxxxxxxxxx>"); + } + private void DeleteSelection(int start, int length, char keychar) + { + SetSelection(start, length); + DeleteCurrentSelection(keychar); + } + private void DeleteEndBetweenLinks(char keychar) + { + _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, keychar); + //_AdjustingSelection = false; + _ProcessingKeys--; + } + private void DeleteStartBetweenLinks(char keychar) + { + _ProcessingKeys++; + DebugSelection("DeleteStartBetweenLinks"); + int slen = SelectionLength + 8; + int sstart = SelectionStart - 7; + //LinkLocation ll = FindBetweenLinks(SelectionStart); + InsertCharBetweenLinks(_RangeStartLink); + DeleteSelection(sstart, slen, keychar); + _ProcessingKeys--; + } + private void DeleteFromStartOfBox(char keychar) + { + _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, keychar); + _ProcessingKeys--; + } + private void DeleteFromStartOfBoxEndBetweenLinks(char keychar) + { + _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) @@ -625,9 +876,6 @@ namespace Volian.Controls.Library { case Keys.V: string buff = Clipboard.GetText(TextDataFormat.UnicodeText); - - // check if insertable? - Console.WriteLine(String.Format("in switch, keydata = {0}, keyvalue = {1}, buff = {2}", e.KeyData, e.KeyValue, buff)); return; case Keys.Home: StepRTB_HomeEndPressed(e); @@ -642,11 +890,20 @@ namespace Volian.Controls.Library switch (e.KeyCode) { case Keys.Left: + if (e.Shift) + { + int newstart = FindStart(); // find start of link ending on. + 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); @@ -655,13 +912,42 @@ namespace Volian.Controls.Library 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); @@ -670,10 +956,23 @@ namespace Volian.Controls.Library 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; @@ -686,46 +985,47 @@ namespace Volian.Controls.Library e.Handled = true; break; case Keys.Delete: - // if it's just a link, delete the link. if the text has embedded links, i.e. text and links - // use DeleteTextAndLink to delete it (just setting selectedrtf to "" fails because of the - // embedded protected text. If it's just text, let the richtextbox handle the delete. - if (_MyLinkText != null) + if (SelectionLength == 0) { - DeleteLink(); - e.Handled = true; - } - else if (SelectedRtf.IndexOf(@"\protect") > -1) - { - // unprotect and then delete text & links. - DeleteTextAndLink(); - e.Handled = true; + foreach (LinkLocation lls in _LinkLocations) + { + if (SelectionStart >= lls.Start - 7 && SelectionStart < lls.End) + { + SetSelection(lls);// Select the link to the right + HandleDeleteKeyWithSelectedText(e, '\x0'); + 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, '\x0'); break; case Keys.Back: - // if not a range, i.e. SelectionLength = 0, then see if backspacing a link - // or just regular text. If link, need to select link, unprotect before delete. - if (SelectionLength == 0 && SelectionStart > 0) + if (_SendBackSpace) { - int tmpss = SelectionStart; - Select(SelectionStart - 1, 0); // see if previous char is protected - if (SelectionProtected) + _SendBackSpace = false; + return; + } + _CheckSelection = true; + if (SelectionLength == 0) + { + foreach (LinkLocation lls in _LinkLocations) { - SelectLinkFromIndex(SelectionStart); - DeleteLink(); - e.Handled = true; + if (SelectionStart > lls.Start - 7 && SelectionStart <= lls.End) + { + SetSelection(lls); + break; + } } - else - Select(tmpss, 0); // go back to original cursor position - } - // if a range, need to see if range includes protected text, if so this is a - // special case, so use DeleteTextAndLink. Otherwise, just let the richtextbox - // delete it. - else if (SelectionLength > 0 && (SelectedRtf.IndexOf(@"\protect") > -1)) - { - // unprotect and then delete text & links. - DeleteTextAndLink(); - e.Handled = true; } + if (SelectionLength > 0) HandleDeleteKeyWithSelectedText(e, '\x0'); break; } } @@ -746,43 +1046,6 @@ namespace Volian.Controls.Library Point cp = PointToClient(Cursor.Position); _MyStepItem.MyStepPanel.CursorMovement(this, cp, key); } - private void DeleteTextAndLink() - { - int start = SelectionStart; - int len = SelectionLength; - for (int i = start; i < len + start; i++) - { - Select(i, 0); - if (SelectionProtected) - { - SelectLinkFromIndex(i); - SelectionProtected = false; - } - } - Select(start, len); - SelectedRtf = ""; - } - void StepRTB_KeyUp(object sender, KeyEventArgs e) - { - switch (e.KeyCode) - { - case Keys.Left: - case Keys.Up: - case Keys.Down: - case Keys.Right: - if (!SelectionProtected) - MyLinkText = null; - else - { - if (e.KeyCode == Keys.Right) - SelectLink(); - else - SelectLinkFromIndex(SelectionStart); - e.Handled = true; - } - break; - } - } private void StepRTB_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { if (!ReadOnly) @@ -790,60 +1053,327 @@ namespace Volian.Controls.Library // add the character with its font depending on the char.... if (!IsControlChar) { - if (e.KeyChar == '\b') return; // return on backspace otherwise, get a block char + bool done = false; + string strpressed = null; if (e.KeyChar == '-') - AddSymbol(@"\u8209?"); + strpressed = GetAddSymbolText(@"\u8209?"); else if (e.KeyChar == '{') - AddRtf("\\{"); + strpressed = "\\{"; else if (e.KeyChar == '}') - AddRtf("\\}"); - else if (e.KeyChar == '[') - AddRtf(@"\b "); + strpressed = "\\}"; else - return; // AddRtf(e.KeyChar.ToString()); - e.Handled = true; // flag that it's been handled, otherwise, will get 2 chars. + strpressed = e.KeyChar.ToString(); + if (e.KeyChar >= ' ') + { + LinkLocation ll = FindBetweenLinks(); + if (ll != null && SelectionLength == 0) // SelectionLength = 0 means insert + { + InsertCharBetweenLinks(ll, e.KeyChar); + e.Handled = true; + } + else if (SelectionLength != 0) + { + HandleDeleteKeyWithSelectedText(new KeyEventArgs(Keys.None), e.KeyChar); + e.Handled = true; + } + } + if (!done) + { + 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. + } } 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 private void SelectLinkFromPoint() { Point cp = PointToClient(_savcurpos); int index = GetCharIndexFromPosition(cp); - SelectLinkFromIndex(index); + SelectLink(index, 0); } - private void SelectLinkFromIndex(int index) + private void SelectLink(int index, int len) { - // Find beginning of the link until no longer protected or beginning of string. - int iMin = index; - Select(index, 0); - while (iMin > 0 && SelectionProtected) - Select(--iMin, 0); - Select(iMin + 1, 0); - - // Now select the entire link - SelectLink(); + FindLink(index, len); + if (SelectedText.IndexOf(@"[END>") > 0) MyLinkText = SelectedText.Substring(0, SelectedText.IndexOf(@"[END>")); + else MyLinkText = SelectedText; } - private void SelectLink() + public bool IsSelectionLinked(int index, int len) { - int index = SelectionStart; - int iMax = index; - Select(index, 0); - while (iMax < TextLength && SelectionProtected) - Select(++iMax, 0); - if (index == 0) index++; // account for link at beginning of box - Select(index - 1, SelectionStart + 1 - index); - MyLinkText = SelectedText; + 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; } + private int FindLink(int startIndex, int len) + { + int sel = startIndex; + int selLength = len; + foreach (LinkLocation ll in _LinkLocations) + { + // add 7 to include the '= ll.Start && sel <= ll.Start + ll.Length) + { + SetSelection(ll.Start, ll.Length); + if (SelectionLength == 0) + { + // try adding 7 to locEnd + SetSelection(ll.Start, ll.Length + 7); + } + Console.WriteLine(string.Format("\nFindLink Start = {0}, Length = {1}\n", SelectionStart, SelectionLength)); + return SelectionStart; + } + } + return SelectionStart; + } + private void DeleteLink() { - SelectionProtected = false; SelectedText = ""; MyLinkText = null; + FindAllLinks(); + } + #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, ""); + MatchCollection matchesRtf = Regex.Matches(Rtf, ""); + 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 FindLinkSelected() + { + DebugPrint("FL----------------Selected>"); + if (_LinkLocations == null) return null; + foreach (LinkLocation ll in _LinkLocations) + if (ll.Start == SelectionStart && ll.Length == SelectionLength) 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() + { + DebugPrint("FINDSTARTUP Start = {0}, Len = {1}", SelectionStart, SelectionLength); + foreach (LinkLocation ll in _LinkLocations) + { + DebugPrint("FINDSTARTUP link start = {0}, link end = {1}", ll.Start, ll.End); + if ((SelectionStart >= ll.Start) && (SelectionStart <= ll.End)) + { + DebugPrint("FINDSTARTUP - in link start = {0}, end = {1}", ll.Start, ll.End); + return ll.Start; + } + } + DebugPrint("FINDSTARTUP - Not in link"); + 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 #endregion #region FontAndStylesSupport private void ToggleFontStyle(FontStyle style, bool att_on) @@ -937,15 +1467,147 @@ namespace Volian.Controls.Library 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; + private 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 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 } #region LinkLocation Class - internal class LinkLocation + public class LinkLocation { - private int _Position; - public int Position + private int _Start; + public int Start { - get { return _Position; } - set { _Position = value; } + get { return _Start; } + set { _Start = value; } } private int _Length; public int Length @@ -953,12 +1615,86 @@ namespace Volian.Controls.Library get { return _Length; } set { _Length = value; } } - - public LinkLocation(int position, int length) + public int End { - _Position = position; - _Length = length; + 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 }