Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / WinForms / Managed / System / WinForms / RichTextBox.cs / 1 / RichTextBox.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using Microsoft.Win32; using System; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Remoting; using System.Runtime.Serialization.Formatters; using System.Security; using System.Security.Permissions; using System.Text; using System.Windows.Forms.ComponentModel; using System.Windows.Forms.Design; using System.Windows.Forms.Layout; using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; using Util = NativeMethods.Util; // ////// /// Rich Text control. The RichTextBox is a control that contains formatted text. /// It supports font selection, boldface, and other type attributes. /// [ClassInterface(ClassInterfaceType.AutoDispatch), ComVisible(true), Docking(DockingBehavior.Ask), Designer("System.Windows.Forms.Design.RichTextBoxDesigner, " + AssemblyRef.SystemDesign), SRDescription(SR.DescriptionRichTextBox) ] public class RichTextBox : TextBoxBase { static TraceSwitch richTextDbg; static TraceSwitch RichTextDbg { get { if (richTextDbg == null) { richTextDbg = new TraceSwitch("RichTextDbg", "Debug info about RichTextBox"); } return richTextDbg; } } ////// /// Paste special flags. /// private const int DV_E_DVASPECT = unchecked((int)0x8004006B); private const int DVASPECT_CONTENT = 1; private const int DVASPECT_THUMBNAIL = 2; private const int DVASPECT_ICON = 4; private const int DVASPECT_DOCPRINT = 8; internal const int INPUT = 0x0001; internal const int OUTPUT = 0x0002; internal const int DIRECTIONMASK = INPUT | OUTPUT; internal const int ANSI = 0x0004; internal const int UNICODE = 0x0008; internal const int FORMATMASK = ANSI | UNICODE; internal const int TEXTLF = 0x0010; internal const int TEXTCRLF = 0x0020; internal const int RTF = 0x0040; internal const int KINDMASK = TEXTLF | TEXTCRLF | RTF; // This is where we store the reched library. private static IntPtr moduleHandle; private static readonly string SZ_RTF_TAG = "{\\rtf"; private const int CHAR_BUFFER_LEN = 512; // Event objects // private static readonly object EVENT_HSCROLL = new object(); private static readonly object EVENT_LINKACTIVATE = new object(); private static readonly object EVENT_IMECHANGE = new object(); private static readonly object EVENT_PROTECTED = new object(); private static readonly object EVENT_REQUESTRESIZE = new object(); private static readonly object EVENT_SELCHANGE = new object(); private static readonly object EVENT_VSCROLL = new object(); // Persistent state // private int bulletIndent; private int rightMargin; private string textRtf; // If not null, takes precedence over cached Text value private string textPlain; private Color selectionBackColorToSetOnHandleCreated; RichTextBoxLanguageOptions languageOption = RichTextBoxLanguageOptions.AutoFont | RichTextBoxLanguageOptions.DualFont; // Non-persistent state // static int logPixelsX; static int logPixelsY; Stream editStream = null; float zoomMultiplier = 1.0f; // used to decide when to fire the selectionChange event. private int curSelStart; private int curSelEnd; private short curSelType; object oleCallback; private static int[] shortcutsToDisable; private static int richEditMajorVersion = 3; //Assume version 3: it'll only be version 2 on Win98, and we don't yet load version 4. private BitVector32 richTextBoxFlags = new BitVector32(); private static readonly BitVector32.Section autoWordSelectionSection = BitVector32.CreateSection(1); private static readonly BitVector32.Section showSelBarSection = BitVector32.CreateSection(1, autoWordSelectionSection); private static readonly BitVector32.Section autoUrlDetectSection = BitVector32.CreateSection(1, showSelBarSection); private static readonly BitVector32.Section fInCtorSection = BitVector32.CreateSection(1, autoUrlDetectSection); private static readonly BitVector32.Section protectedErrorSection = BitVector32.CreateSection(1, fInCtorSection); private static readonly BitVector32.Section linkcursorSection = BitVector32.CreateSection(1, protectedErrorSection); private static readonly BitVector32.Section allowOleDropSection = BitVector32.CreateSection(1, linkcursorSection); private static readonly BitVector32.Section suppressTextChangedEventSection = BitVector32.CreateSection(1, allowOleDropSection); private static readonly BitVector32.Section callOnContentsResizedSection = BitVector32.CreateSection(1, suppressTextChangedEventSection); private static readonly BitVector32.Section richTextShortcutsEnabledSection = BitVector32.CreateSection(1, callOnContentsResizedSection); private static readonly BitVector32.Section allowOleObjectsSection = BitVector32.CreateSection(1, richTextShortcutsEnabledSection); private static readonly BitVector32.Section scrollBarsSection = BitVector32.CreateSection((short) RichTextBoxScrollBars.ForcedBoth, allowOleObjectsSection); private static readonly BitVector32.Section enableAutoDragDropSection = BitVector32.CreateSection(1, scrollBarsSection); ////// /// Constructs a new RichTextBox. /// public RichTextBox() { InConstructor = true; richTextBoxFlags[autoWordSelectionSection] = 0;// This is false by default DetectUrls = true; ScrollBars = RichTextBoxScrollBars.Both; RichTextShortcutsEnabled = true; MaxLength = int.MaxValue; Multiline = true; AutoSize = false; curSelStart = curSelEnd = curSelType = -1; InConstructor = false; } ////// /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop /// may still be used: this should be hidden in the property grid, but not in code /// [Browsable(false)] public override bool AllowDrop { get { return richTextBoxFlags[allowOleDropSection] != 0; } set { if (value) { try { IntSecurity.ClipboardRead.Demand(); } catch (Exception e) { throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); } } richTextBoxFlags[allowOleDropSection] = value ? 1 : 0; UpdateOleCallback(); } } internal bool AllowOleObjects { get { return richTextBoxFlags[allowOleObjectsSection] != 0; } set { richTextBoxFlags[allowOleObjectsSection] = value ? 1 : 0; } } ////// /// [ DefaultValue(false), RefreshProperties(RefreshProperties.Repaint), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) ] public override bool AutoSize { get { return base.AutoSize; } set { base.AutoSize = value; } } ////// Gets or sets a value indicating whether the size /// of the control automatically adjusts when the font assigned to the control /// is changed. /// /// Note: this works differently than other Controls' AutoSize, so we're hiding /// it to avoid confusion. /// ////// /// Controls whether whether mouse selection snaps to whole words. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.RichTextBoxAutoWordSelection) ] public bool AutoWordSelection { get { return richTextBoxFlags[autoWordSelectionSection] != 0; } set { richTextBoxFlags[autoWordSelectionSection] = value ? 1 : 0; if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETOPTIONS, value ? RichTextBoxConstants.ECOOP_OR : RichTextBoxConstants.ECOOP_XOR, RichTextBoxConstants.ECO_AUTOWORDSELECTION); } } } ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// Returns the amount of indent used in a RichTextBox control when /// SelectionBullet is set to true. /// [ SRCategory(SR.CatBehavior), DefaultValue(0), Localizable(true), SRDescription(SR.RichTextBoxBulletIndent) ] public int BulletIndent { get { return bulletIndent; } set { if (value < 0) { throw new ArgumentOutOfRangeException("BulletIndent", SR.GetString(SR.InvalidArgument, "BulletIndent", (value).ToString(CultureInfo.CurrentCulture))); } this.bulletIndent = value; // Call to update the control only if the bullet is set. if (IsHandleCreated && SelectionBullet) SelectionBullet = true; } } private bool CallOnContentsResized { get { return richTextBoxFlags[callOnContentsResizedSection] != 0; } set { richTextBoxFlags[callOnContentsResizedSection] = value ? 1 : 0; } } internal override bool CanRaiseTextChangedEvent { get { return !SuppressTextChangedEvent; } } ////// /// Whether or not there are actions that can be Redone on the RichTextBox control. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxCanRedoDescr) ] public bool CanRedo { get { if (IsHandleCreated) { bool b; b = (int)SendMessage(RichTextBoxConstants.EM_CANREDO, 0, 0) != 0; return b; } return false; } } ////// /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { // Check for library if (moduleHandle == IntPtr.Zero) { moduleHandle = UnsafeNativeMethods.LoadLibrary(RichTextBoxConstants.DLL_RICHEDIT); int lastWin32Error = Marshal.GetLastWin32Error(); if ((long)moduleHandle < (long)32) { throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError,RichTextBoxConstants.DLL_RICHEDIT)); } //Determine whether we're Rich Edit 2.0 or 3.0: see //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp StringBuilder pathBuilder = new StringBuilder(NativeMethods.MAX_PATH); UnsafeNativeMethods.GetModuleFileName(new HandleRef(null, moduleHandle), pathBuilder, pathBuilder.Capacity); string path = pathBuilder.ToString(); new FileIOPermission(FileIOPermissionAccess.Read, path).Assert(); FileVersionInfo versionInfo; try { versionInfo = FileVersionInfo.GetVersionInfo(path); } finally { CodeAccessPermission.RevertAssert(); } Debug.Assert(versionInfo != null && !string.IsNullOrEmpty(versionInfo.ProductVersion), "Couldn't get the version info for the richedit dll"); if (versionInfo != null && !string.IsNullOrEmpty(versionInfo.ProductVersion)) { //Note: this only allows for one digit version int parsedValue; if (int.TryParse(versionInfo.ProductVersion[0].ToString(), out parsedValue)) { richEditMajorVersion = parsedValue; } } } CreateParams cp = base.CreateParams; if (Marshal.SystemDefaultCharSize == 1) { cp.ClassName = RichTextBoxConstants.WC_RICHEDITA; } else { cp.ClassName = RichTextBoxConstants.WC_RICHEDITW; } if (Multiline) { if (((int)ScrollBars & RichTextBoxConstants.RTB_HORIZ) != 0 && !WordWrap) { // RichEd infers word wrap from the absence of horizontal scroll bars cp.Style |= NativeMethods.WS_HSCROLL; if (((int)ScrollBars & RichTextBoxConstants.RTB_FORCE) != 0) cp.Style |= RichTextBoxConstants.ES_DISABLENOSCROLL; } if (((int)ScrollBars & RichTextBoxConstants.RTB_VERT) != 0) { cp.Style |= NativeMethods.WS_VSCROLL; if (((int)ScrollBars & RichTextBoxConstants.RTB_FORCE) != 0) cp.Style |= RichTextBoxConstants.ES_DISABLENOSCROLL; } } // VSWhidbey 94843: Remove the WS_BORDER style from the control, if we're trying to set it, // to prevent the control from displaying the single point rectangle around the 3D border if (BorderStyle.FixedSingle == BorderStyle && ((cp.Style & NativeMethods.WS_BORDER) != 0)) { cp.Style &= (~NativeMethods.WS_BORDER); cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; } return cp; } } // public bool CanUndo {}; <-- inherited from TextBoxBase ///[To be supplied.] ////// /// Controls whether or not the rich edit control will automatically highlight URLs. /// By default, this is true. Note that changing this property will not update text that is /// already present in the RichTextBox control; it only affects text which is entered after the /// property is changed. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.RichTextBoxDetectURLs) ] public bool DetectUrls { get { return richTextBoxFlags[autoUrlDetectSection] != 0; } set { if (value != DetectUrls) { richTextBoxFlags[autoUrlDetectSection] = value ? 1 : 0; if (IsHandleCreated) { this.SendMessage(RichTextBoxConstants.EM_AUTOURLDETECT, value ? 1 : 0, 0); RecreateHandle(); } } } } ///protected override Size DefaultSize { get { return new Size(100, 96); } } /// /// /// We can't just enable drag/drop of text by default: it's a breaking change (VSWhidbey /// 375177). Should be false by default. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.RichTextBoxEnableAutoDragDrop) ] public bool EnableAutoDragDrop { get { return richTextBoxFlags[enableAutoDragDropSection] != 0; } set { if (value) { try { IntSecurity.ClipboardRead.Demand(); } catch (Exception e) { throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); } } richTextBoxFlags[enableAutoDragDropSection] = value ? 1 : 0; UpdateOleCallback(); } } ///public override Color ForeColor { get { return base.ForeColor; } set { if (IsHandleCreated) { if (InternalSetForeColor(value)) { base.ForeColor = value; } } else { base.ForeColor = value; } } } /// public override Font Font { get { return base.Font; } set { if (IsHandleCreated) { if (SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)) > 0) { if (value == null) { base.Font = null; SetCharFormatFont(false, Font); } else { try{ Font f = GetCharFormatFont(false); if (f == null || !f.Equals (value)) { SetCharFormatFont(false, value); // update controlfont from "resolved" font from the attempt // to set the document font... // CallOnContentsResized = true; base.Font = GetCharFormatFont(false); } } finally{ CallOnContentsResized = false; } } } else { base.Font = value; } } else { base.Font = value; } } } internal override Size GetPreferredSizeCore(Size proposedConstraints) { Size scrollBarPadding = Size.Empty; //If the RTB is multiline, we won't have a horizontal scrollbar. if (!WordWrap && Multiline && (ScrollBars & RichTextBoxScrollBars.Horizontal) != 0) { scrollBarPadding.Height += SystemInformation.HorizontalScrollBarHeight; } if (Multiline && (ScrollBars & RichTextBoxScrollBars.Vertical) != 0) { scrollBarPadding.Width += SystemInformation.VerticalScrollBarWidth; } // Subtract the scroll bar padding before measuring proposedConstraints -= scrollBarPadding; Size prefSize = base.GetPreferredSizeCore(proposedConstraints); return prefSize + scrollBarPadding; } private bool InConstructor { get { return richTextBoxFlags[fInCtorSection] != 0; } set { richTextBoxFlags[fInCtorSection] = value ? 1 : 0; } } /// /// Sets or gets the rich text box control' language option. /// The IMF_AUTOFONT flag is set by default. /// The IMF_AUTOKEYBOARD and IMF_IMECANCELCOMPLETE flags are cleared by default. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public RichTextBoxLanguageOptions LanguageOption { get { RichTextBoxLanguageOptions opt; if (IsHandleCreated) { opt = (RichTextBoxLanguageOptions)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETLANGOPTIONS, 0, 0); } else { opt = languageOption; } return opt; } set { if (this.LanguageOption != value) { this.languageOption = value; if (IsHandleCreated) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETLANGOPTIONS, 0, (int) value); } } } } private bool LinkCursor { get { return richTextBoxFlags[linkcursorSection] != 0; } set { richTextBoxFlags[linkcursorSection] = value ? 1 : 0; } } ///[ DefaultValue(int.MaxValue), ] public override int MaxLength { get { return base.MaxLength; } set { base.MaxLength = value; } } /// /// /// [DefaultValue(true)] public override bool Multiline { get { return base.Multiline; } set { base.Multiline = value; } } private bool ProtectedError { get { return richTextBoxFlags[protectedErrorSection] != 0; } set { richTextBoxFlags[protectedErrorSection] = value ? 1 : 0; } } ///[To be supplied.] ////// /// Returns the name of the action that will be performed if the user /// Redo's their last Undone operation. If no operation can be redone, /// an empty string ("") is returned. /// //NOTE: This is overridable, because we want people to be able to // mess with the names if necessary...? [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxRedoActionNameDescr) ] public string RedoActionName { get { if (!CanRedo) return ""; int n; n = (int)SendMessage(RichTextBoxConstants.EM_GETREDONAME, 0, 0); return GetEditorActionName(n); } } /////Description: Specifies whether rich text formatting keyboard shortcuts are enabled. [ DefaultValue(true), Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public bool RichTextShortcutsEnabled { get { return richTextBoxFlags[richTextShortcutsEnabledSection] != 0; } set { if (shortcutsToDisable == null) { shortcutsToDisable = new int[] {(int)Shortcut.CtrlL, (int)Shortcut.CtrlR, (int)Shortcut.CtrlE, (int)Shortcut.CtrlJ}; } richTextBoxFlags[richTextShortcutsEnabledSection] = value ? 1 : 0; } } /// /// /// The right margin of a RichTextBox control. A nonzero margin implies WordWrap. /// [ SRCategory(SR.CatBehavior), DefaultValue(0), Localizable(true), SRDescription(SR.RichTextBoxRightMargin) ] public int RightMargin { get { return rightMargin; } set { if (this.rightMargin != value) { if (value < 0) throw new ArgumentOutOfRangeException("RightMargin", SR.GetString(SR.InvalidLowBoundArgumentEx, "RightMargin", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); this.rightMargin = value; if (value == 0) { // Once you set EM_SETTARGETDEVICE to something nonzero, RichEd will assume // word wrap forever and ever. RecreateHandle(); } else if (IsHandleCreated) { IntPtr hDC = UnsafeNativeMethods.CreateIC("DISPLAY", null, null, new HandleRef(null, IntPtr.Zero)); try { SendMessage(RichTextBoxConstants.EM_SETTARGETDEVICE, hDC, (IntPtr)Pixel2Twip(hDC, value, true)); } finally { if (hDC != IntPtr.Zero) { UnsafeNativeMethods.DeleteDC(new HandleRef(null, hDC)); } } } } } } ////// /// The text of a RichTextBox control, including all Rtf codes. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxRTF), RefreshProperties(RefreshProperties.All) ] public string Rtf { get { if (IsHandleCreated) { return StreamOut(RichTextBoxConstants.SF_RTF); } else if (textPlain != null) { ForceHandleCreate(); return StreamOut(RichTextBoxConstants.SF_RTF); } else { return textRtf; } } set { if (value == null) value = ""; if (value.Equals(Rtf)) return; ForceHandleCreate(); textRtf = value; StreamIn(value, RichTextBoxConstants.SF_RTF); if (CanRaiseTextChangedEvent) { OnTextChanged(EventArgs.Empty); } } } ////// /// The current scrollbar settings for a multi-line rich edit control. /// Possible return values are given by the RichTextBoxScrollBars enumeration. /// [ SRCategory(SR.CatAppearance), DefaultValue(RichTextBoxScrollBars.Both), Localizable(true), SRDescription(SR.RichTextBoxScrollBars) ] public RichTextBoxScrollBars ScrollBars { get { return (RichTextBoxScrollBars) richTextBoxFlags[scrollBarsSection]; } set { // we could be more clever here, but it doesnt seem like this would get set enough // to warrant a clever bitmask. if (!ClientUtils.IsEnumValid_NotSequential(value, (int)value, (int)RichTextBoxScrollBars.Both, (int)RichTextBoxScrollBars.None, (int)RichTextBoxScrollBars.Horizontal, (int)RichTextBoxScrollBars.Vertical, (int)RichTextBoxScrollBars.ForcedHorizontal, (int)RichTextBoxScrollBars.ForcedVertical, (int)RichTextBoxScrollBars.ForcedBoth)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(RichTextBoxScrollBars)); } if (value != ScrollBars) { using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.ScrollBars)) { richTextBoxFlags[scrollBarsSection] = (int) value; RecreateHandle(); } } } } ////// /// The alignment of the paragraphs in a RichTextBox control. /// [ DefaultValue(HorizontalAlignment.Left), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelAlignment) ] public HorizontalAlignment SelectionAlignment { get { HorizontalAlignment selectionAlignment = HorizontalAlignment.Left; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_ALIGNMENT & pf.dwMask) != 0) { switch (pf.wAlignment) { case RichTextBoxConstants.PFA_LEFT: selectionAlignment = HorizontalAlignment.Left; break; case RichTextBoxConstants.PFA_RIGHT: selectionAlignment = HorizontalAlignment.Right; break; case RichTextBoxConstants.PFA_CENTER: selectionAlignment = HorizontalAlignment.Center; break; } } return selectionAlignment; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); } ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_ALIGNMENT; switch (value) { case HorizontalAlignment.Left: pf.wAlignment = RichTextBoxConstants.PFA_LEFT; break; case HorizontalAlignment.Right: pf.wAlignment = RichTextBoxConstants.PFA_RIGHT; break; case HorizontalAlignment.Center: pf.wAlignment = RichTextBoxConstants.PFA_CENTER; break; } // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// Determines if a paragraph in the RichTextBox control /// contains the current selection or insertion point has the bullet style. /// [ DefaultValue(false), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelBullet) ] public bool SelectionBullet { get { RichTextBoxSelectionAttribute selectionBullet = RichTextBoxSelectionAttribute.None; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_NUMBERING & pf.dwMask) != 0) { if (RichTextBoxConstants.PFN_BULLET == pf.wNumbering) { selectionBullet = RichTextBoxSelectionAttribute.All; } } else { // For paragraphs with mixed SelectionBullets, we just return false return false; } return selectionBullet == RichTextBoxSelectionAttribute.All; } set { ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_NUMBERING | RichTextBoxConstants.PFM_OFFSET; if (!value) { pf.wNumbering = 0; pf.dxOffset = 0; } else { pf.wNumbering = RichTextBoxConstants.PFN_BULLET; pf.dxOffset = Pixel2Twip(IntPtr.Zero, bulletIndent, true); } // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// Determines whether text in the RichTextBox control /// appears on the baseline (normal), as a superscript above the baseline, /// or as a subscript below the baseline. /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelCharOffset) ] public int SelectionCharOffset { get { int selCharOffset = 0; ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & RichTextBoxConstants.CFM_OFFSET) != 0) { selCharOffset = cf.yOffset; } else { // The selection contains characters of different offsets, // so we just return the offset of the first character. selCharOffset = cf.yOffset; } return Twip2Pixel(IntPtr.Zero,selCharOffset,false); } set { if (value > 2000 || value < -2000) throw new ArgumentOutOfRangeException("SelectionCharOffset", SR.GetString(SR.InvalidBoundArgument, "SelectionCharOffset", value, -2000, 2000)); ForceHandleCreate(); NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); cf.dwMask = RichTextBoxConstants.CFM_OFFSET; cf.yOffset = Pixel2Twip(IntPtr.Zero, value, false); // Set the format information // SendMessage will force the handle to be created if it hasn't already. Normally, // we would cache property values until the handle is created - but for this property, // it's far more simple to just create the handle. // UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); } } ////// /// The color of the currently selected text in the /// RichTextBox control. /// Returns Color.Empty if the selection has more than one color. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelColor) ] public Color SelectionColor { get { Color selColor = Color.Empty; ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & RichTextBoxConstants.CFM_COLOR) != 0) selColor = ColorTranslator.FromOle(cf.crTextColor); return selColor; } set { ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(true); cf.dwMask = RichTextBoxConstants.CFM_COLOR; cf.dwEffects = 0; cf.crTextColor = ColorTranslator.ToWin32(value); // set the format information UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); } } ////// /// The background color of the currently selected text in the RichTextBox control. /// Returns Color.Empty if the selection has more than one color. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelBackColor) ] public Color SelectionBackColor { get { Color selColor = Color.Empty; if (IsHandleCreated) { NativeMethods.CHARFORMAT2A cf2 = GetCharFormat2(true); // If the effects member contains valid info if ((cf2.dwEffects & RichTextBoxConstants.CFE_AUTOBACKCOLOR) != 0) { selColor = this.BackColor; } else if ((cf2.dwMask & RichTextBoxConstants.CFM_BACKCOLOR) != 0) { selColor = ColorTranslator.FromOle(cf2.crBackColor); } } else { selColor = selectionBackColorToSetOnHandleCreated; } return selColor; } set { //Note: don't compare the value to the old value here: it's possible that //you have a different range selected. selectionBackColorToSetOnHandleCreated = value; if (IsHandleCreated) { NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A(); if (value == Color.Empty) { cf2.dwEffects = RichTextBoxConstants.CFE_AUTOBACKCOLOR; } else { cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR; cf2.crBackColor = ColorTranslator.ToWin32(value); } UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf2); } } } ////// /// The font used to display the currently selected text /// or the characters(s) immediately following the insertion point in the /// RichTextBox control. Null if the selection has more than one font. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelFont) ] public Font SelectionFont { get { return GetCharFormatFont(true); } set { SetCharFormatFont(true, value); } } ////// /// The distance (in pixels) between the left edge of the first line of text /// in the selected paragraph(s) (as specified by the SelectionIndent property) /// and the left edge of subsequent lines of text in the same paragraph(s). /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelHangingIndent) ] public int SelectionHangingIndent { get { int selHangingIndent = 0; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_OFFSET & pf.dwMask) != 0) selHangingIndent = pf.dxOffset; return Twip2Pixel(IntPtr.Zero, selHangingIndent, true); } set { ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_OFFSET; pf.dxOffset = Pixel2Twip(IntPtr.Zero, value, true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// The distance (in pixels) between the left edge of the RichTextBox control and /// the left edge of the text that is selected or added at the current /// insertion point. /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelIndent) ] public int SelectionIndent { get { int selIndent = 0; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_STARTINDENT & pf.dwMask) != 0) selIndent = pf.dxStartIndent; return Twip2Pixel(IntPtr.Zero, selIndent, true); } set { ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_STARTINDENT; pf.dxStartIndent = Pixel2Twip(IntPtr.Zero, value, true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectionLengthDescr) ] public override int SelectionLength { get { if (!IsHandleCreated) { return base.SelectionLength; } // RichTextBox allows the user to select the EOF character, // but we don't want to include this in the SelectionLength. // So instead of sending EM_GETSEL, we just obtain the SelectedText and return // the length of it. // return SelectedText.Length; } set { base.SelectionLength = value; } } ////// Gets or sets the number of characters selected in the text /// box. /// ////// /// true if the current selection prevents any changes to its contents. /// [ DefaultValue(false), SRDescription(SR.RichTextBoxSelProtected), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public bool SelectionProtected { get { ForceHandleCreate(); return GetCharFormat(RichTextBoxConstants.CFM_PROTECTED, RichTextBoxConstants.CFM_PROTECTED) == RichTextBoxSelectionAttribute.All; } set { ForceHandleCreate(); SetCharFormat(RichTextBoxConstants.CFM_PROTECTED, value ? RichTextBoxConstants.CFE_PROTECTED : 0, RichTextBoxSelectionAttribute.All); } } ////// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL) /// in Win9x. /// internal override bool SelectionUsesDbcsOffsetsInWin9x { get { return false; // false for RichEdit, true for Edit. } } ////// /// The currently selected text of a RichTextBox control, including /// all Rtf codes. /// [ DefaultValue(""), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelRTF) ] public string SelectedRtf { get { ForceHandleCreate(); return StreamOut(RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_RTF); } set { ForceHandleCreate(); if (value == null) value = ""; StreamIn(value, RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_RTF); } } ////// /// The distance (in pixels) between the right edge of the RichTextBox control and /// the right edge of the text that is selected or added at the current /// insertion point. /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelRightIndent) ] public int SelectionRightIndent { get { int selRightIndent = 0; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_RIGHTINDENT & pf.dwMask) != 0) selRightIndent = pf.dxRightIndent; return Twip2Pixel(IntPtr.Zero, selRightIndent, true); } set { if (value < 0) throw new ArgumentOutOfRangeException("SelectionRightIndent", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionRightIndent", value, 0)); ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_RIGHTINDENT; pf.dxRightIndent = Pixel2Twip(IntPtr.Zero, value, true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// The absolute tab positions (in pixels) of text in a RichTextBox control. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelTabs) ] public int[] SelectionTabs { get { int[] selTabs = new int[0]; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_TABSTOPS & pf.dwMask) != 0) { selTabs = new int[pf.cTabCount]; for (int x = 0; x < pf.cTabCount; x++) selTabs[x] = Twip2Pixel(IntPtr.Zero, pf.rgxTabs[x], true); } return selTabs; } set { // Verify the argument, and throw an error if is bad if (value != null && value.Length > RichTextBoxConstants.MAX_TAB_STOPS) throw new ArgumentOutOfRangeException("SelectionTabs", SR.GetString(SR.SelTabCountRange)); ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph because // we need to get the number of tabstops to copy UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); pf.cTabCount = (short)((value == null) ? 0 : value.Length); pf.dwMask = RichTextBoxConstants.PFM_TABSTOPS; for (int x = 0; x < pf.cTabCount; x++) pf.rgxTabs[x] = Pixel2Twip(IntPtr.Zero, value[x], true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// The currently selected text of a RichTextBox control; consists of a /// zero length string if no characters are selected. /// [ DefaultValue(""), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelText) ] public override string SelectedText { get { ForceHandleCreate(); String text = StreamOut(RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); return text; } set { ForceHandleCreate(); StreamIn(value, RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); } } ////// /// The type of the current selection. The returned value is one /// of the values enumerated in RichTextBoxSelectionType. /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelTypeDescr) ] public RichTextBoxSelectionTypes SelectionType { get { ForceHandleCreate(); if (SelectionLength > 0) { int n; n = (int)SendMessage(RichTextBoxConstants.EM_SELECTIONTYPE, 0, 0); return (RichTextBoxSelectionTypes)n; } else { return RichTextBoxSelectionTypes.Empty; } } } ////// /// Whether or not the left edge of the control will have a "selection margin" which /// can be used to select entire lines /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.RichTextBoxSelMargin) ] public bool ShowSelectionMargin { get { return richTextBoxFlags[showSelBarSection] != 0; } set { if (value != ShowSelectionMargin) { richTextBoxFlags[showSelBarSection] = value ? 1 : 0; if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETOPTIONS, value ? RichTextBoxConstants.ECOOP_OR : RichTextBoxConstants.ECOOP_XOR, RichTextBoxConstants.ECO_SELECTIONBAR); } } } } ////// /// [ Localizable(true), RefreshProperties(RefreshProperties.All) ] public override string Text { get { if (IsDisposed) { return base.Text; } if (RecreatingHandle || GetAnyDisposingInHierarchy()) { // We can return any old garbage if we're in the process of recreating the handle return ""; } if (!IsHandleCreated && textRtf == null) { if (textPlain != null) { return textPlain; } else { return base.Text; } } else { // if the handle is created, we are golden, however // if the handle isn't created, but textRtf was // specified, we need the RichEdit to translate // for us, so we must create the handle; // ForceHandleCreate(); return StreamOut(RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); } } set { using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Text)) { textRtf = null; if (!IsHandleCreated) { textPlain = value; } else { textPlain = null; if (value == null) { value = ""; } StreamIn(value, RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); // reset Modified SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); } } } } private bool SuppressTextChangedEvent { get { return richTextBoxFlags[suppressTextChangedEventSection] != 0; } set { bool oldValue = SuppressTextChangedEvent; if (value != oldValue) { richTextBoxFlags[suppressTextChangedEventSection] = value ? 1 : 0; CommonProperties.xClearPreferredSizeCache(this); } } } ///[To be supplied.] ///[Browsable(false)] public override int TextLength { get { NativeMethods.GETTEXTLENGTHEX gtl = new NativeMethods.GETTEXTLENGTHEX(); gtl.flags = RichTextBoxConstants.GTL_NUMCHARS; if (Marshal.SystemDefaultCharSize == 1 /*ANSI*/) { gtl.codepage = 0; /* CP_ANSI */; } else { gtl.codepage = 1200; /* CP_UNICODE */ } return (int) UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTLENGTHEX, gtl, 0 /*ignored*/); } } /// /// /// Returns the name of the action that will be undone if the user /// Undo's their last operation. If no operation can be undone, it will /// return an empty string (""). /// //NOTE: This is overridable, because we want people to be able to // mess with the names if necessary...? [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxUndoActionNameDescr) ] public string UndoActionName { get { if (!CanUndo) return ""; int n; n = (int)SendMessage(RichTextBoxConstants.EM_GETUNDONAME, 0, 0); return GetEditorActionName(n); } } private string GetEditorActionName(int actionID) { switch (actionID) { case 0: return SR.GetString(SR.RichTextBox_IDUnknown); case 1: return SR.GetString(SR.RichTextBox_IDTyping); case 2: return SR.GetString(SR.RichTextBox_IDDelete); case 3: return SR.GetString(SR.RichTextBox_IDDragDrop); case 4: return SR.GetString(SR.RichTextBox_IDCut); case 5: return SR.GetString(SR.RichTextBox_IDPaste); default: goto case 0; } } ////// /// The current zoom level for the RichTextBox control. This may be between 1/64 and 64. 1.0 indicates /// no zoom (i.e. normal viewing). Zoom works best with TrueType fonts; /// for non-TrueType fonts, ZoomFactor will be treated as the nearest whole number. /// [ SRCategory(SR.CatBehavior), DefaultValue(1.0f), Localizable(true), SRDescription(SR.RichTextBoxZoomFactor) ] public float ZoomFactor { get { if (IsHandleCreated) { int numerator = 0; int denominator = 0; SendMessage(RichTextBoxConstants.EM_GETZOOM, ref numerator, ref denominator); if ( (numerator != 0) && (denominator != 0) ) { zoomMultiplier = ((float)numerator)/((float)denominator); } else { zoomMultiplier = 1.0f; } return zoomMultiplier; } else return zoomMultiplier; } set { if (zoomMultiplier == value) return; if (value <= 0.015625f || value >= 64.0f) throw new ArgumentOutOfRangeException("ZoomFactor", SR.GetString(SR.InvalidExBoundArgument, "ZoomFactor", (value).ToString(CultureInfo.CurrentCulture), (0.015625f).ToString(CultureInfo.CurrentCulture), (64.0f).ToString(CultureInfo.CurrentCulture))); SendZoomFactor(value); } } ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxContentsResized)] public event ContentsResizedEventHandler ContentsResized { add { Events.AddHandler(EVENT_REQUESTRESIZE, value); } remove { Events.RemoveHandler(EVENT_REQUESTRESIZE, value); } } ///[To be supplied.] ////// /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop /// may still be used: this should be hidden in the property grid, but not in code /// [Browsable(false)] public new event DragEventHandler DragDrop { add { base.DragDrop += value; } remove { base.DragDrop -= value; } } ////// /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop /// may still be used: this should be hidden in the property grid, but not in code /// [Browsable(false)] public new event DragEventHandler DragEnter { add { base.DragEnter += value; } remove { base.DragEnter -= value; } } ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event EventHandler DragLeave { add { base.DragLeave += value; } remove { base.DragLeave -= value; } } ///[To be supplied.] ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event DragEventHandler DragOver { add { base.DragOver += value; } remove { base.DragOver -= value; } } ///[To be supplied.] ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event GiveFeedbackEventHandler GiveFeedback { add { base.GiveFeedback += value; } remove { base.GiveFeedback -= value; } } ///[To be supplied.] ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event QueryContinueDragEventHandler QueryContinueDrag { add { base.QueryContinueDrag += value; } remove { base.QueryContinueDrag -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxHScroll)] public event EventHandler HScroll { add { Events.AddHandler(EVENT_HSCROLL, value); } remove { Events.RemoveHandler(EVENT_HSCROLL, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxLinkClick)] public event LinkClickedEventHandler LinkClicked { add { Events.AddHandler(EVENT_LINKACTIVATE, value); } remove { Events.RemoveHandler(EVENT_LINKACTIVATE, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxIMEChange)] public event EventHandler ImeChange { add { Events.AddHandler(EVENT_IMECHANGE, value); } remove { Events.RemoveHandler(EVENT_IMECHANGE, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxProtected)] public event EventHandler Protected { add { Events.AddHandler(EVENT_PROTECTED, value); } remove { Events.RemoveHandler(EVENT_PROTECTED, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxSelChange)] public event EventHandler SelectionChanged { add { Events.AddHandler(EVENT_SELCHANGE, value); } remove { Events.RemoveHandler(EVENT_SELCHANGE, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxVScroll)] public event EventHandler VScroll { add { Events.AddHandler(EVENT_VSCROLL, value); } remove { Events.RemoveHandler(EVENT_VSCROLL, value); } } ///[To be supplied.] ////// /// Returns a boolean indicating whether the RichTextBoxConstants control can paste the /// given clipboard format. /// public bool CanPaste(DataFormats.Format clipFormat) { bool b = false; b = (int)SendMessage(RichTextBoxConstants.EM_CANPASTE, clipFormat.Id, 0) != 0; return b; } //DrawToBitmap doesn't work for this control, so we should hide it. We'll //still call base so that this has a chance to work if it can. [EditorBrowsable(EditorBrowsableState.Never)] new public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) { base.DrawToBitmap(bitmap, targetBounds); } private unsafe int EditStreamProc(IntPtr dwCookie, IntPtr buf, int cb, out int transferred) { int ret = 0; // assume that everything is Okay byte[] bytes = new byte[cb]; int cookieVal = (int)dwCookie; transferred = 0; try { switch (cookieVal & DIRECTIONMASK) { case RichTextBox.OUTPUT: { if (editStream == null) { editStream = new MemoryStream(); } switch (cookieVal & KINDMASK) { case RichTextBox.RTF: case RichTextBox.TEXTCRLF: Marshal.Copy(buf, bytes, 0, cb); editStream.Write(bytes, 0, cb); break; case RichTextBox.TEXTLF: // Strip out \r characters so that we consistently return // \n for linefeeds. In a future version the RichEdit control // may support a SF_NOXLATCRLF flag which would do this for // us. Internally the RichEdit stores the text with only // a \n, so we want to keep that the same here. // if ((cookieVal & UNICODE) != 0) { Debug.Assert(cb % 2 == 0, "EditStreamProc call out of cycle. Expected to always get character boundary calls"); int requestedCharCount = cb/2; int consumedCharCount = 0; fixed (byte* pb = bytes) { char* pChars = (char*)pb; char* pBuffer = (char*)(long)buf; for (int i=0; i/// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str) { return Find(str, 0, 0, RichTextBoxFinds.None); } ////// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str, RichTextBoxFinds options) { return Find(str, 0, 0, options); } ////// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str, int start, RichTextBoxFinds options) { return Find(str, start, -1, options); } ////// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str, int start, int end, RichTextBoxFinds options) { int textLen = TextLength; if (str == null) throw new ArgumentNullException("str"); if (start < 0 || start > textLen) throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLen)); if (end < -1) throw new ArgumentOutOfRangeException("end", SR.GetString(SR.RichTextFindEndInvalid, end)); bool selectWord = true; NativeMethods.FINDTEXT ft = new NativeMethods.FINDTEXT(); ft.chrg = new NativeMethods.CHARRANGE(); // set up the default values for the FINDTEXT structure, that is // the given string and the whole range of the text stream ft.lpstrText = str; if (end == -1) { end = textLen; } if (start > end) { throw new ArgumentException(SR.GetString(SR.RichTextFindEndInvalid, end)); } if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { // normal // ft.chrg.cpMin = start; ft.chrg.cpMax = end; } else { // reverse // ft.chrg.cpMin = end; ft.chrg.cpMax = start; } // force complete search if we ended up with a zero length search if (ft.chrg.cpMin == ft.chrg.cpMax) { if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { ft.chrg.cpMin = 0; ft.chrg.cpMax = -1; } else { ft.chrg.cpMin = textLen; ft.chrg.cpMax = 0; } } // set up the options for the search int findOptions = 0; if ((options & RichTextBoxFinds.WholeWord) == RichTextBoxFinds.WholeWord) findOptions |= RichTextBoxConstants.FR_WHOLEWORD; if ((options & RichTextBoxFinds.MatchCase) == RichTextBoxFinds.MatchCase) findOptions |= RichTextBoxConstants.FR_MATCHCASE; if ((options & RichTextBoxFinds.NoHighlight) == RichTextBoxFinds.NoHighlight) selectWord = false; if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { // The default for RichEdit 2.0 is to search in reverse findOptions |= RichTextBoxConstants.FR_DOWN; } // Perform the find, will return ubyte position int position; position = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_FINDTEXT, findOptions, ft); // if we didn't find anything, or we don't have to select what was found, // we're done if (position != -1 && selectWord) { // Select the string found, this is done in ubyte units NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); chrg.cpMin = position; //Look for kashidas in the string. A kashida is an arabic visual justification character //that's not semantically meaningful. Searching for ABC might find AB_C (where A,B, and C //represent Arabic characters and _ represents a kashida). We should highlight the text //including the kashida (VSWhidbey 94809). char kashida = (char)0x640; string text = this.Text; string foundString = text.Substring(position, str.Length); int startIndex = foundString.IndexOf(kashida); if (startIndex == -1) { //No kashida in the string chrg.cpMax = position + str.Length; } else { //There's at least one kashida int searchingCursor; //index into search string int foundCursor; //index into Text for (searchingCursor = startIndex, foundCursor = position + startIndex; searchingCursor < str.Length; searchingCursor++, foundCursor++) { while (text[foundCursor] == kashida && str[searchingCursor] != kashida) { foundCursor++; } } chrg.cpMax = foundCursor; } UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, chrg); SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0); } return position; } ////// /// Searches the text in a RichTextBox control for the given characters. /// public int Find(char[] characterSet) { return Find(characterSet, 0, -1); } ////// /// Searches the text in a RichTextBox control for the given characters. /// public int Find(char[] characterSet, int start) { return Find(characterSet, start, -1); } ////// /// Searches the text in a RichTextBox control for the given characters. /// public int Find(char[] characterSet, int start, int end) { // Code used to support ability to search backwards and negate character sets. // The API no longer supports this, but in case we change our mind, I'm leaving // the ability in here. bool forward = true; bool negate = false; int textLength = TextLength; if (characterSet == null) throw new ArgumentNullException("characterSet"); if (start < 0 || start > textLength) throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLength)); if (end < start && end != -1) throw new ArgumentOutOfRangeException("end", SR.GetString(SR.InvalidLowBoundArgumentEx, "end", end, "start")); // Don't do anything if we get nothing to look for if (characterSet.Length == 0) return -1; int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); if (start == end) { start = 0; end = textLen; } if (end == -1) { end = textLen; } NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); // The range of characters we have searched chrg.cpMax = chrg.cpMin = start; // Use the TEXTRANGE to move our text buffer forward // or backwards within the main text NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); // Characters we have slurped into memory in order to search txrg.chrg = new NativeMethods.CHARRANGE(); txrg.chrg.cpMin = chrg.cpMin; txrg.chrg.cpMax = chrg.cpMax; UnsafeNativeMethods.CharBuffer charBuffer; charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(CHAR_BUFFER_LEN + 1); txrg.lpstrText = charBuffer.AllocCoTaskMem(); if (txrg.lpstrText == IntPtr.Zero) throw new OutOfMemoryException(); try { bool done = false; // We want to loop as long as it takes. This loop will grab a // chunk of text out from the control as directed by txrg.chrg; while (!done) { if (forward) { // Move forward by starting at the end of the // previous text window and extending by the // size of our buffer txrg.chrg.cpMin = chrg.cpMax; txrg.chrg.cpMax += CHAR_BUFFER_LEN; } else { // Move backwards by anchoring at the start // of the previous buffer window, and backing // up by the desired size of our buffer txrg.chrg.cpMax = chrg.cpMin; txrg.chrg.cpMin -= CHAR_BUFFER_LEN; // We need to keep our request within the // lower bound of zero if (txrg.chrg.cpMin < 0) txrg.chrg.cpMin = 0; } if (end != -1) txrg.chrg.cpMax = Math.Min(txrg.chrg.cpMax, end); // go get the text in this range, if we didn't get any text then punt int len; len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg); if (len == 0) { chrg.cpMax = chrg.cpMin = -1; // Hit end of control without finding what we wanted break; } // get the data from RichTextBoxConstants into a string for us to use. charBuffer.PutCoTaskMem(txrg.lpstrText); string str = charBuffer.GetString(); // Loop through our text if (forward) { // Start at the begining of the buffer for (int x = 0; x < len; x++) { // Is it in char set? bool found = GetCharInCharSet(str[x], characterSet, negate); if (found) { done = true; break; } // Advance the buffer chrg.cpMax++; } } else { // Span reverse. int x = len; while (x-- != 0) { // Is it in char set? bool found = GetCharInCharSet(str[x], characterSet, negate); if (found) { done = true; break; } // Bring the selection back while keeping it anchored chrg.cpMin--; } } } } finally { // release the resources we got for our GETTEXTRANGE operation. if (txrg.lpstrText != IntPtr.Zero) Marshal.FreeCoTaskMem(txrg.lpstrText); } int index = (forward) ? chrg.cpMax : chrg.cpMin; return index; } private void ForceHandleCreate() { if (!IsHandleCreated) { CreateHandle(); } } // Sends set color message to HWND; doesn't call Control.SetForeColor private bool InternalSetForeColor(Color value) { NativeMethods.CHARFORMATA cf = GetCharFormat(false); if ((cf.dwMask & RichTextBoxConstants.CFM_COLOR) != 0 && ColorTranslator.ToWin32(value) == cf.crTextColor) { return true; } cf.dwMask = RichTextBoxConstants.CFM_COLOR; cf.dwEffects = 0; cf.crTextColor = ColorTranslator.ToWin32(value); return SetCharFormat(RichTextBoxConstants.SCF_ALL, cf); } private NativeMethods.CHARFORMATA GetCharFormat(bool fSelection) { NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETCHARFORMAT, fSelection ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_DEFAULT, cf); return cf; } private NativeMethods.CHARFORMAT2A GetCharFormat2(bool fSelection) { NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETCHARFORMAT, fSelection ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_DEFAULT, cf2); return cf2; } ////// /// ///private RichTextBoxSelectionAttribute GetCharFormat(int mask, int effect) { RichTextBoxSelectionAttribute charFormat = RichTextBoxSelectionAttribute.None; // check to see if the control has been created if (IsHandleCreated) { NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & mask) != 0) // if the text has the desired effect if ((cf.dwEffects & effect) != 0) charFormat = RichTextBoxSelectionAttribute.All; } return charFormat; } Font GetCharFormatFont(bool selectionOnly) { ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(selectionOnly); if ((cf.dwMask & RichTextBoxConstants.CFM_FACE) == 0) { return null; } string fontName = Encoding.Default.GetString(cf.szFaceName); int index = fontName.IndexOf('\0'); if (index != -1) { fontName = fontName.Substring(0, index); } float fontSize = 13; if ((cf.dwMask & RichTextBoxConstants.CFM_SIZE) != 0) { fontSize = (float)cf.yHeight/(float)20.0; if (fontSize == 0 && cf.yHeight > 0) { fontSize = 1; } } FontStyle style = FontStyle.Regular; if ((cf.dwMask & RichTextBoxConstants.CFM_BOLD) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_BOLD) != 0) style |= FontStyle.Bold; if ((cf.dwMask & RichTextBoxConstants.CFM_ITALIC) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_ITALIC) != 0) style |= FontStyle.Italic; if ((cf.dwMask & RichTextBoxConstants.CFM_STRIKEOUT) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_STRIKEOUT) != 0) style |= FontStyle.Strikeout; if ((cf.dwMask & RichTextBoxConstants.CFM_UNDERLINE) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_UNDERLINE) != 0) style |= FontStyle.Underline; try { return new Font(fontName, fontSize, style, GraphicsUnit.Point, cf.bCharSet); } catch { } return null; } /// /// /// Returns the index of the character nearest to the given point. /// public override int GetCharIndexFromPosition(Point pt) { NativeMethods.POINT wpt = new NativeMethods.POINT(pt.X, pt.Y); int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, wpt); string t = this.Text; // EM_CHARFROMPOS will return an invalid number if the last character in the RichEdit // is a newline. // if (index >= t.Length) { index = Math.Max(t.Length - 1, 0); } return index; } ////// /// ///private bool GetCharInCharSet(char c, char[] charSet, bool negate) { bool match = false; int charSetLen = charSet.Length; // Loop through the given character set and compare for a match for (int i = 0; !match && i < charSetLen; i++) match = c == charSet[i]; return negate ? !match : match; } /// /// /// Returns the number of the line containing a specified character position /// in a RichTextBox control. Note that this returns the physical line number /// and not the conceptual line number. For example, if the first conceptual /// line (line number 0) word-wraps and extends to the second line, and if /// you pass the index of a overflowed character, GetLineFromCharIndex would /// return 1 and not 0. /// public override int GetLineFromCharIndex(int index) { return (int)SendMessage(RichTextBoxConstants.EM_EXLINEFROMCHAR, 0, index); } ////// /// Returns the location of the character at the given index. /// public override Point GetPositionFromCharIndex(int index) { if (richEditMajorVersion == 2) { return base.GetPositionFromCharIndex(index); } if (index < 0 || index > Text.Length) { return Point.Empty; } NativeMethods.POINT pt = new NativeMethods.POINT(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_POSFROMCHAR, pt, index); return new Point(pt.x, pt.y); } ////// /// ///private bool GetProtectedError() { if (ProtectedError) { ProtectedError = false; return true; } return false; } /// /// /// Loads the contents of the given RTF or text file into a RichTextBox control. /// public void LoadFile(string path) { LoadFile(path, RichTextBoxStreamType.RichText); } ////// /// Loads the contents of a RTF or text into a RichTextBox control. /// public void LoadFile(string path, RichTextBoxStreamType fileType) { //valid values are 0x0 to 0x4 if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)){ throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); try { LoadFile(file, fileType); } finally { file.Close(); } } ////// /// Loads the contents of a RTF or text into a RichTextBox control. /// public void LoadFile(Stream data, RichTextBoxStreamType fileType) { //valid values are 0x0 to 0x4 if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)) { throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } int flags; switch (fileType) { case RichTextBoxStreamType.RichText: flags = RichTextBoxConstants.SF_RTF; break; case RichTextBoxStreamType.PlainText: this.Rtf = ""; flags = RichTextBoxConstants.SF_TEXT; break; case RichTextBoxStreamType.UnicodePlainText: flags = RichTextBoxConstants.SF_UNICODE | RichTextBoxConstants.SF_TEXT; break; default: throw new ArgumentException(SR.GetString(SR.InvalidFileType)); } StreamIn(data, flags); } ////// /// protected override void OnBackColorChanged(EventArgs e) { if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); } base.OnBackColorChanged(e); } ///[To be supplied.] ///protected override void OnContextMenuChanged(EventArgs e) { base.OnContextMenuChanged(e); UpdateOleCallback(); } /// /// /// protected override void OnRightToLeftChanged(EventArgs e) { base.OnRightToLeftChanged(e); //VSWhidbey# 325345. // When the RTL property is changed, here's what happens. Let's assume that we change from // RTL.No to RTL.Yes. // 1. RecreateHandle is called. // 2. In RTB.OnHandleDestroyed, we cache off any RTF that might have been set. // The RTB has been set to the empty string, so we do get RTF back. The RTF // contains formatting info, but doesn't contain any reading-order info, // so RichEdit defaults to LTR reading order. // 3. In RTB.OnHandleCreated, we check if we have any cached RTF, and if so, // we want to set the RTF to that value. This is to ensure that the original // text doesn't get lost. // 4. In the RTF setter, we get the current RTF, compare it to the old RTF, and // since those are not equal, we set the RichEdit content to the old RTF. // 5. But... since the original RTF had no reading-order info, the reading-order // will default to LTR - thus VSWhidbey #325345. // That's why in Everett we set the text back since that clears the RTF, thus restoring // the reading order to that of the window style. The problem here is that when there's // no initial text (the empty string), then WindowText would not actually set the text, // and we were left with the LTR reading order. There's no longer any initial text (as in Everett, // e.g richTextBox1), since we changed the designers to not set text. Sigh... // So the fix is to force windowtext, whether or not that window text is equal to what's already there. // Note that in doing so we will lose any formatting info you might have set on the RTF. We are okay with that. // We use WindowText rather than Text because this way we can avoid // spurious TextChanged events. // string oldText = WindowText; ForceWindowText(null); ForceWindowText(oldText); } ///[To be supplied.] ////// /// Fires an event when the user changes the control's contents /// are either smaller or larger than the control's window size. /// protected virtual void OnContentsResized(ContentsResizedEventArgs e) { ContentsResizedEventHandler handler = (ContentsResizedEventHandler)Events[EVENT_REQUESTRESIZE]; if (handler != null) handler(this,e); } ////// /// protected override void OnHandleCreated(EventArgs e) { // base.OnHandleCreated is called somewhere in the middle of this curSelStart = curSelEnd = curSelType = -1; // We will always set the control to use the maximum text, it defaults to 32k.. // This must be done before we start loading files, because some files may // be larger than 32k. // UpdateMaxLength(); // This is needed so that the control will fire change and update events // even if it is hidden // SendMessage(RichTextBoxConstants.EM_SETEVENTMASK, 0, RichTextBoxConstants.ENM_PROTECTED | RichTextBoxConstants.ENM_SELCHANGE | RichTextBoxConstants.ENM_DROPFILES | RichTextBoxConstants.ENM_REQUESTRESIZE | RichTextBoxConstants.ENM_IMECHANGE | RichTextBoxConstants.ENM_CHANGE | RichTextBoxConstants.ENM_UPDATE | RichTextBoxConstants.ENM_SCROLL | RichTextBoxConstants.ENM_KEYEVENTS | RichTextBoxConstants.ENM_MOUSEEVENTS | RichTextBoxConstants.ENM_SCROLLEVENTS | RichTextBoxConstants.ENM_LINK); int rm = rightMargin; rightMargin = 0; RightMargin = rm; // this.SendMessage(RichTextBoxConstants.EM_AUTOURLDETECT, DetectUrls ? 1 : 0, 0); if (selectionBackColorToSetOnHandleCreated != Color.Empty) { this.SelectionBackColor = selectionBackColorToSetOnHandleCreated; } // Initialize colors before initializing RTF, otherwise CFE_AUTOCOLOR will be in effect // and our text will all be Color.WindowText. AutoWordSelection = AutoWordSelection; SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); InternalSetForeColor(ForeColor); // base sets the Text property. It's important to do this *after* setting EM_AUTOUrlDETECT. base.OnHandleCreated(e); // vsWhidbey 371584: for some reason, we need to set the OleCallback before setting the RTF property. UpdateOleCallback(); // RTF property takes precedence over Text property // try { SuppressTextChangedEvent = true; if (textRtf != null) { // setting RTF calls back on Text, which relies on textRTF being null string text = textRtf; textRtf = null; Rtf = text; } else if (textPlain != null) { string text = textPlain; textPlain = null; Text = text; } } finally { SuppressTextChangedEvent = false; } // Since we can't send EM_SETSEL until RTF has been set, // we can't rely on base to do it for us. base.SetSelectionOnHandle(); if (ShowSelectionMargin) { // If you call SendMessage instead of PostMessage, the control // will resize itself to the size of the parent's client area. Don't know why... UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETOPTIONS, (IntPtr)RichTextBoxConstants.ECOOP_OR, (IntPtr)RichTextBoxConstants.ECO_SELECTIONBAR); } if (languageOption != this.LanguageOption) { this.LanguageOption = languageOption; } ClearUndo(); SendZoomFactor(zoomMultiplier); SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); } ///[To be supplied.] ////// /// protected override void OnHandleDestroyed(EventArgs e) { base.OnHandleDestroyed(e); if (!InConstructor) { textRtf = Rtf; if (textRtf.Length == 0) textRtf = null; } oleCallback = null; SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); } ///[To be supplied.] ////// /// Fires an event when the user clicks a RichTextBox control's horizontal /// scroll bar. /// protected virtual void OnHScroll(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_HSCROLL]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user clicks on a link /// in a rich-edit control. /// protected virtual void OnLinkClicked(LinkClickedEventArgs e) { LinkClickedEventHandler handler = (LinkClickedEventHandler)Events[EVENT_LINKACTIVATE]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user changes the control's IME conversion status. /// protected virtual void OnImeChange(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_IMECHANGE]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user is taking an action that would change /// a protected range of text in the RichTextBox control. /// protected virtual void OnProtected(EventArgs e) { ProtectedError = true; EventHandler handler = (EventHandler)Events[EVENT_PROTECTED]; if (handler != null) handler(this,e); } ////// /// Fires an event when the current selection of text in the RichTextBox /// control has changed or the insertion point has moved. /// protected virtual void OnSelectionChanged(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_SELCHANGE]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user clicks a RichTextBox control's vertical /// scroll bar. /// protected virtual void OnVScroll(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_VSCROLL]; if (handler != null) handler(this,e); } ////// /// Pastes the contents of the clipboard in the given clipboard format. /// public void Paste(DataFormats.Format clipFormat) { Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); IntSecurity.ClipboardRead.Demand(); PasteUnsafe(clipFormat, 0); } ////// /// Note that this doesn't make a security demand: functions that call this should. /// private void PasteUnsafe(DataFormats.Format clipFormat, int hIcon) { NativeMethods.REPASTESPECIAL rps = null; if (hIcon != 0) { rps = new NativeMethods.REPASTESPECIAL(); rps.dwAspect = DVASPECT_ICON; rps.dwParam = hIcon; } UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_PASTESPECIAL, clipFormat.Id, rps); } ///[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override bool ProcessCmdKey(ref Message m, Keys keyData) { if (this.RichTextShortcutsEnabled == false) { foreach (int shortcutValue in shortcutsToDisable) { if ((int)keyData == shortcutValue) { return true; } } } return base.ProcessCmdKey(ref m, keyData); } /// /// /// Redoes the last undone editing operation. /// public void Redo() { SendMessage(RichTextBoxConstants.EM_REDO, 0, 0); } //NOTE: Undo is implemented on TextBox ////// /// Saves the contents of a RichTextBox control to a file. /// public void SaveFile(string path) { SaveFile(path, RichTextBoxStreamType.RichText); } ////// /// Saves the contents of a RichTextBox control to a file. /// public void SaveFile(string path, RichTextBoxStreamType fileType) { //valid values are 0x0 to 0x4 if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)) { throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } Stream file = File.Create(path); try { SaveFile(file, fileType); } finally { file.Close(); } } ////// /// Saves the contents of a RichTextBox control to a file. /// public void SaveFile(Stream data, RichTextBoxStreamType fileType) { int flags; switch (fileType) { case RichTextBoxStreamType.RichText: flags = RichTextBoxConstants.SF_RTF; break; case RichTextBoxStreamType.PlainText: flags = RichTextBoxConstants.SF_TEXT; break; case RichTextBoxStreamType.UnicodePlainText: flags = RichTextBoxConstants.SF_UNICODE | RichTextBoxConstants.SF_TEXT; break; case RichTextBoxStreamType.RichNoOleObjs: flags = RichTextBoxConstants.SF_RTFNOOBJS; break; case RichTextBoxStreamType.TextTextOleObjs: flags = RichTextBoxConstants.SF_TEXTIZED; break; default: throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } StreamOut(data, flags, true); } ////// /// Core Zoom calculation and message passing (used by ZoomFactor property and CreateHandle() /// ///private void SendZoomFactor(float zoom) { int numerator; int denominator; if (zoom == 1.0f) { denominator = 0; numerator = 0; } else { denominator = 1000; float multiplier = 1000 * zoom; numerator = (int)Math.Ceiling(multiplier); if (numerator >= 64000) { numerator = (int)Math.Floor(multiplier); } } if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETZOOM, numerator, denominator); #if DEBUG // DEBUG CODE: Verify that EM_SETZOOM actually set the zoom int n = 0, d = 0; SendMessage(RichTextBoxConstants.EM_GETZOOM, ref n, ref d); Debug.Assert(n == numerator && d == denominator, "EM_SETZOOM failed"); // END DEBUG CODE #endif } if (numerator != 0) { zoomMultiplier = ((float)numerator)/((float)denominator); } else { zoomMultiplier = 1.0f; } } /// /// /// ///private bool SetCharFormat(int mask, int effect, RichTextBoxSelectionAttribute charFormat) { // check to see if the control has been created if (IsHandleCreated) { NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); cf.dwMask = mask; switch (charFormat) { case RichTextBoxSelectionAttribute.All: cf.dwEffects = effect; break; case RichTextBoxSelectionAttribute.None: cf.dwEffects = 0; break; default: throw new ArgumentException(SR.GetString(SR.UnknownAttr)); } // set the format information return IntPtr.Zero != UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); } return false; } private bool SetCharFormat(int charRange, NativeMethods.CHARFORMATA cf) { return IntPtr.Zero != UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, charRange, cf); } private void SetCharFormatFont(bool selectionOnly, Font value) { ForceHandleCreate(); NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); FontToLogFont(value, logfont); byte[] bytesFaceName; int dwMask = RichTextBoxConstants.CFM_FACE | RichTextBoxConstants.CFM_SIZE | RichTextBoxConstants.CFM_BOLD | RichTextBoxConstants.CFM_ITALIC | RichTextBoxConstants.CFM_STRIKEOUT | RichTextBoxConstants.CFM_UNDERLINE | RichTextBoxConstants.CFM_CHARSET; int dwEffects = 0; if (value.Bold) dwEffects |= RichTextBoxConstants.CFE_BOLD; if (value.Italic) dwEffects |= RichTextBoxConstants.CFE_ITALIC; if (value.Strikeout) dwEffects |= RichTextBoxConstants.CFE_STRIKEOUT; if (value.Underline) dwEffects |= RichTextBoxConstants.CFE_UNDERLINE; if (Marshal.SystemDefaultCharSize == 1) { bytesFaceName = Encoding.Default.GetBytes(logfont.lfFaceName); NativeMethods.CHARFORMATA cfA = new NativeMethods.CHARFORMATA(); for (int i=0; i /// /// This is just here as a minor perf improvement, so we don't have to call expensive RevertAssert. /// When the method exits, we effectively revert the assert. /// [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] static private void FontToLogFont(Font value, NativeMethods.LOGFONT logfont) { value.ToLogFont(logfont); } ////// /// private static void SetupLogPixels(IntPtr hDC) { bool release = false; if (hDC == IntPtr.Zero) { hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); release = true; } if (hDC == IntPtr.Zero) return; logPixelsX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSX); logPixelsY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSY); if (release) UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); } private static int Pixel2Twip(IntPtr hDC, int v, bool xDirection) { SetupLogPixels(hDC); int logP = xDirection ? logPixelsX : logPixelsY; return(int) ((((double)v) / logP) * 72.0 * 20.0); } private static int Twip2Pixel(IntPtr hDC, int v, bool xDirection) { SetupLogPixels(hDC); int logP = xDirection ? logPixelsX : logPixelsY; return(int) (((((double) v) / 20.0) / 72.0) * logP); } ////// /// ///private void StreamIn(string str, int flags) { if (str.Length == 0 ) { // Destroy the selection if callers was setting // selection text // if ((RichTextBoxConstants.SFF_SELECTION & flags) != 0) { SendMessage(NativeMethods.WM_CLEAR, 0, 0); ProtectedError = false; return; } // WM_SETTEXT is allowed even if we have protected text // SendMessage(NativeMethods.WM_SETTEXT, 0, ""); return; } // Rather than work only some of the time with null characters, // we're going to be consistent and never work with them. int nullTerminatedLength = str.IndexOf((char) 0); if (nullTerminatedLength != -1) { str = str.Substring(0, nullTerminatedLength); } // get the string into a byte array byte[] encodedBytes; if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { encodedBytes = Encoding.Unicode.GetBytes(str); } else { encodedBytes = Encoding.Default.GetBytes(str); } editStream = new MemoryStream(encodedBytes.Length); editStream.Write(encodedBytes, 0, encodedBytes.Length); editStream.Position = 0; StreamIn(editStream, flags); } private void StreamIn(Stream data, int flags) { // clear out the selection only if we are replacing all the text // if ((flags & RichTextBoxConstants.SFF_SELECTION) == 0) { NativeMethods.CHARRANGE cr = new NativeMethods.CHARRANGE(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, cr); } try { editStream = data; Debug.Assert(data != null, "StreamIn passed a null stream"); // If SF_RTF is requested then check for the RTF tag at the start // of the file. We don't load if the tag is not there // if ((flags & RichTextBoxConstants.SF_RTF) != 0) { long streamStart = editStream.Position; byte[] bytes = new byte[SZ_RTF_TAG.Length]; editStream.Read(bytes, (int)streamStart, SZ_RTF_TAG.Length); string str = Encoding.Default.GetString(bytes); if (!SZ_RTF_TAG.Equals(str)) throw new ArgumentException(SR.GetString(SR.InvalidFileFormat)); // put us back at the start of the file editStream.Position = streamStart; } int cookieVal = 0; // set up structure to do stream operation NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM(); if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { cookieVal = INPUT | UNICODE; } else { cookieVal = INPUT | ANSI; } if ((flags & RichTextBoxConstants.SF_RTF) != 0) { cookieVal |= RTF; } else { cookieVal |= TEXTLF; } es.dwCookie = (IntPtr) cookieVal; es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc); // gives us TextBox compatible behavior, programatic text change shouldn't // be limited... // SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, Int32.MaxValue); // go get the text for the control //Weird hack needed for 64-bit if (IntPtr.Size == 8) { NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es64); //Assign back dwError value es.dwError = GetErrorValue64(es64); } else { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es); } UpdateMaxLength(); // If we failed to load because of protected // text then return protect event was fired so no // exception is required for the the error if (GetProtectedError()) return; if (es.dwError != 0) throw new InvalidOperationException(SR.GetString(SR.LoadTextError)); // set the modify tag on the control SendMessage(NativeMethods.EM_SETMODIFY, -1, 0); // EM_GETLINECOUNT will cause the RichTextBoxConstants to recalculate its line indexes SendMessage(NativeMethods.EM_GETLINECOUNT, 0, 0); } finally { // release any storage space held. editStream = null; } } /// /// /// ///private string StreamOut(int flags) { Stream stream = new MemoryStream(); StreamOut(stream, flags, false); stream.Position = 0; int streamLength = (int)stream.Length; string result = string.Empty; if (streamLength > 0) { byte[] bytes = new byte[streamLength]; stream.Read(bytes, 0, streamLength); if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { result = Encoding.Unicode.GetString(bytes,0 , bytes.Length); } else { result = Encoding.Default.GetString(bytes, 0, bytes.Length); } // workaround ??? for bug 117325. When the string is modified is can return // with an extra null termination (always?). If it does, get rid of it. if(!String.IsNullOrEmpty(result) && (result[result.Length-1] == '\0')) { result = result.Substring(0, result.Length-1); } } return result; } private void StreamOut(Stream data, int flags, bool includeCrLfs) { // set up the EDITSTREAM structure for the callback. Debug.Assert(data != null, "StreamOut passed a null stream"); editStream = data; try { int cookieVal = 0; NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM(); if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { cookieVal = OUTPUT | UNICODE; } else { cookieVal = OUTPUT | ANSI; } if ((flags & RichTextBoxConstants.SF_RTF) != 0) { cookieVal |= RTF; } else { if (includeCrLfs) { cookieVal |= TEXTCRLF; } else { cookieVal |= TEXTLF; } } es.dwCookie = (IntPtr) cookieVal; es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc); //Get Text //Weird hack needed for 64-bit if (IntPtr.Size == 8) { NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMOUT, flags, es64); //Assign back dwError value es.dwError = GetErrorValue64(es64); } else { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMOUT, flags, es); } // check to make sure things went well if (es.dwError != 0) throw new InvalidOperationException(SR.GetString(SR.SaveTextError)); } finally { // release any storage space held. editStream = null; } } private unsafe NativeMethods.EDITSTREAM64 ConvertToEDITSTREAM64(NativeMethods.EDITSTREAM es) { NativeMethods.EDITSTREAM64 es64 = new NativeMethods.EDITSTREAM64(); fixed (byte* es64p = &es64.contents[0]) { byte *bp; long l; /* l = (long) es.dwCookie; bp = (byte *) &l; for (int i=0; i < sizeof(long); i++) { es64.contents[i] = bp[i]; }*/ *((long *)es64p) = (long) es.dwCookie; /* int il = es.dwError; bp = (byte *) &il; for (int i=0; i < sizeof(int); i++) { es64.contents[i+8] = bp[i]; }*/ *((int *)(es64p + 8)) = es.dwError; l = (long) Marshal.GetFunctionPointerForDelegate(es.pfnCallback); bp = (byte *) &l; for (int i=0; i < sizeof(long); i++) { es64.contents[i+12] = bp[i]; } //*((long *)(es64p + 12)) = (long) Marshal.GetFunctionPointerForDelegate(es.pfnCallback); } return es64; } private unsafe int GetErrorValue64(NativeMethods.EDITSTREAM64 es64) { int errorVal; fixed (byte* es64p = &es64.contents[0]) { errorVal = *((int *)(es64p + 8)); } return errorVal; } /* FOR 64 BIT DEBUGGING private unsafe string PrintBytes(NativeMethods.EDITSTREAM es) { StringBuilder sb = new StringBuilder(); fixed (IntPtr *ip = &es.dwCookie) { byte *bytep = (byte *) ip; for (int i=0; i < Marshal.SizeOf(es); i++) { sb.Append(bytep[i].ToString() + " "); } } return sb.ToString(); } private unsafe string PrintBytes(NativeMethods.EDITSTREAM64 es64) { StringBuilder sb = new StringBuilder(); fixed (byte *bytep = &es64.contents[0]) { for (int i=0; i < Marshal.SizeOf(es64); i++) { sb.Append(bytep[i].ToString() + " "); } } return sb.ToString(); } */ private void UpdateOleCallback() { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "update ole callback (" + AllowDrop + ")"); if (IsHandleCreated) { if (oleCallback == null) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "binding ole callback"); bool unrestricted = false; try { IntSecurity.UnmanagedCode.Demand(); unrestricted = true; } catch (SecurityException) { unrestricted = false; } if (unrestricted) { this.AllowOleObjects = true; } else { this.AllowOleObjects = (0 != (int) SendMessage(RichTextBoxConstants.EM_SETQUERYRTFOBJ, 0, 1)); } oleCallback = CreateRichEditOleCallback(); // Forcibly QI (through IUnknown::QueryInterface) to handle multiple // definitions of the interface. // IntPtr punk = Marshal.GetIUnknownForObject(oleCallback); try { IntPtr pRichEditOleCallback; Guid iidRichEditOleCallback = typeof(UnsafeNativeMethods.IRichEditOleCallback).GUID; Marshal.QueryInterface(punk, ref iidRichEditOleCallback, out pRichEditOleCallback); try { UnsafeNativeMethods.SendCallbackMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETOLECALLBACK, IntPtr.Zero, pRichEditOleCallback); } finally { Marshal.Release(pRichEditOleCallback); } } finally { Marshal.Release(punk); } } UnsafeNativeMethods.DragAcceptFiles(new HandleRef(this, Handle), false); } } //Note: RichTextBox doesn't work like other controls as far as setting ForeColor/ //BackColor -- you need to send messages to update the colors private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e) { if (IsHandleCreated) { if (this.BackColor.IsSystemColor) { SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); } if (this.ForeColor.IsSystemColor) { InternalSetForeColor(ForeColor); } } } /// /// /// Creates the IRichEditOleCallback compatible object for handling RichEdit callbacks. For more /// information look up the MSDN info on this interface. This is designed to be a back door of /// sorts, which is why it is fairly obscure, and uses the RichEdit name instead of RichTextBox. /// [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected virtual object CreateRichEditOleCallback() { return new OleCallback(this); } ////// /// Handles link messages (mouse move, down, up, dblclk, etc) /// ///private void EnLinkMsgHandler(ref Message m) { NativeMethods.ENLINK enlink; //On 64-bit, we do some custom marshalling to get this to work. The richedit control //unfortunately does not respect IA64 struct alignment conventions. See VSWhidbey #122276 & 504502. if (IntPtr.Size == 8) { enlink = ConvertFromENLINK64((NativeMethods.ENLINK64)m.GetLParam(typeof(NativeMethods.ENLINK64))); } else { enlink = (NativeMethods.ENLINK)m.GetLParam(typeof(NativeMethods.ENLINK)); } switch (enlink.msg) { case NativeMethods.WM_SETCURSOR: LinkCursor = true; m.Result = (IntPtr)1; return; // Mouse-down triggers Url; this matches Outlook 2000's behavior. case NativeMethods.WM_LBUTTONDOWN: string linktext = CharRangeToString(enlink.charrange); if (!string.IsNullOrEmpty(linktext)) { OnLinkClicked(new LinkClickedEventArgs(linktext)); } m.Result = (IntPtr)1; return; } m.Result = IntPtr.Zero; return; } /// /// /// Converts a CHARRANGE to a string. Note: The behavior of this is dependent on the current window /// class name being used. We have to create a CharBuffer of the type of RichTextBox DLL we're using, /// not based on the SystemCharWidth. /// ///private string CharRangeToString(NativeMethods.CHARRANGE c) { NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); txrg.chrg = c; Debug.Assert((c.cpMax-c.cpMin)>0, "CHARRANGE was null or negative - can't do it!"); //Windows bug: 64-bit windows returns a bad range for us. VSWhidbey 504502. //Putting in a hack to avoid an unhandled exception. if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) { return string.Empty; } int characters = (c.cpMax-c.cpMin) + 1; // +1 for null termination UnsafeNativeMethods.CharBuffer charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(characters); IntPtr unmanagedBuffer = charBuffer.AllocCoTaskMem(); if (unmanagedBuffer == IntPtr.Zero) throw new OutOfMemoryException(SR.GetString(SR.OutOfMemory)); txrg.lpstrText = unmanagedBuffer; int len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg); Debug.Assert(len != 0, "CHARRANGE from RichTextBox was bad! - impossible?"); charBuffer.PutCoTaskMem(unmanagedBuffer); if (txrg.lpstrText != IntPtr.Zero) Marshal.FreeCoTaskMem(unmanagedBuffer); string result = charBuffer.GetString(); return result; } internal override void UpdateMaxLength() { if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, MaxLength); } } /// /// /// ///private void WmReflectCommand(ref Message m) { // We check if we're in the middle of handle creation because // the rich edit control fires spurious events during this time. // if (m.LParam == Handle && !GetState(STATE_CREATINGHANDLE)) { switch (Util.HIWORD(m.WParam)) { case NativeMethods.EN_HSCROLL: OnHScroll(EventArgs.Empty); break; case NativeMethods.EN_VSCROLL: OnVScroll(EventArgs.Empty); break; default: base.WndProc(ref m); break; } } else { base.WndProc(ref m); } } /// /// /// ///internal void WmReflectNotify(ref Message m) { if (m.HWnd == Handle) { NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); switch (nmhdr.code) { case RichTextBoxConstants.EN_LINK: EnLinkMsgHandler(ref m); break; case RichTextBoxConstants.EN_DROPFILES: NativeMethods.ENDROPFILES endropfiles = (NativeMethods.ENDROPFILES)m.GetLParam(typeof(NativeMethods.ENDROPFILES)); // Only look at the first file. StringBuilder path = new StringBuilder(NativeMethods.MAX_PATH); UnsafeNativeMethods.DragQueryFile(new HandleRef(endropfiles, endropfiles.hDrop), 0, path, NativeMethods.MAX_PATH); // Try to load the file as an RTF try { LoadFile(path.ToString(), RichTextBoxStreamType.RichText); } catch { // we failed to load as rich text so try it as plain text try { LoadFile(path.ToString(), RichTextBoxStreamType.PlainText); } catch { // ignore any problems we have } } m.Result = (IntPtr)1; // tell them we did the drop break; case RichTextBoxConstants.EN_REQUESTRESIZE: if (!CallOnContentsResized) { NativeMethods.REQRESIZE reqResize = (NativeMethods.REQRESIZE)m.GetLParam(typeof(NativeMethods.REQRESIZE)); if (BorderStyle == System.Windows.Forms.BorderStyle.Fixed3D) { reqResize.rc.bottom++; } OnContentsResized(new ContentsResizedEventArgs(Rectangle.FromLTRB(reqResize.rc.left, reqResize.rc.top, reqResize.rc.right, reqResize.rc.bottom))); } break; case RichTextBoxConstants.EN_SELCHANGE: NativeMethods.SELCHANGE selChange = (NativeMethods.SELCHANGE)m.GetLParam(typeof(NativeMethods.SELCHANGE)); WmSelectionChange(selChange); break; case RichTextBoxConstants.EN_PROTECTED: { NativeMethods.ENPROTECTED enprotected; //On 64-bit, we do some custom marshalling to get this to work. The richedit control //unfortunately does not respect IA64 struct alignment conventions. See VSWhidbey #122276 if (IntPtr.Size == 8) { enprotected = ConvertFromENPROTECTED64((NativeMethods.ENPROTECTED64)m.GetLParam(typeof(NativeMethods.ENPROTECTED64))); } else { enprotected = (NativeMethods.ENPROTECTED)m.GetLParam(typeof(NativeMethods.ENPROTECTED)); } switch (enprotected.msg) { case RichTextBoxConstants.EM_SETCHARFORMAT: // Allow change of protected style // NativeMethods.CHARFORMATA charFormat = (NativeMethods.CHARFORMATA)UnsafeNativeMethods.PtrToStructure(enprotected.lParam, typeof(NativeMethods.CHARFORMATA)); if ((charFormat.dwMask & RichTextBoxConstants.CFM_PROTECTED) != 0) { m.Result = IntPtr.Zero; return; } break; // Throw an exception for the following // case RichTextBoxConstants.EM_SETPARAFORMAT: case NativeMethods.EM_REPLACESEL: break; case RichTextBoxConstants.EM_STREAMIN: // Don't allow STREAMIN to replace protected selection // if (((int)enprotected.wParam & RichTextBoxConstants.SFF_SELECTION) != 0) break; m.Result = IntPtr.Zero; return; // Allow the following // case NativeMethods.WM_COPY: case NativeMethods.WM_SETTEXT: case RichTextBoxConstants.EM_EXLIMITTEXT: m.Result = IntPtr.Zero; return; // Beep and disallow change for all other messages // default: SafeNativeMethods.MessageBeep(0); break; } OnProtected(EventArgs.Empty); m.Result = (IntPtr)1; break; } default: base.WndProc(ref m); break; } } else { base.WndProc(ref m); } } private unsafe NativeMethods.ENPROTECTED ConvertFromENPROTECTED64(NativeMethods.ENPROTECTED64 es64) { NativeMethods.ENPROTECTED es = new NativeMethods.ENPROTECTED(); fixed (byte* es64p = &es64.contents[0]) { es.nmhdr = new NativeMethods.NMHDR(); es.chrg = new NativeMethods.CHARRANGE(); es.nmhdr.hwndFrom = Marshal.ReadIntPtr((IntPtr)es64p); es.nmhdr.idFrom = Marshal.ReadIntPtr((IntPtr)(es64p + 8)); es.nmhdr.code = Marshal.ReadInt32((IntPtr)(es64p + 16)); es.msg = Marshal.ReadInt32((IntPtr)(es64p + 24)); es.wParam = Marshal.ReadIntPtr((IntPtr)(es64p + 28)); es.lParam = Marshal.ReadIntPtr((IntPtr)(es64p + 36)); es.chrg.cpMin = Marshal.ReadInt32((IntPtr)(es64p + 44)); es.chrg.cpMax = Marshal.ReadInt32((IntPtr)(es64p + 48)); } return es; } private static unsafe NativeMethods.ENLINK ConvertFromENLINK64(NativeMethods.ENLINK64 es64) { NativeMethods.ENLINK es = new NativeMethods.ENLINK(); fixed (byte* es64p = &es64.contents[0]) { es.nmhdr = new NativeMethods.NMHDR(); es.charrange = new NativeMethods.CHARRANGE(); es.nmhdr.hwndFrom = Marshal.ReadIntPtr((IntPtr)es64p); es.nmhdr.idFrom = Marshal.ReadIntPtr((IntPtr)(es64p + 8)); es.nmhdr.code = Marshal.ReadInt32((IntPtr)(es64p + 16)); es.msg = Marshal.ReadInt32((IntPtr)(es64p + 24)); es.wParam = Marshal.ReadIntPtr((IntPtr)(es64p + 28)); es.lParam = Marshal.ReadIntPtr((IntPtr)(es64p + 36)); es.charrange.cpMin = Marshal.ReadInt32((IntPtr)(es64p + 44)); es.charrange.cpMax = Marshal.ReadInt32((IntPtr)(es64p + 48)); } return es; } /// /// /// ///private void WmSelectionChange(NativeMethods.SELCHANGE selChange) { int selStart = selChange.chrg.cpMin; int selEnd = selChange.chrg.cpMax; short selType = (short)selChange.seltyp; // VSWhidbey 94804: The IME retains characters in the composition window even after MaxLength // has been reached in the rich edit control. So, if the Hangul or HangulFull IME is in use, and the // number of characters in the control is equal to MaxLength, and the selection start equals the // selection end (nothing is currently selected), then kill and restore focus to the control. Then, // to prevent any further partial composition from occurring, post a message back to myself to select // the last character being composed so that any further composition will occur within the context of // the string contained within the control. // // Since the IME window completes the composition string when the control loses focus and the // EIMES_COMPLETECOMPSTRKILLFOCUS status type is set in the control by the EM_SETIMESTATUS message, // simply killing focus and resetting focus to the control will force the contents of the composition // window to be removed. This forces the undo buffer to be emptied and the backspace key will properly // remove the last completed character typed. // Is either the Hangul or HangulFull IME currently in use? if( ImeMode == ImeMode.Hangul || ImeMode == ImeMode.HangulFull ) { // Is the IME CompositionWindow open? int compMode = (int)SendMessage(RichTextBoxConstants.EM_GETIMECOMPMODE, 0, 0); if (RichTextBoxConstants.ICM_NOTOPEN != compMode) { int textLength = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); if (selStart == selEnd && textLength == MaxLength) { SendMessage(NativeMethods.WM_KILLFOCUS, 0, 0); SendMessage(NativeMethods.WM_SETFOCUS, 0, 0); UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.EM_SETSEL, selEnd - 1, selEnd); } } } if (selStart != curSelStart || selEnd != curSelEnd || selType != curSelType) { curSelStart = selStart; curSelEnd = selEnd; curSelType = selType; OnSelectionChanged(EventArgs.Empty); } } /// /// /// ///private void WmSetFont(ref Message m) { // This function would normally cause two TextChanged events to be fired, one // from the base.WndProc, and another from InternalSetForeColor. // To prevent this, we suppress the first event fire. // try { SuppressTextChangedEvent = true; base.WndProc(ref m); } finally { SuppressTextChangedEvent = false; } InternalSetForeColor(ForeColor); } // // // /// /// /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: WmReflectNotify(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: WmReflectCommand(ref m); break; case NativeMethods.WM_SETCURSOR: //NOTE: RichTextBox uses the WM_SETCURSOR message over links to allow us to // change the cursor to a hand. It does this through a synchronous notification // message. So we have to pass the message to the DefWndProc first, and // then, if we receive a notification message in the meantime (indicated by // changing "LinkCursor", we set it to a hand. Otherwise, we call the // WM_SETCURSOR implementation on Control to set it to the user's selection for // the RichTextBox's cursor. // // Similarly, LinkCursor = false; DefWndProc(ref m); if (LinkCursor && !Cursor.Equals(Cursors.WaitCursor)) { UnsafeNativeMethods.SetCursor(new HandleRef(Cursors.Hand, Cursors.Hand.Handle)); m.Result = (IntPtr)1; } else { base.WndProc(ref m); } break; case NativeMethods.WM_SETFONT: WmSetFont(ref m); break; case NativeMethods.WM_IME_NOTIFY: OnImeChange(EventArgs.Empty); base.WndProc(ref m); break; case NativeMethods.WM_GETDLGCODE: base.WndProc(ref m); m.Result = (IntPtr)((AcceptsTab) ? (int)m.Result | NativeMethods.DLGC_WANTTAB : (int)m.Result & ~NativeMethods.DLGC_WANTTAB); break; case NativeMethods.WM_GETOBJECT: base.WndProc(ref m); // OLEACC.DLL uses window class names to identify standard control types. But WinForm controls use app-specific window // classes. Usually this doesn't matter, because system controls always identify their window class explicitly through // the WM_GETOBJECT+OBJID_QUERYCLASSNAMEIDX message. But RICHEDIT20 doesn't do that - so we must do it ourselves. // Otherwise OLEACC will treat rich edit controls as custom controls, so the accessible Role and Value will be wrong. if (unchecked((int)(long)m.LParam) == NativeMethods.OBJID_QUERYCLASSNAMEIDX) { m.Result = (IntPtr) ((Marshal.SystemDefaultCharSize == 1) ? (65536+29) : (65536+30)); } break; case NativeMethods.WM_RBUTTONUP: //Whidbey 317086 //since RichEdit eats up the WM_CONTEXTMENU message, we need to force DefWndProc //to spit out this message again on receiving WM_RBUTTONUP message. By setting UserMouse //style to true, we effectily let the WmMouseUp method in Control.cs to generate //the WM_CONTEXTMENU message for us. bool oldStyle = GetStyle(ControlStyles.UserMouse); SetStyle(ControlStyles.UserMouse, true); base.WndProc(ref m); SetStyle(ControlStyles.UserMouse, oldStyle); break; case NativeMethods.WM_VSCROLL: base.WndProc(ref m); int loWord = Util.LOWORD(m.WParam); if (loWord == NativeMethods.SB_THUMBTRACK) { OnVScroll(EventArgs.Empty); } else if (loWord == NativeMethods.SB_THUMBPOSITION) { OnVScroll(EventArgs.Empty); } break; case NativeMethods.WM_HSCROLL: base.WndProc(ref m); loWord = Util.LOWORD(m.WParam); if (loWord == NativeMethods.SB_THUMBTRACK) { OnHScroll(EventArgs.Empty); } if (loWord == NativeMethods.SB_THUMBPOSITION) { OnHScroll(EventArgs.Empty); } break; default: base.WndProc(ref m); break; } } // I used the visual basic 6 RichText (REOleCB.CPP) as a guide for this private class OleCallback : UnsafeNativeMethods.IRichEditOleCallback { private RichTextBox owner; IDataObject lastDataObject; DragDropEffects lastEffect; internal OleCallback(RichTextBox owner) { this.owner = owner; } public int GetNewStorage(out UnsafeNativeMethods.IStorage storage) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetNewStorage"); if (!this.owner.AllowOleObjects) { storage = null; return NativeMethods.E_FAIL; } // Debug.WriteLine("get new storage"); UnsafeNativeMethods.ILockBytes pLockBytes = UnsafeNativeMethods.CreateILockBytesOnHGlobal(NativeMethods.NullHandleRef, true); Debug.Assert(pLockBytes != null, "pLockBytes is NULL!"); storage = UnsafeNativeMethods.StgCreateDocfileOnILockBytes(pLockBytes, NativeMethods.STGM_SHARE_EXCLUSIVE | NativeMethods.STGM_CREATE | NativeMethods.STGM_READWRITE, 0); Debug.Assert(storage != null, "storage is NULL!"); return NativeMethods.S_OK; } public int GetInPlaceContext(IntPtr lplpFrame, IntPtr lplpDoc, IntPtr lpFrameInfo) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetInPlaceContext"); return NativeMethods.E_NOTIMPL; } public int ShowContainerUI(int fShow) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::ShowContainerUI"); // Do nothing return NativeMethods.S_OK; } public int QueryInsertObject(ref Guid lpclsid, IntPtr lpstg, int cp) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::QueryInsertObject(" + lpclsid.ToString() + ")"); try { IntSecurity.UnmanagedCode.Demand(); return NativeMethods.S_OK; } catch (SecurityException) { // We do not have unmanaged code access, so // we need to restrict what we allow to be loaded } Guid realClsid = new Guid(); int hr = UnsafeNativeMethods.ReadClassStg(new HandleRef(null, lpstg), ref realClsid); Debug.WriteLineIf(RichTextDbg.TraceVerbose, "real clsid:" + realClsid.ToString() + " (hr=" + hr.ToString("X", CultureInfo.InvariantCulture) + ")"); if (!NativeMethods.Succeeded(hr)) { return NativeMethods.S_FALSE; } if (realClsid == Guid.Empty) { realClsid = lpclsid; } switch (realClsid.ToString().ToUpper(CultureInfo.InvariantCulture)) { case "00000315-0000-0000-C000-000000000046": // Metafile case "00000316-0000-0000-C000-000000000046": // DIB case "00000319-0000-0000-C000-000000000046": // EMF case "0003000A-0000-0000-C000-000000000046": //BMP return NativeMethods.S_OK; default: Debug.WriteLineIf(RichTextDbg.TraceVerbose, " denying '" + lpclsid.ToString() + "' from being inserted due to security restrictions"); return NativeMethods.S_FALSE; } } public int DeleteObject(IntPtr lpoleobj) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::DeleteObject"); // Do nothing return NativeMethods.S_OK; } public int QueryAcceptData(IComDataObject lpdataobj, /* CLIPFORMAT* */ IntPtr lpcfFormat, int reco, int fReally, IntPtr hMetaPict) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::QueryAcceptData(reco=" + reco + ")"); if (reco == NativeMethods.RECO_DROP) { if (owner.AllowDrop || owner.EnableAutoDragDrop) { MouseButtons b = Control.MouseButtons; Keys k = Control.ModifierKeys; int keyState = 0; // Due to the order in which we get called, we have to set up the keystate here. // First GetDragDropEffect is called with grfKeyState == 0, and then // QueryAcceptData is called. Since this is the time we want to fire // OnDragEnter, but we have yet to get the keystate, we set it up ourselves. if ((b & MouseButtons.Left) == MouseButtons.Left) { keyState |= NativeMethods.MK_LBUTTON; } if ((b & MouseButtons.Right) == MouseButtons.Right) { keyState |= NativeMethods.MK_RBUTTON; } if ((b & MouseButtons.Middle) == MouseButtons.Middle) { keyState |= NativeMethods.MK_MBUTTON; } if ((k & Keys.Control) == Keys.Control) { keyState |= NativeMethods.MK_CONTROL; } if ((k & Keys.Shift) == Keys.Shift) { keyState |= NativeMethods.MK_SHIFT; } lastDataObject = new DataObject(lpdataobj); if (!owner.EnableAutoDragDrop) { lastEffect = DragDropEffects.None; } DragEventArgs e = new DragEventArgs(lastDataObject, keyState, Control.MousePosition.X, Control.MousePosition.Y, DragDropEffects.All, lastEffect); if (fReally == 0) { // we are just querying // We can get here without GetDragDropEffects actually being called first. // This happens when you drag/drop between two rtb's. Say you drag from rtb1 to rtb2. // GetDragDropEffects will first be called for rtb1, then QueryAcceptData for rtb1 just // like in the local drag case. Then you drag into rtb2. rtb2 will first be called in this method, // and not GetDragDropEffects. Now lastEffect is initialized to None for rtb2, so we would not allow // the drag. Thus we need to set the effect here as well. e.Effect = ((keyState & NativeMethods.MK_CONTROL) == NativeMethods.MK_CONTROL) ? DragDropEffects.Copy : DragDropEffects.Move; owner.OnDragEnter(e); } else { owner.OnDragDrop(e); lastDataObject = null; } lastEffect = e.Effect; if (e.Effect == DragDropEffects.None) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tCancel data"); return NativeMethods.E_FAIL; } else { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tAccept data"); return NativeMethods.S_OK; } } else { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tCancel data, allowdrop == false"); lastDataObject = null; return NativeMethods.E_FAIL; } } else { return NativeMethods.E_NOTIMPL; } } public int ContextSensitiveHelp(int fEnterMode) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::ContextSensitiveHelp"); return NativeMethods.E_NOTIMPL; } public int GetClipboardData(NativeMethods.CHARRANGE lpchrg, int reco, IntPtr lplpdataobj) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetClipboardData"); return NativeMethods.E_NOTIMPL; } public int GetDragDropEffect(bool fDrag, int grfKeyState, ref int pdwEffect) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetDragDropEffect"); if (owner.AllowDrop || owner.EnableAutoDragDrop) { if (fDrag && grfKeyState == 0) { // This is the very first call we receive in a Drag-Drop operation, // so we will let the control know what we support. // Note that we haven't gotten any data yet, so we will let QueryAcceptData // do the OnDragEnter. Note too, that grfKeyState does not yet reflect the // current keystate if (owner.EnableAutoDragDrop) { lastEffect = (DragDropEffects.All | DragDropEffects.None); } else lastEffect = DragDropEffects.None; } else { // We are either dragging over or dropping // The below is the complete reverse of what the docs on MSDN suggest, // but if we follow the docs, we would be firing OnDragDrop all the // time instead of OnDragOver (see bug 99294). MSDN seems to be wrong here. // drag - fDrag = false, grfKeyState != 0 // drop - fDrag = false, grfKeyState = 0 // We only care about the drag. // // When we drop, lastEffect will have the right state if (!fDrag && lastDataObject != null && grfKeyState != 0) { DragEventArgs e = new DragEventArgs(lastDataObject, grfKeyState, Control.MousePosition.X, Control.MousePosition.Y, DragDropEffects.All, lastEffect); // Now tell which of the allowable effects we want to use, but only if we are not already none if (lastEffect != DragDropEffects.None) { e.Effect = ((grfKeyState & NativeMethods.MK_CONTROL) == NativeMethods.MK_CONTROL) ? DragDropEffects.Copy : DragDropEffects.Move; } owner.OnDragOver(e); lastEffect = e.Effect; } } pdwEffect = (int)lastEffect; } else { pdwEffect = (int)DragDropEffects.None; } return NativeMethods.S_OK; } public int GetContextMenu(short seltype, IntPtr lpoleobj, NativeMethods.CHARRANGE lpchrg, out IntPtr hmenu) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetContextMenu"); ContextMenu cm = owner.ContextMenu; if (cm == null || owner.ShortcutsEnabled == false) hmenu = IntPtr.Zero; else { cm.sourceControl = owner; cm.OnPopup(EventArgs.Empty); // RichEd calls DestroyMenu after displaying the context menu IntPtr handle = cm.Handle; // if another control shares the same context menu // then we have to mark the context menu's handles empty because // RichTextBox will delete the menu handles once the popup menu is dismissed. Menu menu = cm; while (true) { int i = 0; int count = menu.ItemCount; for (; i< count; i++) { if (menu.items[i].handle != IntPtr.Zero) { menu = menu.items[i]; break; } } if (i == count) { menu.handle = IntPtr.Zero; menu.created = false; if (menu == cm) break; else menu = ((MenuItem) menu).Menu; } } hmenu = handle; } return NativeMethods.S_OK; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //[To be supplied.] ///// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using Microsoft.Win32; using System; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Remoting; using System.Runtime.Serialization.Formatters; using System.Security; using System.Security.Permissions; using System.Text; using System.Windows.Forms.ComponentModel; using System.Windows.Forms.Design; using System.Windows.Forms.Layout; using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; using Util = NativeMethods.Util; // ////// /// Rich Text control. The RichTextBox is a control that contains formatted text. /// It supports font selection, boldface, and other type attributes. /// [ClassInterface(ClassInterfaceType.AutoDispatch), ComVisible(true), Docking(DockingBehavior.Ask), Designer("System.Windows.Forms.Design.RichTextBoxDesigner, " + AssemblyRef.SystemDesign), SRDescription(SR.DescriptionRichTextBox) ] public class RichTextBox : TextBoxBase { static TraceSwitch richTextDbg; static TraceSwitch RichTextDbg { get { if (richTextDbg == null) { richTextDbg = new TraceSwitch("RichTextDbg", "Debug info about RichTextBox"); } return richTextDbg; } } ////// /// Paste special flags. /// private const int DV_E_DVASPECT = unchecked((int)0x8004006B); private const int DVASPECT_CONTENT = 1; private const int DVASPECT_THUMBNAIL = 2; private const int DVASPECT_ICON = 4; private const int DVASPECT_DOCPRINT = 8; internal const int INPUT = 0x0001; internal const int OUTPUT = 0x0002; internal const int DIRECTIONMASK = INPUT | OUTPUT; internal const int ANSI = 0x0004; internal const int UNICODE = 0x0008; internal const int FORMATMASK = ANSI | UNICODE; internal const int TEXTLF = 0x0010; internal const int TEXTCRLF = 0x0020; internal const int RTF = 0x0040; internal const int KINDMASK = TEXTLF | TEXTCRLF | RTF; // This is where we store the reched library. private static IntPtr moduleHandle; private static readonly string SZ_RTF_TAG = "{\\rtf"; private const int CHAR_BUFFER_LEN = 512; // Event objects // private static readonly object EVENT_HSCROLL = new object(); private static readonly object EVENT_LINKACTIVATE = new object(); private static readonly object EVENT_IMECHANGE = new object(); private static readonly object EVENT_PROTECTED = new object(); private static readonly object EVENT_REQUESTRESIZE = new object(); private static readonly object EVENT_SELCHANGE = new object(); private static readonly object EVENT_VSCROLL = new object(); // Persistent state // private int bulletIndent; private int rightMargin; private string textRtf; // If not null, takes precedence over cached Text value private string textPlain; private Color selectionBackColorToSetOnHandleCreated; RichTextBoxLanguageOptions languageOption = RichTextBoxLanguageOptions.AutoFont | RichTextBoxLanguageOptions.DualFont; // Non-persistent state // static int logPixelsX; static int logPixelsY; Stream editStream = null; float zoomMultiplier = 1.0f; // used to decide when to fire the selectionChange event. private int curSelStart; private int curSelEnd; private short curSelType; object oleCallback; private static int[] shortcutsToDisable; private static int richEditMajorVersion = 3; //Assume version 3: it'll only be version 2 on Win98, and we don't yet load version 4. private BitVector32 richTextBoxFlags = new BitVector32(); private static readonly BitVector32.Section autoWordSelectionSection = BitVector32.CreateSection(1); private static readonly BitVector32.Section showSelBarSection = BitVector32.CreateSection(1, autoWordSelectionSection); private static readonly BitVector32.Section autoUrlDetectSection = BitVector32.CreateSection(1, showSelBarSection); private static readonly BitVector32.Section fInCtorSection = BitVector32.CreateSection(1, autoUrlDetectSection); private static readonly BitVector32.Section protectedErrorSection = BitVector32.CreateSection(1, fInCtorSection); private static readonly BitVector32.Section linkcursorSection = BitVector32.CreateSection(1, protectedErrorSection); private static readonly BitVector32.Section allowOleDropSection = BitVector32.CreateSection(1, linkcursorSection); private static readonly BitVector32.Section suppressTextChangedEventSection = BitVector32.CreateSection(1, allowOleDropSection); private static readonly BitVector32.Section callOnContentsResizedSection = BitVector32.CreateSection(1, suppressTextChangedEventSection); private static readonly BitVector32.Section richTextShortcutsEnabledSection = BitVector32.CreateSection(1, callOnContentsResizedSection); private static readonly BitVector32.Section allowOleObjectsSection = BitVector32.CreateSection(1, richTextShortcutsEnabledSection); private static readonly BitVector32.Section scrollBarsSection = BitVector32.CreateSection((short) RichTextBoxScrollBars.ForcedBoth, allowOleObjectsSection); private static readonly BitVector32.Section enableAutoDragDropSection = BitVector32.CreateSection(1, scrollBarsSection); ////// /// Constructs a new RichTextBox. /// public RichTextBox() { InConstructor = true; richTextBoxFlags[autoWordSelectionSection] = 0;// This is false by default DetectUrls = true; ScrollBars = RichTextBoxScrollBars.Both; RichTextShortcutsEnabled = true; MaxLength = int.MaxValue; Multiline = true; AutoSize = false; curSelStart = curSelEnd = curSelType = -1; InConstructor = false; } ////// /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop /// may still be used: this should be hidden in the property grid, but not in code /// [Browsable(false)] public override bool AllowDrop { get { return richTextBoxFlags[allowOleDropSection] != 0; } set { if (value) { try { IntSecurity.ClipboardRead.Demand(); } catch (Exception e) { throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); } } richTextBoxFlags[allowOleDropSection] = value ? 1 : 0; UpdateOleCallback(); } } internal bool AllowOleObjects { get { return richTextBoxFlags[allowOleObjectsSection] != 0; } set { richTextBoxFlags[allowOleObjectsSection] = value ? 1 : 0; } } ////// /// [ DefaultValue(false), RefreshProperties(RefreshProperties.Repaint), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible) ] public override bool AutoSize { get { return base.AutoSize; } set { base.AutoSize = value; } } ////// Gets or sets a value indicating whether the size /// of the control automatically adjusts when the font assigned to the control /// is changed. /// /// Note: this works differently than other Controls' AutoSize, so we're hiding /// it to avoid confusion. /// ////// /// Controls whether whether mouse selection snaps to whole words. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.RichTextBoxAutoWordSelection) ] public bool AutoWordSelection { get { return richTextBoxFlags[autoWordSelectionSection] != 0; } set { richTextBoxFlags[autoWordSelectionSection] = value ? 1 : 0; if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETOPTIONS, value ? RichTextBoxConstants.ECOOP_OR : RichTextBoxConstants.ECOOP_XOR, RichTextBoxConstants.ECO_AUTOWORDSELECTION); } } } ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// Returns the amount of indent used in a RichTextBox control when /// SelectionBullet is set to true. /// [ SRCategory(SR.CatBehavior), DefaultValue(0), Localizable(true), SRDescription(SR.RichTextBoxBulletIndent) ] public int BulletIndent { get { return bulletIndent; } set { if (value < 0) { throw new ArgumentOutOfRangeException("BulletIndent", SR.GetString(SR.InvalidArgument, "BulletIndent", (value).ToString(CultureInfo.CurrentCulture))); } this.bulletIndent = value; // Call to update the control only if the bullet is set. if (IsHandleCreated && SelectionBullet) SelectionBullet = true; } } private bool CallOnContentsResized { get { return richTextBoxFlags[callOnContentsResizedSection] != 0; } set { richTextBoxFlags[callOnContentsResizedSection] = value ? 1 : 0; } } internal override bool CanRaiseTextChangedEvent { get { return !SuppressTextChangedEvent; } } ////// /// Whether or not there are actions that can be Redone on the RichTextBox control. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxCanRedoDescr) ] public bool CanRedo { get { if (IsHandleCreated) { bool b; b = (int)SendMessage(RichTextBoxConstants.EM_CANREDO, 0, 0) != 0; return b; } return false; } } ////// /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { // Check for library if (moduleHandle == IntPtr.Zero) { moduleHandle = UnsafeNativeMethods.LoadLibrary(RichTextBoxConstants.DLL_RICHEDIT); int lastWin32Error = Marshal.GetLastWin32Error(); if ((long)moduleHandle < (long)32) { throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError,RichTextBoxConstants.DLL_RICHEDIT)); } //Determine whether we're Rich Edit 2.0 or 3.0: see //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp StringBuilder pathBuilder = new StringBuilder(NativeMethods.MAX_PATH); UnsafeNativeMethods.GetModuleFileName(new HandleRef(null, moduleHandle), pathBuilder, pathBuilder.Capacity); string path = pathBuilder.ToString(); new FileIOPermission(FileIOPermissionAccess.Read, path).Assert(); FileVersionInfo versionInfo; try { versionInfo = FileVersionInfo.GetVersionInfo(path); } finally { CodeAccessPermission.RevertAssert(); } Debug.Assert(versionInfo != null && !string.IsNullOrEmpty(versionInfo.ProductVersion), "Couldn't get the version info for the richedit dll"); if (versionInfo != null && !string.IsNullOrEmpty(versionInfo.ProductVersion)) { //Note: this only allows for one digit version int parsedValue; if (int.TryParse(versionInfo.ProductVersion[0].ToString(), out parsedValue)) { richEditMajorVersion = parsedValue; } } } CreateParams cp = base.CreateParams; if (Marshal.SystemDefaultCharSize == 1) { cp.ClassName = RichTextBoxConstants.WC_RICHEDITA; } else { cp.ClassName = RichTextBoxConstants.WC_RICHEDITW; } if (Multiline) { if (((int)ScrollBars & RichTextBoxConstants.RTB_HORIZ) != 0 && !WordWrap) { // RichEd infers word wrap from the absence of horizontal scroll bars cp.Style |= NativeMethods.WS_HSCROLL; if (((int)ScrollBars & RichTextBoxConstants.RTB_FORCE) != 0) cp.Style |= RichTextBoxConstants.ES_DISABLENOSCROLL; } if (((int)ScrollBars & RichTextBoxConstants.RTB_VERT) != 0) { cp.Style |= NativeMethods.WS_VSCROLL; if (((int)ScrollBars & RichTextBoxConstants.RTB_FORCE) != 0) cp.Style |= RichTextBoxConstants.ES_DISABLENOSCROLL; } } // VSWhidbey 94843: Remove the WS_BORDER style from the control, if we're trying to set it, // to prevent the control from displaying the single point rectangle around the 3D border if (BorderStyle.FixedSingle == BorderStyle && ((cp.Style & NativeMethods.WS_BORDER) != 0)) { cp.Style &= (~NativeMethods.WS_BORDER); cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; } return cp; } } // public bool CanUndo {}; <-- inherited from TextBoxBase ///[To be supplied.] ////// /// Controls whether or not the rich edit control will automatically highlight URLs. /// By default, this is true. Note that changing this property will not update text that is /// already present in the RichTextBox control; it only affects text which is entered after the /// property is changed. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.RichTextBoxDetectURLs) ] public bool DetectUrls { get { return richTextBoxFlags[autoUrlDetectSection] != 0; } set { if (value != DetectUrls) { richTextBoxFlags[autoUrlDetectSection] = value ? 1 : 0; if (IsHandleCreated) { this.SendMessage(RichTextBoxConstants.EM_AUTOURLDETECT, value ? 1 : 0, 0); RecreateHandle(); } } } } ///protected override Size DefaultSize { get { return new Size(100, 96); } } /// /// /// We can't just enable drag/drop of text by default: it's a breaking change (VSWhidbey /// 375177). Should be false by default. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.RichTextBoxEnableAutoDragDrop) ] public bool EnableAutoDragDrop { get { return richTextBoxFlags[enableAutoDragDropSection] != 0; } set { if (value) { try { IntSecurity.ClipboardRead.Demand(); } catch (Exception e) { throw new InvalidOperationException(SR.GetString(SR.DragDropRegFailed), e); } } richTextBoxFlags[enableAutoDragDropSection] = value ? 1 : 0; UpdateOleCallback(); } } ///public override Color ForeColor { get { return base.ForeColor; } set { if (IsHandleCreated) { if (InternalSetForeColor(value)) { base.ForeColor = value; } } else { base.ForeColor = value; } } } /// public override Font Font { get { return base.Font; } set { if (IsHandleCreated) { if (SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)) > 0) { if (value == null) { base.Font = null; SetCharFormatFont(false, Font); } else { try{ Font f = GetCharFormatFont(false); if (f == null || !f.Equals (value)) { SetCharFormatFont(false, value); // update controlfont from "resolved" font from the attempt // to set the document font... // CallOnContentsResized = true; base.Font = GetCharFormatFont(false); } } finally{ CallOnContentsResized = false; } } } else { base.Font = value; } } else { base.Font = value; } } } internal override Size GetPreferredSizeCore(Size proposedConstraints) { Size scrollBarPadding = Size.Empty; //If the RTB is multiline, we won't have a horizontal scrollbar. if (!WordWrap && Multiline && (ScrollBars & RichTextBoxScrollBars.Horizontal) != 0) { scrollBarPadding.Height += SystemInformation.HorizontalScrollBarHeight; } if (Multiline && (ScrollBars & RichTextBoxScrollBars.Vertical) != 0) { scrollBarPadding.Width += SystemInformation.VerticalScrollBarWidth; } // Subtract the scroll bar padding before measuring proposedConstraints -= scrollBarPadding; Size prefSize = base.GetPreferredSizeCore(proposedConstraints); return prefSize + scrollBarPadding; } private bool InConstructor { get { return richTextBoxFlags[fInCtorSection] != 0; } set { richTextBoxFlags[fInCtorSection] = value ? 1 : 0; } } /// /// Sets or gets the rich text box control' language option. /// The IMF_AUTOFONT flag is set by default. /// The IMF_AUTOKEYBOARD and IMF_IMECANCELCOMPLETE flags are cleared by default. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public RichTextBoxLanguageOptions LanguageOption { get { RichTextBoxLanguageOptions opt; if (IsHandleCreated) { opt = (RichTextBoxLanguageOptions)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETLANGOPTIONS, 0, 0); } else { opt = languageOption; } return opt; } set { if (this.LanguageOption != value) { this.languageOption = value; if (IsHandleCreated) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETLANGOPTIONS, 0, (int) value); } } } } private bool LinkCursor { get { return richTextBoxFlags[linkcursorSection] != 0; } set { richTextBoxFlags[linkcursorSection] = value ? 1 : 0; } } ///[ DefaultValue(int.MaxValue), ] public override int MaxLength { get { return base.MaxLength; } set { base.MaxLength = value; } } /// /// /// [DefaultValue(true)] public override bool Multiline { get { return base.Multiline; } set { base.Multiline = value; } } private bool ProtectedError { get { return richTextBoxFlags[protectedErrorSection] != 0; } set { richTextBoxFlags[protectedErrorSection] = value ? 1 : 0; } } ///[To be supplied.] ////// /// Returns the name of the action that will be performed if the user /// Redo's their last Undone operation. If no operation can be redone, /// an empty string ("") is returned. /// //NOTE: This is overridable, because we want people to be able to // mess with the names if necessary...? [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxRedoActionNameDescr) ] public string RedoActionName { get { if (!CanRedo) return ""; int n; n = (int)SendMessage(RichTextBoxConstants.EM_GETREDONAME, 0, 0); return GetEditorActionName(n); } } /////Description: Specifies whether rich text formatting keyboard shortcuts are enabled. [ DefaultValue(true), Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public bool RichTextShortcutsEnabled { get { return richTextBoxFlags[richTextShortcutsEnabledSection] != 0; } set { if (shortcutsToDisable == null) { shortcutsToDisable = new int[] {(int)Shortcut.CtrlL, (int)Shortcut.CtrlR, (int)Shortcut.CtrlE, (int)Shortcut.CtrlJ}; } richTextBoxFlags[richTextShortcutsEnabledSection] = value ? 1 : 0; } } /// /// /// The right margin of a RichTextBox control. A nonzero margin implies WordWrap. /// [ SRCategory(SR.CatBehavior), DefaultValue(0), Localizable(true), SRDescription(SR.RichTextBoxRightMargin) ] public int RightMargin { get { return rightMargin; } set { if (this.rightMargin != value) { if (value < 0) throw new ArgumentOutOfRangeException("RightMargin", SR.GetString(SR.InvalidLowBoundArgumentEx, "RightMargin", value.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); this.rightMargin = value; if (value == 0) { // Once you set EM_SETTARGETDEVICE to something nonzero, RichEd will assume // word wrap forever and ever. RecreateHandle(); } else if (IsHandleCreated) { IntPtr hDC = UnsafeNativeMethods.CreateIC("DISPLAY", null, null, new HandleRef(null, IntPtr.Zero)); try { SendMessage(RichTextBoxConstants.EM_SETTARGETDEVICE, hDC, (IntPtr)Pixel2Twip(hDC, value, true)); } finally { if (hDC != IntPtr.Zero) { UnsafeNativeMethods.DeleteDC(new HandleRef(null, hDC)); } } } } } } ////// /// The text of a RichTextBox control, including all Rtf codes. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxRTF), RefreshProperties(RefreshProperties.All) ] public string Rtf { get { if (IsHandleCreated) { return StreamOut(RichTextBoxConstants.SF_RTF); } else if (textPlain != null) { ForceHandleCreate(); return StreamOut(RichTextBoxConstants.SF_RTF); } else { return textRtf; } } set { if (value == null) value = ""; if (value.Equals(Rtf)) return; ForceHandleCreate(); textRtf = value; StreamIn(value, RichTextBoxConstants.SF_RTF); if (CanRaiseTextChangedEvent) { OnTextChanged(EventArgs.Empty); } } } ////// /// The current scrollbar settings for a multi-line rich edit control. /// Possible return values are given by the RichTextBoxScrollBars enumeration. /// [ SRCategory(SR.CatAppearance), DefaultValue(RichTextBoxScrollBars.Both), Localizable(true), SRDescription(SR.RichTextBoxScrollBars) ] public RichTextBoxScrollBars ScrollBars { get { return (RichTextBoxScrollBars) richTextBoxFlags[scrollBarsSection]; } set { // we could be more clever here, but it doesnt seem like this would get set enough // to warrant a clever bitmask. if (!ClientUtils.IsEnumValid_NotSequential(value, (int)value, (int)RichTextBoxScrollBars.Both, (int)RichTextBoxScrollBars.None, (int)RichTextBoxScrollBars.Horizontal, (int)RichTextBoxScrollBars.Vertical, (int)RichTextBoxScrollBars.ForcedHorizontal, (int)RichTextBoxScrollBars.ForcedVertical, (int)RichTextBoxScrollBars.ForcedBoth)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(RichTextBoxScrollBars)); } if (value != ScrollBars) { using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.ScrollBars)) { richTextBoxFlags[scrollBarsSection] = (int) value; RecreateHandle(); } } } } ////// /// The alignment of the paragraphs in a RichTextBox control. /// [ DefaultValue(HorizontalAlignment.Left), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelAlignment) ] public HorizontalAlignment SelectionAlignment { get { HorizontalAlignment selectionAlignment = HorizontalAlignment.Left; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_ALIGNMENT & pf.dwMask) != 0) { switch (pf.wAlignment) { case RichTextBoxConstants.PFA_LEFT: selectionAlignment = HorizontalAlignment.Left; break; case RichTextBoxConstants.PFA_RIGHT: selectionAlignment = HorizontalAlignment.Right; break; case RichTextBoxConstants.PFA_CENTER: selectionAlignment = HorizontalAlignment.Center; break; } } return selectionAlignment; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); } ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_ALIGNMENT; switch (value) { case HorizontalAlignment.Left: pf.wAlignment = RichTextBoxConstants.PFA_LEFT; break; case HorizontalAlignment.Right: pf.wAlignment = RichTextBoxConstants.PFA_RIGHT; break; case HorizontalAlignment.Center: pf.wAlignment = RichTextBoxConstants.PFA_CENTER; break; } // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// Determines if a paragraph in the RichTextBox control /// contains the current selection or insertion point has the bullet style. /// [ DefaultValue(false), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelBullet) ] public bool SelectionBullet { get { RichTextBoxSelectionAttribute selectionBullet = RichTextBoxSelectionAttribute.None; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_NUMBERING & pf.dwMask) != 0) { if (RichTextBoxConstants.PFN_BULLET == pf.wNumbering) { selectionBullet = RichTextBoxSelectionAttribute.All; } } else { // For paragraphs with mixed SelectionBullets, we just return false return false; } return selectionBullet == RichTextBoxSelectionAttribute.All; } set { ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_NUMBERING | RichTextBoxConstants.PFM_OFFSET; if (!value) { pf.wNumbering = 0; pf.dxOffset = 0; } else { pf.wNumbering = RichTextBoxConstants.PFN_BULLET; pf.dxOffset = Pixel2Twip(IntPtr.Zero, bulletIndent, true); } // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// Determines whether text in the RichTextBox control /// appears on the baseline (normal), as a superscript above the baseline, /// or as a subscript below the baseline. /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelCharOffset) ] public int SelectionCharOffset { get { int selCharOffset = 0; ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & RichTextBoxConstants.CFM_OFFSET) != 0) { selCharOffset = cf.yOffset; } else { // The selection contains characters of different offsets, // so we just return the offset of the first character. selCharOffset = cf.yOffset; } return Twip2Pixel(IntPtr.Zero,selCharOffset,false); } set { if (value > 2000 || value < -2000) throw new ArgumentOutOfRangeException("SelectionCharOffset", SR.GetString(SR.InvalidBoundArgument, "SelectionCharOffset", value, -2000, 2000)); ForceHandleCreate(); NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); cf.dwMask = RichTextBoxConstants.CFM_OFFSET; cf.yOffset = Pixel2Twip(IntPtr.Zero, value, false); // Set the format information // SendMessage will force the handle to be created if it hasn't already. Normally, // we would cache property values until the handle is created - but for this property, // it's far more simple to just create the handle. // UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); } } ////// /// The color of the currently selected text in the /// RichTextBox control. /// Returns Color.Empty if the selection has more than one color. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelColor) ] public Color SelectionColor { get { Color selColor = Color.Empty; ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & RichTextBoxConstants.CFM_COLOR) != 0) selColor = ColorTranslator.FromOle(cf.crTextColor); return selColor; } set { ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(true); cf.dwMask = RichTextBoxConstants.CFM_COLOR; cf.dwEffects = 0; cf.crTextColor = ColorTranslator.ToWin32(value); // set the format information UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); } } ////// /// The background color of the currently selected text in the RichTextBox control. /// Returns Color.Empty if the selection has more than one color. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelBackColor) ] public Color SelectionBackColor { get { Color selColor = Color.Empty; if (IsHandleCreated) { NativeMethods.CHARFORMAT2A cf2 = GetCharFormat2(true); // If the effects member contains valid info if ((cf2.dwEffects & RichTextBoxConstants.CFE_AUTOBACKCOLOR) != 0) { selColor = this.BackColor; } else if ((cf2.dwMask & RichTextBoxConstants.CFM_BACKCOLOR) != 0) { selColor = ColorTranslator.FromOle(cf2.crBackColor); } } else { selColor = selectionBackColorToSetOnHandleCreated; } return selColor; } set { //Note: don't compare the value to the old value here: it's possible that //you have a different range selected. selectionBackColorToSetOnHandleCreated = value; if (IsHandleCreated) { NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A(); if (value == Color.Empty) { cf2.dwEffects = RichTextBoxConstants.CFE_AUTOBACKCOLOR; } else { cf2.dwMask = RichTextBoxConstants.CFM_BACKCOLOR; cf2.crBackColor = ColorTranslator.ToWin32(value); } UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf2); } } } ////// /// The font used to display the currently selected text /// or the characters(s) immediately following the insertion point in the /// RichTextBox control. Null if the selection has more than one font. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelFont) ] public Font SelectionFont { get { return GetCharFormatFont(true); } set { SetCharFormatFont(true, value); } } ////// /// The distance (in pixels) between the left edge of the first line of text /// in the selected paragraph(s) (as specified by the SelectionIndent property) /// and the left edge of subsequent lines of text in the same paragraph(s). /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelHangingIndent) ] public int SelectionHangingIndent { get { int selHangingIndent = 0; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_OFFSET & pf.dwMask) != 0) selHangingIndent = pf.dxOffset; return Twip2Pixel(IntPtr.Zero, selHangingIndent, true); } set { ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_OFFSET; pf.dxOffset = Pixel2Twip(IntPtr.Zero, value, true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// The distance (in pixels) between the left edge of the RichTextBox control and /// the left edge of the text that is selected or added at the current /// insertion point. /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelIndent) ] public int SelectionIndent { get { int selIndent = 0; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_STARTINDENT & pf.dwMask) != 0) selIndent = pf.dxStartIndent; return Twip2Pixel(IntPtr.Zero, selIndent, true); } set { ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_STARTINDENT; pf.dxStartIndent = Pixel2Twip(IntPtr.Zero, value, true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectionLengthDescr) ] public override int SelectionLength { get { if (!IsHandleCreated) { return base.SelectionLength; } // RichTextBox allows the user to select the EOF character, // but we don't want to include this in the SelectionLength. // So instead of sending EM_GETSEL, we just obtain the SelectedText and return // the length of it. // return SelectedText.Length; } set { base.SelectionLength = value; } } ////// Gets or sets the number of characters selected in the text /// box. /// ////// /// true if the current selection prevents any changes to its contents. /// [ DefaultValue(false), SRDescription(SR.RichTextBoxSelProtected), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public bool SelectionProtected { get { ForceHandleCreate(); return GetCharFormat(RichTextBoxConstants.CFM_PROTECTED, RichTextBoxConstants.CFM_PROTECTED) == RichTextBoxSelectionAttribute.All; } set { ForceHandleCreate(); SetCharFormat(RichTextBoxConstants.CFM_PROTECTED, value ? RichTextBoxConstants.CFE_PROTECTED : 0, RichTextBoxSelectionAttribute.All); } } ////// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL) /// in Win9x. /// internal override bool SelectionUsesDbcsOffsetsInWin9x { get { return false; // false for RichEdit, true for Edit. } } ////// /// The currently selected text of a RichTextBox control, including /// all Rtf codes. /// [ DefaultValue(""), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelRTF) ] public string SelectedRtf { get { ForceHandleCreate(); return StreamOut(RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_RTF); } set { ForceHandleCreate(); if (value == null) value = ""; StreamIn(value, RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_RTF); } } ////// /// The distance (in pixels) between the right edge of the RichTextBox control and /// the right edge of the text that is selected or added at the current /// insertion point. /// [ DefaultValue(0), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelRightIndent) ] public int SelectionRightIndent { get { int selRightIndent = 0; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_RIGHTINDENT & pf.dwMask) != 0) selRightIndent = pf.dxRightIndent; return Twip2Pixel(IntPtr.Zero, selRightIndent, true); } set { if (value < 0) throw new ArgumentOutOfRangeException("SelectionRightIndent", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectionRightIndent", value, 0)); ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.dwMask = RichTextBoxConstants.PFM_RIGHTINDENT; pf.dxRightIndent = Pixel2Twip(IntPtr.Zero, value, true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// The absolute tab positions (in pixels) of text in a RichTextBox control. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelTabs) ] public int[] SelectionTabs { get { int[] selTabs = new int[0]; ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); // check if alignment has been set yet if ((RichTextBoxConstants.PFM_TABSTOPS & pf.dwMask) != 0) { selTabs = new int[pf.cTabCount]; for (int x = 0; x < pf.cTabCount; x++) selTabs[x] = Twip2Pixel(IntPtr.Zero, pf.rgxTabs[x], true); } return selTabs; } set { // Verify the argument, and throw an error if is bad if (value != null && value.Length > RichTextBoxConstants.MAX_TAB_STOPS) throw new ArgumentOutOfRangeException("SelectionTabs", SR.GetString(SR.SelTabCountRange)); ForceHandleCreate(); NativeMethods.PARAFORMAT pf = new NativeMethods.PARAFORMAT(); pf.rgxTabs = new int[RichTextBoxConstants.MAX_TAB_STOPS]; // get the format for our currently selected paragraph because // we need to get the number of tabstops to copy UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETPARAFORMAT, 0, pf); pf.cTabCount = (short)((value == null) ? 0 : value.Length); pf.dwMask = RichTextBoxConstants.PFM_TABSTOPS; for (int x = 0; x < pf.cTabCount; x++) pf.rgxTabs[x] = Pixel2Twip(IntPtr.Zero, value[x], true); // set the format for our current paragraph or selection UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETPARAFORMAT, 0, pf); } } ////// /// The currently selected text of a RichTextBox control; consists of a /// zero length string if no characters are selected. /// [ DefaultValue(""), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelText) ] public override string SelectedText { get { ForceHandleCreate(); String text = StreamOut(RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); return text; } set { ForceHandleCreate(); StreamIn(value, RichTextBoxConstants.SFF_SELECTION | RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); } } ////// /// The type of the current selection. The returned value is one /// of the values enumerated in RichTextBoxSelectionType. /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxSelTypeDescr) ] public RichTextBoxSelectionTypes SelectionType { get { ForceHandleCreate(); if (SelectionLength > 0) { int n; n = (int)SendMessage(RichTextBoxConstants.EM_SELECTIONTYPE, 0, 0); return (RichTextBoxSelectionTypes)n; } else { return RichTextBoxSelectionTypes.Empty; } } } ////// /// Whether or not the left edge of the control will have a "selection margin" which /// can be used to select entire lines /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.RichTextBoxSelMargin) ] public bool ShowSelectionMargin { get { return richTextBoxFlags[showSelBarSection] != 0; } set { if (value != ShowSelectionMargin) { richTextBoxFlags[showSelBarSection] = value ? 1 : 0; if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETOPTIONS, value ? RichTextBoxConstants.ECOOP_OR : RichTextBoxConstants.ECOOP_XOR, RichTextBoxConstants.ECO_SELECTIONBAR); } } } } ////// /// [ Localizable(true), RefreshProperties(RefreshProperties.All) ] public override string Text { get { if (IsDisposed) { return base.Text; } if (RecreatingHandle || GetAnyDisposingInHierarchy()) { // We can return any old garbage if we're in the process of recreating the handle return ""; } if (!IsHandleCreated && textRtf == null) { if (textPlain != null) { return textPlain; } else { return base.Text; } } else { // if the handle is created, we are golden, however // if the handle isn't created, but textRtf was // specified, we need the RichEdit to translate // for us, so we must create the handle; // ForceHandleCreate(); return StreamOut(RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); } } set { using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Text)) { textRtf = null; if (!IsHandleCreated) { textPlain = value; } else { textPlain = null; if (value == null) { value = ""; } StreamIn(value, RichTextBoxConstants.SF_TEXT | RichTextBoxConstants.SF_UNICODE); // reset Modified SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); } } } } private bool SuppressTextChangedEvent { get { return richTextBoxFlags[suppressTextChangedEventSection] != 0; } set { bool oldValue = SuppressTextChangedEvent; if (value != oldValue) { richTextBoxFlags[suppressTextChangedEventSection] = value ? 1 : 0; CommonProperties.xClearPreferredSizeCache(this); } } } ///[To be supplied.] ///[Browsable(false)] public override int TextLength { get { NativeMethods.GETTEXTLENGTHEX gtl = new NativeMethods.GETTEXTLENGTHEX(); gtl.flags = RichTextBoxConstants.GTL_NUMCHARS; if (Marshal.SystemDefaultCharSize == 1 /*ANSI*/) { gtl.codepage = 0; /* CP_ANSI */; } else { gtl.codepage = 1200; /* CP_UNICODE */ } return (int) UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTLENGTHEX, gtl, 0 /*ignored*/); } } /// /// /// Returns the name of the action that will be undone if the user /// Undo's their last operation. If no operation can be undone, it will /// return an empty string (""). /// //NOTE: This is overridable, because we want people to be able to // mess with the names if necessary...? [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.RichTextBoxUndoActionNameDescr) ] public string UndoActionName { get { if (!CanUndo) return ""; int n; n = (int)SendMessage(RichTextBoxConstants.EM_GETUNDONAME, 0, 0); return GetEditorActionName(n); } } private string GetEditorActionName(int actionID) { switch (actionID) { case 0: return SR.GetString(SR.RichTextBox_IDUnknown); case 1: return SR.GetString(SR.RichTextBox_IDTyping); case 2: return SR.GetString(SR.RichTextBox_IDDelete); case 3: return SR.GetString(SR.RichTextBox_IDDragDrop); case 4: return SR.GetString(SR.RichTextBox_IDCut); case 5: return SR.GetString(SR.RichTextBox_IDPaste); default: goto case 0; } } ////// /// The current zoom level for the RichTextBox control. This may be between 1/64 and 64. 1.0 indicates /// no zoom (i.e. normal viewing). Zoom works best with TrueType fonts; /// for non-TrueType fonts, ZoomFactor will be treated as the nearest whole number. /// [ SRCategory(SR.CatBehavior), DefaultValue(1.0f), Localizable(true), SRDescription(SR.RichTextBoxZoomFactor) ] public float ZoomFactor { get { if (IsHandleCreated) { int numerator = 0; int denominator = 0; SendMessage(RichTextBoxConstants.EM_GETZOOM, ref numerator, ref denominator); if ( (numerator != 0) && (denominator != 0) ) { zoomMultiplier = ((float)numerator)/((float)denominator); } else { zoomMultiplier = 1.0f; } return zoomMultiplier; } else return zoomMultiplier; } set { if (zoomMultiplier == value) return; if (value <= 0.015625f || value >= 64.0f) throw new ArgumentOutOfRangeException("ZoomFactor", SR.GetString(SR.InvalidExBoundArgument, "ZoomFactor", (value).ToString(CultureInfo.CurrentCulture), (0.015625f).ToString(CultureInfo.CurrentCulture), (64.0f).ToString(CultureInfo.CurrentCulture))); SendZoomFactor(value); } } ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxContentsResized)] public event ContentsResizedEventHandler ContentsResized { add { Events.AddHandler(EVENT_REQUESTRESIZE, value); } remove { Events.RemoveHandler(EVENT_REQUESTRESIZE, value); } } ///[To be supplied.] ////// /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop /// may still be used: this should be hidden in the property grid, but not in code /// [Browsable(false)] public new event DragEventHandler DragDrop { add { base.DragDrop += value; } remove { base.DragDrop -= value; } } ////// /// RichTextBox controls have built-in drag and drop support, but AllowDrop, DragEnter, DragDrop /// may still be used: this should be hidden in the property grid, but not in code /// [Browsable(false)] public new event DragEventHandler DragEnter { add { base.DragEnter += value; } remove { base.DragEnter -= value; } } ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event EventHandler DragLeave { add { base.DragLeave += value; } remove { base.DragLeave -= value; } } ///[To be supplied.] ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event DragEventHandler DragOver { add { base.DragOver += value; } remove { base.DragOver -= value; } } ///[To be supplied.] ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event GiveFeedbackEventHandler GiveFeedback { add { base.GiveFeedback += value; } remove { base.GiveFeedback -= value; } } ///[To be supplied.] ////// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event QueryContinueDragEventHandler QueryContinueDrag { add { base.QueryContinueDrag += value; } remove { base.QueryContinueDrag -= value; } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxHScroll)] public event EventHandler HScroll { add { Events.AddHandler(EVENT_HSCROLL, value); } remove { Events.RemoveHandler(EVENT_HSCROLL, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxLinkClick)] public event LinkClickedEventHandler LinkClicked { add { Events.AddHandler(EVENT_LINKACTIVATE, value); } remove { Events.RemoveHandler(EVENT_LINKACTIVATE, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxIMEChange)] public event EventHandler ImeChange { add { Events.AddHandler(EVENT_IMECHANGE, value); } remove { Events.RemoveHandler(EVENT_IMECHANGE, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxProtected)] public event EventHandler Protected { add { Events.AddHandler(EVENT_PROTECTED, value); } remove { Events.RemoveHandler(EVENT_PROTECTED, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxSelChange)] public event EventHandler SelectionChanged { add { Events.AddHandler(EVENT_SELCHANGE, value); } remove { Events.RemoveHandler(EVENT_SELCHANGE, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxVScroll)] public event EventHandler VScroll { add { Events.AddHandler(EVENT_VSCROLL, value); } remove { Events.RemoveHandler(EVENT_VSCROLL, value); } } ///[To be supplied.] ////// /// Returns a boolean indicating whether the RichTextBoxConstants control can paste the /// given clipboard format. /// public bool CanPaste(DataFormats.Format clipFormat) { bool b = false; b = (int)SendMessage(RichTextBoxConstants.EM_CANPASTE, clipFormat.Id, 0) != 0; return b; } //DrawToBitmap doesn't work for this control, so we should hide it. We'll //still call base so that this has a chance to work if it can. [EditorBrowsable(EditorBrowsableState.Never)] new public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds) { base.DrawToBitmap(bitmap, targetBounds); } private unsafe int EditStreamProc(IntPtr dwCookie, IntPtr buf, int cb, out int transferred) { int ret = 0; // assume that everything is Okay byte[] bytes = new byte[cb]; int cookieVal = (int)dwCookie; transferred = 0; try { switch (cookieVal & DIRECTIONMASK) { case RichTextBox.OUTPUT: { if (editStream == null) { editStream = new MemoryStream(); } switch (cookieVal & KINDMASK) { case RichTextBox.RTF: case RichTextBox.TEXTCRLF: Marshal.Copy(buf, bytes, 0, cb); editStream.Write(bytes, 0, cb); break; case RichTextBox.TEXTLF: // Strip out \r characters so that we consistently return // \n for linefeeds. In a future version the RichEdit control // may support a SF_NOXLATCRLF flag which would do this for // us. Internally the RichEdit stores the text with only // a \n, so we want to keep that the same here. // if ((cookieVal & UNICODE) != 0) { Debug.Assert(cb % 2 == 0, "EditStreamProc call out of cycle. Expected to always get character boundary calls"); int requestedCharCount = cb/2; int consumedCharCount = 0; fixed (byte* pb = bytes) { char* pChars = (char*)pb; char* pBuffer = (char*)(long)buf; for (int i=0; i/// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str) { return Find(str, 0, 0, RichTextBoxFinds.None); } ////// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str, RichTextBoxFinds options) { return Find(str, 0, 0, options); } ////// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str, int start, RichTextBoxFinds options) { return Find(str, start, -1, options); } ////// /// Searches the text in a RichTextBox control for a given string. /// public int Find(string str, int start, int end, RichTextBoxFinds options) { int textLen = TextLength; if (str == null) throw new ArgumentNullException("str"); if (start < 0 || start > textLen) throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLen)); if (end < -1) throw new ArgumentOutOfRangeException("end", SR.GetString(SR.RichTextFindEndInvalid, end)); bool selectWord = true; NativeMethods.FINDTEXT ft = new NativeMethods.FINDTEXT(); ft.chrg = new NativeMethods.CHARRANGE(); // set up the default values for the FINDTEXT structure, that is // the given string and the whole range of the text stream ft.lpstrText = str; if (end == -1) { end = textLen; } if (start > end) { throw new ArgumentException(SR.GetString(SR.RichTextFindEndInvalid, end)); } if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { // normal // ft.chrg.cpMin = start; ft.chrg.cpMax = end; } else { // reverse // ft.chrg.cpMin = end; ft.chrg.cpMax = start; } // force complete search if we ended up with a zero length search if (ft.chrg.cpMin == ft.chrg.cpMax) { if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { ft.chrg.cpMin = 0; ft.chrg.cpMax = -1; } else { ft.chrg.cpMin = textLen; ft.chrg.cpMax = 0; } } // set up the options for the search int findOptions = 0; if ((options & RichTextBoxFinds.WholeWord) == RichTextBoxFinds.WholeWord) findOptions |= RichTextBoxConstants.FR_WHOLEWORD; if ((options & RichTextBoxFinds.MatchCase) == RichTextBoxFinds.MatchCase) findOptions |= RichTextBoxConstants.FR_MATCHCASE; if ((options & RichTextBoxFinds.NoHighlight) == RichTextBoxFinds.NoHighlight) selectWord = false; if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) { // The default for RichEdit 2.0 is to search in reverse findOptions |= RichTextBoxConstants.FR_DOWN; } // Perform the find, will return ubyte position int position; position = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_FINDTEXT, findOptions, ft); // if we didn't find anything, or we don't have to select what was found, // we're done if (position != -1 && selectWord) { // Select the string found, this is done in ubyte units NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); chrg.cpMin = position; //Look for kashidas in the string. A kashida is an arabic visual justification character //that's not semantically meaningful. Searching for ABC might find AB_C (where A,B, and C //represent Arabic characters and _ represents a kashida). We should highlight the text //including the kashida (VSWhidbey 94809). char kashida = (char)0x640; string text = this.Text; string foundString = text.Substring(position, str.Length); int startIndex = foundString.IndexOf(kashida); if (startIndex == -1) { //No kashida in the string chrg.cpMax = position + str.Length; } else { //There's at least one kashida int searchingCursor; //index into search string int foundCursor; //index into Text for (searchingCursor = startIndex, foundCursor = position + startIndex; searchingCursor < str.Length; searchingCursor++, foundCursor++) { while (text[foundCursor] == kashida && str[searchingCursor] != kashida) { foundCursor++; } } chrg.cpMax = foundCursor; } UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, chrg); SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0); } return position; } ////// /// Searches the text in a RichTextBox control for the given characters. /// public int Find(char[] characterSet) { return Find(characterSet, 0, -1); } ////// /// Searches the text in a RichTextBox control for the given characters. /// public int Find(char[] characterSet, int start) { return Find(characterSet, start, -1); } ////// /// Searches the text in a RichTextBox control for the given characters. /// public int Find(char[] characterSet, int start, int end) { // Code used to support ability to search backwards and negate character sets. // The API no longer supports this, but in case we change our mind, I'm leaving // the ability in here. bool forward = true; bool negate = false; int textLength = TextLength; if (characterSet == null) throw new ArgumentNullException("characterSet"); if (start < 0 || start > textLength) throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLength)); if (end < start && end != -1) throw new ArgumentOutOfRangeException("end", SR.GetString(SR.InvalidLowBoundArgumentEx, "end", end, "start")); // Don't do anything if we get nothing to look for if (characterSet.Length == 0) return -1; int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); if (start == end) { start = 0; end = textLen; } if (end == -1) { end = textLen; } NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); // The range of characters we have searched chrg.cpMax = chrg.cpMin = start; // Use the TEXTRANGE to move our text buffer forward // or backwards within the main text NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); // Characters we have slurped into memory in order to search txrg.chrg = new NativeMethods.CHARRANGE(); txrg.chrg.cpMin = chrg.cpMin; txrg.chrg.cpMax = chrg.cpMax; UnsafeNativeMethods.CharBuffer charBuffer; charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(CHAR_BUFFER_LEN + 1); txrg.lpstrText = charBuffer.AllocCoTaskMem(); if (txrg.lpstrText == IntPtr.Zero) throw new OutOfMemoryException(); try { bool done = false; // We want to loop as long as it takes. This loop will grab a // chunk of text out from the control as directed by txrg.chrg; while (!done) { if (forward) { // Move forward by starting at the end of the // previous text window and extending by the // size of our buffer txrg.chrg.cpMin = chrg.cpMax; txrg.chrg.cpMax += CHAR_BUFFER_LEN; } else { // Move backwards by anchoring at the start // of the previous buffer window, and backing // up by the desired size of our buffer txrg.chrg.cpMax = chrg.cpMin; txrg.chrg.cpMin -= CHAR_BUFFER_LEN; // We need to keep our request within the // lower bound of zero if (txrg.chrg.cpMin < 0) txrg.chrg.cpMin = 0; } if (end != -1) txrg.chrg.cpMax = Math.Min(txrg.chrg.cpMax, end); // go get the text in this range, if we didn't get any text then punt int len; len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg); if (len == 0) { chrg.cpMax = chrg.cpMin = -1; // Hit end of control without finding what we wanted break; } // get the data from RichTextBoxConstants into a string for us to use. charBuffer.PutCoTaskMem(txrg.lpstrText); string str = charBuffer.GetString(); // Loop through our text if (forward) { // Start at the begining of the buffer for (int x = 0; x < len; x++) { // Is it in char set? bool found = GetCharInCharSet(str[x], characterSet, negate); if (found) { done = true; break; } // Advance the buffer chrg.cpMax++; } } else { // Span reverse. int x = len; while (x-- != 0) { // Is it in char set? bool found = GetCharInCharSet(str[x], characterSet, negate); if (found) { done = true; break; } // Bring the selection back while keeping it anchored chrg.cpMin--; } } } } finally { // release the resources we got for our GETTEXTRANGE operation. if (txrg.lpstrText != IntPtr.Zero) Marshal.FreeCoTaskMem(txrg.lpstrText); } int index = (forward) ? chrg.cpMax : chrg.cpMin; return index; } private void ForceHandleCreate() { if (!IsHandleCreated) { CreateHandle(); } } // Sends set color message to HWND; doesn't call Control.SetForeColor private bool InternalSetForeColor(Color value) { NativeMethods.CHARFORMATA cf = GetCharFormat(false); if ((cf.dwMask & RichTextBoxConstants.CFM_COLOR) != 0 && ColorTranslator.ToWin32(value) == cf.crTextColor) { return true; } cf.dwMask = RichTextBoxConstants.CFM_COLOR; cf.dwEffects = 0; cf.crTextColor = ColorTranslator.ToWin32(value); return SetCharFormat(RichTextBoxConstants.SCF_ALL, cf); } private NativeMethods.CHARFORMATA GetCharFormat(bool fSelection) { NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETCHARFORMAT, fSelection ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_DEFAULT, cf); return cf; } private NativeMethods.CHARFORMAT2A GetCharFormat2(bool fSelection) { NativeMethods.CHARFORMAT2A cf2 = new NativeMethods.CHARFORMAT2A(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETCHARFORMAT, fSelection ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_DEFAULT, cf2); return cf2; } ////// /// ///private RichTextBoxSelectionAttribute GetCharFormat(int mask, int effect) { RichTextBoxSelectionAttribute charFormat = RichTextBoxSelectionAttribute.None; // check to see if the control has been created if (IsHandleCreated) { NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & mask) != 0) // if the text has the desired effect if ((cf.dwEffects & effect) != 0) charFormat = RichTextBoxSelectionAttribute.All; } return charFormat; } Font GetCharFormatFont(bool selectionOnly) { ForceHandleCreate(); NativeMethods.CHARFORMATA cf = GetCharFormat(selectionOnly); if ((cf.dwMask & RichTextBoxConstants.CFM_FACE) == 0) { return null; } string fontName = Encoding.Default.GetString(cf.szFaceName); int index = fontName.IndexOf('\0'); if (index != -1) { fontName = fontName.Substring(0, index); } float fontSize = 13; if ((cf.dwMask & RichTextBoxConstants.CFM_SIZE) != 0) { fontSize = (float)cf.yHeight/(float)20.0; if (fontSize == 0 && cf.yHeight > 0) { fontSize = 1; } } FontStyle style = FontStyle.Regular; if ((cf.dwMask & RichTextBoxConstants.CFM_BOLD) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_BOLD) != 0) style |= FontStyle.Bold; if ((cf.dwMask & RichTextBoxConstants.CFM_ITALIC) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_ITALIC) != 0) style |= FontStyle.Italic; if ((cf.dwMask & RichTextBoxConstants.CFM_STRIKEOUT) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_STRIKEOUT) != 0) style |= FontStyle.Strikeout; if ((cf.dwMask & RichTextBoxConstants.CFM_UNDERLINE) != 0 && (cf.dwEffects & RichTextBoxConstants.CFE_UNDERLINE) != 0) style |= FontStyle.Underline; try { return new Font(fontName, fontSize, style, GraphicsUnit.Point, cf.bCharSet); } catch { } return null; } /// /// /// Returns the index of the character nearest to the given point. /// public override int GetCharIndexFromPosition(Point pt) { NativeMethods.POINT wpt = new NativeMethods.POINT(pt.X, pt.Y); int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, wpt); string t = this.Text; // EM_CHARFROMPOS will return an invalid number if the last character in the RichEdit // is a newline. // if (index >= t.Length) { index = Math.Max(t.Length - 1, 0); } return index; } ////// /// ///private bool GetCharInCharSet(char c, char[] charSet, bool negate) { bool match = false; int charSetLen = charSet.Length; // Loop through the given character set and compare for a match for (int i = 0; !match && i < charSetLen; i++) match = c == charSet[i]; return negate ? !match : match; } /// /// /// Returns the number of the line containing a specified character position /// in a RichTextBox control. Note that this returns the physical line number /// and not the conceptual line number. For example, if the first conceptual /// line (line number 0) word-wraps and extends to the second line, and if /// you pass the index of a overflowed character, GetLineFromCharIndex would /// return 1 and not 0. /// public override int GetLineFromCharIndex(int index) { return (int)SendMessage(RichTextBoxConstants.EM_EXLINEFROMCHAR, 0, index); } ////// /// Returns the location of the character at the given index. /// public override Point GetPositionFromCharIndex(int index) { if (richEditMajorVersion == 2) { return base.GetPositionFromCharIndex(index); } if (index < 0 || index > Text.Length) { return Point.Empty; } NativeMethods.POINT pt = new NativeMethods.POINT(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_POSFROMCHAR, pt, index); return new Point(pt.x, pt.y); } ////// /// ///private bool GetProtectedError() { if (ProtectedError) { ProtectedError = false; return true; } return false; } /// /// /// Loads the contents of the given RTF or text file into a RichTextBox control. /// public void LoadFile(string path) { LoadFile(path, RichTextBoxStreamType.RichText); } ////// /// Loads the contents of a RTF or text into a RichTextBox control. /// public void LoadFile(string path, RichTextBoxStreamType fileType) { //valid values are 0x0 to 0x4 if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)){ throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); try { LoadFile(file, fileType); } finally { file.Close(); } } ////// /// Loads the contents of a RTF or text into a RichTextBox control. /// public void LoadFile(Stream data, RichTextBoxStreamType fileType) { //valid values are 0x0 to 0x4 if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)) { throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } int flags; switch (fileType) { case RichTextBoxStreamType.RichText: flags = RichTextBoxConstants.SF_RTF; break; case RichTextBoxStreamType.PlainText: this.Rtf = ""; flags = RichTextBoxConstants.SF_TEXT; break; case RichTextBoxStreamType.UnicodePlainText: flags = RichTextBoxConstants.SF_UNICODE | RichTextBoxConstants.SF_TEXT; break; default: throw new ArgumentException(SR.GetString(SR.InvalidFileType)); } StreamIn(data, flags); } ////// /// protected override void OnBackColorChanged(EventArgs e) { if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); } base.OnBackColorChanged(e); } ///[To be supplied.] ///protected override void OnContextMenuChanged(EventArgs e) { base.OnContextMenuChanged(e); UpdateOleCallback(); } /// /// /// protected override void OnRightToLeftChanged(EventArgs e) { base.OnRightToLeftChanged(e); //VSWhidbey# 325345. // When the RTL property is changed, here's what happens. Let's assume that we change from // RTL.No to RTL.Yes. // 1. RecreateHandle is called. // 2. In RTB.OnHandleDestroyed, we cache off any RTF that might have been set. // The RTB has been set to the empty string, so we do get RTF back. The RTF // contains formatting info, but doesn't contain any reading-order info, // so RichEdit defaults to LTR reading order. // 3. In RTB.OnHandleCreated, we check if we have any cached RTF, and if so, // we want to set the RTF to that value. This is to ensure that the original // text doesn't get lost. // 4. In the RTF setter, we get the current RTF, compare it to the old RTF, and // since those are not equal, we set the RichEdit content to the old RTF. // 5. But... since the original RTF had no reading-order info, the reading-order // will default to LTR - thus VSWhidbey #325345. // That's why in Everett we set the text back since that clears the RTF, thus restoring // the reading order to that of the window style. The problem here is that when there's // no initial text (the empty string), then WindowText would not actually set the text, // and we were left with the LTR reading order. There's no longer any initial text (as in Everett, // e.g richTextBox1), since we changed the designers to not set text. Sigh... // So the fix is to force windowtext, whether or not that window text is equal to what's already there. // Note that in doing so we will lose any formatting info you might have set on the RTF. We are okay with that. // We use WindowText rather than Text because this way we can avoid // spurious TextChanged events. // string oldText = WindowText; ForceWindowText(null); ForceWindowText(oldText); } ///[To be supplied.] ////// /// Fires an event when the user changes the control's contents /// are either smaller or larger than the control's window size. /// protected virtual void OnContentsResized(ContentsResizedEventArgs e) { ContentsResizedEventHandler handler = (ContentsResizedEventHandler)Events[EVENT_REQUESTRESIZE]; if (handler != null) handler(this,e); } ////// /// protected override void OnHandleCreated(EventArgs e) { // base.OnHandleCreated is called somewhere in the middle of this curSelStart = curSelEnd = curSelType = -1; // We will always set the control to use the maximum text, it defaults to 32k.. // This must be done before we start loading files, because some files may // be larger than 32k. // UpdateMaxLength(); // This is needed so that the control will fire change and update events // even if it is hidden // SendMessage(RichTextBoxConstants.EM_SETEVENTMASK, 0, RichTextBoxConstants.ENM_PROTECTED | RichTextBoxConstants.ENM_SELCHANGE | RichTextBoxConstants.ENM_DROPFILES | RichTextBoxConstants.ENM_REQUESTRESIZE | RichTextBoxConstants.ENM_IMECHANGE | RichTextBoxConstants.ENM_CHANGE | RichTextBoxConstants.ENM_UPDATE | RichTextBoxConstants.ENM_SCROLL | RichTextBoxConstants.ENM_KEYEVENTS | RichTextBoxConstants.ENM_MOUSEEVENTS | RichTextBoxConstants.ENM_SCROLLEVENTS | RichTextBoxConstants.ENM_LINK); int rm = rightMargin; rightMargin = 0; RightMargin = rm; // this.SendMessage(RichTextBoxConstants.EM_AUTOURLDETECT, DetectUrls ? 1 : 0, 0); if (selectionBackColorToSetOnHandleCreated != Color.Empty) { this.SelectionBackColor = selectionBackColorToSetOnHandleCreated; } // Initialize colors before initializing RTF, otherwise CFE_AUTOCOLOR will be in effect // and our text will all be Color.WindowText. AutoWordSelection = AutoWordSelection; SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); InternalSetForeColor(ForeColor); // base sets the Text property. It's important to do this *after* setting EM_AUTOUrlDETECT. base.OnHandleCreated(e); // vsWhidbey 371584: for some reason, we need to set the OleCallback before setting the RTF property. UpdateOleCallback(); // RTF property takes precedence over Text property // try { SuppressTextChangedEvent = true; if (textRtf != null) { // setting RTF calls back on Text, which relies on textRTF being null string text = textRtf; textRtf = null; Rtf = text; } else if (textPlain != null) { string text = textPlain; textPlain = null; Text = text; } } finally { SuppressTextChangedEvent = false; } // Since we can't send EM_SETSEL until RTF has been set, // we can't rely on base to do it for us. base.SetSelectionOnHandle(); if (ShowSelectionMargin) { // If you call SendMessage instead of PostMessage, the control // will resize itself to the size of the parent's client area. Don't know why... UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETOPTIONS, (IntPtr)RichTextBoxConstants.ECOOP_OR, (IntPtr)RichTextBoxConstants.ECO_SELECTIONBAR); } if (languageOption != this.LanguageOption) { this.LanguageOption = languageOption; } ClearUndo(); SendZoomFactor(zoomMultiplier); SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); } ///[To be supplied.] ////// /// protected override void OnHandleDestroyed(EventArgs e) { base.OnHandleDestroyed(e); if (!InConstructor) { textRtf = Rtf; if (textRtf.Length == 0) textRtf = null; } oleCallback = null; SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(UserPreferenceChangedHandler); } ///[To be supplied.] ////// /// Fires an event when the user clicks a RichTextBox control's horizontal /// scroll bar. /// protected virtual void OnHScroll(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_HSCROLL]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user clicks on a link /// in a rich-edit control. /// protected virtual void OnLinkClicked(LinkClickedEventArgs e) { LinkClickedEventHandler handler = (LinkClickedEventHandler)Events[EVENT_LINKACTIVATE]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user changes the control's IME conversion status. /// protected virtual void OnImeChange(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_IMECHANGE]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user is taking an action that would change /// a protected range of text in the RichTextBox control. /// protected virtual void OnProtected(EventArgs e) { ProtectedError = true; EventHandler handler = (EventHandler)Events[EVENT_PROTECTED]; if (handler != null) handler(this,e); } ////// /// Fires an event when the current selection of text in the RichTextBox /// control has changed or the insertion point has moved. /// protected virtual void OnSelectionChanged(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_SELCHANGE]; if (handler != null) handler(this,e); } ////// /// Fires an event when the user clicks a RichTextBox control's vertical /// scroll bar. /// protected virtual void OnVScroll(EventArgs e) { EventHandler handler = (EventHandler)Events[EVENT_VSCROLL]; if (handler != null) handler(this,e); } ////// /// Pastes the contents of the clipboard in the given clipboard format. /// public void Paste(DataFormats.Format clipFormat) { Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); IntSecurity.ClipboardRead.Demand(); PasteUnsafe(clipFormat, 0); } ////// /// Note that this doesn't make a security demand: functions that call this should. /// private void PasteUnsafe(DataFormats.Format clipFormat, int hIcon) { NativeMethods.REPASTESPECIAL rps = null; if (hIcon != 0) { rps = new NativeMethods.REPASTESPECIAL(); rps.dwAspect = DVASPECT_ICON; rps.dwParam = hIcon; } UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_PASTESPECIAL, clipFormat.Id, rps); } ///[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override bool ProcessCmdKey(ref Message m, Keys keyData) { if (this.RichTextShortcutsEnabled == false) { foreach (int shortcutValue in shortcutsToDisable) { if ((int)keyData == shortcutValue) { return true; } } } return base.ProcessCmdKey(ref m, keyData); } /// /// /// Redoes the last undone editing operation. /// public void Redo() { SendMessage(RichTextBoxConstants.EM_REDO, 0, 0); } //NOTE: Undo is implemented on TextBox ////// /// Saves the contents of a RichTextBox control to a file. /// public void SaveFile(string path) { SaveFile(path, RichTextBoxStreamType.RichText); } ////// /// Saves the contents of a RichTextBox control to a file. /// public void SaveFile(string path, RichTextBoxStreamType fileType) { //valid values are 0x0 to 0x4 if (!ClientUtils.IsEnumValid(fileType, (int)fileType, (int)RichTextBoxStreamType.RichText, (int)RichTextBoxStreamType.UnicodePlainText)) { throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } Stream file = File.Create(path); try { SaveFile(file, fileType); } finally { file.Close(); } } ////// /// Saves the contents of a RichTextBox control to a file. /// public void SaveFile(Stream data, RichTextBoxStreamType fileType) { int flags; switch (fileType) { case RichTextBoxStreamType.RichText: flags = RichTextBoxConstants.SF_RTF; break; case RichTextBoxStreamType.PlainText: flags = RichTextBoxConstants.SF_TEXT; break; case RichTextBoxStreamType.UnicodePlainText: flags = RichTextBoxConstants.SF_UNICODE | RichTextBoxConstants.SF_TEXT; break; case RichTextBoxStreamType.RichNoOleObjs: flags = RichTextBoxConstants.SF_RTFNOOBJS; break; case RichTextBoxStreamType.TextTextOleObjs: flags = RichTextBoxConstants.SF_TEXTIZED; break; default: throw new InvalidEnumArgumentException("fileType", (int)fileType, typeof(RichTextBoxStreamType)); } StreamOut(data, flags, true); } ////// /// Core Zoom calculation and message passing (used by ZoomFactor property and CreateHandle() /// ///private void SendZoomFactor(float zoom) { int numerator; int denominator; if (zoom == 1.0f) { denominator = 0; numerator = 0; } else { denominator = 1000; float multiplier = 1000 * zoom; numerator = (int)Math.Ceiling(multiplier); if (numerator >= 64000) { numerator = (int)Math.Floor(multiplier); } } if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_SETZOOM, numerator, denominator); #if DEBUG // DEBUG CODE: Verify that EM_SETZOOM actually set the zoom int n = 0, d = 0; SendMessage(RichTextBoxConstants.EM_GETZOOM, ref n, ref d); Debug.Assert(n == numerator && d == denominator, "EM_SETZOOM failed"); // END DEBUG CODE #endif } if (numerator != 0) { zoomMultiplier = ((float)numerator)/((float)denominator); } else { zoomMultiplier = 1.0f; } } /// /// /// ///private bool SetCharFormat(int mask, int effect, RichTextBoxSelectionAttribute charFormat) { // check to see if the control has been created if (IsHandleCreated) { NativeMethods.CHARFORMATA cf = new NativeMethods.CHARFORMATA(); cf.dwMask = mask; switch (charFormat) { case RichTextBoxSelectionAttribute.All: cf.dwEffects = effect; break; case RichTextBoxSelectionAttribute.None: cf.dwEffects = 0; break; default: throw new ArgumentException(SR.GetString(SR.UnknownAttr)); } // set the format information return IntPtr.Zero != UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, RichTextBoxConstants.SCF_SELECTION, cf); } return false; } private bool SetCharFormat(int charRange, NativeMethods.CHARFORMATA cf) { return IntPtr.Zero != UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, charRange, cf); } private void SetCharFormatFont(bool selectionOnly, Font value) { ForceHandleCreate(); NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT(); FontToLogFont(value, logfont); byte[] bytesFaceName; int dwMask = RichTextBoxConstants.CFM_FACE | RichTextBoxConstants.CFM_SIZE | RichTextBoxConstants.CFM_BOLD | RichTextBoxConstants.CFM_ITALIC | RichTextBoxConstants.CFM_STRIKEOUT | RichTextBoxConstants.CFM_UNDERLINE | RichTextBoxConstants.CFM_CHARSET; int dwEffects = 0; if (value.Bold) dwEffects |= RichTextBoxConstants.CFE_BOLD; if (value.Italic) dwEffects |= RichTextBoxConstants.CFE_ITALIC; if (value.Strikeout) dwEffects |= RichTextBoxConstants.CFE_STRIKEOUT; if (value.Underline) dwEffects |= RichTextBoxConstants.CFE_UNDERLINE; if (Marshal.SystemDefaultCharSize == 1) { bytesFaceName = Encoding.Default.GetBytes(logfont.lfFaceName); NativeMethods.CHARFORMATA cfA = new NativeMethods.CHARFORMATA(); for (int i=0; i /// /// This is just here as a minor perf improvement, so we don't have to call expensive RevertAssert. /// When the method exits, we effectively revert the assert. /// [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] static private void FontToLogFont(Font value, NativeMethods.LOGFONT logfont) { value.ToLogFont(logfont); } ////// /// private static void SetupLogPixels(IntPtr hDC) { bool release = false; if (hDC == IntPtr.Zero) { hDC = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); release = true; } if (hDC == IntPtr.Zero) return; logPixelsX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSX); logPixelsY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, hDC), NativeMethods.LOGPIXELSY); if (release) UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, hDC)); } private static int Pixel2Twip(IntPtr hDC, int v, bool xDirection) { SetupLogPixels(hDC); int logP = xDirection ? logPixelsX : logPixelsY; return(int) ((((double)v) / logP) * 72.0 * 20.0); } private static int Twip2Pixel(IntPtr hDC, int v, bool xDirection) { SetupLogPixels(hDC); int logP = xDirection ? logPixelsX : logPixelsY; return(int) (((((double) v) / 20.0) / 72.0) * logP); } ////// /// ///private void StreamIn(string str, int flags) { if (str.Length == 0 ) { // Destroy the selection if callers was setting // selection text // if ((RichTextBoxConstants.SFF_SELECTION & flags) != 0) { SendMessage(NativeMethods.WM_CLEAR, 0, 0); ProtectedError = false; return; } // WM_SETTEXT is allowed even if we have protected text // SendMessage(NativeMethods.WM_SETTEXT, 0, ""); return; } // Rather than work only some of the time with null characters, // we're going to be consistent and never work with them. int nullTerminatedLength = str.IndexOf((char) 0); if (nullTerminatedLength != -1) { str = str.Substring(0, nullTerminatedLength); } // get the string into a byte array byte[] encodedBytes; if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { encodedBytes = Encoding.Unicode.GetBytes(str); } else { encodedBytes = Encoding.Default.GetBytes(str); } editStream = new MemoryStream(encodedBytes.Length); editStream.Write(encodedBytes, 0, encodedBytes.Length); editStream.Position = 0; StreamIn(editStream, flags); } private void StreamIn(Stream data, int flags) { // clear out the selection only if we are replacing all the text // if ((flags & RichTextBoxConstants.SFF_SELECTION) == 0) { NativeMethods.CHARRANGE cr = new NativeMethods.CHARRANGE(); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, cr); } try { editStream = data; Debug.Assert(data != null, "StreamIn passed a null stream"); // If SF_RTF is requested then check for the RTF tag at the start // of the file. We don't load if the tag is not there // if ((flags & RichTextBoxConstants.SF_RTF) != 0) { long streamStart = editStream.Position; byte[] bytes = new byte[SZ_RTF_TAG.Length]; editStream.Read(bytes, (int)streamStart, SZ_RTF_TAG.Length); string str = Encoding.Default.GetString(bytes); if (!SZ_RTF_TAG.Equals(str)) throw new ArgumentException(SR.GetString(SR.InvalidFileFormat)); // put us back at the start of the file editStream.Position = streamStart; } int cookieVal = 0; // set up structure to do stream operation NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM(); if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { cookieVal = INPUT | UNICODE; } else { cookieVal = INPUT | ANSI; } if ((flags & RichTextBoxConstants.SF_RTF) != 0) { cookieVal |= RTF; } else { cookieVal |= TEXTLF; } es.dwCookie = (IntPtr) cookieVal; es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc); // gives us TextBox compatible behavior, programatic text change shouldn't // be limited... // SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, Int32.MaxValue); // go get the text for the control //Weird hack needed for 64-bit if (IntPtr.Size == 8) { NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es64); //Assign back dwError value es.dwError = GetErrorValue64(es64); } else { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es); } UpdateMaxLength(); // If we failed to load because of protected // text then return protect event was fired so no // exception is required for the the error if (GetProtectedError()) return; if (es.dwError != 0) throw new InvalidOperationException(SR.GetString(SR.LoadTextError)); // set the modify tag on the control SendMessage(NativeMethods.EM_SETMODIFY, -1, 0); // EM_GETLINECOUNT will cause the RichTextBoxConstants to recalculate its line indexes SendMessage(NativeMethods.EM_GETLINECOUNT, 0, 0); } finally { // release any storage space held. editStream = null; } } /// /// /// ///private string StreamOut(int flags) { Stream stream = new MemoryStream(); StreamOut(stream, flags, false); stream.Position = 0; int streamLength = (int)stream.Length; string result = string.Empty; if (streamLength > 0) { byte[] bytes = new byte[streamLength]; stream.Read(bytes, 0, streamLength); if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { result = Encoding.Unicode.GetString(bytes,0 , bytes.Length); } else { result = Encoding.Default.GetString(bytes, 0, bytes.Length); } // workaround ??? for bug 117325. When the string is modified is can return // with an extra null termination (always?). If it does, get rid of it. if(!String.IsNullOrEmpty(result) && (result[result.Length-1] == '\0')) { result = result.Substring(0, result.Length-1); } } return result; } private void StreamOut(Stream data, int flags, bool includeCrLfs) { // set up the EDITSTREAM structure for the callback. Debug.Assert(data != null, "StreamOut passed a null stream"); editStream = data; try { int cookieVal = 0; NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM(); if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) { cookieVal = OUTPUT | UNICODE; } else { cookieVal = OUTPUT | ANSI; } if ((flags & RichTextBoxConstants.SF_RTF) != 0) { cookieVal |= RTF; } else { if (includeCrLfs) { cookieVal |= TEXTCRLF; } else { cookieVal |= TEXTLF; } } es.dwCookie = (IntPtr) cookieVal; es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc); //Get Text //Weird hack needed for 64-bit if (IntPtr.Size == 8) { NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMOUT, flags, es64); //Assign back dwError value es.dwError = GetErrorValue64(es64); } else { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMOUT, flags, es); } // check to make sure things went well if (es.dwError != 0) throw new InvalidOperationException(SR.GetString(SR.SaveTextError)); } finally { // release any storage space held. editStream = null; } } private unsafe NativeMethods.EDITSTREAM64 ConvertToEDITSTREAM64(NativeMethods.EDITSTREAM es) { NativeMethods.EDITSTREAM64 es64 = new NativeMethods.EDITSTREAM64(); fixed (byte* es64p = &es64.contents[0]) { byte *bp; long l; /* l = (long) es.dwCookie; bp = (byte *) &l; for (int i=0; i < sizeof(long); i++) { es64.contents[i] = bp[i]; }*/ *((long *)es64p) = (long) es.dwCookie; /* int il = es.dwError; bp = (byte *) &il; for (int i=0; i < sizeof(int); i++) { es64.contents[i+8] = bp[i]; }*/ *((int *)(es64p + 8)) = es.dwError; l = (long) Marshal.GetFunctionPointerForDelegate(es.pfnCallback); bp = (byte *) &l; for (int i=0; i < sizeof(long); i++) { es64.contents[i+12] = bp[i]; } //*((long *)(es64p + 12)) = (long) Marshal.GetFunctionPointerForDelegate(es.pfnCallback); } return es64; } private unsafe int GetErrorValue64(NativeMethods.EDITSTREAM64 es64) { int errorVal; fixed (byte* es64p = &es64.contents[0]) { errorVal = *((int *)(es64p + 8)); } return errorVal; } /* FOR 64 BIT DEBUGGING private unsafe string PrintBytes(NativeMethods.EDITSTREAM es) { StringBuilder sb = new StringBuilder(); fixed (IntPtr *ip = &es.dwCookie) { byte *bytep = (byte *) ip; for (int i=0; i < Marshal.SizeOf(es); i++) { sb.Append(bytep[i].ToString() + " "); } } return sb.ToString(); } private unsafe string PrintBytes(NativeMethods.EDITSTREAM64 es64) { StringBuilder sb = new StringBuilder(); fixed (byte *bytep = &es64.contents[0]) { for (int i=0; i < Marshal.SizeOf(es64); i++) { sb.Append(bytep[i].ToString() + " "); } } return sb.ToString(); } */ private void UpdateOleCallback() { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "update ole callback (" + AllowDrop + ")"); if (IsHandleCreated) { if (oleCallback == null) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "binding ole callback"); bool unrestricted = false; try { IntSecurity.UnmanagedCode.Demand(); unrestricted = true; } catch (SecurityException) { unrestricted = false; } if (unrestricted) { this.AllowOleObjects = true; } else { this.AllowOleObjects = (0 != (int) SendMessage(RichTextBoxConstants.EM_SETQUERYRTFOBJ, 0, 1)); } oleCallback = CreateRichEditOleCallback(); // Forcibly QI (through IUnknown::QueryInterface) to handle multiple // definitions of the interface. // IntPtr punk = Marshal.GetIUnknownForObject(oleCallback); try { IntPtr pRichEditOleCallback; Guid iidRichEditOleCallback = typeof(UnsafeNativeMethods.IRichEditOleCallback).GUID; Marshal.QueryInterface(punk, ref iidRichEditOleCallback, out pRichEditOleCallback); try { UnsafeNativeMethods.SendCallbackMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETOLECALLBACK, IntPtr.Zero, pRichEditOleCallback); } finally { Marshal.Release(pRichEditOleCallback); } } finally { Marshal.Release(punk); } } UnsafeNativeMethods.DragAcceptFiles(new HandleRef(this, Handle), false); } } //Note: RichTextBox doesn't work like other controls as far as setting ForeColor/ //BackColor -- you need to send messages to update the colors private void UserPreferenceChangedHandler(object o, UserPreferenceChangedEventArgs e) { if (IsHandleCreated) { if (this.BackColor.IsSystemColor) { SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor)); } if (this.ForeColor.IsSystemColor) { InternalSetForeColor(ForeColor); } } } /// /// /// Creates the IRichEditOleCallback compatible object for handling RichEdit callbacks. For more /// information look up the MSDN info on this interface. This is designed to be a back door of /// sorts, which is why it is fairly obscure, and uses the RichEdit name instead of RichTextBox. /// [SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected virtual object CreateRichEditOleCallback() { return new OleCallback(this); } ////// /// Handles link messages (mouse move, down, up, dblclk, etc) /// ///private void EnLinkMsgHandler(ref Message m) { NativeMethods.ENLINK enlink; //On 64-bit, we do some custom marshalling to get this to work. The richedit control //unfortunately does not respect IA64 struct alignment conventions. See VSWhidbey #122276 & 504502. if (IntPtr.Size == 8) { enlink = ConvertFromENLINK64((NativeMethods.ENLINK64)m.GetLParam(typeof(NativeMethods.ENLINK64))); } else { enlink = (NativeMethods.ENLINK)m.GetLParam(typeof(NativeMethods.ENLINK)); } switch (enlink.msg) { case NativeMethods.WM_SETCURSOR: LinkCursor = true; m.Result = (IntPtr)1; return; // Mouse-down triggers Url; this matches Outlook 2000's behavior. case NativeMethods.WM_LBUTTONDOWN: string linktext = CharRangeToString(enlink.charrange); if (!string.IsNullOrEmpty(linktext)) { OnLinkClicked(new LinkClickedEventArgs(linktext)); } m.Result = (IntPtr)1; return; } m.Result = IntPtr.Zero; return; } /// /// /// Converts a CHARRANGE to a string. Note: The behavior of this is dependent on the current window /// class name being used. We have to create a CharBuffer of the type of RichTextBox DLL we're using, /// not based on the SystemCharWidth. /// ///private string CharRangeToString(NativeMethods.CHARRANGE c) { NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); txrg.chrg = c; Debug.Assert((c.cpMax-c.cpMin)>0, "CHARRANGE was null or negative - can't do it!"); //Windows bug: 64-bit windows returns a bad range for us. VSWhidbey 504502. //Putting in a hack to avoid an unhandled exception. if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) { return string.Empty; } int characters = (c.cpMax-c.cpMin) + 1; // +1 for null termination UnsafeNativeMethods.CharBuffer charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(characters); IntPtr unmanagedBuffer = charBuffer.AllocCoTaskMem(); if (unmanagedBuffer == IntPtr.Zero) throw new OutOfMemoryException(SR.GetString(SR.OutOfMemory)); txrg.lpstrText = unmanagedBuffer; int len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg); Debug.Assert(len != 0, "CHARRANGE from RichTextBox was bad! - impossible?"); charBuffer.PutCoTaskMem(unmanagedBuffer); if (txrg.lpstrText != IntPtr.Zero) Marshal.FreeCoTaskMem(unmanagedBuffer); string result = charBuffer.GetString(); return result; } internal override void UpdateMaxLength() { if (IsHandleCreated) { SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, MaxLength); } } /// /// /// ///private void WmReflectCommand(ref Message m) { // We check if we're in the middle of handle creation because // the rich edit control fires spurious events during this time. // if (m.LParam == Handle && !GetState(STATE_CREATINGHANDLE)) { switch (Util.HIWORD(m.WParam)) { case NativeMethods.EN_HSCROLL: OnHScroll(EventArgs.Empty); break; case NativeMethods.EN_VSCROLL: OnVScroll(EventArgs.Empty); break; default: base.WndProc(ref m); break; } } else { base.WndProc(ref m); } } /// /// /// ///internal void WmReflectNotify(ref Message m) { if (m.HWnd == Handle) { NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); switch (nmhdr.code) { case RichTextBoxConstants.EN_LINK: EnLinkMsgHandler(ref m); break; case RichTextBoxConstants.EN_DROPFILES: NativeMethods.ENDROPFILES endropfiles = (NativeMethods.ENDROPFILES)m.GetLParam(typeof(NativeMethods.ENDROPFILES)); // Only look at the first file. StringBuilder path = new StringBuilder(NativeMethods.MAX_PATH); UnsafeNativeMethods.DragQueryFile(new HandleRef(endropfiles, endropfiles.hDrop), 0, path, NativeMethods.MAX_PATH); // Try to load the file as an RTF try { LoadFile(path.ToString(), RichTextBoxStreamType.RichText); } catch { // we failed to load as rich text so try it as plain text try { LoadFile(path.ToString(), RichTextBoxStreamType.PlainText); } catch { // ignore any problems we have } } m.Result = (IntPtr)1; // tell them we did the drop break; case RichTextBoxConstants.EN_REQUESTRESIZE: if (!CallOnContentsResized) { NativeMethods.REQRESIZE reqResize = (NativeMethods.REQRESIZE)m.GetLParam(typeof(NativeMethods.REQRESIZE)); if (BorderStyle == System.Windows.Forms.BorderStyle.Fixed3D) { reqResize.rc.bottom++; } OnContentsResized(new ContentsResizedEventArgs(Rectangle.FromLTRB(reqResize.rc.left, reqResize.rc.top, reqResize.rc.right, reqResize.rc.bottom))); } break; case RichTextBoxConstants.EN_SELCHANGE: NativeMethods.SELCHANGE selChange = (NativeMethods.SELCHANGE)m.GetLParam(typeof(NativeMethods.SELCHANGE)); WmSelectionChange(selChange); break; case RichTextBoxConstants.EN_PROTECTED: { NativeMethods.ENPROTECTED enprotected; //On 64-bit, we do some custom marshalling to get this to work. The richedit control //unfortunately does not respect IA64 struct alignment conventions. See VSWhidbey #122276 if (IntPtr.Size == 8) { enprotected = ConvertFromENPROTECTED64((NativeMethods.ENPROTECTED64)m.GetLParam(typeof(NativeMethods.ENPROTECTED64))); } else { enprotected = (NativeMethods.ENPROTECTED)m.GetLParam(typeof(NativeMethods.ENPROTECTED)); } switch (enprotected.msg) { case RichTextBoxConstants.EM_SETCHARFORMAT: // Allow change of protected style // NativeMethods.CHARFORMATA charFormat = (NativeMethods.CHARFORMATA)UnsafeNativeMethods.PtrToStructure(enprotected.lParam, typeof(NativeMethods.CHARFORMATA)); if ((charFormat.dwMask & RichTextBoxConstants.CFM_PROTECTED) != 0) { m.Result = IntPtr.Zero; return; } break; // Throw an exception for the following // case RichTextBoxConstants.EM_SETPARAFORMAT: case NativeMethods.EM_REPLACESEL: break; case RichTextBoxConstants.EM_STREAMIN: // Don't allow STREAMIN to replace protected selection // if (((int)enprotected.wParam & RichTextBoxConstants.SFF_SELECTION) != 0) break; m.Result = IntPtr.Zero; return; // Allow the following // case NativeMethods.WM_COPY: case NativeMethods.WM_SETTEXT: case RichTextBoxConstants.EM_EXLIMITTEXT: m.Result = IntPtr.Zero; return; // Beep and disallow change for all other messages // default: SafeNativeMethods.MessageBeep(0); break; } OnProtected(EventArgs.Empty); m.Result = (IntPtr)1; break; } default: base.WndProc(ref m); break; } } else { base.WndProc(ref m); } } private unsafe NativeMethods.ENPROTECTED ConvertFromENPROTECTED64(NativeMethods.ENPROTECTED64 es64) { NativeMethods.ENPROTECTED es = new NativeMethods.ENPROTECTED(); fixed (byte* es64p = &es64.contents[0]) { es.nmhdr = new NativeMethods.NMHDR(); es.chrg = new NativeMethods.CHARRANGE(); es.nmhdr.hwndFrom = Marshal.ReadIntPtr((IntPtr)es64p); es.nmhdr.idFrom = Marshal.ReadIntPtr((IntPtr)(es64p + 8)); es.nmhdr.code = Marshal.ReadInt32((IntPtr)(es64p + 16)); es.msg = Marshal.ReadInt32((IntPtr)(es64p + 24)); es.wParam = Marshal.ReadIntPtr((IntPtr)(es64p + 28)); es.lParam = Marshal.ReadIntPtr((IntPtr)(es64p + 36)); es.chrg.cpMin = Marshal.ReadInt32((IntPtr)(es64p + 44)); es.chrg.cpMax = Marshal.ReadInt32((IntPtr)(es64p + 48)); } return es; } private static unsafe NativeMethods.ENLINK ConvertFromENLINK64(NativeMethods.ENLINK64 es64) { NativeMethods.ENLINK es = new NativeMethods.ENLINK(); fixed (byte* es64p = &es64.contents[0]) { es.nmhdr = new NativeMethods.NMHDR(); es.charrange = new NativeMethods.CHARRANGE(); es.nmhdr.hwndFrom = Marshal.ReadIntPtr((IntPtr)es64p); es.nmhdr.idFrom = Marshal.ReadIntPtr((IntPtr)(es64p + 8)); es.nmhdr.code = Marshal.ReadInt32((IntPtr)(es64p + 16)); es.msg = Marshal.ReadInt32((IntPtr)(es64p + 24)); es.wParam = Marshal.ReadIntPtr((IntPtr)(es64p + 28)); es.lParam = Marshal.ReadIntPtr((IntPtr)(es64p + 36)); es.charrange.cpMin = Marshal.ReadInt32((IntPtr)(es64p + 44)); es.charrange.cpMax = Marshal.ReadInt32((IntPtr)(es64p + 48)); } return es; } /// /// /// ///private void WmSelectionChange(NativeMethods.SELCHANGE selChange) { int selStart = selChange.chrg.cpMin; int selEnd = selChange.chrg.cpMax; short selType = (short)selChange.seltyp; // VSWhidbey 94804: The IME retains characters in the composition window even after MaxLength // has been reached in the rich edit control. So, if the Hangul or HangulFull IME is in use, and the // number of characters in the control is equal to MaxLength, and the selection start equals the // selection end (nothing is currently selected), then kill and restore focus to the control. Then, // to prevent any further partial composition from occurring, post a message back to myself to select // the last character being composed so that any further composition will occur within the context of // the string contained within the control. // // Since the IME window completes the composition string when the control loses focus and the // EIMES_COMPLETECOMPSTRKILLFOCUS status type is set in the control by the EM_SETIMESTATUS message, // simply killing focus and resetting focus to the control will force the contents of the composition // window to be removed. This forces the undo buffer to be emptied and the backspace key will properly // remove the last completed character typed. // Is either the Hangul or HangulFull IME currently in use? if( ImeMode == ImeMode.Hangul || ImeMode == ImeMode.HangulFull ) { // Is the IME CompositionWindow open? int compMode = (int)SendMessage(RichTextBoxConstants.EM_GETIMECOMPMODE, 0, 0); if (RichTextBoxConstants.ICM_NOTOPEN != compMode) { int textLength = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); if (selStart == selEnd && textLength == MaxLength) { SendMessage(NativeMethods.WM_KILLFOCUS, 0, 0); SendMessage(NativeMethods.WM_SETFOCUS, 0, 0); UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), NativeMethods.EM_SETSEL, selEnd - 1, selEnd); } } } if (selStart != curSelStart || selEnd != curSelEnd || selType != curSelType) { curSelStart = selStart; curSelEnd = selEnd; curSelType = selType; OnSelectionChanged(EventArgs.Empty); } } /// /// /// ///private void WmSetFont(ref Message m) { // This function would normally cause two TextChanged events to be fired, one // from the base.WndProc, and another from InternalSetForeColor. // To prevent this, we suppress the first event fire. // try { SuppressTextChangedEvent = true; base.WndProc(ref m); } finally { SuppressTextChangedEvent = false; } InternalSetForeColor(ForeColor); } // // // /// /// /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: WmReflectNotify(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: WmReflectCommand(ref m); break; case NativeMethods.WM_SETCURSOR: //NOTE: RichTextBox uses the WM_SETCURSOR message over links to allow us to // change the cursor to a hand. It does this through a synchronous notification // message. So we have to pass the message to the DefWndProc first, and // then, if we receive a notification message in the meantime (indicated by // changing "LinkCursor", we set it to a hand. Otherwise, we call the // WM_SETCURSOR implementation on Control to set it to the user's selection for // the RichTextBox's cursor. // // Similarly, LinkCursor = false; DefWndProc(ref m); if (LinkCursor && !Cursor.Equals(Cursors.WaitCursor)) { UnsafeNativeMethods.SetCursor(new HandleRef(Cursors.Hand, Cursors.Hand.Handle)); m.Result = (IntPtr)1; } else { base.WndProc(ref m); } break; case NativeMethods.WM_SETFONT: WmSetFont(ref m); break; case NativeMethods.WM_IME_NOTIFY: OnImeChange(EventArgs.Empty); base.WndProc(ref m); break; case NativeMethods.WM_GETDLGCODE: base.WndProc(ref m); m.Result = (IntPtr)((AcceptsTab) ? (int)m.Result | NativeMethods.DLGC_WANTTAB : (int)m.Result & ~NativeMethods.DLGC_WANTTAB); break; case NativeMethods.WM_GETOBJECT: base.WndProc(ref m); // OLEACC.DLL uses window class names to identify standard control types. But WinForm controls use app-specific window // classes. Usually this doesn't matter, because system controls always identify their window class explicitly through // the WM_GETOBJECT+OBJID_QUERYCLASSNAMEIDX message. But RICHEDIT20 doesn't do that - so we must do it ourselves. // Otherwise OLEACC will treat rich edit controls as custom controls, so the accessible Role and Value will be wrong. if (unchecked((int)(long)m.LParam) == NativeMethods.OBJID_QUERYCLASSNAMEIDX) { m.Result = (IntPtr) ((Marshal.SystemDefaultCharSize == 1) ? (65536+29) : (65536+30)); } break; case NativeMethods.WM_RBUTTONUP: //Whidbey 317086 //since RichEdit eats up the WM_CONTEXTMENU message, we need to force DefWndProc //to spit out this message again on receiving WM_RBUTTONUP message. By setting UserMouse //style to true, we effectily let the WmMouseUp method in Control.cs to generate //the WM_CONTEXTMENU message for us. bool oldStyle = GetStyle(ControlStyles.UserMouse); SetStyle(ControlStyles.UserMouse, true); base.WndProc(ref m); SetStyle(ControlStyles.UserMouse, oldStyle); break; case NativeMethods.WM_VSCROLL: base.WndProc(ref m); int loWord = Util.LOWORD(m.WParam); if (loWord == NativeMethods.SB_THUMBTRACK) { OnVScroll(EventArgs.Empty); } else if (loWord == NativeMethods.SB_THUMBPOSITION) { OnVScroll(EventArgs.Empty); } break; case NativeMethods.WM_HSCROLL: base.WndProc(ref m); loWord = Util.LOWORD(m.WParam); if (loWord == NativeMethods.SB_THUMBTRACK) { OnHScroll(EventArgs.Empty); } if (loWord == NativeMethods.SB_THUMBPOSITION) { OnHScroll(EventArgs.Empty); } break; default: base.WndProc(ref m); break; } } // I used the visual basic 6 RichText (REOleCB.CPP) as a guide for this private class OleCallback : UnsafeNativeMethods.IRichEditOleCallback { private RichTextBox owner; IDataObject lastDataObject; DragDropEffects lastEffect; internal OleCallback(RichTextBox owner) { this.owner = owner; } public int GetNewStorage(out UnsafeNativeMethods.IStorage storage) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetNewStorage"); if (!this.owner.AllowOleObjects) { storage = null; return NativeMethods.E_FAIL; } // Debug.WriteLine("get new storage"); UnsafeNativeMethods.ILockBytes pLockBytes = UnsafeNativeMethods.CreateILockBytesOnHGlobal(NativeMethods.NullHandleRef, true); Debug.Assert(pLockBytes != null, "pLockBytes is NULL!"); storage = UnsafeNativeMethods.StgCreateDocfileOnILockBytes(pLockBytes, NativeMethods.STGM_SHARE_EXCLUSIVE | NativeMethods.STGM_CREATE | NativeMethods.STGM_READWRITE, 0); Debug.Assert(storage != null, "storage is NULL!"); return NativeMethods.S_OK; } public int GetInPlaceContext(IntPtr lplpFrame, IntPtr lplpDoc, IntPtr lpFrameInfo) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetInPlaceContext"); return NativeMethods.E_NOTIMPL; } public int ShowContainerUI(int fShow) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::ShowContainerUI"); // Do nothing return NativeMethods.S_OK; } public int QueryInsertObject(ref Guid lpclsid, IntPtr lpstg, int cp) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::QueryInsertObject(" + lpclsid.ToString() + ")"); try { IntSecurity.UnmanagedCode.Demand(); return NativeMethods.S_OK; } catch (SecurityException) { // We do not have unmanaged code access, so // we need to restrict what we allow to be loaded } Guid realClsid = new Guid(); int hr = UnsafeNativeMethods.ReadClassStg(new HandleRef(null, lpstg), ref realClsid); Debug.WriteLineIf(RichTextDbg.TraceVerbose, "real clsid:" + realClsid.ToString() + " (hr=" + hr.ToString("X", CultureInfo.InvariantCulture) + ")"); if (!NativeMethods.Succeeded(hr)) { return NativeMethods.S_FALSE; } if (realClsid == Guid.Empty) { realClsid = lpclsid; } switch (realClsid.ToString().ToUpper(CultureInfo.InvariantCulture)) { case "00000315-0000-0000-C000-000000000046": // Metafile case "00000316-0000-0000-C000-000000000046": // DIB case "00000319-0000-0000-C000-000000000046": // EMF case "0003000A-0000-0000-C000-000000000046": //BMP return NativeMethods.S_OK; default: Debug.WriteLineIf(RichTextDbg.TraceVerbose, " denying '" + lpclsid.ToString() + "' from being inserted due to security restrictions"); return NativeMethods.S_FALSE; } } public int DeleteObject(IntPtr lpoleobj) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::DeleteObject"); // Do nothing return NativeMethods.S_OK; } public int QueryAcceptData(IComDataObject lpdataobj, /* CLIPFORMAT* */ IntPtr lpcfFormat, int reco, int fReally, IntPtr hMetaPict) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::QueryAcceptData(reco=" + reco + ")"); if (reco == NativeMethods.RECO_DROP) { if (owner.AllowDrop || owner.EnableAutoDragDrop) { MouseButtons b = Control.MouseButtons; Keys k = Control.ModifierKeys; int keyState = 0; // Due to the order in which we get called, we have to set up the keystate here. // First GetDragDropEffect is called with grfKeyState == 0, and then // QueryAcceptData is called. Since this is the time we want to fire // OnDragEnter, but we have yet to get the keystate, we set it up ourselves. if ((b & MouseButtons.Left) == MouseButtons.Left) { keyState |= NativeMethods.MK_LBUTTON; } if ((b & MouseButtons.Right) == MouseButtons.Right) { keyState |= NativeMethods.MK_RBUTTON; } if ((b & MouseButtons.Middle) == MouseButtons.Middle) { keyState |= NativeMethods.MK_MBUTTON; } if ((k & Keys.Control) == Keys.Control) { keyState |= NativeMethods.MK_CONTROL; } if ((k & Keys.Shift) == Keys.Shift) { keyState |= NativeMethods.MK_SHIFT; } lastDataObject = new DataObject(lpdataobj); if (!owner.EnableAutoDragDrop) { lastEffect = DragDropEffects.None; } DragEventArgs e = new DragEventArgs(lastDataObject, keyState, Control.MousePosition.X, Control.MousePosition.Y, DragDropEffects.All, lastEffect); if (fReally == 0) { // we are just querying // We can get here without GetDragDropEffects actually being called first. // This happens when you drag/drop between two rtb's. Say you drag from rtb1 to rtb2. // GetDragDropEffects will first be called for rtb1, then QueryAcceptData for rtb1 just // like in the local drag case. Then you drag into rtb2. rtb2 will first be called in this method, // and not GetDragDropEffects. Now lastEffect is initialized to None for rtb2, so we would not allow // the drag. Thus we need to set the effect here as well. e.Effect = ((keyState & NativeMethods.MK_CONTROL) == NativeMethods.MK_CONTROL) ? DragDropEffects.Copy : DragDropEffects.Move; owner.OnDragEnter(e); } else { owner.OnDragDrop(e); lastDataObject = null; } lastEffect = e.Effect; if (e.Effect == DragDropEffects.None) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tCancel data"); return NativeMethods.E_FAIL; } else { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tAccept data"); return NativeMethods.S_OK; } } else { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "\tCancel data, allowdrop == false"); lastDataObject = null; return NativeMethods.E_FAIL; } } else { return NativeMethods.E_NOTIMPL; } } public int ContextSensitiveHelp(int fEnterMode) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::ContextSensitiveHelp"); return NativeMethods.E_NOTIMPL; } public int GetClipboardData(NativeMethods.CHARRANGE lpchrg, int reco, IntPtr lplpdataobj) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetClipboardData"); return NativeMethods.E_NOTIMPL; } public int GetDragDropEffect(bool fDrag, int grfKeyState, ref int pdwEffect) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetDragDropEffect"); if (owner.AllowDrop || owner.EnableAutoDragDrop) { if (fDrag && grfKeyState == 0) { // This is the very first call we receive in a Drag-Drop operation, // so we will let the control know what we support. // Note that we haven't gotten any data yet, so we will let QueryAcceptData // do the OnDragEnter. Note too, that grfKeyState does not yet reflect the // current keystate if (owner.EnableAutoDragDrop) { lastEffect = (DragDropEffects.All | DragDropEffects.None); } else lastEffect = DragDropEffects.None; } else { // We are either dragging over or dropping // The below is the complete reverse of what the docs on MSDN suggest, // but if we follow the docs, we would be firing OnDragDrop all the // time instead of OnDragOver (see bug 99294). MSDN seems to be wrong here. // drag - fDrag = false, grfKeyState != 0 // drop - fDrag = false, grfKeyState = 0 // We only care about the drag. // // When we drop, lastEffect will have the right state if (!fDrag && lastDataObject != null && grfKeyState != 0) { DragEventArgs e = new DragEventArgs(lastDataObject, grfKeyState, Control.MousePosition.X, Control.MousePosition.Y, DragDropEffects.All, lastEffect); // Now tell which of the allowable effects we want to use, but only if we are not already none if (lastEffect != DragDropEffects.None) { e.Effect = ((grfKeyState & NativeMethods.MK_CONTROL) == NativeMethods.MK_CONTROL) ? DragDropEffects.Copy : DragDropEffects.Move; } owner.OnDragOver(e); lastEffect = e.Effect; } } pdwEffect = (int)lastEffect; } else { pdwEffect = (int)DragDropEffects.None; } return NativeMethods.S_OK; } public int GetContextMenu(short seltype, IntPtr lpoleobj, NativeMethods.CHARRANGE lpchrg, out IntPtr hmenu) { Debug.WriteLineIf(RichTextDbg.TraceVerbose, "IRichEditOleCallback::GetContextMenu"); ContextMenu cm = owner.ContextMenu; if (cm == null || owner.ShortcutsEnabled == false) hmenu = IntPtr.Zero; else { cm.sourceControl = owner; cm.OnPopup(EventArgs.Empty); // RichEd calls DestroyMenu after displaying the context menu IntPtr handle = cm.Handle; // if another control shares the same context menu // then we have to mark the context menu's handles empty because // RichTextBox will delete the menu handles once the popup menu is dismissed. Menu menu = cm; while (true) { int i = 0; int count = menu.ItemCount; for (; i< count; i++) { if (menu.items[i].handle != IntPtr.Zero) { menu = menu.items[i]; break; } } if (i == count) { menu.handle = IntPtr.Zero; menu.created = false; if (menu == cm) break; else menu = ((MenuItem) menu).Menu; } } hmenu = handle; } return NativeMethods.S_OK; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.[To be supplied.] ///
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Timeline.cs
- FileSystemWatcher.cs
- DesignerActionList.cs
- SecuritySessionFilter.cs
- EditorPart.cs
- CookielessHelper.cs
- DataTablePropertyDescriptor.cs
- EncryptedPackageFilter.cs
- StorageComplexPropertyMapping.cs
- FileVersionInfo.cs
- GeometryGroup.cs
- DataSourceIDConverter.cs
- XmlEnumAttribute.cs
- CmsInterop.cs
- CodeCastExpression.cs
- AuthorizationBehavior.cs
- QueryOutputWriter.cs
- ListViewItemSelectionChangedEvent.cs
- Model3DGroup.cs
- XmlComplianceUtil.cs
- Transactions.cs
- ComponentRenameEvent.cs
- Permission.cs
- EventLevel.cs
- TextDecorationCollection.cs
- ObjectResult.cs
- Point4D.cs
- ItemCheckedEvent.cs
- BlockCollection.cs
- SerializationEventsCache.cs
- XmlHierarchicalEnumerable.cs
- SocketPermission.cs
- _UncName.cs
- TypeSource.cs
- PropertyChangedEventArgs.cs
- EntityViewGenerationConstants.cs
- ErrorFormatter.cs
- UIElement3D.cs
- ProgressBarAutomationPeer.cs
- CellParaClient.cs
- FormViewInsertEventArgs.cs
- RTTrackingProfile.cs
- ListControlDataBindingHandler.cs
- EventProvider.cs
- Literal.cs
- MaskPropertyEditor.cs
- ClipboardProcessor.cs
- XsltInput.cs
- ApplicationFileCodeDomTreeGenerator.cs
- WindowsFormsSectionHandler.cs
- DataGridViewBindingCompleteEventArgs.cs
- LinqDataSource.cs
- AutomationFocusChangedEventArgs.cs
- SqlDelegatedTransaction.cs
- PointAnimationClockResource.cs
- DropSource.cs
- AnnotationService.cs
- ScriptComponentDescriptor.cs
- MediaElement.cs
- SymLanguageVendor.cs
- relpropertyhelper.cs
- JsonDeserializer.cs
- coordinatorfactory.cs
- controlskin.cs
- TypeSemantics.cs
- ToolStripKeyboardHandlingService.cs
- TableParaClient.cs
- OutputCacheSettings.cs
- SqlNodeAnnotations.cs
- PathFigureCollectionConverter.cs
- ServiceObjectContainer.cs
- MimePart.cs
- XmlDataSourceView.cs
- MetadataItemCollectionFactory.cs
- base64Transforms.cs
- ColorTranslator.cs
- SmtpCommands.cs
- SqlExpressionNullability.cs
- DiscoveryDefaults.cs
- DeflateStreamAsyncResult.cs
- CopyAttributesAction.cs
- WorkerRequest.cs
- SiteMapNodeCollection.cs
- SqlGatherProducedAliases.cs
- PingReply.cs
- CodeTypeConstructor.cs
- Vector3D.cs
- ToolboxItemAttribute.cs
- XmlBufferReader.cs
- PointIndependentAnimationStorage.cs
- ResourceReferenceExpression.cs
- StyleHelper.cs
- ProtocolsSection.cs
- ConnectionManagementElement.cs
- Enlistment.cs
- SqlBulkCopyColumnMappingCollection.cs
- DownloadProgressEventArgs.cs
- DataList.cs
- TdsParserStateObject.cs
- SpAudioStreamWrapper.cs