forked from olejorgensen/CompactView
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRichTextBoxHelper.cs
526 lines (449 loc) · 19.3 KB
/
RichTextBoxHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
/**************************************************************************
Copyright (C) 2011-2014 Iván Costales Suárez
This file is part of CompactView.
CompactView is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CompactView is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CompactView. If not, see <http://www.gnu.org/licenses/>.
CompactView web site <http://sourceforge.net/p/compactview/>.
**************************************************************************/
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
/// <summary>
/// Helper to provide extended methods for the RichTextBox control
/// </summary>
public class RichTextBoxHelper
{
private RichTextBox richTextBox;
private BeforePagePrintDelegate beforePagePrintDelegate;
private int posIniChar;
/// <summary>
/// Get the total number of pages needed to print the document
/// </summary>
public int TotalPages { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether only calculation will be performed or whether the text will rendered also
/// </summary>
private bool MeasureOnly { get; set; }
public RichTextBoxHelper(RichTextBox richTextBox)
{
this.richTextBox = richTextBox;
}
public void Print(PrintType printType, Margins margins, BeforePagePrintDelegate beforePagePrintDelegate)
{
var doc = new PrintDocument();
doc.BeginPrint += new PrintEventHandler(Document_BeginPrint);
doc.EndPrint += new PrintEventHandler(Document_EndPrint);
doc.PrintPage += new PrintPageEventHandler(Document_PrintPage);
doc.DefaultPageSettings.Margins = margins ?? new Margins(20, 20, 20, 20);
doc.DocumentName = "CompactView document";
this.beforePagePrintDelegate = beforePagePrintDelegate;
try
{
switch (printType)
{
case PrintType.DirectPrint:
MeasureOnly = false;
doc.Print();
break;
case PrintType.ShowPrintDialog:
case PrintType.ShowPrintDialogWithTotalPages:
if (printType == PrintType.ShowPrintDialogWithTotalPages)
{
MeasureOnly = true;
PrintController controller = doc.PrintController;
doc.PrintController = new PreviewPrintController();
doc.Print(); // Only for calculate total pages
doc.PrintController = controller;
}
else
{
TotalPages = int.MaxValue;
}
MeasureOnly = false;
var printDlg = new PrintDialog
{
Document = doc,
AllowSomePages = true
};
printDlg.PrinterSettings.FromPage = 1;
printDlg.PrinterSettings.MinimumPage = 1;
printDlg.PrinterSettings.ToPage = TotalPages;
printDlg.PrinterSettings.MaximumPage = TotalPages;
if (printDlg.ShowDialog() == DialogResult.OK)
doc.Print();
break;
case PrintType.PrintPreview:
MeasureOnly = false;
var previewDlg = new PrintPreviewDialog
{
WindowState = FormWindowState.Maximized,
Document = doc,
ShowIcon = false
};
previewDlg.ShowDialog();
break;
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
private void Document_BeginPrint(object sender, PrintEventArgs e)
{
posIniChar = 0; // Start at the beginning of the text
TotalPages = 0; // Initialize pages counter
}
private void Document_PrintPage(object sender, PrintPageEventArgs e)
{
// Print RichTextBox content starting at the indicated character. Store the last character printed.
posIniChar = DoPrint(posIniChar, richTextBox.TextLength, e);
// Check for more pages
e.HasMorePages = posIniChar < richTextBox.TextLength;
TotalPages++;
}
private void Document_EndPrint(object sender, PrintEventArgs e)
{
// Clean up cached data
PrintDone();
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct CHARRANGE
{
public Int32 cpMin;
public Int32 cpMax;
}
[StructLayout(LayoutKind.Sequential)]
private struct FORMATRANGE
{
public IntPtr hdc;
public IntPtr hdcTarget;
public RECT rc;
public RECT rcPage;
public CHARRANGE chrg;
public static FORMATRANGE Set(PrintPageEventArgs e, int charIndexIni, int charIndexEnd)
{
FORMATRANGE fr;
// Specify device context of output device
fr.hdc = e.Graphics.GetHdc();
// Use the same DC for measuring and rendering
fr.hdcTarget = fr.hdc;
// Specify the area inside page margins to render and print
fr.rc.top = HundredthInchToTwips(e.MarginBounds.Top);
fr.rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom);
fr.rc.left = HundredthInchToTwips(e.MarginBounds.Left);
fr.rc.right = HundredthInchToTwips(e.MarginBounds.Right);
// Specify the page area
fr.rcPage.top = HundredthInchToTwips(e.PageBounds.Top);
fr.rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom);
fr.rcPage.left = HundredthInchToTwips(e.PageBounds.Left);
fr.rcPage.right = HundredthInchToTwips(e.PageBounds.Right);
// Specify characters to print
fr.chrg.cpMin = charIndexIni;
fr.chrg.cpMax = charIndexEnd;
return fr;
}
}
[DllImport("user32.dll")]
private static extern Int32 SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, IntPtr lParam);
[DllImport("user32.dll")]
private extern static Int32 SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, ref Point lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, Orientation nBar);
[DllImport("user32.dll")]
public static extern int SetScrollPos(IntPtr hWnd, Orientation nBar, int nPos, bool bRedraw);
// Windows Messages defines
private const Int32 WM_USER = 0x400;
private const Int32 EM_FORMATRANGE = WM_USER + 57;
/// <summary>
/// Calculate or render the contents of our RichTextBox for printing
/// </summary>
/// <param name="e">The PrintPageEventArgs object from the PrintPage event</param>
/// <param name="posIniChar">Index of first character to be printed</param>
/// <param name="posEndChar">Index of last character to be printed</param>
/// <returns>Index of last character that fitted on the page, plus 1</returns>
public int DoPrint(int posIniChar, int posEndChar, PrintPageEventArgs e)
{
// Fill in the FORMATRANGE struct
var fr = FORMATRANGE.Set(e, posIniChar, posEndChar);
// Allocate memory for the FORMATRANGE struct and copy this to the memory
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr));
Marshal.StructureToPtr(fr, lParam, false);
// Zero wParam means measure, non-zero wParam means render
Int32 wParam = (MeasureOnly ? 0 : 1);
// Measure to get end char index needed for call delegate before print
if (beforePagePrintDelegate != null && !MeasureOnly)
{
int nextIndex = SendMessage(richTextBox.Handle, EM_FORMATRANGE, 0, lParam);
beforePagePrintDelegate(posIniChar, nextIndex - 1, e);
}
// Send the Win32 message for printing
int res = SendMessage(richTextBox.Handle, EM_FORMATRANGE, wParam, lParam);
// Free allocated memory
Marshal.FreeCoTaskMem(lParam);
// Release the device context handle obtained by a previous call
e.Graphics.ReleaseHdc(fr.hdc);
return res;
}
/// <summary>
/// Convert between 1/100 inch (unit used by the .NET framework) and twips (1/1440 inch, used by Win32 API calls)
/// </summary>
/// <param name="n">Value in 1/100 inch</param>
/// <returns>Value in twips (1/1440 inch)</returns>
private static Int32 HundredthInchToTwips(int n)
{
return (Int32)(n * 14.4);
}
/// <summary>
/// Free cached data after printing
/// </summary>
public void PrintDone()
{
var lParam = new IntPtr(0);
SendMessage(richTextBox.Handle, EM_FORMATRANGE, 0, lParam);
}
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
private struct CHARFORMAT2
{
public int cbSize;
public UInt32 dwMask;
public UInt32 dwEffects;
public Int32 yHeight;
public Int32 yOffset;
public Int32 crTextColor;
public byte bCharSet;
public byte bPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string szFaceName;
public short wWeight;
public short sSpacing;
public int crBackColor;
public int lcid;
public int dwReserved;
public short sStyle;
public short wKerning;
public byte bUnderlineType;
public byte bAnimation;
public byte bRevAuthor;
public byte bReserved1;
}
// Windows Messages defines
private const Int32 EM_SETCHARFORMAT = WM_USER + 68;
// Constants for EM_SETCHARFORMAT/EM_GETCHARFORMAT
private const Int32 SCF_SELECTION = 0x0001;
private const Int32 SCF_WORD = 0x0002;
// Constants for CHARFORMAT2 member dwMask
private const UInt32 CFM_BOLD = 0x00000001;
private const UInt32 CFM_ITALIC = 0x00000002;
private const UInt32 CFM_UNDERLINE = 0x00000004;
private const UInt32 CFM_STRIKEOUT = 0x00000008;
private const UInt32 CFM_FACE = 0x20000000;
private const UInt32 CFM_SIZE = 0x80000000;
private const UInt32 CFM_COLOR = 0x40000000;
private const UInt32 CFM_BACKCOLOR = 0x4000000;
// Constants for CHARFORMAT2 member dwEffects
private const UInt32 CFE_BOLD = 0x00000001;
private const UInt32 CFE_ITALIC = 0x00000002;
private const UInt32 CFE_UNDERLINE = 0x00000004;
private const UInt32 CFE_STRIKEOUT = 0x00000008;
/// <summary>
/// Sets the font name for the selected text of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="fontName">Name of the font to use</param>
/// <returns>Returns true on success, false on failure</returns>
public static bool SelectionFontName(RichTextBox richTextBox, string fontName)
{
var cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_FACE;
cf.szFaceName = fontName.Length > 32 ? fontName.Remove(32) : fontName;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
int res = SendMessage(richTextBox.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
return (res == 0);
}
/// <summary>
/// Sets the font size for the selected text of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="fontSize">Font size to use</param>
/// <returns>Returns true on success, false on failure</returns>
public static bool SelectionFontSize(RichTextBox richTextBox, int fontSize)
{
var cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_SIZE;
cf.yHeight = fontSize * 20; // yHeight is in 1/20 pt
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
int res = SendMessage(richTextBox.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
return (res == 0);
}
/// <summary>
/// Sets the font style for the selected text of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="fontStyle">Font style to apply to selected text</param>
/// <returns>Returns true on success, false on failure</returns>
public static bool SelectionFontStyle(RichTextBox richTextBox, FontStyle fontStyle)
{
uint mask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
uint effect = 0;
if ((fontStyle & FontStyle.Bold) != 0)
effect |= CFE_BOLD;
if ((fontStyle & FontStyle.Italic) != 0)
effect |= CFE_ITALIC;
if ((fontStyle & FontStyle.Underline) != 0)
effect |= CFE_UNDERLINE;
if ((fontStyle & FontStyle.Strikeout) != 0)
effect |= CFE_STRIKEOUT;
return SetSelectionStyle(richTextBox, Color.Empty, Color.Empty, mask, effect);
}
/// <summary>
/// Sets the font color for the selected text of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="color">Color to apply</param>
/// <returns>Returns true on success, false on failure</returns>
public static bool SelectionFontColor(RichTextBox richTextBox, Color color)
{
return SetSelectionStyle(richTextBox, color, Color.Empty, CFM_COLOR, 0);
}
/// <summary>
/// Sets the font color for the word in the selected point of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="color">Color to apply</param>
/// <returns>Returns true on success, false on failure</returns>
public static bool WordFontColor(RichTextBox richTextBox, Color color)
{
var cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.crTextColor = ColorTranslator.ToWin32(color);
cf.crBackColor = ColorTranslator.ToWin32(Color.Empty);
cf.dwMask = CFM_COLOR;
cf.dwEffects = 0;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
int res = SendMessage(richTextBox.Handle, EM_SETCHARFORMAT, SCF_SELECTION | SCF_WORD, lParam);
return (res == 0);
}
/// <summary>
/// Sets the background color for the selected text of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="color">Color to apply</param>
/// <returns>Returns true on success, false on failure</returns>
public static bool SelectionBackColor(RichTextBox richTextBox, Color color)
{
return SetSelectionStyle(richTextBox, Color.Empty, color, CFM_BACKCOLOR, 0);
}
/// <summary>
/// Sets the font style for the selected text of the RichTextBox
/// </summary>
/// <param name="richTextBox">RichTextBox control</param>
/// <param name="mask">Mask to determine which styles will be modified</param>
/// <param name="effect">New values for the styles</param>
/// <returns>Returns true on success, false on failure</returns>
private static bool SetSelectionStyle(RichTextBox richTextBox, Color fontColor, Color backColor, UInt32 mask, UInt32 effect)
{
var cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.crTextColor = ColorTranslator.ToWin32(fontColor);
cf.crBackColor = ColorTranslator.ToWin32(backColor);
cf.dwMask = mask;
cf.dwEffects = effect;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
int res = SendMessage(richTextBox.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
return (res == 0);
}
public static void SetRedraw(RichTextBox richTextBox, bool enableRedraw)
{
const int WM_SETREDRAW = 0x000B;
SendMessage(richTextBox.Handle, WM_SETREDRAW, enableRedraw ? 1 : 0, IntPtr.Zero);
}
public static int GetFirstVisibleCharIndex(RichTextBox richTextBox)
{
const int EM_CHARFROMPOS = 0x00D7;
var point = new Point(1, 1);
return SendMessage(richTextBox.Handle, EM_CHARFROMPOS, 0, ref point);
}
public static int GetLastVisibleCharIndex(RichTextBox richTextBox)
{
const int EM_CHARFROMPOS = 0x00D7;
var point = new Point(richTextBox.ClientRectangle.Right - 1, richTextBox.ClientRectangle.Bottom - 1);
return SendMessage(richTextBox.Handle, EM_CHARFROMPOS, 0, ref point);
}
public static int GetFirstVisibleLine(RichTextBox richTextBox)
{
const int EM_GETFIRSTVISIBLELINE = 0x00CE;
return SendMessage(richTextBox.Handle, EM_GETFIRSTVISIBLELINE, 0, IntPtr.Zero);
}
public static int GetVisibleLines(RichTextBox richTextBox)
{
Size size = TextRenderer.MeasureText("H", richTextBox.Font);
return (int)Math.Ceiling((double)richTextBox.ClientRectangle.Height / size.Height);
}
public static void HideSelection(RichTextBox richTextBox, bool hide)
{
const int WM_USER = 0x400;
const int EM_HIDESELECTION = WM_USER + 63;
SendMessage(richTextBox.Handle, EM_HIDESELECTION, hide ? 1 : 0, IntPtr.Zero);
}
public static void ScrollLines(RichTextBox richTextBox, int linesToScroll)
{
const int EM_LINESCROLL = 0x00B6;
SendMessage(richTextBox.Handle, EM_LINESCROLL, 0, (IntPtr)linesToScroll);
}
public static int GetHScroll(RichTextBox richTextBox)
{
return GetScrollPos(richTextBox.Handle, Orientation.Horizontal);
}
public static void SetHScroll(RichTextBox richTextBox, int position)
{
const int SB_THUMBPOSITION = 4;
const int GWL_STYLE = (-16);
const UInt32 WS_HSCROLL = 0x100000;
const int WM_HSCROLL = 0x0114;
int style = GetWindowLong(richTextBox.Handle, GWL_STYLE);
bool hScroll = (style & WS_HSCROLL) != 0;
if (hScroll)
{
SetScrollPos(richTextBox.Handle, Orientation.Horizontal, position, true);
SendMessage(richTextBox.Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * position, IntPtr.Zero);
}
}
public static int GetEventMask(RichTextBox richTextBox)
{
const int EM_GETEVENTMASK = WM_USER + 59;
return SendMessage(richTextBox.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
}
public static void SetEventMask(RichTextBox richTextBox, int mask)
{
const int EM_SETEVENTMASK = WM_USER + 69;
SendMessage(richTextBox.Handle, EM_SETEVENTMASK, 0, (IntPtr)mask);
}
}