882 lines
27 KiB
C#

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;
namespace TestWndProc
{
public delegate void RTBEvent(object sender, EventArgs args);
public partial class MyRTB : RichTextBox
{
#region Events
public event RTBEvent RTBSelectionChanged;
private void OnRTBSelectionChanged(object sender, EventArgs args)
{
if (RTBSelectionChanged != null) RTBSelectionChanged(sender, args);
}
public event RTBEvent LinkLocationsChanged;
private void OnLinkLocationChanged(object sender, EventArgs args)
{
if(LinkLocationsChanged != null) LinkLocationsChanged(sender, args);
}
public event RTBEvent RTBRangeStatusChanged;
private void OnRTBRangeStatusChanged(object sender, EventArgs args)
{
if (RTBRangeStatusChanged != null) RTBRangeStatusChanged(sender, args);
}
/// <summary>
/// This event is not raised during all the in-between changes for link deletions
/// </summary>
public event RTBEvent RTBTextChanged;
private void OnRTBTextChanged(object sender, EventArgs args)
{
if (RTBTextChanged != null) RTBTextChanged(sender, args);
}
#endregion
#region public properties
private bool _ProcessKeystrokes = true;
public bool ProcessKeystrokes
{
get { return _ProcessKeystrokes; }
set { _ProcessKeystrokes = value; }
}
#endregion
#region CaretPosition
//[DllImport("user32")]
//private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);
[DllImport("user32")]
private static extern int GetCaretPos(ref Point lpPoint);
[DllImport("user32")]
private static extern bool SetCaretPos(int X, int Y);
//private int EM_LINEINDEX = 0xbb;
//private int EM_LINEFROMCHAR = 0xc9;
//private int EM_GETSEL = 0xb0;
/// <summary>
/// Gets the caret current (X, Y) position.
/// </summary>
/// <value>
/// Point struct
/// </value>
public Point CaretXYPosition
{
get
{
Point pt = Point.Empty;
// get a Point struct with the caret current (X, Y)
// position
GetCaretPos(ref pt);
// return the Point struct with the caret current
// (X, Y) position
return pt;
}
}
public int CaretIndex
{
get { return GetCharIndexFromPosition(CaretXYPosition); }
}
public void SetCaretPostoSelectionStart()
{
Point pt = GetPositionFromCharIndex(SelectionStart);
//bool result = SetCaretPos(pt.X,pt.Y);
bool result = SetCaretPos(-1, pt.Y);
DebugPrint("SetCaretPos({0},{1}) = {2}", pt.X, pt.Y, result);
}
#endregion
#region Constructors
public MyRTB()
{
InitializeComponent();
SetupEventHandlers();
}
public MyRTB(IContainer container)
{
container.Add(this);
InitializeComponent();
SetupEventHandlers();
}
private void SetupEventHandlers()
{
this.MouseDown += new MouseEventHandler(MyRTB_MouseDown);
this.MouseUp += new MouseEventHandler(MyRTB_MouseUp);
this.KeyDown += new KeyEventHandler(MyRTB_KeyDown);
this.KeyPress += new KeyPressEventHandler(MyRTB_KeyPress);
this.TextChanged += new EventHandler(MyRTB_TextChanged);
this.SelectionChanged += new EventHandler(MyRTB_SelectionChanged);
}
#endregion
#region Keyboard Handlers
void MyRTB_KeyPress(object sender, KeyPressEventArgs e)
{
DebugPrint("KeyPress: {0},{1}", e.KeyChar, (int)e.KeyChar);
if (e.KeyChar >= ' ')
{
LinkLocation ll = FindBetweenLinks();
if (ll != null && SelectionLength == 0) // SelectionLength = 0 means insert
{
InsertCharBetweenLinks(ll, e.KeyChar);
e.Handled = true;
}
else if (ll != null) // Otherwise it is an overstrike or a delete followed by inserting a character
{
DeleteStartBetweenLinks();
}
else if (SelectedText.EndsWith(@"[END><START]"))
{
DeleteEndBetweenLinks();
}
}
}
private bool _SendBackSpace = false;
void MyRTB_KeyDown(object sender, KeyEventArgs e)
{
if (_AdjustingSelection)
DebugPrint("======================>AdjustingSelection is true in Keydown");
if (_ProcessingKeys > 0)
DebugPrint("Key down: {0} Shift {1} processing keys = {2}", e.KeyCode, e.Shift, _ProcessingKeys);
if (!ProcessKeystrokes) return;
switch (e.KeyCode)
{
case Keys.Left:
DebugPrint("Key down: {0} Shift {1} processing keys = {2}", e.KeyCode, e.Shift, _ProcessingKeys);
DebugSelection("Left");
if (e.Shift) return;
LinkLocation ll = FindLinkSelected();
if (ll != null)
{
_AdjustingSelection = true;
if (ll.StartsBetween || ll.Start == 7)
SetSelection(ll.Start, 0);
else
SetSelection(ll.Start - 7, 0);
_AdjustingSelection = false;
e.SuppressKeyPress = true;
}
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)
{
//RtbSendKeys("{Left}");// This should select the ll
SetSelection(lls);
break;
}
}
}
if (SelectionLength > 0) HandleDeleteKeyWithSelectedText(e);
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);
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);
break;
}
}
#endregion
#region Mouse Handlers
void MyRTB_MouseUp(object sender, MouseEventArgs e)
{
_MouseDown = false;
HandleSelectionChange();
}
void MyRTB_MouseDown(object sender, MouseEventArgs e)
{
_MouseDown = true;
}
#endregion
#region Event Handlers
void MyRTB_SelectionChanged(object sender, EventArgs e)
{
HandleSelectionChange();
}
private bool _ProcessingDelete;
void MyRTB_TextChanged(object sender, EventArgs e)
{
if(!_IdentifyingLinks)
FindAllLinks();
if (!_ProcessingDelete && !_IdentifyingLinks)
{
OnRTBTextChanged(this, new EventArgs());
}
}
#endregion
#region SelectionStack
Stack<SelectionData> _SelectionStack = new Stack<SelectionData>();
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
bool _AdjustingSelection = false;
private bool _CheckSelection = false;
int _ProcessingKeys = 0;
private bool _MouseDown = false;
List<LinkLocation> _linkLocations;
public List<LinkLocation> LinkLocations
{
get { return _linkLocations; }
}
private int _FALLevel = 0;
private bool _IdentifyingLinks = false;
public void FindAllLinks()
{
if (_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<LinkLocation>();
MatchCollection matches = Regex.Matches(str, "<START](.*?)[[]END>");
MatchCollection matchesRtf = Regex.Matches(Rtf, "<START](.*?)[[]END>");
LinkLocation thisLink = null;
for (int i = 0; i < matches.Count; i++) //each (Match match in matches)
{ //KBR
Match match = matches[i];
Match matchrtf = matchesRtf[i];
thisLink = new LinkLocation(match.Index + 7, // If the [END> is immediately followed by <START] include it
match.Length - (((match.Index + match.Length + 7 <= str.Length) && (str.Substring(match.Index + match.Length, 7) == "<START]")) ? 0 : 7),
match.Value, matchrtf.Index, matchrtf.Length, thisLink);
_linkLocations.Add(thisLink);
} //KBR
}
private void IdentifyLinks()
{
_IdentifyingLinks = true;
foreach (LinkLocation ll in _linkLocations)
{
SetSelection(ll); // subtract off start token!
RTBAPI.CharFormatTwo charFormat = RTBAPI.GetCharFormat(this, RTBAPI.RTBSelection.SCF_SELECTION);
//charFormat.crBackColor = Color.Blue;
// 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(this, RTBAPI.RTBSelection.SCF_SELECTION, charFormat);
}
_IdentifyingLinks = false;
}
private LinkLocation FindBetweenLinks()
{
return FindBetweenLinks(SelectionStart);
}
private LinkLocation FindBetweenLinks(int start)
{
DebugPrint("FL----------------Between>");
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;
}
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 Selection Handlers
private void HandleSelectionChange()
{
bool startingValue = _AdjustingSelection;
if (_IdentifyingLinks || _ProcessingDelete) return;
if (ProcessKeystrokes)
{
if (!_MouseDown && !_AdjustingSelection)
{
if (_linkLocations != null)
{
_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 <START] token. If it does, adjust the start
int newStart = SelectionStart + (SelectedText.StartsWith("<START]") ? 7 : 0);
SetSelection(newStart, llend.End - newStart);
}
else
{
if (SelectedText.EndsWith("<START]") && !SelectedText.EndsWith("[END><START]"))
{
DebugPrint("HSC===================>Removing ending <START] token");
SelectionLength = SelectionLength - 7;
}
if (SelectedText.StartsWith("<START]"))
{
DebugPrint("HSC===================>Removing starting <START] token");
SetSelection(SelectionStart + 7, SelectionLength - 7);
}
}
_AdjustingSelection = false;
}
}
}
if (startingValue != _AdjustingSelection)
DebugPrint("================> _AdjustingSelection problem");
DebugPrint("RS------ SelectionChange > {0}", FindRangeStatus());
OnRTBSelectionChanged(this, new EventArgs());
}
#endregion
#region Delete Handlers
private void HandleDeleteKeyWithSelectedText(KeyEventArgs e)
{
_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();
e.SuppressKeyPress = true;
break;
case RangeStatus.Before_Between: //myRTB1.SelectedText.EndsWith(@"[END><START]")
DeleteEndBetweenLinks();
e.SuppressKeyPress = true;
break;
case RangeStatus.Between_EndBox:
case RangeStatus.Between_EndLink:
case RangeStatus.Between_After:
DeleteStartBetweenLinks();
e.SuppressKeyPress = true;
break;
case RangeStatus.Between_Between:
DeleteBetweenBetweenLinks();
e.SuppressKeyPress=true;
break;
case RangeStatus.StartLink_EndBox:
case RangeStatus.StartLink_EndLink:
case RangeStatus.StartLink_After:
ExpandSelectionToIncludeStart();
DeleteCurrentSelection();
e.SuppressKeyPress = true;
break;
case RangeStatus.StartLink_Between:
ExpandSelectionToIncludeStart();
DeleteEndBetweenLinks();
e.SuppressKeyPress = true;
break;
case RangeStatus.StartBox_EndLink:
case RangeStatus.StartBox_EndBox:
case RangeStatus.StartBox_After:
DeleteFromStartOfBox();
e.SuppressKeyPress = true;
break;
case RangeStatus.StartBox_Between:
DeleteFromStartOfBoxEndBetweenLinks();
e.SuppressKeyPress=true;
break;
// REMOVE THESE CASES OR PUT A MESSAGE OUT SAYING ERROR IN CODE - CASE
// NOT HANDLED
case RangeStatus.Before_StartLink:
case RangeStatus.Between_StartLink:
case RangeStatus.StartLink_StartLink:
case RangeStatus.StartBox_StartLink:
break;
}
_ProcessingDelete = false;
MyRTB_TextChanged(this, new EventArgs());
}
private void InsertCharBetweenLinks(LinkLocation ll)
{
InsertCharBetweenLinks(ll, ' ', false);
}
private void InsertCharBetweenLinks(LinkLocation ll, char charToAdd)
{
InsertCharBetweenLinks(ll, charToAdd, true);
}
/// <summary>
/// This inserts a space in between two links.
/// It actually inserts "\v0 {space}\v " between the END tag and the START tag
/// </summary>
/// <param name="ll"></param>
/// <param name="charToAdd"></param>
/// <param name="setSelect"></param>
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 <START] - 1 for the character typed
DebugPrint("ICBL^^^^^^^^^^^^^^^>>>");
}
private void ExpandSelectionToIncludeStart()
{
//_AdjustingSelection = true;
SetSelection(SelectionStart - 7, SelectionLength + 7); // Expand selection to include start
//_AdjustingSelection = false;
}
private void DeleteBetweenBetweenLinks()
{
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();// Delete Selection
}
/// <summary>
/// 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.
/// </summary>
private void DeleteCurrentSelection()
{
DebugPrint("vvvvvvvvvvvvxxxxxxxxxxxx>");
DebugSelection("Before X");
SelectedText = "X"; // replace text with X
DebugSelection("After X");
DebugPrint("------------xxxxxxxxxxxx>");
_SendBackSpace = true;
RtbSendKeys("{BS}"); // remove X
Application.DoEvents();
DebugSelection("After BS");
DebugPrint("^^^^^^^^^^^^xxxxxxxxxxxx>");
}
private void DeleteSelection(int start, int length)
{
SetSelection(start, length);
DeleteCurrentSelection();
}
private void DeleteEndBetweenLinks()
{
_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);
//_AdjustingSelection = false;
_ProcessingKeys--;
}
private void DeleteStartBetweenLinks()
{
_ProcessingKeys++;
DebugSelection("DeleteStartBetweenLinks");
int slen = SelectionLength + 8;
int sstart = SelectionStart - 7;
//LinkLocation ll = FindBetweenLinks(SelectionStart);
InsertCharBetweenLinks(_RangeStartLink);
DeleteSelection(sstart, slen);
_ProcessingKeys--;
}
private void DeleteFromStartOfBox()
{
_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);
_ProcessingKeys--;
}
private void DeleteFromStartOfBoxEndBetweenLinks()
{
_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 <START] + 2 for the spaces that are added
//LinkLocation ll = FindBetweenLinks(SelectionStart + SelectionLength);
InsertCharBetweenLinks(_RangeEndLink.NextLink);
//RtbSendKeys("{RIGHT} "); // open for space between links which separates END/START tokens
//int sLen = myRTB1.SelectionStart;
SetSelection(0, 0);
//RtbSendKeys(" "); // open for space between links which separates END/START tokens
SelectedText = " "; // open for space between links which separates END/START tokens
DeleteSelection(0, sLen);
_ProcessingKeys--;
}
#endregion
#region Debug
private bool _ShowDebug = true;
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 SendKeys
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
//DebugSelection(keys);
}
#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 (startLink != null)
// startLink.Show("startLink");
//if (endLink != null)
// endLink.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 LinkLocation
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
}