1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Drawing;
|
---|
4 | using System.Runtime.InteropServices;
|
---|
5 | using System.Windows.Forms;
|
---|
6 |
|
---|
7 | namespace Common
|
---|
8 | {
|
---|
9 | public partial class RichTextBoxEx : RichTextBox
|
---|
10 | {
|
---|
11 | private bool linksActive;
|
---|
12 | public delegate void LinkClickHandler(string link);
|
---|
13 | public event LinkClickHandler LinkClick;
|
---|
14 | public string Title { get; set; }
|
---|
15 | public Form ParentForm;
|
---|
16 | public List<LinkMatch> LinkMatches;
|
---|
17 | public FormFind FormFind;
|
---|
18 | public string PreviousRtf;
|
---|
19 | private ToolStripMenuItem tsmiUndo;
|
---|
20 | private ToolStripMenuItem tsmiRedo;
|
---|
21 | private ToolStripMenuItem tsmiCut;
|
---|
22 | private ToolStripMenuItem tsmiCopy;
|
---|
23 | private ToolStripMenuItem tsmiPaste;
|
---|
24 | private ToolStripMenuItem tsmiDelete;
|
---|
25 | private ToolStripMenuItem tsmiSelectAll;
|
---|
26 | private ToolStripMenuItem tsmiFind;
|
---|
27 | private ToolStripMenuItem tsmiShowInWindow;
|
---|
28 | private bool interfaceUpdateEnabled = true;
|
---|
29 |
|
---|
30 | public RichTextBoxEx()
|
---|
31 | {
|
---|
32 | //InitializeComponent();
|
---|
33 | KeyDown += HandleKeyDown;
|
---|
34 | KeyUp += HandleKeyUp;
|
---|
35 | MouseClick += HandleMouseClick;
|
---|
36 | MouseLeave += HandleMouseLeave;
|
---|
37 | Leave += HandleLeave;
|
---|
38 | SelectionChanged += delegate
|
---|
39 | {
|
---|
40 | if (interfaceUpdateEnabled) UpdateInterface();
|
---|
41 | };
|
---|
42 | AddContextMenu();
|
---|
43 | LinkMatches = new List<LinkMatch>();
|
---|
44 | }
|
---|
45 |
|
---|
46 | protected override void OnPaint(PaintEventArgs pe)
|
---|
47 | {
|
---|
48 | base.OnPaint(pe);
|
---|
49 | }
|
---|
50 |
|
---|
51 | private void ShowRichTextBoxLinks(RichTextBox richTextBox)
|
---|
52 | {
|
---|
53 | bool lastReadOnlyState = ReadOnly;
|
---|
54 | ReadOnly = false;
|
---|
55 | interfaceUpdateEnabled = false;
|
---|
56 | PreviousRtf = Rtf;
|
---|
57 | RichTextBoxContext context = new RichTextBoxContext();
|
---|
58 | context.SaveContext(this);
|
---|
59 | List<int> linkMatchStart = new List<int>();
|
---|
60 | foreach (var linkMatch in LinkMatches)
|
---|
61 | {
|
---|
62 | linkMatchStart.Add(-2);
|
---|
63 | }
|
---|
64 |
|
---|
65 | List<string> linkMatchStartString = new List<string>();
|
---|
66 | foreach (var linkMatch in LinkMatches)
|
---|
67 | linkMatchStartString.Add(linkMatch.StartString.ToLowerInvariant());
|
---|
68 |
|
---|
69 | string content = richTextBox.Text;
|
---|
70 | string contentLowerCase = content.ToLowerInvariant();
|
---|
71 |
|
---|
72 | RichTextBox tempRichTextBox = new RichTextBox();
|
---|
73 | tempRichTextBox.Rtf = richTextBox.Rtf;
|
---|
74 |
|
---|
75 | int contentStart = 0;
|
---|
76 | do
|
---|
77 | {
|
---|
78 | int firstIndex = content.Length;
|
---|
79 | LinkMatch firstMatch = null;
|
---|
80 | if ((content.Length - contentStart) > 0)
|
---|
81 | {
|
---|
82 | int i = 0;
|
---|
83 | foreach (var linkMatch in LinkMatches)
|
---|
84 | {
|
---|
85 | if ((linkMatchStart[i] < contentStart) && (linkMatchStart[i] != -1))
|
---|
86 | {
|
---|
87 | if (linkMatch.CaseSensitive)
|
---|
88 | {
|
---|
89 | linkMatchStart[i] = content.IndexOf(linkMatch.StartString, contentStart, StringComparison.Ordinal);
|
---|
90 | } else
|
---|
91 | {
|
---|
92 | linkMatchStart[i] = contentLowerCase.IndexOf(linkMatchStartString[i], contentStart, StringComparison.Ordinal);
|
---|
93 | }
|
---|
94 | }
|
---|
95 | if ((linkMatchStart[i] != -1) && (linkMatchStart[i] < firstIndex))
|
---|
96 | {
|
---|
97 | firstMatch = linkMatch;
|
---|
98 | firstIndex = linkMatchStart[i];
|
---|
99 | }
|
---|
100 | i++;
|
---|
101 | }
|
---|
102 | if (firstMatch == null) break;
|
---|
103 | }
|
---|
104 | else break;
|
---|
105 |
|
---|
106 | int index = firstIndex;
|
---|
107 | string startString = firstMatch.StartString;
|
---|
108 |
|
---|
109 | int linkLength = startString.Length;
|
---|
110 | var selectionStart = index;
|
---|
111 |
|
---|
112 | if (firstMatch.ExecuteMatch(content, index + startString.Length, out var linkTextAfter))
|
---|
113 | {
|
---|
114 | if ((firstMatch.StartString != "") || ((firstMatch.StartString == "") && (linkTextAfter.Length == 5) &&
|
---|
115 | ((index < 1) ||
|
---|
116 | ((index >= 1) && ((content[index - 1] == ' ') || (content[index - 1] == '\n'))))
|
---|
117 | &&
|
---|
118 | ((index + linkTextAfter.Length + 1 >= content.Length) ||
|
---|
119 | ((index + linkTextAfter.Length + 1 < content.Length) && ((content[index + linkTextAfter.Length] == ' ') ||
|
---|
120 | (content[index + linkTextAfter.Length] == '\r') || (content[index + linkTextAfter.Length] == '\n') ||
|
---|
121 | (content[index + linkTextAfter.Length] == ',') || (content[index + linkTextAfter.Length] == ';') ||
|
---|
122 | (content[index + linkTextAfter.Length] == '.'))))
|
---|
123 | ))
|
---|
124 | {
|
---|
125 | linkLength += linkTextAfter.Length;
|
---|
126 |
|
---|
127 | string linkText = content.Substring(index, linkLength);
|
---|
128 | tempRichTextBox.SelectionStart = selectionStart;
|
---|
129 | tempRichTextBox.SelectionLength = linkText.Length;
|
---|
130 | tempRichTextBox.SelectionFont = new Font(tempRichTextBox.SelectionFont.FontFamily, tempRichTextBox.SelectionFont.Size, FontStyle.Underline);
|
---|
131 | tempRichTextBox.SelectionColor = Color.LightBlue;
|
---|
132 | }
|
---|
133 | }
|
---|
134 | selectionStart += linkLength;
|
---|
135 | if (linkLength == 0) linkLength = 1;
|
---|
136 | contentStart = index + linkLength;
|
---|
137 | } while (true);
|
---|
138 |
|
---|
139 | //richTextBox.SelectionStart = 0;
|
---|
140 | richTextBox.SelectAll();
|
---|
141 | richTextBox.SelectedRtf = tempRichTextBox.Rtf;
|
---|
142 |
|
---|
143 | context.LoadContext(this);
|
---|
144 | interfaceUpdateEnabled = true;
|
---|
145 | ReadOnly = lastReadOnlyState;
|
---|
146 | }
|
---|
147 |
|
---|
148 | [DllImport("user32.dll")]
|
---|
149 | private static extern int SendMessage(IntPtr hwndLock, Int32 wMsg, Int32 wParam, Int32 lParam);
|
---|
150 |
|
---|
151 | const int WM_USER = 0x400;
|
---|
152 | const int EM_GETUNDONAME = WM_USER + 86;
|
---|
153 |
|
---|
154 | public enum UndoNameId
|
---|
155 | {
|
---|
156 | Unknown = 0,
|
---|
157 | Typing = 1,
|
---|
158 | Delete = 2,
|
---|
159 | DragDrop = 3,
|
---|
160 | Cut = 4,
|
---|
161 | Paste = 5,
|
---|
162 | AutoTable = 6
|
---|
163 | };
|
---|
164 |
|
---|
165 | public UndoNameId UndoActionId
|
---|
166 | {
|
---|
167 | get
|
---|
168 | {
|
---|
169 | if (!CanUndo) return UndoNameId.Unknown;
|
---|
170 | UndoNameId n;
|
---|
171 | n = (UndoNameId)SendMessage(Handle, EM_GETUNDONAME, 0, 0);
|
---|
172 | return n;
|
---|
173 | }
|
---|
174 | }
|
---|
175 |
|
---|
176 | public void UndoUnknownActions()
|
---|
177 | {
|
---|
178 | int i = 0;
|
---|
179 | while (CanUndo && (UndoActionId == UndoNameId.Unknown))
|
---|
180 | {
|
---|
181 | Undo();
|
---|
182 | i++;
|
---|
183 | if (i > 1000) break;
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | private void HideRichTextBoxLinks()
|
---|
188 | {
|
---|
189 | bool lastReadOnlyState = ReadOnly;
|
---|
190 | ReadOnly = false;
|
---|
191 | interfaceUpdateEnabled = false;
|
---|
192 | RichTextBoxContext context = new RichTextBoxContext();
|
---|
193 | context.SaveContext(this);
|
---|
194 | Rtf = PreviousRtf;
|
---|
195 | UndoUnknownActions();
|
---|
196 |
|
---|
197 | context.LoadContext(this);
|
---|
198 | interfaceUpdateEnabled = true;
|
---|
199 | ReadOnly = lastReadOnlyState;
|
---|
200 | }
|
---|
201 |
|
---|
202 | private void HandleMouseLeave(object sender, EventArgs e)
|
---|
203 | {
|
---|
204 | HideLinks();
|
---|
205 | }
|
---|
206 |
|
---|
207 | private void HandleLeave(object sender, EventArgs e)
|
---|
208 | {
|
---|
209 | HideLinks();
|
---|
210 | }
|
---|
211 |
|
---|
212 | private void HandleKeyDown(object sender, KeyEventArgs e)
|
---|
213 | {
|
---|
214 | if ((e.KeyCode == Keys.ControlKey) && !linksActive)
|
---|
215 | {
|
---|
216 | linksActive = true;
|
---|
217 | ShowRichTextBoxLinks(this);
|
---|
218 | }
|
---|
219 | }
|
---|
220 |
|
---|
221 | private void HandleKeyUp(object sender, KeyEventArgs e)
|
---|
222 | {
|
---|
223 | if ((e.KeyCode == Keys.ControlKey) && linksActive)
|
---|
224 | {
|
---|
225 | HideRichTextBoxLinks();
|
---|
226 | linksActive = false;
|
---|
227 | }
|
---|
228 | }
|
---|
229 |
|
---|
230 | private void HandleMouseClick(object sender, MouseEventArgs e)
|
---|
231 | {
|
---|
232 | if ((e.Button == MouseButtons.Left) && linksActive)
|
---|
233 | {
|
---|
234 | int linkStart;
|
---|
235 | int linkEnd;
|
---|
236 | int mousePointerCharIndex = GetCharIndexFromPosition(e.Location);
|
---|
237 | SelectionStart = mousePointerCharIndex;
|
---|
238 | SelectionLength = 1;
|
---|
239 | do
|
---|
240 | {
|
---|
241 | if (SelectionFont.Underline && (SelectionStart > 0) && (SelectedText[0] != '\n'))
|
---|
242 | {
|
---|
243 | SelectionStart -= 1;
|
---|
244 | }
|
---|
245 | else
|
---|
246 | {
|
---|
247 | linkStart = SelectionStart;
|
---|
248 | SelectionLength = 1;
|
---|
249 | if (SelectedText[0] == '\n') linkStart += 1;
|
---|
250 | else if (!SelectionFont.Underline || (SelectionStart > 0)) linkStart += 1;
|
---|
251 | break;
|
---|
252 | }
|
---|
253 | } while (true);
|
---|
254 |
|
---|
255 | SelectionStart = mousePointerCharIndex;
|
---|
256 | SelectionLength = 1;
|
---|
257 | do
|
---|
258 | {
|
---|
259 | if (SelectionFont.Underline && (SelectionStart < Text.Length) && (SelectedText[0] != '\n'))
|
---|
260 | {
|
---|
261 | SelectionStart += 1;
|
---|
262 | }
|
---|
263 | else
|
---|
264 | {
|
---|
265 | linkEnd = SelectionStart;
|
---|
266 | if (!SelectionFont.Underline || (SelectionStart < Text.Length)) linkEnd -= 1;
|
---|
267 | if ((linkEnd + 1) <= Text.Length) linkEnd += 1;
|
---|
268 | break;
|
---|
269 | }
|
---|
270 | } while (true);
|
---|
271 |
|
---|
272 | if (linkStart < linkEnd)
|
---|
273 | {
|
---|
274 | string link = Text.Substring(linkStart, linkEnd - linkStart);
|
---|
275 | if (link != "")
|
---|
276 | {
|
---|
277 | foreach (var linkMatch in LinkMatches)
|
---|
278 | {
|
---|
279 | if ((link.Length >= linkMatch.StartString.Length) && (
|
---|
280 | (linkMatch.CaseSensitive && (link.Substring(0, linkMatch.StartString.Length) == linkMatch.StartString)) ||
|
---|
281 | (!linkMatch.CaseSensitive && (link.ToLowerInvariant().Substring(0, linkMatch.StartString.Length) == linkMatch.StartString.ToLower()))))
|
---|
282 | {
|
---|
283 | string linkNumber = link.Substring(linkMatch.StartString.Length).Trim(new char[] { ' ', '#' });
|
---|
284 | if (int.TryParse(linkNumber, out int number))
|
---|
285 | {
|
---|
286 | linkMatch.ExecuteLinkAction(number);
|
---|
287 | break;
|
---|
288 | }
|
---|
289 | }
|
---|
290 | }
|
---|
291 |
|
---|
292 | LinkClick?.Invoke(link);
|
---|
293 | HideRichTextBoxLinks();
|
---|
294 | linksActive = false;
|
---|
295 | }
|
---|
296 | }
|
---|
297 | }
|
---|
298 | }
|
---|
299 |
|
---|
300 | public void ShowInWindow()
|
---|
301 | {
|
---|
302 | Form textForm = new Form
|
---|
303 | {
|
---|
304 | Name = "FormRichTextBox",
|
---|
305 | Width = 600,
|
---|
306 | Height = 300,
|
---|
307 | FormBorderStyle = FormBorderStyle.Sizable,
|
---|
308 | Text = Title,
|
---|
309 | Font = new Font(Font.FontFamily, Font.Size),
|
---|
310 | StartPosition = FormStartPosition.CenterScreen,
|
---|
311 | Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath),
|
---|
312 | };
|
---|
313 | RichTextBoxEx richTextBox = new RichTextBoxEx
|
---|
314 | {
|
---|
315 | ParentForm = textForm,
|
---|
316 | Dock = DockStyle.Fill,
|
---|
317 | Text = Text,
|
---|
318 | Title = Title,
|
---|
319 | LinkMatches = LinkMatches,
|
---|
320 | ReadOnly = ReadOnly,
|
---|
321 | LinkClick = delegate(string linkText) { LinkClick?.Invoke(linkText); }
|
---|
322 | };
|
---|
323 | textForm.Controls.Add(richTextBox);
|
---|
324 | textForm.Load += delegate
|
---|
325 | {
|
---|
326 | Theme.UseTheme(textForm);
|
---|
327 | DpiScaling.Apply(textForm);
|
---|
328 | new FormDimensions().Load(textForm, ParentForm);
|
---|
329 | richTextBox.ClearUndo();
|
---|
330 | };
|
---|
331 | textForm.FormClosing += delegate
|
---|
332 | {
|
---|
333 | new FormDimensions().Save(textForm, ParentForm);
|
---|
334 | };
|
---|
335 | textForm.Show();
|
---|
336 | }
|
---|
337 |
|
---|
338 | private void UpdateInterface()
|
---|
339 | {
|
---|
340 | tsmiUndo.Enabled = CanUndo && !ReadOnly;
|
---|
341 | tsmiRedo.Enabled = CanRedo && !ReadOnly;
|
---|
342 | tsmiCut.Enabled = (SelectionLength != 0) && !ReadOnly;
|
---|
343 | tsmiCopy.Enabled = SelectionLength != 0;
|
---|
344 | tsmiPaste.Enabled = Clipboard.ContainsText() && !ReadOnly;
|
---|
345 | tsmiDelete.Enabled = (SelectionLength != 0) && !ReadOnly;
|
---|
346 | tsmiSelectAll.Enabled = (TextLength > 0) && (SelectionLength < TextLength);
|
---|
347 | }
|
---|
348 |
|
---|
349 | private void HideLinks()
|
---|
350 | {
|
---|
351 | if (linksActive)
|
---|
352 | {
|
---|
353 | HideRichTextBoxLinks();
|
---|
354 | linksActive = false;
|
---|
355 | }
|
---|
356 | }
|
---|
357 |
|
---|
358 | public void AddContextMenu()
|
---|
359 | {
|
---|
360 | if (ContextMenuStrip == null)
|
---|
361 | {
|
---|
362 | ContextMenuStrip cms = new ContextMenuStrip { ShowImageMargin = false };
|
---|
363 |
|
---|
364 | tsmiUndo = new ToolStripMenuItem("Undo");
|
---|
365 | tsmiUndo.Click += (sender, e) =>
|
---|
366 | {
|
---|
367 | HideLinks();
|
---|
368 | RichTextBoxContext context = new RichTextBoxContext();
|
---|
369 | context.SaveContext(this);
|
---|
370 | UndoUnknownActions();
|
---|
371 | context.LoadContext(this);
|
---|
372 | if (CanUndo) Undo();
|
---|
373 | };
|
---|
374 | tsmiUndo.ShortcutKeys = Keys.Z | Keys.Control;
|
---|
375 | cms.Items.Add(tsmiUndo);
|
---|
376 |
|
---|
377 | tsmiRedo = new ToolStripMenuItem("Redo");
|
---|
378 | tsmiRedo.Click += (sender, e) =>
|
---|
379 | {
|
---|
380 | HideLinks();
|
---|
381 | if (CanRedo) Redo();
|
---|
382 | };
|
---|
383 | tsmiRedo.ShortcutKeys = Keys.Y | Keys.Control;
|
---|
384 | cms.Items.Add(tsmiRedo);
|
---|
385 |
|
---|
386 | cms.Items.Add(new ToolStripSeparator());
|
---|
387 |
|
---|
388 | tsmiCut = new ToolStripMenuItem("Cut");
|
---|
389 | tsmiCut.Click += (sender, e) =>
|
---|
390 | {
|
---|
391 | HideLinks();
|
---|
392 | Clipboard.SetText(SelectedText);
|
---|
393 | SelectedText = "";
|
---|
394 | };
|
---|
395 | tsmiCut.ShortcutKeys = Keys.X | Keys.Control;
|
---|
396 | cms.Items.Add(tsmiCut);
|
---|
397 |
|
---|
398 | tsmiCopy = new ToolStripMenuItem("Copy");
|
---|
399 | tsmiCopy.Click += (sender, e) =>
|
---|
400 | {
|
---|
401 | HideLinks();
|
---|
402 | Clipboard.SetText(SelectedText);
|
---|
403 | };
|
---|
404 | tsmiCopy.ShortcutKeys = Keys.C | Keys.Control;
|
---|
405 | cms.Items.Add(tsmiCopy);
|
---|
406 |
|
---|
407 | tsmiPaste = new ToolStripMenuItem("Paste");
|
---|
408 | tsmiPaste.Click += (sender, e) =>
|
---|
409 | {
|
---|
410 | HideLinks();
|
---|
411 | Paste();
|
---|
412 | };
|
---|
413 | tsmiPaste.ShortcutKeys = Keys.V | Keys.Control;
|
---|
414 | cms.Items.Add(tsmiPaste);
|
---|
415 |
|
---|
416 | tsmiDelete = new ToolStripMenuItem("Delete");
|
---|
417 | tsmiDelete.Click += (sender, e) => {
|
---|
418 | HideLinks();
|
---|
419 | SelectedText = "";
|
---|
420 | };
|
---|
421 | cms.Items.Add(tsmiDelete);
|
---|
422 |
|
---|
423 | cms.Items.Add(new ToolStripSeparator());
|
---|
424 |
|
---|
425 | tsmiSelectAll = new ToolStripMenuItem("Select All");
|
---|
426 | tsmiSelectAll.Click += (sender, e) => {
|
---|
427 | HideLinks();
|
---|
428 | SelectionStart = 0;
|
---|
429 | SelectionLength = Text.Length;
|
---|
430 | };
|
---|
431 | tsmiSelectAll.ShortcutKeys = Keys.A | Keys.Control;
|
---|
432 | cms.Items.Add(tsmiSelectAll);
|
---|
433 |
|
---|
434 | cms.Items.Add(new ToolStripSeparator());
|
---|
435 |
|
---|
436 | tsmiFind = new ToolStripMenuItem("Find");
|
---|
437 | tsmiFind.Click += (sender, e) => {
|
---|
438 | HideLinks();
|
---|
439 | if (FormFind == null)
|
---|
440 | {
|
---|
441 | FormFind = new FormFind();
|
---|
442 | FormFind.richTextBox = this;
|
---|
443 | FormFind.Owner = ParentForm;
|
---|
444 | }
|
---|
445 | FormFind.Show();
|
---|
446 | FormFind.BringToFront();
|
---|
447 | };
|
---|
448 | tsmiFind.ShortcutKeys = Keys.F | Keys.Control;
|
---|
449 | cms.Items.Add(tsmiFind);
|
---|
450 |
|
---|
451 | tsmiShowInWindow = new ToolStripMenuItem("Show in window");
|
---|
452 | tsmiShowInWindow.Click += (sender, e) =>
|
---|
453 | {
|
---|
454 | HideLinks();
|
---|
455 | ShowInWindow();
|
---|
456 | };
|
---|
457 | tsmiShowInWindow.ShortcutKeys = Keys.W | Keys.Control;
|
---|
458 | cms.Items.Add(tsmiShowInWindow);
|
---|
459 |
|
---|
460 | cms.Opening += delegate
|
---|
461 | {
|
---|
462 | HideLinks();
|
---|
463 | UpdateInterface();
|
---|
464 | };
|
---|
465 |
|
---|
466 | ContextMenuStrip = cms;
|
---|
467 | }
|
---|
468 | }
|
---|
469 | }
|
---|
470 |
|
---|
471 | public class RichTextBoxContext
|
---|
472 | {
|
---|
473 | private Point oldScrollPoint;
|
---|
474 | private Point oldScrollOffset;
|
---|
475 | private int oldStart;
|
---|
476 | private int oldLength;
|
---|
477 |
|
---|
478 | [DllImport("user32.dll")]
|
---|
479 | private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
|
---|
480 | [DllImport("user32.dll")]
|
---|
481 | private static extern int SendMessage(IntPtr hwndLock, Int32 wMsg, Int32 wParam, ref Point pt);
|
---|
482 | [DllImport("User32.dll")]
|
---|
483 | static extern int GetScrollPos(IntPtr hWnd, int nBar);
|
---|
484 | [DllImport("user32.dll")]
|
---|
485 | static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
|
---|
486 |
|
---|
487 | private const int WM_SETREDRAW = 0x0b;
|
---|
488 | const int WM_USER = 0x400;
|
---|
489 | const int EM_HIDESELECTION = WM_USER + 63;
|
---|
490 | const int EM_GETEVENTMASK = WM_USER + 59;
|
---|
491 | const int EM_SETEVENTMASK = WM_USER + 69;
|
---|
492 | const int EM_GETSCROLLPOS = WM_USER + 221;
|
---|
493 | const int EM_SETSCROLLPOS = WM_USER + 222;
|
---|
494 |
|
---|
495 | public void SaveContext(RichTextBox richTextBox)
|
---|
496 | {
|
---|
497 | SendMessage(richTextBox.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
|
---|
498 | oldScrollPoint = new Point();
|
---|
499 | SendMessage(richTextBox.Handle, EM_GETSCROLLPOS, 0, ref oldScrollPoint);
|
---|
500 | oldScrollOffset = richTextBox.AutoScrollOffset;
|
---|
501 | oldStart = richTextBox.SelectionStart;
|
---|
502 | oldLength = richTextBox.SelectionLength;
|
---|
503 | }
|
---|
504 |
|
---|
505 | public void LoadContext(RichTextBox richTextBox)
|
---|
506 | {
|
---|
507 | richTextBox.SelectionStart = oldStart;
|
---|
508 | richTextBox.SelectionLength = oldLength;
|
---|
509 | richTextBox.AutoScrollOffset = oldScrollOffset;
|
---|
510 | SendMessage(richTextBox.Handle, EM_SETSCROLLPOS, 0, ref oldScrollPoint);
|
---|
511 | SendMessage(richTextBox.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
|
---|
512 | richTextBox.Invalidate();
|
---|
513 | }
|
---|
514 | }
|
---|
515 |
|
---|
516 | public class LinkMatch
|
---|
517 | {
|
---|
518 | public string StartString;
|
---|
519 | public bool CaseSensitive;
|
---|
520 | public delegate bool MatchHandler(string inContent, int startIndex, out string outContent);
|
---|
521 | public delegate void LinkActionHandler(int number);
|
---|
522 | public event MatchHandler Match;
|
---|
523 | public event LinkActionHandler LinkAction;
|
---|
524 |
|
---|
525 | public LinkMatch(string startString, MatchHandler matchHandler, LinkActionHandler action)
|
---|
526 | {
|
---|
527 | StartString = startString;
|
---|
528 | Match = matchHandler;
|
---|
529 | LinkAction = action;
|
---|
530 | }
|
---|
531 |
|
---|
532 | public bool ExecuteMatch(string inContent, int startIndex, out string outContent)
|
---|
533 | {
|
---|
534 | return Match(inContent, startIndex, out outContent);
|
---|
535 | }
|
---|
536 |
|
---|
537 | public void ExecuteLinkAction(int number)
|
---|
538 | {
|
---|
539 | LinkAction?.Invoke(number);
|
---|
540 | }
|
---|
541 |
|
---|
542 | /// <summary>
|
---|
543 | /// Check text content for occurence number after start text of numeric link (e.g. ABC 12345 or ABC12345).
|
---|
544 | /// </summary>
|
---|
545 | /// <param name="content">Text after startString which should be checked</param>
|
---|
546 | /// <param name="linkText">Found second part of link after startString</param>
|
---|
547 | /// <returns></returns>
|
---|
548 | public static bool MatchNumber(string content, int startIndex, out string linkText)
|
---|
549 | {
|
---|
550 | linkText = "";
|
---|
551 | int linkLength = 0;
|
---|
552 | int numberStart = -1;
|
---|
553 |
|
---|
554 | // Try to connect to following number
|
---|
555 | numberStart = startIndex;
|
---|
556 | while (((content.Length - numberStart) >= 1) && ((content[numberStart] == ' ') || (content[numberStart] == '#')))
|
---|
557 | {
|
---|
558 | numberStart = numberStart + 1;
|
---|
559 | linkLength += 1;
|
---|
560 | }
|
---|
561 |
|
---|
562 | int i = 0;
|
---|
563 | while ((i < (content.Length - numberStart)) && (content[numberStart + i] >= '0') && (content[numberStart + i] <= '9'))
|
---|
564 | {
|
---|
565 | i++;
|
---|
566 | }
|
---|
567 | var number = content.Substring(numberStart, i);
|
---|
568 |
|
---|
569 | if (int.TryParse(number, out int intNumber))
|
---|
570 | {
|
---|
571 | linkLength += number.Length;
|
---|
572 | linkText = content.Substring(startIndex, linkLength);
|
---|
573 | }
|
---|
574 | return linkText != "";
|
---|
575 | }
|
---|
576 | }
|
---|
577 | } |
---|