using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Printing; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; //using WhelkMud.Interfaces; namespace AyaNova { #region Control enums /// /// Specifies the style of underline that should be /// applied to the text. /// public enum UnderlineStyle { /// /// No underlining. /// None = 0, /// /// Standard underlining across all words. /// Normal = 1, /// /// Standard underlining broken between words. /// Word = 2, /// /// Double line underlining. /// Double = 3, /// /// Dotted underlining. /// Dotted = 4, /// /// Dashed underlining. /// Dash = 5, /// /// Dash-dot ("-.-.") underlining. /// DashDot = 6, /// /// Dash-dot-dot ("-..-..") underlining. /// DashDotDot = 7, /// /// Wave underlining (like spelling mistakes in MS Word). /// Wave = 8, /// /// Extra thick standard underlining. /// Thick = 9, /// /// Extra thin standard underlining. /// HairLine = 10, /// /// Double thickness wave underlining. /// DoubleWave = 11, /// /// Thick wave underlining. /// HeavyWave = 12, /// /// Extra long dash underlining. /// LongDash = 13 } /// /// Specifies the color of underline that should be /// applied to the text. /// /// /// I named these colors by their appearance, so some /// of them might not be what you expect. Please email /// me if you feel one should be changed. /// public enum UnderlineColor { /// Black. Black = 0x00, /// None. None = 0x00, /// Blue. Blue = 0x10, /// Cyan. Cyan = 0x20, /// Lime green. LimeGreen = 0x30, /// Magenta. Magenta = 0x40, /// Red. Red = 0x50, /// Yellow. Yellow = 0x60, /// White. White = 0x70, /// DarkBlue. DarkBlue = 0x80, /// DarkCyan. DarkCyan = 0x90, /// Green. Green = 0xA0, /// Dark magenta. DarkMagenta = 0xB0, /// Brown. Brown = 0xC0, /// Olive green. OliveGreen = 0xD0, /// Dark gray. DarkGray = 0xE0, /// Gray. Gray = 0xF0 } // Enum for possible RTF colors public enum RtfColor { Black, Maroon, Green, Olive, Navy, Purple, Teal, Gray, Silver, Red, Lime, Yellow, Blue, Fuchsia, Aqua, White } #endregion public class ExtendedRichTextBox : RichTextBox { #region Private fields and constructors private int _Updating = 0; private int _OldEventMask = 0; private ToolTip myToolTip; public ExtendedRichTextBox() { //required for my own link code this.DetectUrls = true; this.myToolTip = new ToolTip(); this.ContextMenu = new ContextMenu(); // this.LinkClicked += new LinkClickedEventHandler(MudRichTextBox_LinkClicked); // Initialize default text and background colors textColor = RtfColor.Black; highlightColor = RtfColor.White; // Initialize the dictionary mapping color codes to definitions rtfColor = new Dictionary(); rtfColor[RtfColor.Aqua] = RtfColorDef.Aqua; rtfColor[RtfColor.Black] = RtfColorDef.Black; rtfColor[RtfColor.Blue] = RtfColorDef.Blue; rtfColor[RtfColor.Fuchsia] = RtfColorDef.Fuchsia; rtfColor[RtfColor.Gray] = RtfColorDef.Gray; rtfColor[RtfColor.Green] = RtfColorDef.Green; rtfColor[RtfColor.Lime] = RtfColorDef.Lime; rtfColor[RtfColor.Maroon] = RtfColorDef.Maroon; rtfColor[RtfColor.Navy] = RtfColorDef.Navy; rtfColor[RtfColor.Olive] = RtfColorDef.Olive; rtfColor[RtfColor.Purple] = RtfColorDef.Purple; rtfColor[RtfColor.Red] = RtfColorDef.Red; rtfColor[RtfColor.Silver] = RtfColorDef.Silver; rtfColor[RtfColor.Teal] = RtfColorDef.Teal; rtfColor[RtfColor.White] = RtfColorDef.White; rtfColor[RtfColor.Yellow] = RtfColorDef.Yellow; // Initialize the dictionary mapping default Framework font families to // RTF font families rtfFontFamily = new Dictionary(); rtfFontFamily[FontFamily.GenericMonospace.Name] = RtfFontFamilyDef.Modern; rtfFontFamily[FontFamily.GenericSansSerif.Name] = RtfFontFamilyDef.Swiss; rtfFontFamily[FontFamily.GenericSerif.Name] = RtfFontFamilyDef.Roman; rtfFontFamily[FF_UNKNOWN] = RtfFontFamilyDef.Unknown; // Get the horizontal and vertical resolutions at which the object is // being displayed using (Graphics _graphics = this.CreateGraphics()) { xDpi = _graphics.DpiX; yDpi = _graphics.DpiY; } HideCaret(Handle); } #endregion #region Elements required to create an RTF document /* RTF HEADER * ---------- * * \rtf[N] - For text to be considered to be RTF, it must be enclosed in this tag. * rtf1 is used because the RichTextBox conforms to RTF Specification * version 1. * \ansi - The character set. * \ansicpg[N] - Specifies that unicode characters might be embedded. ansicpg1252 * is the default used by Windows. * \deff[N] - The default font. \deff0 means the default font is the first font * found. * \deflang[N] - The default language. \deflang1033 specifies US English. * */ private const string RTF_HEADER = @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033"; /* RTF DOCUMENT AREA * ----------------- * * \viewkind[N] - The type of view or zoom level. \viewkind4 specifies normal view. * \uc[N] - The number of bytes corresponding to a Unicode character. * \pard - Resets to default paragraph properties * \cf[N] - Foreground color. \cf1 refers to the color at index 1 in * the color table * \f[N] - Font number. \f0 refers to the font at index 0 in the font * table. * \fs[N] - Font size in half-points. * */ private const string RTF_DOCUMENT_PRE = @"\viewkind4\uc1\pard\cf1\f0\fs20"; private const string RTF_DOCUMENT_POST = @"\cf0\fs17}"; private string RTF_IMAGE_POST = @"}"; // Represents an unknown font family private const string FF_UNKNOWN = "UNKNOWN"; // Specifies the flags/options for the unmanaged call to the GDI+ method // Metafile.EmfToWmfBits(). private enum EmfToWmfBitsFlags { // Use the default conversion EmfToWmfBitsFlagsDefault = 0x00000000, // Embedded the source of the EMF metafiel within the resulting WMF // metafile EmfToWmfBitsFlagsEmbedEmf = 0x00000001, // Place a 22-byte header in the resulting WMF file. The header is // required for the metafile to be considered placeable. EmfToWmfBitsFlagsIncludePlaceable = 0x00000002, // Don't simulate clipping by using the XOR operator. EmfToWmfBitsFlagsNoXORClip = 0x00000004 }; #endregion #region Windows API private const int WM_VSCROLL = 0x115; private const int WM_HSCROLL = 0x114; private const int SB_LINEUP = 0; private const int SB_LINEDOWN = 1; private const int SB_PAGEUP = 2; private const int SB_PAGEDOWN = 3; private const int SB_THUMBPOSITION = 4; private const int SB_THUMBTRACK = 5; private const int SB_TOP = 6; private const int SB_BOTTOM = 7; private const int SB_ENDSCROLL = 8; private const int WM_SETREDRAW = 0x0B; private const int EM_SETEVENTMASK = 0x0431; private const int EM_SETCHARFORMAT = 0x0444; private const int EM_GETCHARFORMAT = 0x043A; private const int EM_GETPARAFORMAT = 0x043D; private const int EM_SETPARAFORMAT = 0x0447; private const int EM_SETTYPOGRAPHYOPTIONS = 0x04CA; private const int CFM_UNDERLINETYPE = 0x800000; private const int CFM_BACKCOLOR = 0x4000000; private const int CFE_AUTOBACKCOLOR = 0x4000000; private const int SCF_SELECTION = 0x01; private const int PFM_ALIGNMENT = 0x08; private const int TO_ADVANCEDTYPOGRAPHY = 0x01; // These are the scroll bar constants. private const int SBS_HORIZ = 0; private const int SBS_VERT = 1; // Get which bits. private const int SIF_RANGE = 0x0001; private const int SIF_PAGE = 0x0002; private const int SIF_POS = 0x0004; private const int SIF_DISABLENOSCROLL = 0x0008; private const int SIF_TRACKPOS = 0x0010; private const int SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS); [DllImport("user32", CharSet = CharSet.Auto)] private static extern int SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); [DllImport("user32", CharSet = CharSet.Auto)] private static extern int SendMessage(HandleRef hWnd, int msg, int wParam, ref CHARFORMAT2 lParam); [DllImport("user32", CharSet = CharSet.Auto)] private static extern int SendMessage(HandleRef hWnd, int msg, int wParam, ref PARAFORMAT2 lParam); [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)] private static extern int SetWindowTheme( HandleRef hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pszSubAppName, [MarshalAs(UnmanagedType.LPWStr)] string pszSubIdList); /// /// The HideCaret function removes the caret from the screen. /// [DllImport("user32.dll")] protected static extern bool HideCaret(IntPtr hWnd); /// /// This will find the scroll position of the specified window. /// /// the window to send the message to /// the number of the sroll bar to look at /// [DllImport("user32", CharSet = CharSet.Auto)] private static extern int GetScrollInfo(HandleRef hWnd, int nBar, ref SCROLLINFO info); /// /// Contains information about character formatting in a rich edit control. /// /// works with all Rich Edit versions. [StructLayout(LayoutKind.Sequential)] private struct CHARFORMAT { public int cbSize; public uint dwMask; public uint dwEffects; public int yHeight; public int yOffset; public int crTextColor; public byte bCharSet; public byte bPitchAndFamily; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public char[] szFaceName; } /// /// Contains information about character formatting in a rich edit control. /// /// requires Rich Edit 2.0. [StructLayout(LayoutKind.Sequential)] private struct CHARFORMAT2 { public int cbSize; public uint dwMask; public uint dwEffects; public int yHeight; public int yOffset; public int crTextColor; public byte bCharSet; public byte bPitchAndFamily; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public char[] szFaceName; public short wWeight; public short sSpacing; public int crBackColor; public int LCID; public uint dwReserved; public short sStyle; public short wKerning; public byte bUnderlineType; public byte bAnimation; public byte bRevAuthor; } /// /// Contains information about paragraph formatting in a rich edit control. /// /// works with all Rich Edit versions. [StructLayout(LayoutKind.Sequential)] private struct PARAFORMAT { public int cbSize; public uint dwMask; public short wNumbering; public short wReserved; public int dxStartIndent; public int dxRightIndent; public int dxOffset; public short wAlignment; public short cTabCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public int[] rgxTabs; } /// /// Contains information about paragraph formatting in a rich edit control. /// /// requires Rich Edit 2.0. [StructLayout(LayoutKind.Sequential)] private struct PARAFORMAT2 { public int cbSize; public uint dwMask; public short wNumbering; public short wReserved; public int dxStartIndent; public int dxRightIndent; public int dxOffset; public short wAlignment; public short cTabCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public int[] rgxTabs; public int dySpaceBefore; public int dySpaceAfter; public int dyLineSpacing; public short sStyle; public byte bLineSpacingRule; public byte bOutlineLevel; public short wShadingWeight; public short wShadingStyle; public short wNumberingStart; public short wNumberingStyle; public short wNumberingTab; public short wBorderSpace; public short wBorderWidth; public short wBorders; } /// /// Contains information the scroll bar positions. /// /// works with all Rich Edit versions. [StructLayout(LayoutKind.Sequential)] private struct SCROLLINFO { public int cbSize; public int fMask; public int nMin; public int nMax; public int nPage; public int nPos; public int nTrackPos; } // Definitions for colors in an RTF document private struct RtfColorDef { public const string Black = @"\red0\green0\blue0"; public const string Maroon = @"\red128\green0\blue0"; public const string Green = @"\red0\green128\blue0"; public const string Olive = @"\red128\green128\blue0"; public const string Navy = @"\red0\green0\blue128"; public const string Purple = @"\red128\green0\blue128"; public const string Teal = @"\red0\green128\blue128"; public const string Gray = @"\red128\green128\blue128"; public const string Silver = @"\red192\green192\blue192"; public const string Red = @"\red255\green0\blue0"; public const string Lime = @"\red0\green255\blue0"; public const string Yellow = @"\red255\green255\blue0"; public const string Blue = @"\red0\green0\blue255"; public const string Fuchsia = @"\red255\green0\blue255"; public const string Aqua = @"\red0\green255\blue255"; public const string White = @"\red255\green255\blue255"; } // Control words for RTF font families private struct RtfFontFamilyDef { public const string Unknown = @"\fnil"; public const string Roman = @"\froman"; public const string Swiss = @"\fswiss"; public const string Modern = @"\fmodern"; public const string Script = @"\fscript"; public const string Decor = @"\fdecor"; public const string Technical = @"\ftech"; public const string BiDirect = @"\fbidi"; } #endregion #region Property: SelectionUnderlineStyle /// /// Gets or sets the underline style to apply to the current selection or insertion point. /// /// A that represents the underline style to /// apply to the current text selection or to text entered after the insertion point. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public UnderlineStyle SelectionUnderlineStyle { get { CHARFORMAT2 fmt = new CHARFORMAT2(); fmt.cbSize = Marshal.SizeOf(fmt); // Get the underline style SendMessage(new HandleRef(this, Handle), EM_GETCHARFORMAT, SCF_SELECTION, ref fmt); if ((fmt.dwMask & CFM_UNDERLINETYPE) == 0) { return UnderlineStyle.None; } else { byte style = (byte)(fmt.bUnderlineType & 0x0F); return (UnderlineStyle)style; } } set { // Ensure we don't alter the color UnderlineColor color = SelectionUnderlineColor; // Ensure we don't show it if it shouldn't be shown if (value == UnderlineStyle.None) color = UnderlineColor.Black; // Set the underline type CHARFORMAT2 fmt = new CHARFORMAT2(); fmt.cbSize = Marshal.SizeOf(fmt); fmt.dwMask = CFM_UNDERLINETYPE; fmt.bUnderlineType = (byte)((byte)value | (byte)color); SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref fmt); } } #endregion #region Property: SelectionUnderlineColor /// /// Gets or sets the underline color to apply to the current selection or insertion point. /// /// A that represents the underline color to /// apply to the current text selection or to text entered after the insertion point. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public UnderlineColor SelectionUnderlineColor { get { CHARFORMAT2 fmt = new CHARFORMAT2(); fmt.cbSize = Marshal.SizeOf(fmt); // Get the underline color SendMessage(new HandleRef(this, Handle), EM_GETCHARFORMAT, SCF_SELECTION, ref fmt); if ((fmt.dwMask & CFM_UNDERLINETYPE) == 0) { return UnderlineColor.None; } else { byte style = (byte)(fmt.bUnderlineType & 0xF0); return (UnderlineColor)style; } } set { // If the an underline color of "None" is specified, remove underline effect if (value == UnderlineColor.None) { SelectionUnderlineStyle = UnderlineStyle.None; } else { // Ensure we don't alter the style UnderlineStyle style = SelectionUnderlineStyle; // Ensure we don't show it if it shouldn't be shown if (style == UnderlineStyle.None) value = UnderlineColor.Black; // Set the underline color CHARFORMAT2 fmt = new CHARFORMAT2(); fmt.cbSize = Marshal.SizeOf(fmt); fmt.dwMask = CFM_UNDERLINETYPE; fmt.bUnderlineType = (byte)((byte)style | (byte)value); SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref fmt); } } } #endregion #region Method: BeginUpdate /// /// Maintains performance while updating. /// /// /// /// It is recommended to call this method before doing /// any major updates that you do not wish the user to /// see. Remember to call EndUpdate when you are finished /// with the update. Nested calls are supported. /// /// /// Calling this method will prevent redrawing. It will /// also setup the event mask of the underlying richedit /// control so that no events are sent. /// /// public void BeginUpdate() { // Deal with nested calls _Updating++; if (_Updating > 1) return; // Prevent the control from raising any events _OldEventMask = SendMessage(new HandleRef(this, Handle), EM_SETEVENTMASK, 0, 0); // Prevent the control from redrawing itself SendMessage(new HandleRef(this, Handle), WM_SETREDRAW, 0, 0); } #endregion #region Method: EndUpdate /// /// Resumes drawing and event handling. /// /// /// This method should be called every time a call is made /// made to BeginUpdate. It resets the event mask to it's /// original value and enables redrawing of the control. /// public void EndUpdate() { // Deal with nested calls _Updating--; if (_Updating > 0) return; // Allow the control to redraw itself SendMessage(new HandleRef(this, Handle), WM_SETREDRAW, 1, 0); // Allow the control to raise event messages SendMessage(new HandleRef(this, Handle), EM_SETEVENTMASK, 0, _OldEventMask); } #endregion #region ScrollBarDetails [DllImport("user32.dll")] private static extern int SendMessage(IntPtr hwndLock, Int32 wMsg, Int32 wParam, ref Point pt); public Point ScrollPos { get { const int EM_GETSCROLLPOS = 0x0400 + 221; Point pt = new Point(); SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref pt); return pt; } set { const int EM_SETSCROLLPOS = 0x0400 + 222; SendMessage(this.Handle, EM_SETSCROLLPOS, 0, ref value); } } /// /// This scrolls the scroll bar down to the bottom of the window. /// public void ScrollToBottom() { SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_BOTTOM, 0); } /// /// Scrolls the data up one page. /// public void ScrollPageUp() { SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_PAGEUP, 0); } /// /// Scrolls the data down one page. /// public void ScrollPageDown() { SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_PAGEDOWN, 0); } /// /// Scrolls the data up one page. /// public void ScrollLineUp(int num) { for (int i = 0; i < num; i++) { SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_LINEUP, 0); } } /// /// Scrolls the data down one page. /// public void ScrollLineDown(int num) { for (int i = 0; i < num; i++) { SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_LINEDOWN, 0); } } /// /// This is the information associated with the scroll bar, showing it's position /// and other details. /// /// the scroll bar information public ScrollBarInformation VerticalScrollInformation { get { SCROLLINFO info = new SCROLLINFO(); info.cbSize = Marshal.SizeOf(info); info.fMask = SIF_ALL; int ret = GetScrollInfo(new HandleRef(this, Handle), SBS_VERT, ref info); return new ScrollBarInformation(info.nMin, info.nMax, info.nPage, info.nPos, info.nTrackPos); } } #endregion #region Rtf Privates // Not used in this application. Descriptions can be found with documentation // of Windows GDI function SetMapMode private const int MM_TEXT = 1; private const int MM_LOMETRIC = 2; private const int MM_HIMETRIC = 3; private const int MM_LOENGLISH = 4; private const int MM_HIENGLISH = 5; private const int MM_TWIPS = 6; // Ensures that the metafile maintains a 1:1 aspect ratio private const int MM_ISOTROPIC = 7; // Allows the x-coordinates and y-coordinates of the metafile to be adjusted // independently private const int MM_ANISOTROPIC = 8; // The number of hundredths of millimeters (0.01 mm) in an inch // For more information, see GetImagePrefix() method. private const int HMM_PER_INCH = 2540; // The number of twips in an inch // For more information, see GetImagePrefix() method. private const int TWIPS_PER_INCH = 1440; // The default text color private RtfColor textColor; // The default text background color private RtfColor highlightColor; // Dictionary that maps color enums to RTF color codes private Dictionary rtfColor; // Dictionary that mapas Framework font families to RTF font families private Dictionary rtfFontFamily; // The horizontal resolution at which the control is being displayed private float xDpi; // The vertical resolution at which the control is being displayed private float yDpi; #endregion #region Append RTF or Text to RichTextBox Contents /// /// Assumes the string passed as a paramter is valid RTF text and attempts /// to append it as RTF to the content of the control. /// /// public void AppendRtf(string _rtf) { // Move caret to the end of the text this.Select(this.TextLength, 0); // Since SelectedRtf is null, this will append the string to the // end of the existing RTF this.SelectedRtf = _rtf; } /// /// Assumes that the string passed as a parameter is valid RTF text and /// attempts to insert it as RTF into the content of the control. /// /// /// NOTE: The text is inserted wherever the caret is at the time of the call, /// and if any text is selected, that text is replaced. /// /// public void InsertRtf(string _rtf) { this.SelectedRtf = _rtf; } /// /// Appends the text using the current font, text, and highlight colors. /// /// public void AppendTextAsRtf(string _text) { AppendTextAsRtf(_text, this.Font); } /// /// Appends the text using the given font, and current text and highlight /// colors. /// /// /// public void AppendTextAsRtf(string _text, Font _font) { AppendTextAsRtf(_text, _font, textColor); } /// /// Appends the text using the given font and text color, and the current /// highlight color. /// /// /// /// public void AppendTextAsRtf(string _text, Font _font, RtfColor _textColor) { AppendTextAsRtf(_text, _font, _textColor, highlightColor); } /// /// Appends the text using the given font, text, and highlight colors. Simply /// moves the caret to the end of the RichTextBox's text and makes a call to /// insert. /// /// /// /// /// public void AppendTextAsRtf(string _text, Font _font, RtfColor _textColor, RtfColor _backColor) { // Move carret to the end of the text this.Select(this.TextLength, 0); InsertTextAsRtf(_text, _font, _textColor, _backColor); } #endregion #region RTF Insert Plain Text /// /// Inserts the text using the current font, text, and highlight colors. /// /// public void InsertTextAsRtf(string _text) { InsertTextAsRtf(_text, this.Font); } /// /// Inserts the text using the given font, and current text and highlight /// colors. /// /// /// public void InsertTextAsRtf(string _text, Font _font) { InsertTextAsRtf(_text, _font, textColor); } /// /// Inserts the text using the given font and text color, and the current /// highlight color. /// /// /// /// public void InsertTextAsRtf(string _text, Font _font, RtfColor _textColor) { InsertTextAsRtf(_text, _font, _textColor, highlightColor); } /// /// Inserts the text using the given font, text, and highlight colors. The /// text is wrapped in RTF codes so that the specified formatting is kept. /// You can only assign valid RTF to the RichTextBox.Rtf property, else /// an exception is thrown. The RTF string should follow this format ... /// /// {\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{[FONTS]}{\colortbl ;[COLORS]}} /// \viewkind4\uc1\pard\cf1\f0\fs20 [DOCUMENT AREA] } /// /// /// /// NOTE: The text is inserted wherever the caret is at the time of the call, /// and if any text is selected, that text is replaced. /// /// /// /// /// public void InsertTextAsRtf(string _text, Font _font, RtfColor _textColor, RtfColor _backColor) { StringBuilder _rtf = new StringBuilder(); // Append the RTF header _rtf.Append(RTF_HEADER); // Create the font table from the font passed in and append it to the // RTF string _rtf.Append(GetFontTable(_font)); // Create the color table from the colors passed in and append it to the // RTF string _rtf.Append(GetColorTable(_textColor, _backColor)); // Create the document area from the text to be added as RTF and append // it to the RTF string. _rtf.Append(GetDocumentArea(_text, _font)); this.SelectedRtf = _rtf.ToString(); } /// /// Creates the Document Area of the RTF being inserted. The document area /// (in this case) consists of the text being added as RTF and all the /// formatting specified in the Font object passed in. This should have the /// form ... /// /// \viewkind4\uc1\pard\cf1\f0\fs20 [DOCUMENT AREA] } /// /// /// /// /// /// The document area as a string. /// private string GetDocumentArea(string _text, Font _font) { StringBuilder _doc = new StringBuilder(); // Append the standard RTF document area control string _doc.Append(RTF_DOCUMENT_PRE); // Set the highlight color (the color behind the text) to the // third color in the color table. See GetColorTable for more details. _doc.Append(@"\highlight2"); // If the font is bold, attach corresponding tag if (_font.Bold) _doc.Append(@"\b"); // If the font is italic, attach corresponding tag if (_font.Italic) _doc.Append(@"\i"); // If the font is strikeout, attach corresponding tag if (_font.Strikeout) _doc.Append(@"\strike"); // If the font is underlined, attach corresponding tag if (_font.Underline) _doc.Append(@"\ul"); // Set the font to the first font in the font table. // See GetFontTable for more details. _doc.Append(@"\f0"); // Set the size of the font. In RTF, font size is measured in // half-points, so the font size is twice the value obtained from // Font.SizeInPoints _doc.Append(@"\fs"); _doc.Append((int)Math.Round((2 * _font.SizeInPoints))); // Apppend a space before starting actual text (for clarity) _doc.Append(@" "); // Append actual text, however, replace newlines with RTF \par. // Any other special text should be handled here (e.g.) tabs, etc. _doc.Append(_text.Replace("\n", @"\par ")); // RTF isn't strict when it comes to closing control words, but what the // heck ... // Remove the highlight _doc.Append(@"\highlight0"); // If font is bold, close tag if (_font.Bold) _doc.Append(@"\b0"); // If font is italic, close tag if (_font.Italic) _doc.Append(@"\i0"); // If font is strikeout, close tag if (_font.Strikeout) _doc.Append(@"\strike0"); // If font is underlined, cloes tag if (_font.Underline) _doc.Append(@"\ulnone"); // Revert back to default font and size _doc.Append(@"\f0"); _doc.Append(@"\fs20"); // Close the document area control string _doc.Append(RTF_DOCUMENT_POST); return _doc.ToString(); } #endregion #region RTF Insert Image /// /// Inserts an image into the RichTextBox. The image is wrapped in a Windows /// Format Metafile, because although Microsoft discourages the use of a WMF, /// the RichTextBox (and even MS Word), wraps an image in a WMF before inserting /// the image into a document. The WMF is attached in HEX format (a string of /// HEX numbers). /// /// The RTF Specification v1.6 says that you should be able to insert bitmaps, /// .jpegs, .gifs, .pngs, and Enhanced Metafiles (.emf) directly into an RTF /// document without the WMF wrapper. This works fine with MS Word, /// however, when you don't wrap images in a WMF, WordPad and /// RichTextBoxes simply ignore them. Both use the riched20.dll or msfted.dll. /// /// /// NOTE: The image is inserted wherever the caret is at the time of the call, /// and if any text is selected, that text is replaced. /// /// public void InsertImage(Image _image) { StringBuilder _rtf = new StringBuilder(); // Append the RTF header _rtf.Append(RTF_HEADER); // Create the font table using the RichTextBox's current font and append // it to the RTF string _rtf.Append(GetFontTable(this.Font)); // Create the image control string and append it to the RTF string _rtf.Append(GetImagePrefix(_image)); // Create the Windows Metafile and append its bytes in HEX format _rtf.Append(GetRtfImage(_image)); // Close the RTF image control string _rtf.Append(RTF_IMAGE_POST); this.SelectedRtf = _rtf.ToString(); //TODO: need to calc and return checksum here? } /// /// My overload to get the text of an image with tag /// /// /// /// /// public string GetImageAsRtfText(Image _image) { StringBuilder _rtf = new StringBuilder(); // Append the RTF header _rtf.Append(RTF_HEADER); // Create the font table using the RichTextBox's current font and append // it to the RTF string _rtf.Append(GetFontTable(this.Font)); // Create the image control string and append it to the RTF string _rtf.Append(GetImagePrefix(_image)); // Create the Windows Metafile and append its bytes in HEX format _rtf.Append(GetRtfImage(_image)); // Close the RTF image control string _rtf.Append(RTF_IMAGE_POST); return _rtf.ToString(); } /// /// Creates the RTF control string that describes the image being inserted. /// This description (in this case) specifies that the image is an /// MM_ANISOTROPIC metafile, meaning that both X and Y axes can be scaled /// independently. The control string also gives the images current dimensions, /// and its target dimensions, so if you want to control the size of the /// image being inserted, this would be the place to do it. The prefix should /// have the form ... /// /// {\pict\wmetafile8\picw[A]\pich[B]\picwgoal[C]\pichgoal[D] /// /// where ... /// /// A = current width of the metafile in hundredths of millimeters (0.01mm) /// = Image Width in Inches * Number of (0.01mm) per inch /// = (Image Width in Pixels / Graphics Context's Horizontal Resolution) * 2540 /// = (Image Width in Pixels / Graphics.DpiX) * 2540 /// /// B = current height of the metafile in hundredths of millimeters (0.01mm) /// = Image Height in Inches * Number of (0.01mm) per inch /// = (Image Height in Pixels / Graphics Context's Vertical Resolution) * 2540 /// = (Image Height in Pixels / Graphics.DpiX) * 2540 /// /// C = target width of the metafile in twips /// = Image Width in Inches * Number of twips per inch /// = (Image Width in Pixels / Graphics Context's Horizontal Resolution) * 1440 /// = (Image Width in Pixels / Graphics.DpiX) * 1440 /// /// D = target height of the metafile in twips /// = Image Height in Inches * Number of twips per inch /// = (Image Height in Pixels / Graphics Context's Horizontal Resolution) * 1440 /// = (Image Height in Pixels / Graphics.DpiX) * 1440 /// /// /// /// The Graphics Context's resolution is simply the current resolution at which /// windows is being displayed. Normally it's 96 dpi, but instead of assuming /// I just added the code. /// /// According to Ken Howe at pbdr.com, "Twips are screen-independent units /// used to ensure that the placement and proportion of screen elements in /// your screen application are the same on all display systems." /// /// Units Used /// ---------- /// 1 Twip = 1/20 Point /// 1 Point = 1/72 Inch /// 1 Twip = 1/1440 Inch /// /// 1 Inch = 2.54 cm /// 1 Inch = 25.4 mm /// 1 Inch = 2540 (0.01)mm /// /// /// private string GetImagePrefix(Image _image) { StringBuilder _rtf = new StringBuilder(); // Calculate the current width of the image in (0.01)mm int picw = (int)Math.Round((_image.Width / xDpi) * HMM_PER_INCH); // Calculate the current height of the image in (0.01)mm int pich = (int)Math.Round((_image.Height / yDpi) * HMM_PER_INCH); // Calculate the target width of the image in twips int picwgoal = (int)Math.Round((_image.Width / xDpi) * TWIPS_PER_INCH); // Calculate the target height of the image in twips int pichgoal = (int)Math.Round((_image.Height / yDpi) * TWIPS_PER_INCH); //picw seems to be ok for storing tag // Append values to RTF string _rtf.Append(@"{\pict\wmetafile8"); //If these are present then it causes a scaling issue //with the sautinsoft control when you open the image in ayanova //after saving it //_rtf.Append(@"\picw"); //_rtf.Append(picw); //_rtf.Append(@"\pich"); //_rtf.Append(pich); _rtf.Append(@"\picwgoal"); _rtf.Append(picwgoal); _rtf.Append(@"\pichgoal"); _rtf.Append(pichgoal); _rtf.Append(" "); return _rtf.ToString(); } /// /// Use the EmfToWmfBits function in the GDI+ specification to convert a /// Enhanced Metafile to a Windows Metafile /// /// /// A handle to the Enhanced Metafile to be converted /// /// /// The size of the buffer used to store the Windows Metafile bits returned /// /// /// An array of bytes used to hold the Windows Metafile bits returned /// /// /// The mapping mode of the image. This control uses MM_ANISOTROPIC. /// /// /// Flags used to specify the format of the Windows Metafile returned /// [DllImportAttribute("gdiplus.dll")] private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize, byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags); /// /// Wraps the image in an Enhanced Metafile by drawing the image onto the /// graphics context, then converts the Enhanced Metafile to a Windows /// Metafile, and finally appends the bits of the Windows Metafile in HEX /// to a string and returns the string. /// /// /// /// A string containing the bits of a Windows Metafile in HEX /// private string GetRtfImage(Image _image) { StringBuilder _rtf = null; // Used to store the enhanced metafile MemoryStream _stream = null; // Used to create the metafile and draw the image Graphics _graphics = null; // The enhanced metafile Metafile _metaFile = null; // Handle to the device context used to create the metafile IntPtr _hdc; try { _rtf = new StringBuilder(); _stream = new MemoryStream(); // Get a graphics context from the RichTextBox using (_graphics = this.CreateGraphics()) { // Get the device context from the graphics context _hdc = _graphics.GetHdc(); // Create a new Enhanced Metafile from the device context _metaFile = new Metafile(_stream, _hdc); // Release the device context _graphics.ReleaseHdc(_hdc); } // Get a graphics context from the Enhanced Metafile using (_graphics = Graphics.FromImage(_metaFile)) { // Draw the image on the Enhanced Metafile _graphics.DrawImage(_image, new Rectangle(0, 0, _image.Width, _image.Height)); } // Get the handle of the Enhanced Metafile IntPtr _hEmf = _metaFile.GetHenhmetafile(); // A call to EmfToWmfBits with a null buffer return the size of the // buffer need to store the WMF bits. Use this to get the buffer // size. uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault); // Create an array to hold the bits byte[] _buffer = new byte[_bufferSize]; // A call to EmfToWmfBits with a valid buffer copies the bits into the // buffer an returns the number of bits in the WMF. uint _convertedSize = GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault); // Append the bits to the RTF string for (int i = 0; i < _buffer.Length; ++i) { _rtf.Append(String.Format("{0:x2}", _buffer[i])); } return _rtf.ToString(); } finally { if (_graphics != null) _graphics.Dispose(); if (_metaFile != null) _metaFile.Dispose(); if (_stream != null) _stream.Close(); } } #endregion #region RTF Helpers /// /// Creates a font table from a font object. When an Insert or Append /// operation is performed a font is either specified or the default font /// is used. In any case, on any Insert or Append, only one font is used, /// thus the font table will always contain a single font. The font table /// should have the form ... /// /// {\fonttbl{\f0\[FAMILY]\fcharset0 [FONT_NAME];} /// /// /// private string GetFontTable(Font _font) { StringBuilder _fontTable = new StringBuilder(); // Append table control string _fontTable.Append("{\\fonttbl{\\f0"); _fontTable.Append("\\"); // If the font's family corresponds to an RTF family, append the // RTF family name, else, append the RTF for unknown font family. if (rtfFontFamily.ContainsKey(_font.FontFamily.Name)) { _fontTable.Append(rtfFontFamily[_font.FontFamily.Name]); } else { _fontTable.Append(rtfFontFamily[FF_UNKNOWN]); } // \fcharset specifies the character set of a font in the font table. // 0 is for ANSI. _fontTable.Append("\\fcharset0 "); // Append the name of the font _fontTable.Append(_font.Name); // Close control string _fontTable.Append(";}}"); return _fontTable.ToString(); } /// /// Creates a font table from the RtfColor structure. When an Insert or Append /// operation is performed, _textColor and _backColor are either specified /// or the default is used. In any case, on any Insert or Append, only three /// colors are used. The default color of the RichTextBox (signified by a /// semicolon (;) without a definition), is always the first color (index 0) in /// the color table. The second color is always the text color, and the third /// is always the highlight color (color behind the text). The color table /// should have the form ... /// /// {\colortbl ;[TEXT_COLOR];[HIGHLIGHT_COLOR];} /// /// /// /// /// private string GetColorTable(RtfColor _textColor, RtfColor _backColor) { StringBuilder _colorTable = new StringBuilder(); // Append color table control string and default font (;) _colorTable.Append(@"{\colortbl ;"); // Append the text color _colorTable.Append(rtfColor[_textColor]); _colorTable.Append(@";"); // Append the highlight color _colorTable.Append(rtfColor[_backColor]); _colorTable.Append(@";}\n"); return _colorTable.ToString(); } /// /// Called by overrided RichTextBox.Rtf accessor. /// Removes the null character from the RTF. This is residue from developing /// the control for a specific instant messaging protocol and can be ommitted. /// /// /// RTF without null character private string RemoveBadChars(string _originalRtf) { return _originalRtf.Replace("\0", ""); } #endregion #region Printing //Convert the unit used by the .NET framework (1/100 inch) //and the unit used by Win32 API calls (twips 1/1440 inch) private const double anInch = 14.4; [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } [StructLayout(LayoutKind.Sequential)] private struct CHARRANGE { public int cpMin; //First character of range (0 for start of doc) public int cpMax; //Last character of range (-1 for end of doc) } [StructLayout(LayoutKind.Sequential)] private struct FORMATRANGE { public IntPtr hdc; //Actual DC to draw on public IntPtr hdcTarget; //Target DC for determining text formatting public RECT rc; //Region of the DC to draw to (in twips) public RECT rcPage; //Region of the whole DC (page size) (in twips) public CHARRANGE chrg; //Range of text to draw (see earlier declaration) } private const int WM_USER = 0x0400; private const int EM_FORMATRANGE = WM_USER + 57; [DllImport("USER32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); // Render the contents of the RichTextBox for printing // Return the last character printed + 1 (printing start from this point for next page) public int Print(int charFrom, int charTo, PrintPageEventArgs e) { //Calculate the area to render and print RECT rectToPrint; rectToPrint.Top = (int)(e.MarginBounds.Top * anInch); rectToPrint.Bottom = (int)(e.MarginBounds.Bottom * anInch); rectToPrint.Left = (int)(e.MarginBounds.Left * anInch); rectToPrint.Right = (int)(e.MarginBounds.Right * anInch); //Calculate the size of the page RECT rectPage; rectPage.Top = (int)(e.PageBounds.Top * anInch); rectPage.Bottom = (int)(e.PageBounds.Bottom * anInch); rectPage.Left = (int)(e.PageBounds.Left * anInch); rectPage.Right = (int)(e.PageBounds.Right * anInch); IntPtr hdc = e.Graphics.GetHdc(); FORMATRANGE fmtRange; fmtRange.chrg.cpMax = charTo; //Indicate character from to character to fmtRange.chrg.cpMin = charFrom; fmtRange.hdc = hdc; //Use the same DC for measuring and rendering fmtRange.hdcTarget = hdc; //Point at printer hDC fmtRange.rc = rectToPrint; //Indicate the area on page to print fmtRange.rcPage = rectPage; //Indicate size of page IntPtr res = IntPtr.Zero; IntPtr wparam = IntPtr.Zero; wparam = new IntPtr(1); //Get the pointer to the FORMATRANGE structure in memory IntPtr lparam = IntPtr.Zero; lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange)); Marshal.StructureToPtr(fmtRange, lparam, false); //Send the rendered data for printing res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam); //Free the block of memory allocated Marshal.FreeCoTaskMem(lparam); //Release the device context handle obtained by a previous call e.Graphics.ReleaseHdc(hdc); //Return last + 1 character printer return res.ToInt32(); } #endregion #region Link Clicked void MudRichTextBox_LinkClicked(object sender, LinkClickedEventArgs e) { // Call Process.Start method to open a browser // with link text as URL. System.Diagnostics.Process.Start(e.LinkText); } #endregion #region WndProc protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_VSCROLL: base.WndProc(ref m); if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBTRACK) { OnVScroll(EventArgs.Empty); } if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBPOSITION) { OnVScroll(EventArgs.Empty); } break; case WM_HSCROLL: base.WndProc(ref m); if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBTRACK) { OnHScroll(EventArgs.Empty); } if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBPOSITION) { OnHScroll(EventArgs.Empty); } break; default: base.WndProc(ref m); break; } } #endregion #region Command keys protected override bool ProcessCmdKey(ref Message m, Keys keyData) { if (keyData == (Keys.Control | Keys.V) || keyData==(Keys.Control | Keys.X)) return true; else return base.ProcessCmdKey(ref m, keyData); } #endregion #region Custom arbitrary hyperlinks //requried for custom hyperlinks below private const UInt32 CFM_LINK = 0x00000020; private const UInt32 CFE_LINK = 0x0020; //http://www.codeproject.com/KB/edit/RichTextBoxLinks.aspx /// /// Insert a given text as a link into the RichTextBox at the current insert position. /// /// Text to be inserted public void InsertLink(string text) { InsertLink(text, this.SelectionStart); } /// /// Insert a given text at a given position as a link. /// /// Text to be inserted /// Insert position public void InsertLink(string text, int position) { if (position < 0 || position > this.Text.Length) throw new ArgumentOutOfRangeException("position"); this.SelectionStart = position; this.SelectedText = text; this.Select(position, text.Length); this.SetSelectionLink(true); this.Select(position + text.Length, 0); } /// /// Insert a given text at at the current input position as a link. /// The link text is followed by a hash (#) and the given hyperlink text, both of /// them invisible. /// When clicked on, the whole link text and hyperlink string are given in the /// LinkClickedEventArgs. /// /// Text to be inserted /// Invisible hyperlink string to be inserted public void InsertLink(string text, string hyperlink) { InsertLink(text, hyperlink, this.SelectionStart); } /// /// Insert a given text at a given position as a link. The link text is followed by /// a hash (#) and the given hyperlink text, both of them invisible. /// When clicked on, the whole link text and hyperlink string are given in the /// LinkClickedEventArgs. /// /// Text to be inserted /// Invisible hyperlink string to be inserted /// Insert position public void InsertLink(string text, string hyperlink, int position) { if (position < 0 || position > this.Text.Length) throw new ArgumentOutOfRangeException("position"); //string startLinkStr = @"LINKSTART"; //string endLinkStr = @"#LINKEND"; //int linkStartPos = position - startLinkStr.Length; //this.SelectionStart = position; //this.SelectedRtf = @"{\rtf1\ansi\v LINKSTART\v0" + text + @"\v #" + hyperlink + @"#LINKEND\v0}"; //this.Select(position, text.Length + hyperlink.Length + 1 + endLinkStr.Length + startLinkStr.Length); //this.SetSelectionLink(true); //this.SelectionProtected = true; //this.Select(position + text.Length + hyperlink.Length + 1, 0); //Original this.SelectionStart = position; this.SelectedRtf = @"{\rtf1\ansi " + text + @"\v #" + hyperlink + @"\v0}"; this.Select(position, text.Length + hyperlink.Length + 1); this.SetSelectionLink(true); this.Select(position + text.Length + hyperlink.Length + 1, 0); } public void ShowLinks() { int curPos = 0; string startTextStr = "LINKSTART"; string endTextStr = "#"; string endLinkStr = "#LINKEND"; while (RtfHasMoreLinks(curPos)) { // Find hidden link text int sPos = this.Text.IndexOf(startTextStr, curPos); int hStartIndex = sPos; sPos += startTextStr.Length; int ePos = this.Text.IndexOf(endTextStr, sPos); // Now we have the text of link string linkText = this.Text.Substring(sPos, (ePos - sPos)); // Get the start position of link int sLinkPos = ePos + endTextStr.Length; int eLinkPos = this.Text.IndexOf(endLinkStr, sPos); // The link string hLink = this.Text.Substring(sLinkPos, (eLinkPos - sLinkPos)); int hEndIndex = eLinkPos + endLinkStr.Length; // Show the link in box ShowDistinctLink(hStartIndex, hEndIndex, linkText, hLink); // Save the current end post at current position curPos = eLinkPos; } } private bool RtfHasMoreLinks(int curPos) { return (this.Text.IndexOf("LINKSTART", curPos) > curPos ? true : false); } private void ShowDistinctLink(int sPos, int ePos, string displayText, string hyperlink) { if (sPos < 0 || sPos > ePos) throw new ArgumentOutOfRangeException("position"); this.Select(sPos, (ePos - sPos)); this.SetSelectionLink(true); this.Select(sPos + displayText.Length + hyperlink.Length + 1, 0); } /// /// Set the current selection's link style /// /// true: set link style, false: clear link style public void SetSelectionLink(bool link) { SetSelectionStyle(CFM_LINK, link ? CFE_LINK : 0); } /// /// Get the link style for the current selection /// /// 0: link style not set, 1: link style set, -1: mixed public int GetSelectionLink() { return GetSelectionStyle(CFM_LINK, CFE_LINK); } private void SetSelectionStyle(UInt32 mask, UInt32 effect) { CHARFORMAT2 cf = new CHARFORMAT2(); cf.cbSize = Marshal.SizeOf(cf); cf.dwMask = mask; cf.dwEffects = effect; IntPtr wpar = new IntPtr(SCF_SELECTION); IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf)); Marshal.StructureToPtr(cf, lpar, false); IntPtr res = SendMessage(Handle, EM_SETCHARFORMAT, wpar, lpar); Marshal.FreeCoTaskMem(lpar); } private int GetSelectionStyle(UInt32 mask, UInt32 effect) { CHARFORMAT2 cf = new CHARFORMAT2(); cf.cbSize = Marshal.SizeOf(cf); cf.szFaceName = new char[32]; IntPtr wpar = new IntPtr(SCF_SELECTION); IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf)); Marshal.StructureToPtr(cf, lpar, false); IntPtr res = SendMessage(Handle, EM_GETCHARFORMAT, wpar, lpar); cf = (CHARFORMAT2)Marshal.PtrToStructure(lpar, typeof(CHARFORMAT2)); int state; // dwMask holds the information which properties are consistent throughout the selection: if ((cf.dwMask & mask) == mask) { if ((cf.dwEffects & effect) == effect) state = 1; else state = 0; } else { state = -1; } Marshal.FreeCoTaskMem(lpar); return state; } #endregion } /// /// This class contains all the scroll bar information. /// public class ScrollBarInformation { #region scrollbarinformation int nMin = 0; int nMax = 0; int nPage = 0; int nPos = 0; int nTrackPos = 0; /// /// Sets up an empty scroll bar information class. /// public ScrollBarInformation() { } /// /// This sets up the scroll bar information with all the basic details. /// /// the minimum size /// the maximum size /// the size of the page /// the position of the thingy /// this is updated while the scroll bar is wiggling up and down. public ScrollBarInformation(int min, int max, int page, int pos, int trackpos) { this.nMin = min; this.nMax = max; this.nPage = page; this.nPos = pos; this.nTrackPos = trackpos; } /// /// Specifies the minimum scrolling position. /// /// the minimum scrolling position public int Minimum { get { return nMin; } set { nMin = value; } } /// /// Specifies the maximum scrolling position. /// /// the maximum scrolling position public int Maximum { get { return nMax; } set { nMax = value; } } /// /// Specifies the page size. A scroll bar uses this value to determine the /// appropriate size of the proportional scroll box. /// /// public int Page { get { return nPage; } set { nPage = value; } } /// /// The position of the thumb inside the scroll bar. /// /// public int Position { get { return nPos; } set { nPos = value; } } /// /// Specifies the immediate position of a scroll box that the user is dragging. /// An application can retrieve this value while processing the SB_THUMBTRACK /// request code. An application cannot set the immediate scroll position; the /// SetScrollInfo function ignores this member. /// /// the immediated position of the scroll box public int TrackPosition { get { return nTrackPos; } set { nTrackPos = value; } } #endregion } }