882 lines
27 KiB
C#
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
|
|
}
|