|
//------------------------------------------------------------------------------
// <copyright file="RichTextBox.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
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 System.Runtime.Versioning;
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
using Util = NativeMethods.Util;
//
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox"]/*' />
/// <devdoc>
/// Rich Text control. The RichTextBox is a control that contains formatted text.
/// It supports font selection, boldface, and other type attributes.
/// </devdoc>
[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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DV_E_DVASPECT"]/*' />
/// <devdoc>
/// Paste special flags.
/// </devdoc>
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);
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.RichTextBox"]/*' />
/// <devdoc>
/// Constructs a new RichTextBox.
/// </devdoc>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.AllowDrop"]/*' />
/// <devdoc>
/// 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
/// </devdoc>
[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;
}
}
/// <include file='doc\TextBoxBase.uex' path='docs/doc[@for="TextBoxBase.AutoSize"]/*' />
/// <devdoc>
/// <para>
/// 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.
/// </para>
/// </devdoc>
[
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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.AutoWordSelection"]/*' />
/// <devdoc>
/// Controls whether whether mouse selection snaps to whole words.
/// </devdoc>
[
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);
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.BackgroundImage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Image BackgroundImage {
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.BackgroundImageLayout"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override ImageLayout BackgroundImageLayout {
get {
return base.BackgroundImageLayout;
}
set {
base.BackgroundImageLayout = value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.BulletIndent"]/*' />
/// <devdoc>
/// Returns the amount of indent used in a RichTextBox control when
/// SelectionBullet is set to true.
/// </devdoc>
[
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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.CanRedo"]/*' />
/// <devdoc>
/// Whether or not there are actions that can be Redone on the RichTextBox control.
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.RichTextBoxCanRedoDescr)
]
public bool CanRedo {
get {
if (IsHandleCreated) {
bool b;
b = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_CANREDO, 0, 0)) != 0;
return b;
}
return false;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.CreateParams"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override CreateParams CreateParams {
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
get {
// Check for library
if (moduleHandle == IntPtr.Zero) {
string richEditControlDllVersion = LocalAppContextSwitches.DoNotLoadLatestRichEditControl ? RichTextBoxConstants.DLL_RICHEDIT : RichTextBoxConstants.DLL_RICHEDIT_41;
moduleHandle = UnsafeNativeMethods.LoadLibraryFromSystemPathIfAvailable(richEditControlDllVersion);
int lastWin32Error = Marshal.GetLastWin32Error();
// This code has been here since the inception of the project,
// we can’t determine why we have to compare w/ 32 here.
// This fails on 3-GB mode, (once the dll is loaded above 3GB memory space) (see Dev10 bug#732388)
if ((ulong)moduleHandle < (ulong)32) {
throw new Win32Exception(lastWin32Error, SR.GetString(SR.LoadDLLError, richEditControlDllVersion));
}
//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 = UnsafeNativeMethods.GetModuleFileNameLongPath(new HandleRef(null, moduleHandle));
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 = LocalAppContextSwitches.DoNotLoadLatestRichEditControl ? RichTextBoxConstants.WC_RICHEDITA : RichTextBoxConstants.WC_RICHEDITA_41;
}
else {
cp.ClassName = LocalAppContextSwitches.DoNotLoadLatestRichEditControl ? RichTextBoxConstants.WC_RICHEDITW : RichTextBoxConstants.WC_RICHEDITW_41;
}
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
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DetectUrls"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DefaultSize"]/*' />
protected override Size DefaultSize {
get {
return new Size(100, 96);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.EnableAutoDragDrop"]/*' />
/// <devdoc>
/// We can't just enable drag/drop of text by default: it's a breaking change (VSWhidbey
/// 375177). Should be false by default.
/// </devdoc>
[
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();
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ForeColor"]/*' />
public override Color ForeColor {
get {
return base.ForeColor;
}
set {
if (IsHandleCreated) {
if (InternalSetForeColor(value)) {
base.ForeColor = value;
}
}
else {
base.ForeColor = value;
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Font"]/*' />
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; }
}
/// <devdoc>
/// 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.
/// </devdoc>
[
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; }
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.MaxLength"]/*' />
[
DefaultValue(int.MaxValue),
]
public override int MaxLength {
get {
return base.MaxLength;
}
set {
base.MaxLength = value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Multiline"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[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; }
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.RedoActionName"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
//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 = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_GETREDONAME, 0, 0));
return GetEditorActionName(n);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.RichTextBoxShortcutsEnabled"]/*' />
//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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.RightMargin"]/*' />
/// <devdoc>
/// The right margin of a RichTextBox control. A nonzero margin implies WordWrap.
/// </devdoc>
[
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));
}
}
}
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Rtf"]/*' />
/// <devdoc>
/// The text of a RichTextBox control, including all Rtf codes.
/// </devdoc>
[
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);
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ScrollBars"]/*' />
/// <devdoc>
/// The current scrollbar settings for a multi-line rich edit control.
/// Possible return values are given by the RichTextBoxScrollBars enumeration.
/// </devdoc>
[
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();
}
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionAlignment"]/*' />
/// <devdoc>
/// The alignment of the paragraphs in a RichTextBox control.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionBullet"]/*' />
/// <devdoc>
/// Determines if a paragraph in the RichTextBox control
/// contains the current selection or insertion point has the bullet style.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionCharOffset"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionColor"]/*' />
/// <devdoc>
/// The color of the currently selected text in the
/// RichTextBox control.
/// Returns Color.Empty if the selection has more than one color.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionBackColor"]/*' />
/// <devdoc>
/// The background color of the currently selected text in the RichTextBox control.
/// Returns Color.Empty if the selection has more than one color.
/// </devdoc>
[
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);
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionFont"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.RichTextBoxSelFont)
]
public Font SelectionFont {
get {
return GetCharFormatFont(true);
}
set {
SetCharFormatFont(true, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionHangingIndent"]/*' />
/// <devdoc>
/// 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).
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionIndent"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionLength"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the number of characters selected in the text
/// box.
/// </para>
/// </devdoc>
[
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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionProtected"]/*' />
/// <devdoc>
/// true if the current selection prevents any changes to its contents.
/// </devdoc>
[
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);
}
}
/// <devdoc>
/// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL)
/// in Win9x.
/// </devdoc>
internal override bool SelectionUsesDbcsOffsetsInWin9x {
get {
return false; // false for RichEdit, true for Edit.
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectedRtf"]/*' />
/// <devdoc>
/// The currently selected text of a RichTextBox control, including
/// all Rtf codes.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionRightIndent"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionTabs"]/*' />
/// <devdoc>
/// The absolute tab positions (in pixels) of text in a RichTextBox control.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectedText"]/*' />
/// <devdoc>
/// The currently selected text of a RichTextBox control; consists of a
/// zero length string if no characters are selected.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionType"]/*' />
/// <devdoc>
/// The type of the current selection. The returned value is one
/// of the values enumerated in RichTextBoxSelectionType.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.RichTextBoxSelTypeDescr)
]
public RichTextBoxSelectionTypes SelectionType {
get {
ForceHandleCreate();
if (SelectionLength > 0) {
int n;
n = unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_SELECTIONTYPE, 0, 0));
return (RichTextBoxSelectionTypes)n;
}
else {
return RichTextBoxSelectionTypes.Empty;
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ShowSelectionMargin"]/*' />
/// <devdoc>
/// Whether or not the left edge of the control will have a "selection margin" which
/// can be used to select entire lines
/// </devdoc>
[
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);
}
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Text"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
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);
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.TextLength"]/*' />
[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 unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTLENGTHEX, gtl, 0 /*ignored*/));
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.UndoActionName"]/*' />
/// <devdoc>
/// 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 ("").
/// </devdoc>
//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 = unchecked( (int) (long)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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ZoomFactor"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ContentsResized"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxContentsResized)]
public event ContentsResizedEventHandler ContentsResized {
add {
Events.AddHandler(EVENT_REQUESTRESIZE, value);
}
remove {
Events.RemoveHandler(EVENT_REQUESTRESIZE, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DragDrop"]/*' />
/// <devdoc>
/// 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
/// </devdoc>
[Browsable(false)]
public new event DragEventHandler DragDrop {
add {
base.DragDrop += value;
}
remove {
base.DragDrop -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DragEnter"]/*' />
/// <devdoc>
/// 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
/// </devdoc>
[Browsable(false)]
public new event DragEventHandler DragEnter {
add {
base.DragEnter += value;
}
remove {
base.DragEnter -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DragLeave"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler DragLeave {
add {
base.DragLeave += value;
}
remove {
base.DragLeave -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.DragOver"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event DragEventHandler DragOver {
add {
base.DragOver += value;
}
remove {
base.DragOver -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GiveFeedback"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event GiveFeedbackEventHandler GiveFeedback {
add {
base.GiveFeedback += value;
}
remove {
base.GiveFeedback -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.QueryContinueDrag"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event QueryContinueDragEventHandler QueryContinueDrag {
add {
base.QueryContinueDrag += value;
}
remove {
base.QueryContinueDrag -= value;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.HScroll"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxHScroll)]
public event EventHandler HScroll {
add {
Events.AddHandler(EVENT_HSCROLL, value);
}
remove {
Events.RemoveHandler(EVENT_HSCROLL, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.LinkClicked"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxLinkClick)]
public event LinkClickedEventHandler LinkClicked {
add {
Events.AddHandler(EVENT_LINKACTIVATE, value);
}
remove {
Events.RemoveHandler(EVENT_LINKACTIVATE, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ImeChange"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxIMEChange)]
public event EventHandler ImeChange {
add {
Events.AddHandler(EVENT_IMECHANGE, value);
}
remove {
Events.RemoveHandler(EVENT_IMECHANGE, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Protected"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxProtected)]
public event EventHandler Protected {
add {
Events.AddHandler(EVENT_PROTECTED, value);
}
remove {
Events.RemoveHandler(EVENT_PROTECTED, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SelectionChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxSelChange)]
public event EventHandler SelectionChanged {
add {
Events.AddHandler(EVENT_SELCHANGE, value);
}
remove {
Events.RemoveHandler(EVENT_SELCHANGE, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.VScroll"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.RichTextBoxVScroll)]
public event EventHandler VScroll {
add {
Events.AddHandler(EVENT_VSCROLL, value);
}
remove {
Events.RemoveHandler(EVENT_VSCROLL, value);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.CanPaste"]/*' />
/// <devdoc>
/// Returns a boolean indicating whether the RichTextBoxConstants control can paste the
/// given clipboard format.
/// </devdoc>
public bool CanPaste(DataFormats.Format clipFormat) {
bool b = false;
b = unchecked( (int) (long)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<requestedCharCount; i++) {
if (*pBuffer == '\r') {
pBuffer++;
continue;
}
*pChars = *pBuffer;
pChars++;
pBuffer++;
consumedCharCount++;
}
}
editStream.Write(bytes, 0, consumedCharCount * 2);
}
else {
int requestedCharCount = cb;
int consumedCharCount = 0;
fixed (byte* pb = bytes) {
byte* pChars = (byte*)pb;
byte* pBuffer = (byte*)(long)buf;
for (int i=0; i<requestedCharCount; i++) {
if (*pBuffer == (byte)'\r') {
pBuffer++;
continue;
}
*pChars = *pBuffer;
pChars++;
pBuffer++;
consumedCharCount++;
}
}
editStream.Write(bytes, 0, consumedCharCount);
}
break;
}
// set up number of bytes transferred
transferred = cb;
break;
}
case RichTextBox.INPUT: {
// vsWhidbey 339358: several customers complained that they were getting Random NullReference exceptions inside EditStreamProc.
// We had a case of acustomer using Everett bits and another case of a customer using Whidbey Beta1 bits.
// We don't have a repro in house which makes it problematic to determine the cause for this behavior.
// Looking at the code it seems that the only posibility for editStream to be null is when the user
// calls RichTextBox::LoadFile(Stream, RichTextBoxStreamType) with a null Stream.
// However, the user said that his app is not using LoadFile method.
// The only possibility left open is that the native Edit control sends random calls into EditStreamProc.
// We have to guard against this.
if (editStream != null) {
transferred = editStream.Read(bytes, 0, cb);
Marshal.Copy(bytes, 0, buf, transferred);
// set up number of bytes transferred
if (transferred < 0) transferred = 0;
} else {
// Set transferred to 0 so the native Edit controls knows that they should stop calling our EditStreamProc.
transferred = 0;
}
break;
}
}
}
#if DEBUG
catch (IOException e) {
Debug.Fail("Failed to edit proc operation.", e.ToString());
transferred = 0;
ret = 1;
}
#else
catch (IOException) {
transferred = 0;
ret = 1;
}
#endif
return ret; // tell the RichTextBoxConstants how we are doing 0 - Okay, 1 - quit
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for a given string.
/// </devdoc>
public int Find(string str) {
return Find(str, 0, 0, RichTextBoxFinds.None);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find1"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for a given string.
/// </devdoc>
public int Find(string str, RichTextBoxFinds options) {
return Find(str, 0, 0, options);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find2"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for a given string.
/// </devdoc>
public int Find(string str, int start, RichTextBoxFinds options) {
return Find(str, start, -1, options);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find3"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for a given string.
/// </devdoc>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find4"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for the given characters.
/// </devdoc>
public int Find(char[] characterSet) {
return Find(characterSet, 0, -1);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find5"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for the given characters.
/// </devdoc>
public int Find(char[] characterSet, int start) {
return Find(characterSet, start, -1);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find6"]/*' />
/// <devdoc>
/// Searches the text in a RichTextBox control for the given characters.
/// </devdoc>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GetCharFormat"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GetCharIndexFromPosition"]/*' />
/// <devdoc>
/// Returns the index of the character nearest to the given point.
/// </devdoc>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GetCharInCharSet"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GetLineFromCharIndex"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
public override int GetLineFromCharIndex(int index) {
return unchecked( (int) (long)SendMessage(RichTextBoxConstants.EM_EXLINEFROMCHAR, 0, index));
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GetPositionFromCharIndex"]/*' />
/// <devdoc>
/// Returns the location of the character at the given index.
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.GetProtectedError"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private bool GetProtectedError() {
if (ProtectedError) {
ProtectedError = false;
return true;
}
return false;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.LoadFile"]/*' />
/// <devdoc>
/// Loads the contents of the given RTF or text file into a RichTextBox control.
/// </devdoc>
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public void LoadFile(string path) {
LoadFile(path, RichTextBoxStreamType.RichText);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.LoadFile1"]/*' />
/// <devdoc>
/// Loads the contents of a RTF or text into a RichTextBox control.
/// </devdoc>
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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();
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.LoadFile2"]/*' />
/// <devdoc>
/// Loads the contents of a RTF or text into a RichTextBox control.
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnBackColorChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnBackColorChanged(EventArgs e) {
if (IsHandleCreated)
{
SendMessage(RichTextBoxConstants.EM_SETBKGNDCOLOR, 0, ColorTranslator.ToWin32(BackColor));
}
base.OnBackColorChanged(e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnContextMenuChanged"]/*' />
protected override void OnContextMenuChanged(EventArgs e)
{
base.OnContextMenuChanged(e);
UpdateOleCallback();
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnRightToLeftChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnContentsResized"]/*' />
/// <devdoc>
/// Fires an event when the user changes the control's contents
/// are either smaller or larger than the control's window size.
/// </devdoc>
protected virtual void OnContentsResized(ContentsResizedEventArgs e) {
ContentsResizedEventHandler handler = (ContentsResizedEventHandler)Events[EVENT_REQUESTRESIZE];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnHandleCreated"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnHandleDestroyed"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnHScroll"]/*' />
/// <devdoc>
/// Fires an event when the user clicks a RichTextBox control's horizontal
/// scroll bar.
/// </devdoc>
protected virtual void OnHScroll(EventArgs e) {
EventHandler handler = (EventHandler)Events[EVENT_HSCROLL];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnLinkClicked"]/*' />
/// <devdoc>
/// Fires an event when the user clicks on a link
/// in a rich-edit control.
/// </devdoc>
protected virtual void OnLinkClicked(LinkClickedEventArgs e) {
LinkClickedEventHandler handler = (LinkClickedEventHandler)Events[EVENT_LINKACTIVATE];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnImeChange"]/*' />
/// <devdoc>
/// Fires an event when the user changes the control's IME conversion status.
/// </devdoc>
protected virtual void OnImeChange(EventArgs e) {
EventHandler handler = (EventHandler)Events[EVENT_IMECHANGE];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnProtected"]/*' />
/// <devdoc>
/// Fires an event when the user is taking an action that would change
/// a protected range of text in the RichTextBox control.
/// </devdoc>
protected virtual void OnProtected(EventArgs e) {
ProtectedError = true;
EventHandler handler = (EventHandler)Events[EVENT_PROTECTED];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnSelectionChanged"]/*' />
/// <devdoc>
/// Fires an event when the current selection of text in the RichTextBox
/// control has changed or the insertion point has moved.
/// </devdoc>
protected virtual void OnSelectionChanged(EventArgs e) {
EventHandler handler = (EventHandler)Events[EVENT_SELCHANGE];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.OnVScroll"]/*' />
/// <devdoc>
/// Fires an event when the user clicks a RichTextBox control's vertical
/// scroll bar.
/// </devdoc>
protected virtual void OnVScroll(EventArgs e) {
EventHandler handler = (EventHandler)Events[EVENT_VSCROLL];
if (handler != null) handler(this,e);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Paste"]/*' />
/// <devdoc>
/// Pastes the contents of the clipboard in the given clipboard format.
/// </devdoc>
public void Paste(DataFormats.Format clipFormat) {
Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded");
IntSecurity.ClipboardRead.Demand();
PasteUnsafe(clipFormat, 0);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Paste1"]/*' />
/// <devdoc>
/// Note that this doesn't make a security demand: functions that call this should.
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.ProcessCmdKey"]/*' />
[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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Redo"]/*' />
/// <devdoc>
/// Redoes the last undone editing operation.
/// </devdoc>
public void Redo() {
SendMessage(RichTextBoxConstants.EM_REDO, 0, 0);
}
//NOTE: Undo is implemented on TextBox
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SaveFile"]/*' />
/// <devdoc>
/// Saves the contents of a RichTextBox control to a file.
/// </devdoc>
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public void SaveFile(string path) {
SaveFile(path, RichTextBoxStreamType.RichText);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SaveFile1"]/*' />
/// <devdoc>
/// Saves the contents of a RichTextBox control to a file.
/// </devdoc>
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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();
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SaveFile2"]/*' />
/// <devdoc>
/// Saves the contents of a RichTextBox control to a file.
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SendZoomFactor"]/*' />
/// <devdoc>
/// Core Zoom calculation and message passing (used by ZoomFactor property and CreateHandle()
/// </devdoc>
/// <internalonly/>
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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SetCharFormat"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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<bytesFaceName.Length; i++) cfA.szFaceName[i] = bytesFaceName[i];
cfA.dwMask = dwMask;
cfA.dwEffects = dwEffects;
cfA.yHeight = (int) (value.SizeInPoints * 20);
cfA.bCharSet = logfont.lfCharSet;
cfA.bPitchAndFamily = logfont.lfPitchAndFamily;
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, selectionOnly ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_ALL, cfA);
}
else
{
bytesFaceName = Encoding.Unicode.GetBytes(logfont.lfFaceName);
NativeMethods.CHARFORMATW cfW = new NativeMethods.CHARFORMATW();
for (int i=0; i<bytesFaceName.Length; i++) cfW.szFaceName[i] = bytesFaceName[i];
cfW.dwMask = dwMask;
cfW.dwEffects = dwEffects;
cfW.yHeight = (int) (value.SizeInPoints * 20);
cfW.bCharSet = logfont.lfCharSet;
cfW.bPitchAndFamily = logfont.lfPitchAndFamily;
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_SETCHARFORMAT, selectionOnly ? RichTextBoxConstants.SCF_SELECTION : RichTextBoxConstants.SCF_ALL, cfW);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.FontToLogFont"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")]
static private void FontToLogFont(Font value, NativeMethods.LOGFONT logfont) {
value.ToLogFont(logfont);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.SetupLogPixels"]/*' />
/// <devdoc>
/// </devdoc>
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);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.StreamIn"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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;
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.StreamOut"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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 != unchecked( (int) (long)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);
}
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.CreateRichEditOleCallback"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
[SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected virtual object CreateRichEditOleCallback() {
return new OleCallback(this);
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.EnLinkMsgHandler"]/*' />
/// <devdoc>
/// Handles link messages (mouse move, down, up, dblclk, etc)
/// </devdoc>
/// <internalonly/>
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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.CharRangeToString"]/*' />
/// <devdoc>
/// 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.
/// </devdoc>
/// <internalonly/>
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.WmReflectCommand"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.WmReflectNotify"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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);
if (UnsafeNativeMethods.DragQueryFileLongPath(new HandleRef(endropfiles, endropfiles.hDrop), 0, path) != 0)
{
// 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 ((unchecked( (int) (long)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;
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.WmSelectionChange"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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 = unchecked( (int) (long)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);
}
}
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.WmSetFont"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
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);
}
// <internalonly/>
// </doc>
//
/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.WndProc"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[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) ? unchecked( (int) (long)m.Result) | NativeMethods.DLGC_WANTTAB : unchecked( (int) (long)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;
}
}
}
}
|