Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / TextEditor.cs / 1305600 / TextEditor.cs
#pragma warning disable 1634, 1691 // To enable presharp warning disables (#pragma suppress) below. // -------------------------------------------------------------------------- // // File: TextEditor.cs // // Copyright (C) Microsoft Corporation. All rights reserved. // // Description: Text editing service for controls. // //--------------------------------------------------------------------------- namespace System.Windows.Documents { using MS.Internal; using System.Globalization; using System.Threading; using System.ComponentModel; using System.Text; using System.Collections; // ArrayList using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Windows.Threading; using System.Windows.Input; using System.Windows.Controls; // ScrollChangedEventArgs using System.Windows.Controls.Primitives; // CharacterCasing, TextBoxBase using System.Windows.Media; using System.Windows.Markup; using MS.Utility; using MS.Win32; using MS.Internal.Documents; using MS.Internal.Commands; // CommandHelpers ////// Text editing service for controls. /// internal class TextEditor { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Initialize the TextEditor /// /// /// TextContainer representing a content to edit. /// /// /// FrameworkElement on which all events for the user interaction will be /// processed. /// /// /// If true the TextEditor will enable undo support /// internal TextEditor(ITextContainer textContainer, FrameworkElement uiScope, bool isUndoEnabled) { // Validate parameters Invariant.Assert(uiScope != null); // Set non-zero property defaults. _acceptsRichContent = true; // Attach the editor instance to the scope _textContainer = textContainer; _uiScope = uiScope; // Enable undo manager for this uiScope if (isUndoEnabled && _textContainer is TextContainer) { ((TextContainer)_textContainer).EnableUndo(_uiScope); } // Create TextSelection and link it to text container _selection = new TextSelection(this); textContainer.TextSelection = _selection; // Create DragDropProcess // _dragDropProcess = new TextEditorDragDrop._DragDropProcess(this); // By default we use IBeam cursor _cursor = Cursors.IBeam; // Add InputLanguageChanged event handler TextEditorTyping._AddInputLanguageChangedEventHandler(this); // Listen to both TextContainer.EndChanging and TextContainer.Changed events TextContainer.Changed += new TextContainerChangedEventHandler(OnTextContainerChanged); // Add IsEnabled event handler for cleaning the caret element when uiScope is disabled _uiScope.IsEnabledChanged += new DependencyPropertyChangedEventHandler(OnIsEnabledChanged); // Attach this instance of text editor to its uiScope _uiScope.SetValue(TextEditor.InstanceProperty, this); // The IsSpellerEnabled property might have been set before this // TextEditor was instantiated -- check if we need to rev // up speller support. if ((bool)_uiScope.GetValue(SpellCheck.IsEnabledProperty)) { SetSpellCheckEnabled(true); SetCustomDictionaries(true); } // If no IME/TextServices are installed, we have no native reasources // to clean up at Finalizer. if (!TextServicesLoader.ServicesInstalled) { GC.SuppressFinalize(this); } } #endregion Constructors //------------------------------------------------------ // // Finalizer // //----------------------------------------------------- #region Finalizer ////// The Finalizer will release the resources that were not released earlier. /// ~TextEditor() { // Detach TextStore that TextStore will be unregisted from Cicero. // And clean all reference of the native resources. DetachTextStore(true /* finalizer */); } #endregion Finalizer //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Notification that the EditBehavior is being removed from the /// scope to which it was attached. /// ////// innternal - ta make it accessible from TextEditor class. /// ////// Critical - as this calls Critical method ImmComposition.GetImmComposition(). /// Safe - as this just gets the ImmComposition for the current element and invokes /// the OnDetach event. /// [SecurityCritical, SecurityTreatAsSafe] internal void OnDetach() { Invariant.Assert(_textContainer != null); // Make sure the speller is shut down. SetSpellCheckEnabled(false); // Delete UndoManager UndoManager undoManager = UndoManager.GetUndoManager(_uiScope); if(undoManager != null) { if (_textContainer is TextContainer) { ((TextContainer)_textContainer).DisableUndo(_uiScope); } else { UndoManager.DetachUndoManager(_uiScope); } } // Release TextContainer _textContainer.TextSelection = null; // Remove InputLanguageChanged event handler TextEditorTyping._RemoveInputLanguageChangedEventHandler(this); // Remove both TextContainer.Changed event handlers _textContainer.Changed -= new TextContainerChangedEventHandler(OnTextContainerChanged); // Remove IsEnabled event handler that use for cleaning the caret element when uiScope is disabled _uiScope.IsEnabledChanged -= new DependencyPropertyChangedEventHandler(OnIsEnabledChanged); // Cancel any pending InitTextStore callback that might still // be in the queue. _pendingTextStoreInit = false; // Shut down the Cicero. DetachTextStore(false /* finalizer */); // Shut down IMM32. if (_immComposition != null) { // _immComposition comes from getting of the focus on the editor with the enabled IMM. // _immComposition.OnDetach will remove the events handler and then detach editor. _immComposition.OnDetach(); _immComposition = null; } else { // Make sure immComposition.OnDetach if there is the remaining ImmComposition // that can be happened after getting the lost focus. ImmComposition immComposition = ImmComposition.GetImmComposition(_uiScope); if (immComposition != null) { immComposition.OnDetach(); } } // detach fromm textview this.TextView = null; // Delete selection object, caret and highlight _selection.OnDetach(); _selection = null; _uiScope.ClearValue(TextEditor.InstanceProperty); _uiScope = null; _textContainer = null; } ////// We don't need TextStore after Dispatcher is disposed. /// DetachTextStore is called from Finalizer or UICntext.Dispose event callback. /// Finalizer calls this to release Cicero's resources. Then we don't need /// a call back from UIContex.Dispose any more. And we can release _weakThis. /// private void DetachTextStore(bool finalizer) { // We don't need this TextStore any more. // TextStore needs to be unregisted from Cicero so clean all reference // of the native resources. if (_textstore != null) { _textstore.OnDetach(finalizer); _textstore = null; } if (_weakThis != null) { _weakThis.StopListening(); _weakThis = null; } if (!finalizer) { // Cicero's resources have been released. // We don't have to get Finalizer called now. GC.SuppressFinalize(this); } } // Worker method for set_IsSpellCheckEnabled. // Note that enabling the spell checker is also gated on the IsReadOnly // and IsEnabled properties of the current UiScope. internal void SetSpellCheckEnabled(bool value) { value = value && !this.IsReadOnly && this._IsEnabled; if (value && _speller == null) { // Start up the speller. _speller = new Speller(this); } else if (!value && _speller != null) { // Shut down the speller. _speller.Detach(); _speller = null; } } ////// Loads custom dictionaries /// /// ///internal void SetCustomDictionaries(bool add) { TextBoxBase textBoxBase = _uiScope as TextBoxBase; // We want CustomDictionaries to take effect only on TextBoxBase derived classes. if (textBoxBase == null) { return; } if (_speller != null) { CustomDictionarySources dictionarySources = (CustomDictionarySources)SpellCheck.GetCustomDictionaries(textBoxBase); _speller.SetCustomDictionaries(dictionarySources, add); } } // Forwards a spelling reform property change off to the speller. internal void SetSpellingReform(SpellingReform spellingReform) { if (_speller != null) { _speller.SetSpellingReform(spellingReform); } } // Queries a FrameworkElement for its TextView internal static ITextView GetTextView(UIElement scope) { IServiceProvider serviceProvider = scope as IServiceProvider; return (serviceProvider != null) ? serviceProvider.GetService(typeof(ITextView)) as ITextView : null; } // Maps a FrameworkElement to its TextSelection, if any. internal static ITextSelection GetTextSelection(FrameworkElement frameworkElement) { TextEditor textEditor = TextEditor._GetTextEditor(frameworkElement); return (textEditor == null) ? null : textEditor.Selection; } // Registers all text editing command handlers for a given control type // // If registerEventListeners is false, caller is responsible for calling OnXXXEvent methods on TextEditor from // UIElement and FrameworkElement virtual overrides (piggy backing on the // UIElement/FrameworkElement class listeners). If true, TextEditor will register // its own class listener for events it needs. // // This method will always register private command listeners. /// /// Critical:This code register command handlers for texteditor related events and commands (OnGotFocus) /// TreatAsSafe: This just hooks up methods that are internal to this class /// [SecurityCritical, SecurityTreatAsSafe] internal static void RegisterCommandHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) { // Check if we already registered handlers for this type Invariant.Assert(_registeredEditingTypes != null); lock (_registeredEditingTypes) { for (int i = 0; i < _registeredEditingTypes.Count; i++) { // If controlType is or derives from some already registered class - we are done if (((Type)_registeredEditingTypes[i]).IsAssignableFrom(controlType)) { return; } // Check if controlType is not a superclass of some registered class. // This is erroneus condition, which must be avoided. // Otherwise the same handlers will be attached to some class twice. if (controlType.IsAssignableFrom((Type)_registeredEditingTypes[i])) { throw new InvalidOperationException( SR.Get(SRID.TextEditorCanNotRegisterCommandHandler, ((Type)_registeredEditingTypes[i]).Name, controlType.Name)); } } // The class was not yet registered. Add it to the list before starting registering handlers. _registeredEditingTypes.Add(controlType); } // Mouse TextEditorMouse._RegisterClassHandlers(controlType, registerEventListeners); if (!readOnly) { // Typing TextEditorTyping._RegisterClassHandlers(controlType, registerEventListeners); } // Drag-and-drop TextEditorDragDrop._RegisterClassHandlers(controlType, readOnly, registerEventListeners); // Cut-Copy-Paste TextEditorCopyPaste._RegisterClassHandlers(controlType, acceptsRichContent, readOnly, registerEventListeners); // Selection Commands TextEditorSelection._RegisterClassHandlers(controlType, registerEventListeners); if (!readOnly) { // Paragraph Formatting TextEditorParagraphs._RegisterClassHandlers(controlType, acceptsRichContent, registerEventListeners); } // ContextMenu TextEditorContextMenu._RegisterClassHandlers(controlType, registerEventListeners); if (!readOnly) { // Spelling TextEditorSpelling._RegisterClassHandlers(controlType, registerEventListeners); } if (acceptsRichContent && !readOnly) { // Character Formatting TextEditorCharacters._RegisterClassHandlers(controlType, registerEventListeners); // Editing Commands: List Editing TextEditorLists._RegisterClassHandlers(controlType, registerEventListeners); // Editing Commands: Table Editing if (_isTableEditingEnabled) { TextEditorTables._RegisterClassHandlers(controlType, registerEventListeners); } } // Focus // ----- if (registerEventListeners) { EventManager.RegisterClassHandler(controlType, Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnGotKeyboardFocus)); EventManager.RegisterClassHandler(controlType, Keyboard.LostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnLostKeyboardFocus)); EventManager.RegisterClassHandler(controlType, UIElement.LostFocusEvent, new RoutedEventHandler(OnLostFocus)); } // Undo-Redo // --------- if (!readOnly) { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Undo, new ExecutedRoutedEventHandler(OnUndo), new CanExecuteRoutedEventHandler(OnQueryStatusUndo), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyUndo), SR.Get(SRID.KeyUndoDisplayString)), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyAltUndo), SR.Get(SRID.KeyAltUndoDisplayString))); CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Redo, new ExecutedRoutedEventHandler(OnRedo), new CanExecuteRoutedEventHandler(OnQueryStatusRedo), SRID.KeyRedo, SRID.KeyRedoDisplayString); } } // Worker for TextBox/RichTextBox.GetSpellingErrorAtPosition. internal SpellingError GetSpellingErrorAtPosition(ITextPointer position, LogicalDirection direction) { return TextEditorSpelling.GetSpellingErrorAtPosition(this, position, direction); } // Returns the error (if any) at the current selection. internal SpellingError GetSpellingErrorAtSelection() { return TextEditorSpelling.GetSpellingErrorAtSelection(this); } // Worker for TextBox/RichTextBox.GetNextSpellingErrorPosition. internal ITextPointer GetNextSpellingErrorPosition(ITextPointer position, LogicalDirection direction) { return TextEditorSpelling.GetNextSpellingErrorPosition(this, position, direction); } // Replaces a TextRange's content with a string. // Applies LanguageProperty based on current input language. // internal void SetText(ITextRange range, string text, CultureInfo cultureInfo) { // Input text range.Text = text; // mark the range with the current input language on the start position. if (range is TextRange) { MarkCultureProperty((TextRange)range, cultureInfo); } } // Replaces the current selection with a string. // Applies any springloaded properties to the text. // Applies LanguageProperty based on current input language. // Clears the cached caret X offset. // internal void SetSelectedText(string text, CultureInfo cultureInfo) { // Insert the text and tag it with culture property. SetText(this.Selection, text, cultureInfo); // Apply springload formatting ((TextSelection)this.Selection).ApplySpringloadFormatting(); // Forget previously suggested caret horizontal position. TextEditorSelection._ClearSuggestedX(this); } ////// Used for marking the span of incoming text with input language /// based on the current input language. The default input language is /// designated in FrameworkElement.LanguageProperty which has the /// default value of "en-US" but this can be changed at any tree node /// by xml:lang attribute /// internal void MarkCultureProperty(TextRange range, CultureInfo inputCultureInfo) { // Invariant.Assert(this.UiScope != null); if (!this.AcceptsRichContent) { return; } // Get the current culture infomation to mark the input culture information XmlLanguage language = (XmlLanguage)((ITextPointer)range.Start).GetValue(FrameworkElement.LanguageProperty); Invariant.Assert(language != null); // Compare the culture info between the current position and the input culture. // Set the input culture info if the current has the different culture info with input. if (!String.Equals(inputCultureInfo.IetfLanguageTag, language.IetfLanguageTag, StringComparison.OrdinalIgnoreCase)) { range.ApplyPropertyValue(FrameworkElement.LanguageProperty, XmlLanguage.GetLanguage(inputCultureInfo.IetfLanguageTag)); } // Get the input language's flow direction FlowDirection inputFlowDirection; if (inputCultureInfo.TextInfo.IsRightToLeft) { inputFlowDirection = FlowDirection.RightToLeft; } else { inputFlowDirection = FlowDirection.LeftToRight; } // Get the current flow direction FlowDirection currentFlowDirection = (FlowDirection)((ITextPointer)range.Start).GetValue(FrameworkElement.FlowDirectionProperty); // Set the FlowDirection property properly if the input language's flow direction // doesn't match with the current flow direction. if (currentFlowDirection != inputFlowDirection) { range.ApplyPropertyValue(FrameworkElement.FlowDirectionProperty, inputFlowDirection); } } internal void RequestExtendSelection(Point point) { if (_mouseSelectionState == null) { _mouseSelectionState = new MouseSelectionState(); _mouseSelectionState.Timer = new DispatcherTimer(DispatcherPriority.Normal); _mouseSelectionState.Timer.Tick += new EventHandler(HandleMouseSelectionTick); // 400ms is the default value for MenuShowDelay. Creating timer with smaller value may // cause Dispatcher queue starvation. _mouseSelectionState.Timer.Interval = TimeSpan.FromMilliseconds(Math.Max(SystemParameters.MenuShowDelay, 200)); _mouseSelectionState.Timer.Start(); _mouseSelectionState.Point = point; // Simulate the first Tick HandleMouseSelectionTick(_mouseSelectionState.Timer, EventArgs.Empty); } else { _mouseSelectionState.Point = point; } } internal void CancelExtendSelection() { if (_mouseSelectionState != null) { _mouseSelectionState.Timer.Stop(); _mouseSelectionState.Timer.Tick -= new EventHandler(HandleMouseSelectionTick); _mouseSelectionState = null; } } ////// Helper used to check if UiScope has a ToolTip which is open. If so, this method closes the tool tip. /// KeyDown and MouseDown event handlers use this helper to check for this condition. /// internal void CloseToolTip() { PopupControlService popupControlService = PopupControlService.Current; if (popupControlService.CurrentToolTip != null && popupControlService.CurrentToolTip.IsOpen && popupControlService.CurrentToolTip.PlacementTarget == _uiScope) { popupControlService.CurrentToolTip.IsOpen = false; } } ////// Undo worker. /// ////// Critical:Calls Composition.Complete which has a link demand /// TreatAsSafe: Does not expose the call /// [SecurityCritical, SecurityTreatAsSafe] internal void Undo() { TextEditorTyping._FlushPendingInputItems(this); // Complete the composition string before undo CompleteComposition(); _undoState = UndoState.Undo; bool forceLayoutUpdate = this.Selection.CoversEntireContent; try { // _selection.BeginChangeNoUndo(); try { UndoManager undoManager = _GetUndoManager(); if (undoManager != null && undoManager.UndoCount > undoManager.MinUndoStackCount) { undoManager.Undo(1); } // Forget previously suggested horizontal position // TextEditorSelection._ClearSuggestedX(this); // Break typing merge for undo TextEditorTyping._BreakTypingSequence(this); // Clear springload formatting if (_selection is TextSelection) { ((TextSelection)_selection).ClearSpringloadFormatting(); } } finally { _selection.EndChange(); } } finally { _undoState = UndoState.Normal; } // If we replaced the entire document content, background layout will // kick in. Force it to complete now. if (forceLayoutUpdate) { this.Selection.ValidateLayout(); } } ////// Redo worker. /// internal void Redo() { TextEditorTyping._FlushPendingInputItems(this); _undoState = UndoState.Redo; bool forceLayoutUpdate = this.Selection.CoversEntireContent; try { _selection.BeginChangeNoUndo(); try { UndoManager undoManager = _GetUndoManager(); if (undoManager != null && undoManager.RedoCount > 0) { undoManager.Redo(1); } // Forget previously suggested horizontal position // TextEditorSelection._ClearSuggestedX(this); // Break typing merge for undo TextEditorTyping._BreakTypingSequence(this); // Clear springload formatting if (_selection is TextSelection) { ((TextSelection)_selection).ClearSpringloadFormatting(); } } finally { _selection.EndChange(); } } finally { _undoState = UndoState.Normal; } // If we replaced the entire document content, background layout will // kick in. Force it to complete now. if (forceLayoutUpdate) { this.Selection.ValidateLayout(); } } internal void OnPreviewKeyDown(KeyEventArgs e) { TextEditorTyping.OnPreviewKeyDown(_uiScope, e); } internal void OnKeyDown(KeyEventArgs e) { TextEditorTyping.OnKeyDown(_uiScope, e); } internal void OnKeyUp(KeyEventArgs e) { TextEditorTyping.OnKeyUp(_uiScope, e); } internal void OnTextInput(TextCompositionEventArgs e) { TextEditorTyping.OnTextInput(_uiScope, e); } internal void OnMouseDown(MouseButtonEventArgs e) { TextEditorMouse.OnMouseDown(_uiScope, e); } internal void OnMouseMove(MouseEventArgs e) { TextEditorMouse.OnMouseMove(_uiScope, e); } internal void OnMouseUp(MouseButtonEventArgs e) { TextEditorMouse.OnMouseUp(_uiScope, e); } internal void OnQueryCursor(QueryCursorEventArgs e) { TextEditorMouse.OnQueryCursor(_uiScope, e); } internal void OnQueryContinueDrag(QueryContinueDragEventArgs e) { TextEditorDragDrop.OnQueryContinueDrag(_uiScope, e); } internal void OnGiveFeedback(GiveFeedbackEventArgs e) { TextEditorDragDrop.OnGiveFeedback(_uiScope, e); } internal void OnDragEnter(DragEventArgs e) { TextEditorDragDrop.OnDragEnter(_uiScope, e); } internal void OnDragOver(DragEventArgs e) { TextEditorDragDrop.OnDragOver(_uiScope, e); } internal void OnDragLeave(DragEventArgs e) { TextEditorDragDrop.OnDragLeave(_uiScope, e); } internal void OnDrop(DragEventArgs e) { TextEditorDragDrop.OnDrop(_uiScope, e); } internal void OnContextMenuOpening(ContextMenuEventArgs e) { TextEditorContextMenu.OnContextMenuOpening(_uiScope, e); } internal void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) { OnGotKeyboardFocus(_uiScope, e); } internal void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { OnLostKeyboardFocus(_uiScope, e); } internal void OnLostFocus(RoutedEventArgs e) { OnLostFocus(_uiScope, e); } #endregion Internal Methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties //...................................................... // // Dependency Properties // //...................................................... #region Dependency Properties ////// IsReadOnly attached property speficies if the content within a scope /// of some FrameworkElement is editable. /// internal static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.RegisterAttached( "IsReadOnly", typeof(bool), typeof(TextEditor), new FrameworkPropertyMetadata( false, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnIsReadOnlyChanged))); ////// TextEditor.AllowOvertype property controls how TextEditor treats INS key. /// When set to true INS key toggles overtype mode. /// Otherwise it is ignored. /// ////// internal static readonly DependencyProperty AllowOvertypeProperty = DependencyProperty.RegisterAttached( "AllowOvertype", typeof(bool), typeof(TextEditor), new FrameworkPropertyMetadata(true)); /// /// TextEditor.PageHeight attached property for pageup/down /// internal static readonly DependencyProperty PageHeightProperty = DependencyProperty.RegisterAttached( "PageHeight", typeof(double), typeof(TextEditor), new FrameworkPropertyMetadata(0d)); #endregion Dependency Properties //...................................................... // // Properties - Relations With Other Components // //...................................................... #region Properties - Relations With Other Components // The content TextContainer. internal ITextContainer TextContainer { get { return _textContainer; } } ////// A FrameworkElement to which this instance on TextEditor /// is attached. /// ///internal FrameworkElement UiScope { get { return _uiScope; } } /// /// A FrameworkElement to which this instance on TextEditor /// is attached. /// ///internal ITextView TextView { get { return _textView; } set { if (value != _textView) { if (_textView != null) { // Remove layout updated handler. _textView.Updated -= new EventHandler(OnTextViewUpdated); _textView = null; // Make sure that caret is destroyed for this text view (if any) // This must be called after clearing _textView _selection.UpdateCaretAndHighlight(); } if (value != null) { _textView = value; // Init a layout invalidation listener. _textView.Updated += new EventHandler(OnTextViewUpdated); // Make sure that caret is present for this text view _selection.UpdateCaretAndHighlight(); } } } } /// /// The TextSelection associated with this TextEditor. /// internal ITextSelection Selection { get { return _selection; } } // TextStore - needed in TextSelection to notify it about movements // internal TextStore TextStore { get { return _textstore; } } ////// ImmComposition implementation, used when _immEnabled. /// internal ImmComposition ImmComposition { get { return _immEnabled ? _immComposition : null; } } #endregion Properties - Rlations With Other Components //...................................................... // // Properties - Text Editor Behavior Parameterization // //...................................................... #region Properties - Text Editor Behavior Parameterization ////// If true, the TextEditor will accept the return/enter key, /// otherwise it will be ignored. Default is true. /// internal bool AcceptsReturn { get { return _uiScope == null ? true : (bool)_uiScope.GetValue(KeyboardNavigation.AcceptsReturnProperty); } } ////// If true, the TextEditor will accept the tab key, otherwise /// it will be ignored. Default is true. /// internal bool AcceptsTab { get { return _uiScope == null ? true : (bool)_uiScope.GetValue(TextBoxBase.AcceptsTabProperty); } set { Invariant.Assert(_uiScope != null); if (AcceptsTab != value) { _uiScope.SetValue(TextBoxBase.AcceptsTabProperty, value); } } } ////// If true, text selection will be enabled but the TextEditor will /// not modify content. Default is false. /// ////// Use TextSelection.HideCaret to stop the caret from rendering. /// internal bool IsReadOnly { get { // We use local flag _isReadOnly for masking inheritable setting of IsReadOnly if (_isReadOnly) { return true; } else { return _uiScope == null ? false : (bool)_uiScope.GetValue(TextEditor.IsReadOnlyProperty); } } set { // This setting does not affect logical tree setting; // it only applies to this particular editor. // Nested editors be in editable or non-editable state // independently on this flag. _isReadOnly = value; } } ////// Enables and disables spell checking on the document. /// ////// Defaults to false. /// internal bool IsSpellCheckEnabled { get { return _uiScope == null ? false : (bool)_uiScope.GetValue(SpellCheck.IsEnabledProperty); } set { Invariant.Assert(_uiScope != null); _uiScope.SetValue(SpellCheck.IsEnabledProperty, value); } } ////// If true, the TextEditor will accept xml markup for paragraphs and inline formatting. /// Default is true. /// internal bool AcceptsRichContent { get { return _acceptsRichContent; } set { _acceptsRichContent = value; } } ////// Clr accessor to AllowOvertypeProperty. /// internal bool AllowOvertype { get { return _uiScope == null ? true : (bool)_uiScope.GetValue(TextEditor.AllowOvertypeProperty); } } ////// Maximum length of text being edited (_selection.Text). More precisely, the /// user is not allowed to input text beyond this length. /// Default is 0, which means unlimited. /// internal int MaxLength { get { return _uiScope == null ? 0 : (int)_uiScope.GetValue(TextBox.MaxLengthProperty); } } ////// Controls whether input text is converted to upper or lower case. /// Default is CharacterCasing.Normal, which causes no conversion. /// internal CharacterCasing CharacterCasing { get { return _uiScope == null ? CharacterCasing.Normal : (CharacterCasing)_uiScope.GetValue(TextBox.CharacterCasingProperty); } } ////// Controls whether heuristics for selecting whole words on mouse drag are active /// Default is false. /// internal bool AutoWordSelection { get { return _uiScope == null ? false : (bool)_uiScope.GetValue(RichTextBox.AutoWordSelectionProperty); } } ////// Controls whether or not the caret is visible when the text editor is read-only. /// Default is false. /// internal bool IsReadOnlyCaretVisible { get { return _uiScope == null ? false : (bool)_uiScope.GetValue(TextBoxBase.IsReadOnlyCaretVisibleProperty); } } #endregion Properties - Text Editor Behavior Parameterization // A property exposing our current undo context. // // UndoState.Undo while inside OnUndo or // UndoState.Redo while inside OnRedo or // UndoState.Normal otherwise. internal UndoState UndoState { get { return _undoState; } } // Flag that indicates whether the UiScope has a context menu open. internal bool IsContextMenuOpen { get { return _isContextMenuOpen; } set { _isContextMenuOpen = value; } } // Speller instance for TextEditorSpelling. internal Speller Speller { get { return _speller; } } #endregion Internal Properties //----------------------------------------------------- // // Class Internal Methods // //----------------------------------------------------- #region Class Internal Methods // Maps a FrameworkElement to its TextEditor, if any. internal static TextEditor _GetTextEditor(object element) { return (element is DependencyObject) ? (((DependencyObject)element).ReadLocalValue(InstanceProperty) as TextEditor) : null; } ////// Returns the UndoManager, if any, associated with this editor instance. /// internal UndoManager _GetUndoManager() { UndoManager undoManager = null; if (this.TextContainer is TextContainer) { undoManager = ((TextContainer)this.TextContainer).UndoManager; } return undoManager; } ////// Filter input text based on MaxLength, and CharacterCasing. /// /// /// text to filter /// /// /// target range to be inserted or replaced /// ////// filtered text /// internal string _FilterText(string textData, ITextRange range) { return _FilterText(textData, range.Start.GetOffsetToPosition(range.End)); } internal string _FilterText(string textData, int charsToReplaceCount) { return _FilterText(textData, charsToReplaceCount, true); } internal string _FilterText(string textData, ITextRange range, bool filterMaxLength) { return _FilterText(textData, range.Start.GetOffsetToPosition(range.End), filterMaxLength); } internal string _FilterText(string textData, int charsToReplaceCount, bool filterMaxLength) { // We only filter text for plain text content if (!this.AcceptsRichContent) { if (filterMaxLength && this.MaxLength > 0) { ITextContainer textContainer = this.TextContainer; int currentLength = textContainer.SymbolCount - charsToReplaceCount; int extraCharsAllowed = Math.Max(0, this.MaxLength - currentLength); // Does textData length exceed allowed char length? if (textData.Length > extraCharsAllowed) { int splitPosition = textData.Length - extraCharsAllowed; if (IsBadSplitPosition(textData, splitPosition)) { extraCharsAllowed--; } textData = textData.Substring(0, extraCharsAllowed); } } if (this.CharacterCasing == CharacterCasing.Upper) { // Get CultureInfo from the current input language for ToUpper/ToLower. textData = textData.ToUpper(InputLanguageManager.Current.CurrentInputLanguage); } else if (this.CharacterCasing == CharacterCasing.Lower) { // Get CultureInfo from the current input language for ToUpper/ToLower. textData = textData.ToLower(InputLanguageManager.Current.CurrentInputLanguage); } if (!this.AcceptsReturn) { int endOfFirstLine = textData.IndexOf(Environment.NewLine, StringComparison.Ordinal); if (endOfFirstLine >= 0) { textData = textData.Substring(0, endOfFirstLine); } endOfFirstLine = textData.IndexOfAny(TextPointerBase.NextLineCharacters); if (endOfFirstLine >= 0) { textData = textData.Substring(0, endOfFirstLine); } } if (!this.AcceptsTab) { textData = textData.Replace('\t', ' '); } } return textData; } // This checks if the source is in UiScope or its style. // The key events or text input events should not be handled if the source is out side of // element (or style). // For example, when focus elment is UiScope's context menu, // TextEditor should ignore those events. internal bool _IsSourceInScope(object source) { if (source == this.UiScope) { return true; } if ((source is FrameworkElement) && ((FrameworkElement)source).TemplatedParent == this.UiScope) { return true; } return false; } ////// Complete the composition string. /// internal void CompleteComposition() { if (TextStore != null) { TextStore.CompleteComposition(); } if (ImmComposition != null) { ImmComposition.CompleteComposition(); } } #endregion Class Internal Methods //----------------------------------------------------- // // Class Internal Properties // //------------------------------------------------------ #region Class Internal Properties ////// Returns true if the IsEnabled ptorperty is set to true for ui scope of the editor. /// internal bool _IsEnabled { get { return _uiScope == null ? false : _uiScope.IsEnabled; } } internal bool _OvertypeMode { get { return _overtypeMode; } set { _overtypeMode = value; } } // Find the scroller from the render scope of TextEdior internal FrameworkElement _Scroller { get { FrameworkElement scroller = this.TextView == null ? null : (this.TextView.RenderScope as FrameworkElement); while (scroller != null && scroller != this.UiScope) { scroller = FrameworkElement.GetFrameworkParent(scroller) as FrameworkElement; if (scroller is ScrollViewer || scroller is ScrollContentPresenter) { return scroller; } } return null; } } // TLS for TextEditor and dependent classes. // // Note we demand allocate, but then never clear the TLS slot. // This means we will leak one TextEditorThreadLocalStore per // thread if TextEditors are allocated then freed on the thread. // The alternative, ref counting the TLS, would require a finalizer // which is at least as expensive as one object per thread, and // much more complicated. internal static TextEditorThreadLocalStore _ThreadLocalStore { get { TextEditorThreadLocalStore store; store = (TextEditorThreadLocalStore)Thread.GetData(_threadLocalStoreSlot); if (store == null) { store = new TextEditorThreadLocalStore(); Thread.SetData(_threadLocalStoreSlot, store); } return store; } } // Content change counter internal long _ContentChangeCounter { get { return _contentChangeCounter; } } // Enables or disables table editing commands. // False by default, only enabled via reflection for lexicon.exe in V1. internal static bool IsTableEditingEnabled { get { return _isTableEditingEnabled; } set { _isTableEditingEnabled = value; } } // Cached position used to restore selection moving position // after colliding with document start or end handling // SelectUp/DownByLine commands. internal ITextPointer _NextLineAdvanceMovingPosition { get { return _nextLineAdvanceMovingPosition; } set { _nextLineAdvanceMovingPosition = value; } } // If true _nextLineAdvanceMovingPosition represents a position at the head // of the document (stored in response to a OnSelectUpByLineCommand). Otherwise, // _nextLineAdvanceMovingPosition represents a position at the tail // of the document (stored in response to a OnSelectDownByLineCommand). internal bool _IsNextLineAdvanceMovingPositionAtDocumentHead { get { return _isNextLineAdvanceMovingPositionAtDocumentHead; } set { _isNextLineAdvanceMovingPositionAtDocumentHead = value; } } #endregion Class Internal Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods //...................................................... // // Misceleneous // //...................................................... ////// Helper for _FilterText(). /// Inspects if text[position-1] and text[position] form a surrogate pair or Environment.NewLine. /// Input string to inspect. /// Split position. /// private bool IsBadSplitPosition(string text, int position) { // if ((text[position - 1] == '\r' && text[position] == '\n') || (Char.IsHighSurrogate(text, position - 1) && Char.IsLowSurrogate(text, position))) { return true; } return false; } private void HandleMouseSelectionTick(object sender, EventArgs e) { if (_mouseSelectionState != null && !_mouseSelectionState.BringIntoViewInProgress && this.TextView != null && this.TextView.IsValid && TextEditorSelection.IsPaginated(this.TextView)) { _mouseSelectionState.BringIntoViewInProgress = true; this.TextView.BringPointIntoViewCompleted += new BringPointIntoViewCompletedEventHandler(HandleBringPointIntoViewCompleted); this.TextView.BringPointIntoViewAsync(_mouseSelectionState.Point, this); } } ///True if bad split position, false otherwise. ////// Handler for ITextView.BringPointIntoViewCompleted event. /// private void HandleBringPointIntoViewCompleted(object sender, BringPointIntoViewCompletedEventArgs e) { ITextPointer cursorPosition; Rect lastCharacterRect; Invariant.Assert(sender is ITextView); ((ITextView)sender).BringPointIntoViewCompleted -= new BringPointIntoViewCompletedEventHandler(HandleBringPointIntoViewCompleted); // If the mouse selection state is not available, it means that the mouse was // already released. It may happen when there is delay in view transitions // (i.e. page transition in DocumentViewerBase). // In such case ignore this event. if (_mouseSelectionState == null) { return; } _mouseSelectionState.BringIntoViewInProgress = false; if (e != null && !e.Cancelled && e.Error == null) { Invariant.Assert(e.UserState == this && this.TextView == sender); cursorPosition = e.Position; if (cursorPosition != null) { // Check end-of-container condition if (cursorPosition.GetNextInsertionPosition(LogicalDirection.Forward) == null && cursorPosition.ParentType != null) // { // We are at the end of text container. Check whether mouse is farther than a last character lastCharacterRect = cursorPosition.GetCharacterRect(LogicalDirection.Backward); if (e.Point.X > lastCharacterRect.X + lastCharacterRect.Width) { cursorPosition = this.TextContainer.End; } } // Move the caret/selection to match the cursor position. this.Selection.ExtendSelectionByMouse(cursorPosition, _forceWordSelection, _forceParagraphSelection); } else { CancelExtendSelection(); } } else { CancelExtendSelection(); } } // This method is called asynchronously after the first layout update. ////// Critical: Calls critical code (TextServicesLoader.Load) /// TreatAsSafe: Queries for the TSF thread manager, a safe operation /// [SecurityCritical, SecurityTreatAsSafe] private object InitTextStore(object o) { // We might have been detached before this callback got dispatched. if (!_pendingTextStoreInit) { return null; } // Init a TSF TextStore if any TIPs/IMEs are installed. if (_textContainer is TextContainer && TextServicesHost.Current != null) { // We need to make sure we get back a valid thread manager first since it's possible for // TextServicesLoader.ServicesInstalled to return true without TSF being usable. UnsafeNativeMethods.ITfThreadMgr threadManager = TextServicesLoader.Load(); if (threadManager != null) { // Demand create the TextStore. if (_textstore == null) { _textstore = new TextStore(this); _weakThis = new TextEditorShutDownListener(this); } _textstore.OnAttach(); Marshal.ReleaseComObject(threadManager); } } _pendingTextStoreInit = false; return null; } // ................................................................ // // Event Handlers: Internal Events // // ................................................................ ////// Handler for TextContainer.Changed event. /// /// /// private void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e) { // Set short short-term dirty indicator to true. // The indicator is used in TextEditorMouse.MoveFocusToUiScope to check // that there is no side effects happened in content during focus movement this._contentChangeCounter++; } // TextView.Updated event handler. private void OnTextViewUpdated(object sender, EventArgs e) { // The TextSelection needs to know about the change now. _selection.OnTextViewUpdated(); // The TextView calls this method synchronously, before it finishes its Arrange // pass, so defer the remaining work until the TextView is valid. this.UiScope.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(OnTextViewUpdatedWorker), EventArgs.Empty); if (!_textStoreInitStarted) { Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(InitTextStore), null); _pendingTextStoreInit = true; _textStoreInitStarted = true; } } // Responds to OnTextViewUpdated calls. private object OnTextViewUpdatedWorker(object o) { // Ignore the event if the editor has been detached from its scope if (this.TextView == null) { return null; } if (_textstore != null) { _textstore.OnLayoutUpdated(); } // IMM32's OnLostFocus handler. Clean the composition string if it exists. if (_immEnabled) { if (_immComposition != null) { _immComposition.OnLayoutUpdated(); } } return null; } // IsEnabledChanged event handler for cleaning the caret element when uiScope is disabled. private static void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } This._selection.UpdateCaretAndHighlight(); // Update the speller checker status. This.SetSpellCheckEnabled(This.IsSpellCheckEnabled); } // Callback for chagnes to the IsReadOnly property. private static void OnIsReadOnlyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { FrameworkElement frameworkElement = sender as FrameworkElement; if (frameworkElement == null) { return; } TextEditor This = TextEditor._GetTextEditor(frameworkElement); if (This == null) { return; } // Update the spell check status. This.SetSpellCheckEnabled(This.IsSpellCheckEnabled); // Finalize any active IME composition when transitioning to true. if ((bool)e.NewValue == true && This._textstore != null) { This._textstore.CompleteCompositionAsync(); } } // ................................................................ // // Focus // // ................................................................ // GotKeyboardFocusEvent handler. ////// Critical - adjusts internal state dealing with focus (including notifying /// unmanaged IME about this) /// Safe - exposes no state, passes no state to the IME. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { // Ignore the event if the sender is not new focus element. if (sender != e.NewFocus) { return; } TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope // or if the element getting focus isn't our uiScope (in which case, it's our child) if (!This._IsEnabled) { return; } // Cicero's OnGotKeyboardFocus handler. It updates the focus DIM. if (This._textstore != null) { This._textstore.OnGotFocus(); } // IMM32's OnGotFocus handler. Ready for the composition string. if (_immEnabled) { This._immComposition = ImmComposition.GetImmComposition(This._uiScope); if (This._immComposition != null) { This._immComposition.OnGotFocus(This); } } // Redraw the caret to show the BiDi/normal caret. This._selection.RefreshCaret(); // Make selection visible // Note: Do not scroll to bring caret into view upon GotKeyboardFocus. This._selection.UpdateCaretAndHighlight(); } // LostKeyboardFocusEvent handler // // Stop the caret from blinking ////// Critical - manipulates focus, including calling critical method (GetImmComposition) /// Safe - exposes no state, passes no state to the IME. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { // Ignore the event if the sender is not old focus element. if (sender != e.OldFocus) { return; } TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope // or the element losing focus isn't our uiScope (in which case, it's our child) if (!This._IsEnabled) { return; } // Note: Do not scroll to bring caret into view upon LostKeyboardFocus. This._selection.UpdateCaretAndHighlight(); // Call the TextStore's OnLostfocus handler. Finalizes the curernt composition, if any. if (This._textstore != null) { This._textstore.OnLostFocus(); } // IMM32's OnLostFocus handler. Clean the composition string if it exists. if (_immEnabled) { if (This._immComposition != null) { // Call ImmComposition OnLostFocus to clean up the event handler(SelectionChanged). This._immComposition.OnLostFocus(); // Set _immComposition as null not to access it until get new from the getting focus. This._immComposition = null; } } } // LostFocusedElementEvent handler. private static void OnLostFocus(object sender, RoutedEventArgs e) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } // Un-vanish the cursor. TextEditorTyping._ShowCursor(); // Ignore the event if the editor has been detached from its scope // or the element losing focus isn't our uiScope (in which case, it's our child) if (!This._IsEnabled) { return; } // Flush input queue and complete typing undo unit TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); // Release column resizing adorner, and interrupt table resising process (if any) if (This._tableColResizeInfo != null) { This._tableColResizeInfo.DisposeAdorner(); This._tableColResizeInfo = null; } // Hide selection This._selection.UpdateCaretAndHighlight(); } // ................................................................ // // Undo-Redo // // ................................................................ ////// Undo command event handler. /// private static void OnUndo(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)target); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope if (!This._IsEnabled) { return; } if (This.IsReadOnly) { return; } This.Undo(); } ////// Redo command event handler. /// private static void OnRedo(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)target); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope if (!This._IsEnabled) { return; } if (This.IsReadOnly) { return; } This.Redo(); } ////// Undo command QueryStatus handler. /// private static void OnQueryStatusUndo(object sender, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } UndoManager undoManager = This._GetUndoManager(); if (undoManager != null && undoManager.UndoCount > undoManager.MinUndoStackCount) { args.CanExecute = true; } } ////// Redo command QueryStatus handler. /// private static void OnQueryStatusRedo(object sender, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } UndoManager undoManager = This._GetUndoManager(); if (undoManager != null && undoManager.RedoCount > 0) { args.CanExecute = true; } } #endregion Private methods //------------------------------------------------------ // // Private Types // //----------------------------------------------------- #region Private Types // We need to an event handler for Dispatcher's Dispose but we don't want to have // a strong referrence fromDispatcher. So TextEditorShutDownListener wraps this. private sealed class TextEditorShutDownListener : ShutDownListener { ////// Critical: accesses AppDomain.DomainUnload event /// TreatAsSafe: This code does not take any parameter or return state. /// It simply attaches private callbacks. /// [SecurityCritical,SecurityTreatAsSafe] public TextEditorShutDownListener(TextEditor target) : base(target, ShutDownEvents.DomainUnload | ShutDownEvents.DispatcherShutdown) { } internal override void OnShutDown(object target, object sender, EventArgs e) { TextEditor editor = (TextEditor)target; editor.DetachTextStore(false /* finalizer */); } } private class MouseSelectionState { internal DispatcherTimer Timer; internal Point Point; internal bool BringIntoViewInProgress; } #endregion Private Types //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // Attached property used to map FrameworkElement arguments // to command handlers back to TextEditor instances. private static readonly DependencyProperty InstanceProperty = DependencyProperty.RegisterAttached( // "Instance", typeof(TextEditor), typeof(TextEditor), // new FrameworkPropertyMetadata((object)null)); // internal Dispatcher _dispatcher; // Read-only setting for this level of an editor. // This flag is supposed to mask inheritable IsReadOnly property when set to true. // When the flag is false we are supposed to get IsReadOnly from UIScope private bool _isReadOnly; // Register for classes to which we already added static command handlers private static ArrayList _registeredEditingTypes = new ArrayList(4); // TextConatiner representing a text subject to editing private ITextContainer _textContainer; // Content change counter is supposed to be used in various // dirty-condition detection scenarios. // In particular, it is used in TextEditorMouse.MoveFocusToUiScope to check // that there is no side effects happened in content during focus movement private long _contentChangeCounter; // Control to which this editor is attached private FrameworkElement _uiScope; private ITextView _textView; // TextSelection maintained within this _uiScope private ITextSelection _selection; // Flag turned true to indicate overtype mode private bool _overtypeMode; // Preserved horizontal position for verical caret movement internal Double _suggestedX; // ITextStoreACP implementation, used when text services (IMEs, etc.) // are available. private TextStore _textstore; // We need to an event handler for Dispatcher's Dispose but we don't want to have // a strong referrence fromDispatcher. So TextEditorShutDownListener wraps this. private TextEditorShutDownListener _weakThis; // The speller. If null, spell check is not enabled. private Speller _speller; // Flag set true after scheduling a callback to InitTextStore. private bool _textStoreInitStarted; // If true, we're waiting for the Dispatcher to dispatch a callback // to InitTextStore. private bool _pendingTextStoreInit; // Mouse cursor defined in MouseMove handler to be used in OnQueryCursor method internal Cursor _cursor; // Merged typing undo unit. // Undo unit creation is nontrivial here: // It requires a logic for merging consequtive typing. // For that purpose we store a unit created by typing and reopen it // when next typing occurs. // We should however discard this unit each time when selection // moves to another position. // internal IParentUndoUnit _typingUndoUnit; // An object for storing dragdrop state during dragging internal TextEditorDragDrop._DragDropProcess _dragDropProcess; internal bool _forceWordSelection; internal bool _forceParagraphSelection; // Resizing operation information for table column internal TextRangeEditTables.TableColumnResizeInfo _tableColResizeInfo; // Tracking whether or not a given change is part of an undo or redo private UndoState _undoState = UndoState.Normal; // If true, the TextEditor will accept xml markup for paragraphs and inline formatting. // Default is true. private bool _acceptsRichContent; // If the system is IMM enabled, this is true. private static bool _immEnabled = SafeSystemMetrics.IsImmEnabled ; // ImmComposition implementation, used when _immEnabled. private ImmComposition _immComposition; // Thread local storage for TextEditor and dependent classes. private static LocalDataStoreSlot _threadLocalStoreSlot = Thread.AllocateDataSlot(); // Flag indicating that MouseDown handler is in progress, // to ignore all MouseMoves caused by CaptureMouse call. internal bool _mouseCapturingInProgress; // Mouse selection support. private MouseSelectionState _mouseSelectionState; // Flag that indicates whether the UiScope has a context menu open. // This flag is set/reset in ContextMenuOpening/ContextMenuClosing event handlers // respectively in TextEditorContextMenu.cs. // TextSelection.UpdateCaretAndHighlight() uses this flag to detect the case // when the UiScope has a context menu open. private bool _isContextMenuOpen; // Enables or disables table editing commands. // False by default, only enabled via reflection for lexicon.exe in V1. private static bool _isTableEditingEnabled; // Cached position used to restore selection moving position // after colliding with document start or end handling // SelectUp/DownByLine commands. private ITextPointer _nextLineAdvanceMovingPosition; // If true _nextLineAdvanceMovingPosition represents a position at the head // of the document (stored in response to a OnSelectUpByLineCommand). Otherwise, // _nextLineAdvanceMovingPosition represents a position at the tail // of the document (stored in response to a OnSelectDownByLineCommand). internal bool _isNextLineAdvanceMovingPositionAtDocumentHead; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. #pragma warning disable 1634, 1691 // To enable presharp warning disables (#pragma suppress) below. // -------------------------------------------------------------------------- // // File: TextEditor.cs // // Copyright (C) Microsoft Corporation. All rights reserved. // // Description: Text editing service for controls. // //--------------------------------------------------------------------------- namespace System.Windows.Documents { using MS.Internal; using System.Globalization; using System.Threading; using System.ComponentModel; using System.Text; using System.Collections; // ArrayList using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Windows.Threading; using System.Windows.Input; using System.Windows.Controls; // ScrollChangedEventArgs using System.Windows.Controls.Primitives; // CharacterCasing, TextBoxBase using System.Windows.Media; using System.Windows.Markup; using MS.Utility; using MS.Win32; using MS.Internal.Documents; using MS.Internal.Commands; // CommandHelpers ////// Text editing service for controls. /// internal class TextEditor { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Initialize the TextEditor /// /// /// TextContainer representing a content to edit. /// /// /// FrameworkElement on which all events for the user interaction will be /// processed. /// /// /// If true the TextEditor will enable undo support /// internal TextEditor(ITextContainer textContainer, FrameworkElement uiScope, bool isUndoEnabled) { // Validate parameters Invariant.Assert(uiScope != null); // Set non-zero property defaults. _acceptsRichContent = true; // Attach the editor instance to the scope _textContainer = textContainer; _uiScope = uiScope; // Enable undo manager for this uiScope if (isUndoEnabled && _textContainer is TextContainer) { ((TextContainer)_textContainer).EnableUndo(_uiScope); } // Create TextSelection and link it to text container _selection = new TextSelection(this); textContainer.TextSelection = _selection; // Create DragDropProcess // _dragDropProcess = new TextEditorDragDrop._DragDropProcess(this); // By default we use IBeam cursor _cursor = Cursors.IBeam; // Add InputLanguageChanged event handler TextEditorTyping._AddInputLanguageChangedEventHandler(this); // Listen to both TextContainer.EndChanging and TextContainer.Changed events TextContainer.Changed += new TextContainerChangedEventHandler(OnTextContainerChanged); // Add IsEnabled event handler for cleaning the caret element when uiScope is disabled _uiScope.IsEnabledChanged += new DependencyPropertyChangedEventHandler(OnIsEnabledChanged); // Attach this instance of text editor to its uiScope _uiScope.SetValue(TextEditor.InstanceProperty, this); // The IsSpellerEnabled property might have been set before this // TextEditor was instantiated -- check if we need to rev // up speller support. if ((bool)_uiScope.GetValue(SpellCheck.IsEnabledProperty)) { SetSpellCheckEnabled(true); SetCustomDictionaries(true); } // If no IME/TextServices are installed, we have no native reasources // to clean up at Finalizer. if (!TextServicesLoader.ServicesInstalled) { GC.SuppressFinalize(this); } } #endregion Constructors //------------------------------------------------------ // // Finalizer // //----------------------------------------------------- #region Finalizer ////// The Finalizer will release the resources that were not released earlier. /// ~TextEditor() { // Detach TextStore that TextStore will be unregisted from Cicero. // And clean all reference of the native resources. DetachTextStore(true /* finalizer */); } #endregion Finalizer //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Notification that the EditBehavior is being removed from the /// scope to which it was attached. /// ////// innternal - ta make it accessible from TextEditor class. /// ////// Critical - as this calls Critical method ImmComposition.GetImmComposition(). /// Safe - as this just gets the ImmComposition for the current element and invokes /// the OnDetach event. /// [SecurityCritical, SecurityTreatAsSafe] internal void OnDetach() { Invariant.Assert(_textContainer != null); // Make sure the speller is shut down. SetSpellCheckEnabled(false); // Delete UndoManager UndoManager undoManager = UndoManager.GetUndoManager(_uiScope); if(undoManager != null) { if (_textContainer is TextContainer) { ((TextContainer)_textContainer).DisableUndo(_uiScope); } else { UndoManager.DetachUndoManager(_uiScope); } } // Release TextContainer _textContainer.TextSelection = null; // Remove InputLanguageChanged event handler TextEditorTyping._RemoveInputLanguageChangedEventHandler(this); // Remove both TextContainer.Changed event handlers _textContainer.Changed -= new TextContainerChangedEventHandler(OnTextContainerChanged); // Remove IsEnabled event handler that use for cleaning the caret element when uiScope is disabled _uiScope.IsEnabledChanged -= new DependencyPropertyChangedEventHandler(OnIsEnabledChanged); // Cancel any pending InitTextStore callback that might still // be in the queue. _pendingTextStoreInit = false; // Shut down the Cicero. DetachTextStore(false /* finalizer */); // Shut down IMM32. if (_immComposition != null) { // _immComposition comes from getting of the focus on the editor with the enabled IMM. // _immComposition.OnDetach will remove the events handler and then detach editor. _immComposition.OnDetach(); _immComposition = null; } else { // Make sure immComposition.OnDetach if there is the remaining ImmComposition // that can be happened after getting the lost focus. ImmComposition immComposition = ImmComposition.GetImmComposition(_uiScope); if (immComposition != null) { immComposition.OnDetach(); } } // detach fromm textview this.TextView = null; // Delete selection object, caret and highlight _selection.OnDetach(); _selection = null; _uiScope.ClearValue(TextEditor.InstanceProperty); _uiScope = null; _textContainer = null; } ////// We don't need TextStore after Dispatcher is disposed. /// DetachTextStore is called from Finalizer or UICntext.Dispose event callback. /// Finalizer calls this to release Cicero's resources. Then we don't need /// a call back from UIContex.Dispose any more. And we can release _weakThis. /// private void DetachTextStore(bool finalizer) { // We don't need this TextStore any more. // TextStore needs to be unregisted from Cicero so clean all reference // of the native resources. if (_textstore != null) { _textstore.OnDetach(finalizer); _textstore = null; } if (_weakThis != null) { _weakThis.StopListening(); _weakThis = null; } if (!finalizer) { // Cicero's resources have been released. // We don't have to get Finalizer called now. GC.SuppressFinalize(this); } } // Worker method for set_IsSpellCheckEnabled. // Note that enabling the spell checker is also gated on the IsReadOnly // and IsEnabled properties of the current UiScope. internal void SetSpellCheckEnabled(bool value) { value = value && !this.IsReadOnly && this._IsEnabled; if (value && _speller == null) { // Start up the speller. _speller = new Speller(this); } else if (!value && _speller != null) { // Shut down the speller. _speller.Detach(); _speller = null; } } ////// Loads custom dictionaries /// /// ///internal void SetCustomDictionaries(bool add) { TextBoxBase textBoxBase = _uiScope as TextBoxBase; // We want CustomDictionaries to take effect only on TextBoxBase derived classes. if (textBoxBase == null) { return; } if (_speller != null) { CustomDictionarySources dictionarySources = (CustomDictionarySources)SpellCheck.GetCustomDictionaries(textBoxBase); _speller.SetCustomDictionaries(dictionarySources, add); } } // Forwards a spelling reform property change off to the speller. internal void SetSpellingReform(SpellingReform spellingReform) { if (_speller != null) { _speller.SetSpellingReform(spellingReform); } } // Queries a FrameworkElement for its TextView internal static ITextView GetTextView(UIElement scope) { IServiceProvider serviceProvider = scope as IServiceProvider; return (serviceProvider != null) ? serviceProvider.GetService(typeof(ITextView)) as ITextView : null; } // Maps a FrameworkElement to its TextSelection, if any. internal static ITextSelection GetTextSelection(FrameworkElement frameworkElement) { TextEditor textEditor = TextEditor._GetTextEditor(frameworkElement); return (textEditor == null) ? null : textEditor.Selection; } // Registers all text editing command handlers for a given control type // // If registerEventListeners is false, caller is responsible for calling OnXXXEvent methods on TextEditor from // UIElement and FrameworkElement virtual overrides (piggy backing on the // UIElement/FrameworkElement class listeners). If true, TextEditor will register // its own class listener for events it needs. // // This method will always register private command listeners. /// /// Critical:This code register command handlers for texteditor related events and commands (OnGotFocus) /// TreatAsSafe: This just hooks up methods that are internal to this class /// [SecurityCritical, SecurityTreatAsSafe] internal static void RegisterCommandHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) { // Check if we already registered handlers for this type Invariant.Assert(_registeredEditingTypes != null); lock (_registeredEditingTypes) { for (int i = 0; i < _registeredEditingTypes.Count; i++) { // If controlType is or derives from some already registered class - we are done if (((Type)_registeredEditingTypes[i]).IsAssignableFrom(controlType)) { return; } // Check if controlType is not a superclass of some registered class. // This is erroneus condition, which must be avoided. // Otherwise the same handlers will be attached to some class twice. if (controlType.IsAssignableFrom((Type)_registeredEditingTypes[i])) { throw new InvalidOperationException( SR.Get(SRID.TextEditorCanNotRegisterCommandHandler, ((Type)_registeredEditingTypes[i]).Name, controlType.Name)); } } // The class was not yet registered. Add it to the list before starting registering handlers. _registeredEditingTypes.Add(controlType); } // Mouse TextEditorMouse._RegisterClassHandlers(controlType, registerEventListeners); if (!readOnly) { // Typing TextEditorTyping._RegisterClassHandlers(controlType, registerEventListeners); } // Drag-and-drop TextEditorDragDrop._RegisterClassHandlers(controlType, readOnly, registerEventListeners); // Cut-Copy-Paste TextEditorCopyPaste._RegisterClassHandlers(controlType, acceptsRichContent, readOnly, registerEventListeners); // Selection Commands TextEditorSelection._RegisterClassHandlers(controlType, registerEventListeners); if (!readOnly) { // Paragraph Formatting TextEditorParagraphs._RegisterClassHandlers(controlType, acceptsRichContent, registerEventListeners); } // ContextMenu TextEditorContextMenu._RegisterClassHandlers(controlType, registerEventListeners); if (!readOnly) { // Spelling TextEditorSpelling._RegisterClassHandlers(controlType, registerEventListeners); } if (acceptsRichContent && !readOnly) { // Character Formatting TextEditorCharacters._RegisterClassHandlers(controlType, registerEventListeners); // Editing Commands: List Editing TextEditorLists._RegisterClassHandlers(controlType, registerEventListeners); // Editing Commands: Table Editing if (_isTableEditingEnabled) { TextEditorTables._RegisterClassHandlers(controlType, registerEventListeners); } } // Focus // ----- if (registerEventListeners) { EventManager.RegisterClassHandler(controlType, Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnGotKeyboardFocus)); EventManager.RegisterClassHandler(controlType, Keyboard.LostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(OnLostKeyboardFocus)); EventManager.RegisterClassHandler(controlType, UIElement.LostFocusEvent, new RoutedEventHandler(OnLostFocus)); } // Undo-Redo // --------- if (!readOnly) { CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Undo, new ExecutedRoutedEventHandler(OnUndo), new CanExecuteRoutedEventHandler(OnQueryStatusUndo), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyUndo), SR.Get(SRID.KeyUndoDisplayString)), KeyGesture.CreateFromResourceStrings(SR.Get(SRID.KeyAltUndo), SR.Get(SRID.KeyAltUndoDisplayString))); CommandHelpers.RegisterCommandHandler(controlType, ApplicationCommands.Redo, new ExecutedRoutedEventHandler(OnRedo), new CanExecuteRoutedEventHandler(OnQueryStatusRedo), SRID.KeyRedo, SRID.KeyRedoDisplayString); } } // Worker for TextBox/RichTextBox.GetSpellingErrorAtPosition. internal SpellingError GetSpellingErrorAtPosition(ITextPointer position, LogicalDirection direction) { return TextEditorSpelling.GetSpellingErrorAtPosition(this, position, direction); } // Returns the error (if any) at the current selection. internal SpellingError GetSpellingErrorAtSelection() { return TextEditorSpelling.GetSpellingErrorAtSelection(this); } // Worker for TextBox/RichTextBox.GetNextSpellingErrorPosition. internal ITextPointer GetNextSpellingErrorPosition(ITextPointer position, LogicalDirection direction) { return TextEditorSpelling.GetNextSpellingErrorPosition(this, position, direction); } // Replaces a TextRange's content with a string. // Applies LanguageProperty based on current input language. // internal void SetText(ITextRange range, string text, CultureInfo cultureInfo) { // Input text range.Text = text; // mark the range with the current input language on the start position. if (range is TextRange) { MarkCultureProperty((TextRange)range, cultureInfo); } } // Replaces the current selection with a string. // Applies any springloaded properties to the text. // Applies LanguageProperty based on current input language. // Clears the cached caret X offset. // internal void SetSelectedText(string text, CultureInfo cultureInfo) { // Insert the text and tag it with culture property. SetText(this.Selection, text, cultureInfo); // Apply springload formatting ((TextSelection)this.Selection).ApplySpringloadFormatting(); // Forget previously suggested caret horizontal position. TextEditorSelection._ClearSuggestedX(this); } ////// Used for marking the span of incoming text with input language /// based on the current input language. The default input language is /// designated in FrameworkElement.LanguageProperty which has the /// default value of "en-US" but this can be changed at any tree node /// by xml:lang attribute /// internal void MarkCultureProperty(TextRange range, CultureInfo inputCultureInfo) { // Invariant.Assert(this.UiScope != null); if (!this.AcceptsRichContent) { return; } // Get the current culture infomation to mark the input culture information XmlLanguage language = (XmlLanguage)((ITextPointer)range.Start).GetValue(FrameworkElement.LanguageProperty); Invariant.Assert(language != null); // Compare the culture info between the current position and the input culture. // Set the input culture info if the current has the different culture info with input. if (!String.Equals(inputCultureInfo.IetfLanguageTag, language.IetfLanguageTag, StringComparison.OrdinalIgnoreCase)) { range.ApplyPropertyValue(FrameworkElement.LanguageProperty, XmlLanguage.GetLanguage(inputCultureInfo.IetfLanguageTag)); } // Get the input language's flow direction FlowDirection inputFlowDirection; if (inputCultureInfo.TextInfo.IsRightToLeft) { inputFlowDirection = FlowDirection.RightToLeft; } else { inputFlowDirection = FlowDirection.LeftToRight; } // Get the current flow direction FlowDirection currentFlowDirection = (FlowDirection)((ITextPointer)range.Start).GetValue(FrameworkElement.FlowDirectionProperty); // Set the FlowDirection property properly if the input language's flow direction // doesn't match with the current flow direction. if (currentFlowDirection != inputFlowDirection) { range.ApplyPropertyValue(FrameworkElement.FlowDirectionProperty, inputFlowDirection); } } internal void RequestExtendSelection(Point point) { if (_mouseSelectionState == null) { _mouseSelectionState = new MouseSelectionState(); _mouseSelectionState.Timer = new DispatcherTimer(DispatcherPriority.Normal); _mouseSelectionState.Timer.Tick += new EventHandler(HandleMouseSelectionTick); // 400ms is the default value for MenuShowDelay. Creating timer with smaller value may // cause Dispatcher queue starvation. _mouseSelectionState.Timer.Interval = TimeSpan.FromMilliseconds(Math.Max(SystemParameters.MenuShowDelay, 200)); _mouseSelectionState.Timer.Start(); _mouseSelectionState.Point = point; // Simulate the first Tick HandleMouseSelectionTick(_mouseSelectionState.Timer, EventArgs.Empty); } else { _mouseSelectionState.Point = point; } } internal void CancelExtendSelection() { if (_mouseSelectionState != null) { _mouseSelectionState.Timer.Stop(); _mouseSelectionState.Timer.Tick -= new EventHandler(HandleMouseSelectionTick); _mouseSelectionState = null; } } ////// Helper used to check if UiScope has a ToolTip which is open. If so, this method closes the tool tip. /// KeyDown and MouseDown event handlers use this helper to check for this condition. /// internal void CloseToolTip() { PopupControlService popupControlService = PopupControlService.Current; if (popupControlService.CurrentToolTip != null && popupControlService.CurrentToolTip.IsOpen && popupControlService.CurrentToolTip.PlacementTarget == _uiScope) { popupControlService.CurrentToolTip.IsOpen = false; } } ////// Undo worker. /// ////// Critical:Calls Composition.Complete which has a link demand /// TreatAsSafe: Does not expose the call /// [SecurityCritical, SecurityTreatAsSafe] internal void Undo() { TextEditorTyping._FlushPendingInputItems(this); // Complete the composition string before undo CompleteComposition(); _undoState = UndoState.Undo; bool forceLayoutUpdate = this.Selection.CoversEntireContent; try { // _selection.BeginChangeNoUndo(); try { UndoManager undoManager = _GetUndoManager(); if (undoManager != null && undoManager.UndoCount > undoManager.MinUndoStackCount) { undoManager.Undo(1); } // Forget previously suggested horizontal position // TextEditorSelection._ClearSuggestedX(this); // Break typing merge for undo TextEditorTyping._BreakTypingSequence(this); // Clear springload formatting if (_selection is TextSelection) { ((TextSelection)_selection).ClearSpringloadFormatting(); } } finally { _selection.EndChange(); } } finally { _undoState = UndoState.Normal; } // If we replaced the entire document content, background layout will // kick in. Force it to complete now. if (forceLayoutUpdate) { this.Selection.ValidateLayout(); } } ////// Redo worker. /// internal void Redo() { TextEditorTyping._FlushPendingInputItems(this); _undoState = UndoState.Redo; bool forceLayoutUpdate = this.Selection.CoversEntireContent; try { _selection.BeginChangeNoUndo(); try { UndoManager undoManager = _GetUndoManager(); if (undoManager != null && undoManager.RedoCount > 0) { undoManager.Redo(1); } // Forget previously suggested horizontal position // TextEditorSelection._ClearSuggestedX(this); // Break typing merge for undo TextEditorTyping._BreakTypingSequence(this); // Clear springload formatting if (_selection is TextSelection) { ((TextSelection)_selection).ClearSpringloadFormatting(); } } finally { _selection.EndChange(); } } finally { _undoState = UndoState.Normal; } // If we replaced the entire document content, background layout will // kick in. Force it to complete now. if (forceLayoutUpdate) { this.Selection.ValidateLayout(); } } internal void OnPreviewKeyDown(KeyEventArgs e) { TextEditorTyping.OnPreviewKeyDown(_uiScope, e); } internal void OnKeyDown(KeyEventArgs e) { TextEditorTyping.OnKeyDown(_uiScope, e); } internal void OnKeyUp(KeyEventArgs e) { TextEditorTyping.OnKeyUp(_uiScope, e); } internal void OnTextInput(TextCompositionEventArgs e) { TextEditorTyping.OnTextInput(_uiScope, e); } internal void OnMouseDown(MouseButtonEventArgs e) { TextEditorMouse.OnMouseDown(_uiScope, e); } internal void OnMouseMove(MouseEventArgs e) { TextEditorMouse.OnMouseMove(_uiScope, e); } internal void OnMouseUp(MouseButtonEventArgs e) { TextEditorMouse.OnMouseUp(_uiScope, e); } internal void OnQueryCursor(QueryCursorEventArgs e) { TextEditorMouse.OnQueryCursor(_uiScope, e); } internal void OnQueryContinueDrag(QueryContinueDragEventArgs e) { TextEditorDragDrop.OnQueryContinueDrag(_uiScope, e); } internal void OnGiveFeedback(GiveFeedbackEventArgs e) { TextEditorDragDrop.OnGiveFeedback(_uiScope, e); } internal void OnDragEnter(DragEventArgs e) { TextEditorDragDrop.OnDragEnter(_uiScope, e); } internal void OnDragOver(DragEventArgs e) { TextEditorDragDrop.OnDragOver(_uiScope, e); } internal void OnDragLeave(DragEventArgs e) { TextEditorDragDrop.OnDragLeave(_uiScope, e); } internal void OnDrop(DragEventArgs e) { TextEditorDragDrop.OnDrop(_uiScope, e); } internal void OnContextMenuOpening(ContextMenuEventArgs e) { TextEditorContextMenu.OnContextMenuOpening(_uiScope, e); } internal void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) { OnGotKeyboardFocus(_uiScope, e); } internal void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { OnLostKeyboardFocus(_uiScope, e); } internal void OnLostFocus(RoutedEventArgs e) { OnLostFocus(_uiScope, e); } #endregion Internal Methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties //...................................................... // // Dependency Properties // //...................................................... #region Dependency Properties ////// IsReadOnly attached property speficies if the content within a scope /// of some FrameworkElement is editable. /// internal static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.RegisterAttached( "IsReadOnly", typeof(bool), typeof(TextEditor), new FrameworkPropertyMetadata( false, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnIsReadOnlyChanged))); ////// TextEditor.AllowOvertype property controls how TextEditor treats INS key. /// When set to true INS key toggles overtype mode. /// Otherwise it is ignored. /// ////// internal static readonly DependencyProperty AllowOvertypeProperty = DependencyProperty.RegisterAttached( "AllowOvertype", typeof(bool), typeof(TextEditor), new FrameworkPropertyMetadata(true)); /// /// TextEditor.PageHeight attached property for pageup/down /// internal static readonly DependencyProperty PageHeightProperty = DependencyProperty.RegisterAttached( "PageHeight", typeof(double), typeof(TextEditor), new FrameworkPropertyMetadata(0d)); #endregion Dependency Properties //...................................................... // // Properties - Relations With Other Components // //...................................................... #region Properties - Relations With Other Components // The content TextContainer. internal ITextContainer TextContainer { get { return _textContainer; } } ////// A FrameworkElement to which this instance on TextEditor /// is attached. /// ///internal FrameworkElement UiScope { get { return _uiScope; } } /// /// A FrameworkElement to which this instance on TextEditor /// is attached. /// ///internal ITextView TextView { get { return _textView; } set { if (value != _textView) { if (_textView != null) { // Remove layout updated handler. _textView.Updated -= new EventHandler(OnTextViewUpdated); _textView = null; // Make sure that caret is destroyed for this text view (if any) // This must be called after clearing _textView _selection.UpdateCaretAndHighlight(); } if (value != null) { _textView = value; // Init a layout invalidation listener. _textView.Updated += new EventHandler(OnTextViewUpdated); // Make sure that caret is present for this text view _selection.UpdateCaretAndHighlight(); } } } } /// /// The TextSelection associated with this TextEditor. /// internal ITextSelection Selection { get { return _selection; } } // TextStore - needed in TextSelection to notify it about movements // internal TextStore TextStore { get { return _textstore; } } ////// ImmComposition implementation, used when _immEnabled. /// internal ImmComposition ImmComposition { get { return _immEnabled ? _immComposition : null; } } #endregion Properties - Rlations With Other Components //...................................................... // // Properties - Text Editor Behavior Parameterization // //...................................................... #region Properties - Text Editor Behavior Parameterization ////// If true, the TextEditor will accept the return/enter key, /// otherwise it will be ignored. Default is true. /// internal bool AcceptsReturn { get { return _uiScope == null ? true : (bool)_uiScope.GetValue(KeyboardNavigation.AcceptsReturnProperty); } } ////// If true, the TextEditor will accept the tab key, otherwise /// it will be ignored. Default is true. /// internal bool AcceptsTab { get { return _uiScope == null ? true : (bool)_uiScope.GetValue(TextBoxBase.AcceptsTabProperty); } set { Invariant.Assert(_uiScope != null); if (AcceptsTab != value) { _uiScope.SetValue(TextBoxBase.AcceptsTabProperty, value); } } } ////// If true, text selection will be enabled but the TextEditor will /// not modify content. Default is false. /// ////// Use TextSelection.HideCaret to stop the caret from rendering. /// internal bool IsReadOnly { get { // We use local flag _isReadOnly for masking inheritable setting of IsReadOnly if (_isReadOnly) { return true; } else { return _uiScope == null ? false : (bool)_uiScope.GetValue(TextEditor.IsReadOnlyProperty); } } set { // This setting does not affect logical tree setting; // it only applies to this particular editor. // Nested editors be in editable or non-editable state // independently on this flag. _isReadOnly = value; } } ////// Enables and disables spell checking on the document. /// ////// Defaults to false. /// internal bool IsSpellCheckEnabled { get { return _uiScope == null ? false : (bool)_uiScope.GetValue(SpellCheck.IsEnabledProperty); } set { Invariant.Assert(_uiScope != null); _uiScope.SetValue(SpellCheck.IsEnabledProperty, value); } } ////// If true, the TextEditor will accept xml markup for paragraphs and inline formatting. /// Default is true. /// internal bool AcceptsRichContent { get { return _acceptsRichContent; } set { _acceptsRichContent = value; } } ////// Clr accessor to AllowOvertypeProperty. /// internal bool AllowOvertype { get { return _uiScope == null ? true : (bool)_uiScope.GetValue(TextEditor.AllowOvertypeProperty); } } ////// Maximum length of text being edited (_selection.Text). More precisely, the /// user is not allowed to input text beyond this length. /// Default is 0, which means unlimited. /// internal int MaxLength { get { return _uiScope == null ? 0 : (int)_uiScope.GetValue(TextBox.MaxLengthProperty); } } ////// Controls whether input text is converted to upper or lower case. /// Default is CharacterCasing.Normal, which causes no conversion. /// internal CharacterCasing CharacterCasing { get { return _uiScope == null ? CharacterCasing.Normal : (CharacterCasing)_uiScope.GetValue(TextBox.CharacterCasingProperty); } } ////// Controls whether heuristics for selecting whole words on mouse drag are active /// Default is false. /// internal bool AutoWordSelection { get { return _uiScope == null ? false : (bool)_uiScope.GetValue(RichTextBox.AutoWordSelectionProperty); } } ////// Controls whether or not the caret is visible when the text editor is read-only. /// Default is false. /// internal bool IsReadOnlyCaretVisible { get { return _uiScope == null ? false : (bool)_uiScope.GetValue(TextBoxBase.IsReadOnlyCaretVisibleProperty); } } #endregion Properties - Text Editor Behavior Parameterization // A property exposing our current undo context. // // UndoState.Undo while inside OnUndo or // UndoState.Redo while inside OnRedo or // UndoState.Normal otherwise. internal UndoState UndoState { get { return _undoState; } } // Flag that indicates whether the UiScope has a context menu open. internal bool IsContextMenuOpen { get { return _isContextMenuOpen; } set { _isContextMenuOpen = value; } } // Speller instance for TextEditorSpelling. internal Speller Speller { get { return _speller; } } #endregion Internal Properties //----------------------------------------------------- // // Class Internal Methods // //----------------------------------------------------- #region Class Internal Methods // Maps a FrameworkElement to its TextEditor, if any. internal static TextEditor _GetTextEditor(object element) { return (element is DependencyObject) ? (((DependencyObject)element).ReadLocalValue(InstanceProperty) as TextEditor) : null; } ////// Returns the UndoManager, if any, associated with this editor instance. /// internal UndoManager _GetUndoManager() { UndoManager undoManager = null; if (this.TextContainer is TextContainer) { undoManager = ((TextContainer)this.TextContainer).UndoManager; } return undoManager; } ////// Filter input text based on MaxLength, and CharacterCasing. /// /// /// text to filter /// /// /// target range to be inserted or replaced /// ////// filtered text /// internal string _FilterText(string textData, ITextRange range) { return _FilterText(textData, range.Start.GetOffsetToPosition(range.End)); } internal string _FilterText(string textData, int charsToReplaceCount) { return _FilterText(textData, charsToReplaceCount, true); } internal string _FilterText(string textData, ITextRange range, bool filterMaxLength) { return _FilterText(textData, range.Start.GetOffsetToPosition(range.End), filterMaxLength); } internal string _FilterText(string textData, int charsToReplaceCount, bool filterMaxLength) { // We only filter text for plain text content if (!this.AcceptsRichContent) { if (filterMaxLength && this.MaxLength > 0) { ITextContainer textContainer = this.TextContainer; int currentLength = textContainer.SymbolCount - charsToReplaceCount; int extraCharsAllowed = Math.Max(0, this.MaxLength - currentLength); // Does textData length exceed allowed char length? if (textData.Length > extraCharsAllowed) { int splitPosition = textData.Length - extraCharsAllowed; if (IsBadSplitPosition(textData, splitPosition)) { extraCharsAllowed--; } textData = textData.Substring(0, extraCharsAllowed); } } if (this.CharacterCasing == CharacterCasing.Upper) { // Get CultureInfo from the current input language for ToUpper/ToLower. textData = textData.ToUpper(InputLanguageManager.Current.CurrentInputLanguage); } else if (this.CharacterCasing == CharacterCasing.Lower) { // Get CultureInfo from the current input language for ToUpper/ToLower. textData = textData.ToLower(InputLanguageManager.Current.CurrentInputLanguage); } if (!this.AcceptsReturn) { int endOfFirstLine = textData.IndexOf(Environment.NewLine, StringComparison.Ordinal); if (endOfFirstLine >= 0) { textData = textData.Substring(0, endOfFirstLine); } endOfFirstLine = textData.IndexOfAny(TextPointerBase.NextLineCharacters); if (endOfFirstLine >= 0) { textData = textData.Substring(0, endOfFirstLine); } } if (!this.AcceptsTab) { textData = textData.Replace('\t', ' '); } } return textData; } // This checks if the source is in UiScope or its style. // The key events or text input events should not be handled if the source is out side of // element (or style). // For example, when focus elment is UiScope's context menu, // TextEditor should ignore those events. internal bool _IsSourceInScope(object source) { if (source == this.UiScope) { return true; } if ((source is FrameworkElement) && ((FrameworkElement)source).TemplatedParent == this.UiScope) { return true; } return false; } ////// Complete the composition string. /// internal void CompleteComposition() { if (TextStore != null) { TextStore.CompleteComposition(); } if (ImmComposition != null) { ImmComposition.CompleteComposition(); } } #endregion Class Internal Methods //----------------------------------------------------- // // Class Internal Properties // //------------------------------------------------------ #region Class Internal Properties ////// Returns true if the IsEnabled ptorperty is set to true for ui scope of the editor. /// internal bool _IsEnabled { get { return _uiScope == null ? false : _uiScope.IsEnabled; } } internal bool _OvertypeMode { get { return _overtypeMode; } set { _overtypeMode = value; } } // Find the scroller from the render scope of TextEdior internal FrameworkElement _Scroller { get { FrameworkElement scroller = this.TextView == null ? null : (this.TextView.RenderScope as FrameworkElement); while (scroller != null && scroller != this.UiScope) { scroller = FrameworkElement.GetFrameworkParent(scroller) as FrameworkElement; if (scroller is ScrollViewer || scroller is ScrollContentPresenter) { return scroller; } } return null; } } // TLS for TextEditor and dependent classes. // // Note we demand allocate, but then never clear the TLS slot. // This means we will leak one TextEditorThreadLocalStore per // thread if TextEditors are allocated then freed on the thread. // The alternative, ref counting the TLS, would require a finalizer // which is at least as expensive as one object per thread, and // much more complicated. internal static TextEditorThreadLocalStore _ThreadLocalStore { get { TextEditorThreadLocalStore store; store = (TextEditorThreadLocalStore)Thread.GetData(_threadLocalStoreSlot); if (store == null) { store = new TextEditorThreadLocalStore(); Thread.SetData(_threadLocalStoreSlot, store); } return store; } } // Content change counter internal long _ContentChangeCounter { get { return _contentChangeCounter; } } // Enables or disables table editing commands. // False by default, only enabled via reflection for lexicon.exe in V1. internal static bool IsTableEditingEnabled { get { return _isTableEditingEnabled; } set { _isTableEditingEnabled = value; } } // Cached position used to restore selection moving position // after colliding with document start or end handling // SelectUp/DownByLine commands. internal ITextPointer _NextLineAdvanceMovingPosition { get { return _nextLineAdvanceMovingPosition; } set { _nextLineAdvanceMovingPosition = value; } } // If true _nextLineAdvanceMovingPosition represents a position at the head // of the document (stored in response to a OnSelectUpByLineCommand). Otherwise, // _nextLineAdvanceMovingPosition represents a position at the tail // of the document (stored in response to a OnSelectDownByLineCommand). internal bool _IsNextLineAdvanceMovingPositionAtDocumentHead { get { return _isNextLineAdvanceMovingPositionAtDocumentHead; } set { _isNextLineAdvanceMovingPositionAtDocumentHead = value; } } #endregion Class Internal Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods //...................................................... // // Misceleneous // //...................................................... ////// Helper for _FilterText(). /// Inspects if text[position-1] and text[position] form a surrogate pair or Environment.NewLine. /// Input string to inspect. /// Split position. /// private bool IsBadSplitPosition(string text, int position) { // if ((text[position - 1] == '\r' && text[position] == '\n') || (Char.IsHighSurrogate(text, position - 1) && Char.IsLowSurrogate(text, position))) { return true; } return false; } private void HandleMouseSelectionTick(object sender, EventArgs e) { if (_mouseSelectionState != null && !_mouseSelectionState.BringIntoViewInProgress && this.TextView != null && this.TextView.IsValid && TextEditorSelection.IsPaginated(this.TextView)) { _mouseSelectionState.BringIntoViewInProgress = true; this.TextView.BringPointIntoViewCompleted += new BringPointIntoViewCompletedEventHandler(HandleBringPointIntoViewCompleted); this.TextView.BringPointIntoViewAsync(_mouseSelectionState.Point, this); } } ///True if bad split position, false otherwise. ////// Handler for ITextView.BringPointIntoViewCompleted event. /// private void HandleBringPointIntoViewCompleted(object sender, BringPointIntoViewCompletedEventArgs e) { ITextPointer cursorPosition; Rect lastCharacterRect; Invariant.Assert(sender is ITextView); ((ITextView)sender).BringPointIntoViewCompleted -= new BringPointIntoViewCompletedEventHandler(HandleBringPointIntoViewCompleted); // If the mouse selection state is not available, it means that the mouse was // already released. It may happen when there is delay in view transitions // (i.e. page transition in DocumentViewerBase). // In such case ignore this event. if (_mouseSelectionState == null) { return; } _mouseSelectionState.BringIntoViewInProgress = false; if (e != null && !e.Cancelled && e.Error == null) { Invariant.Assert(e.UserState == this && this.TextView == sender); cursorPosition = e.Position; if (cursorPosition != null) { // Check end-of-container condition if (cursorPosition.GetNextInsertionPosition(LogicalDirection.Forward) == null && cursorPosition.ParentType != null) // { // We are at the end of text container. Check whether mouse is farther than a last character lastCharacterRect = cursorPosition.GetCharacterRect(LogicalDirection.Backward); if (e.Point.X > lastCharacterRect.X + lastCharacterRect.Width) { cursorPosition = this.TextContainer.End; } } // Move the caret/selection to match the cursor position. this.Selection.ExtendSelectionByMouse(cursorPosition, _forceWordSelection, _forceParagraphSelection); } else { CancelExtendSelection(); } } else { CancelExtendSelection(); } } // This method is called asynchronously after the first layout update. ////// Critical: Calls critical code (TextServicesLoader.Load) /// TreatAsSafe: Queries for the TSF thread manager, a safe operation /// [SecurityCritical, SecurityTreatAsSafe] private object InitTextStore(object o) { // We might have been detached before this callback got dispatched. if (!_pendingTextStoreInit) { return null; } // Init a TSF TextStore if any TIPs/IMEs are installed. if (_textContainer is TextContainer && TextServicesHost.Current != null) { // We need to make sure we get back a valid thread manager first since it's possible for // TextServicesLoader.ServicesInstalled to return true without TSF being usable. UnsafeNativeMethods.ITfThreadMgr threadManager = TextServicesLoader.Load(); if (threadManager != null) { // Demand create the TextStore. if (_textstore == null) { _textstore = new TextStore(this); _weakThis = new TextEditorShutDownListener(this); } _textstore.OnAttach(); Marshal.ReleaseComObject(threadManager); } } _pendingTextStoreInit = false; return null; } // ................................................................ // // Event Handlers: Internal Events // // ................................................................ ////// Handler for TextContainer.Changed event. /// /// /// private void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e) { // Set short short-term dirty indicator to true. // The indicator is used in TextEditorMouse.MoveFocusToUiScope to check // that there is no side effects happened in content during focus movement this._contentChangeCounter++; } // TextView.Updated event handler. private void OnTextViewUpdated(object sender, EventArgs e) { // The TextSelection needs to know about the change now. _selection.OnTextViewUpdated(); // The TextView calls this method synchronously, before it finishes its Arrange // pass, so defer the remaining work until the TextView is valid. this.UiScope.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(OnTextViewUpdatedWorker), EventArgs.Empty); if (!_textStoreInitStarted) { Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(InitTextStore), null); _pendingTextStoreInit = true; _textStoreInitStarted = true; } } // Responds to OnTextViewUpdated calls. private object OnTextViewUpdatedWorker(object o) { // Ignore the event if the editor has been detached from its scope if (this.TextView == null) { return null; } if (_textstore != null) { _textstore.OnLayoutUpdated(); } // IMM32's OnLostFocus handler. Clean the composition string if it exists. if (_immEnabled) { if (_immComposition != null) { _immComposition.OnLayoutUpdated(); } } return null; } // IsEnabledChanged event handler for cleaning the caret element when uiScope is disabled. private static void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } This._selection.UpdateCaretAndHighlight(); // Update the speller checker status. This.SetSpellCheckEnabled(This.IsSpellCheckEnabled); } // Callback for chagnes to the IsReadOnly property. private static void OnIsReadOnlyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { FrameworkElement frameworkElement = sender as FrameworkElement; if (frameworkElement == null) { return; } TextEditor This = TextEditor._GetTextEditor(frameworkElement); if (This == null) { return; } // Update the spell check status. This.SetSpellCheckEnabled(This.IsSpellCheckEnabled); // Finalize any active IME composition when transitioning to true. if ((bool)e.NewValue == true && This._textstore != null) { This._textstore.CompleteCompositionAsync(); } } // ................................................................ // // Focus // // ................................................................ // GotKeyboardFocusEvent handler. ////// Critical - adjusts internal state dealing with focus (including notifying /// unmanaged IME about this) /// Safe - exposes no state, passes no state to the IME. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { // Ignore the event if the sender is not new focus element. if (sender != e.NewFocus) { return; } TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope // or if the element getting focus isn't our uiScope (in which case, it's our child) if (!This._IsEnabled) { return; } // Cicero's OnGotKeyboardFocus handler. It updates the focus DIM. if (This._textstore != null) { This._textstore.OnGotFocus(); } // IMM32's OnGotFocus handler. Ready for the composition string. if (_immEnabled) { This._immComposition = ImmComposition.GetImmComposition(This._uiScope); if (This._immComposition != null) { This._immComposition.OnGotFocus(This); } } // Redraw the caret to show the BiDi/normal caret. This._selection.RefreshCaret(); // Make selection visible // Note: Do not scroll to bring caret into view upon GotKeyboardFocus. This._selection.UpdateCaretAndHighlight(); } // LostKeyboardFocusEvent handler // // Stop the caret from blinking ////// Critical - manipulates focus, including calling critical method (GetImmComposition) /// Safe - exposes no state, passes no state to the IME. /// [SecurityCritical, SecurityTreatAsSafe] private static void OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { // Ignore the event if the sender is not old focus element. if (sender != e.OldFocus) { return; } TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope // or the element losing focus isn't our uiScope (in which case, it's our child) if (!This._IsEnabled) { return; } // Note: Do not scroll to bring caret into view upon LostKeyboardFocus. This._selection.UpdateCaretAndHighlight(); // Call the TextStore's OnLostfocus handler. Finalizes the curernt composition, if any. if (This._textstore != null) { This._textstore.OnLostFocus(); } // IMM32's OnLostFocus handler. Clean the composition string if it exists. if (_immEnabled) { if (This._immComposition != null) { // Call ImmComposition OnLostFocus to clean up the event handler(SelectionChanged). This._immComposition.OnLostFocus(); // Set _immComposition as null not to access it until get new from the getting focus. This._immComposition = null; } } } // LostFocusedElementEvent handler. private static void OnLostFocus(object sender, RoutedEventArgs e) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } // Un-vanish the cursor. TextEditorTyping._ShowCursor(); // Ignore the event if the editor has been detached from its scope // or the element losing focus isn't our uiScope (in which case, it's our child) if (!This._IsEnabled) { return; } // Flush input queue and complete typing undo unit TextEditorTyping._FlushPendingInputItems(This); TextEditorTyping._BreakTypingSequence(This); // Release column resizing adorner, and interrupt table resising process (if any) if (This._tableColResizeInfo != null) { This._tableColResizeInfo.DisposeAdorner(); This._tableColResizeInfo = null; } // Hide selection This._selection.UpdateCaretAndHighlight(); } // ................................................................ // // Undo-Redo // // ................................................................ ////// Undo command event handler. /// private static void OnUndo(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)target); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope if (!This._IsEnabled) { return; } if (This.IsReadOnly) { return; } This.Undo(); } ////// Redo command event handler. /// private static void OnRedo(object target, ExecutedRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)target); if (This == null) { return; } // Ignore the event if the editor has been detached from its scope if (!This._IsEnabled) { return; } if (This.IsReadOnly) { return; } This.Redo(); } ////// Undo command QueryStatus handler. /// private static void OnQueryStatusUndo(object sender, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } UndoManager undoManager = This._GetUndoManager(); if (undoManager != null && undoManager.UndoCount > undoManager.MinUndoStackCount) { args.CanExecute = true; } } ////// Redo command QueryStatus handler. /// private static void OnQueryStatusRedo(object sender, CanExecuteRoutedEventArgs args) { TextEditor This = TextEditor._GetTextEditor((FrameworkElement)sender); if (This == null) { return; } UndoManager undoManager = This._GetUndoManager(); if (undoManager != null && undoManager.RedoCount > 0) { args.CanExecute = true; } } #endregion Private methods //------------------------------------------------------ // // Private Types // //----------------------------------------------------- #region Private Types // We need to an event handler for Dispatcher's Dispose but we don't want to have // a strong referrence fromDispatcher. So TextEditorShutDownListener wraps this. private sealed class TextEditorShutDownListener : ShutDownListener { ////// Critical: accesses AppDomain.DomainUnload event /// TreatAsSafe: This code does not take any parameter or return state. /// It simply attaches private callbacks. /// [SecurityCritical,SecurityTreatAsSafe] public TextEditorShutDownListener(TextEditor target) : base(target, ShutDownEvents.DomainUnload | ShutDownEvents.DispatcherShutdown) { } internal override void OnShutDown(object target, object sender, EventArgs e) { TextEditor editor = (TextEditor)target; editor.DetachTextStore(false /* finalizer */); } } private class MouseSelectionState { internal DispatcherTimer Timer; internal Point Point; internal bool BringIntoViewInProgress; } #endregion Private Types //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // Attached property used to map FrameworkElement arguments // to command handlers back to TextEditor instances. private static readonly DependencyProperty InstanceProperty = DependencyProperty.RegisterAttached( // "Instance", typeof(TextEditor), typeof(TextEditor), // new FrameworkPropertyMetadata((object)null)); // internal Dispatcher _dispatcher; // Read-only setting for this level of an editor. // This flag is supposed to mask inheritable IsReadOnly property when set to true. // When the flag is false we are supposed to get IsReadOnly from UIScope private bool _isReadOnly; // Register for classes to which we already added static command handlers private static ArrayList _registeredEditingTypes = new ArrayList(4); // TextConatiner representing a text subject to editing private ITextContainer _textContainer; // Content change counter is supposed to be used in various // dirty-condition detection scenarios. // In particular, it is used in TextEditorMouse.MoveFocusToUiScope to check // that there is no side effects happened in content during focus movement private long _contentChangeCounter; // Control to which this editor is attached private FrameworkElement _uiScope; private ITextView _textView; // TextSelection maintained within this _uiScope private ITextSelection _selection; // Flag turned true to indicate overtype mode private bool _overtypeMode; // Preserved horizontal position for verical caret movement internal Double _suggestedX; // ITextStoreACP implementation, used when text services (IMEs, etc.) // are available. private TextStore _textstore; // We need to an event handler for Dispatcher's Dispose but we don't want to have // a strong referrence fromDispatcher. So TextEditorShutDownListener wraps this. private TextEditorShutDownListener _weakThis; // The speller. If null, spell check is not enabled. private Speller _speller; // Flag set true after scheduling a callback to InitTextStore. private bool _textStoreInitStarted; // If true, we're waiting for the Dispatcher to dispatch a callback // to InitTextStore. private bool _pendingTextStoreInit; // Mouse cursor defined in MouseMove handler to be used in OnQueryCursor method internal Cursor _cursor; // Merged typing undo unit. // Undo unit creation is nontrivial here: // It requires a logic for merging consequtive typing. // For that purpose we store a unit created by typing and reopen it // when next typing occurs. // We should however discard this unit each time when selection // moves to another position. // internal IParentUndoUnit _typingUndoUnit; // An object for storing dragdrop state during dragging internal TextEditorDragDrop._DragDropProcess _dragDropProcess; internal bool _forceWordSelection; internal bool _forceParagraphSelection; // Resizing operation information for table column internal TextRangeEditTables.TableColumnResizeInfo _tableColResizeInfo; // Tracking whether or not a given change is part of an undo or redo private UndoState _undoState = UndoState.Normal; // If true, the TextEditor will accept xml markup for paragraphs and inline formatting. // Default is true. private bool _acceptsRichContent; // If the system is IMM enabled, this is true. private static bool _immEnabled = SafeSystemMetrics.IsImmEnabled ; // ImmComposition implementation, used when _immEnabled. private ImmComposition _immComposition; // Thread local storage for TextEditor and dependent classes. private static LocalDataStoreSlot _threadLocalStoreSlot = Thread.AllocateDataSlot(); // Flag indicating that MouseDown handler is in progress, // to ignore all MouseMoves caused by CaptureMouse call. internal bool _mouseCapturingInProgress; // Mouse selection support. private MouseSelectionState _mouseSelectionState; // Flag that indicates whether the UiScope has a context menu open. // This flag is set/reset in ContextMenuOpening/ContextMenuClosing event handlers // respectively in TextEditorContextMenu.cs. // TextSelection.UpdateCaretAndHighlight() uses this flag to detect the case // when the UiScope has a context menu open. private bool _isContextMenuOpen; // Enables or disables table editing commands. // False by default, only enabled via reflection for lexicon.exe in V1. private static bool _isTableEditingEnabled; // Cached position used to restore selection moving position // after colliding with document start or end handling // SelectUp/DownByLine commands. private ITextPointer _nextLineAdvanceMovingPosition; // If true _nextLineAdvanceMovingPosition represents a position at the head // of the document (stored in response to a OnSelectUpByLineCommand). Otherwise, // _nextLineAdvanceMovingPosition represents a position at the tail // of the document (stored in response to a OnSelectDownByLineCommand). internal bool _isNextLineAdvanceMovingPositionAtDocumentHead; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ConfigUtil.cs
- BitmapMetadata.cs
- ReaderWriterLockWrapper.cs
- WindowsFormsLinkLabel.cs
- DoubleMinMaxAggregationOperator.cs
- BasePattern.cs
- Queue.cs
- Label.cs
- AssemblyCacheEntry.cs
- Buffer.cs
- FormsAuthenticationTicket.cs
- XmlTextReader.cs
- ConstructorNeedsTagAttribute.cs
- DataPagerFieldCollection.cs
- ClientTarget.cs
- DataKey.cs
- ObjectDataSourceFilteringEventArgs.cs
- WindowsPen.cs
- ContainerUIElement3D.cs
- FileDetails.cs
- PropertyValueChangedEvent.cs
- WebPartConnectionsConfigureVerb.cs
- ObjectStateManager.cs
- DatePickerAutomationPeer.cs
- ScriptHandlerFactory.cs
- TCPClient.cs
- HyperlinkAutomationPeer.cs
- RelationshipEndCollection.cs
- InputLanguageManager.cs
- WebReferenceOptions.cs
- DefaultSection.cs
- RenameRuleObjectDialog.Designer.cs
- X509ScopedServiceCertificateElement.cs
- RangeContentEnumerator.cs
- SqlSupersetValidator.cs
- MenuEventArgs.cs
- ObjectStateFormatter.cs
- BoundField.cs
- SelectionListDesigner.cs
- FirstMatchCodeGroup.cs
- EventManager.cs
- ToolZone.cs
- DataGridViewRowsAddedEventArgs.cs
- UniqueSet.cs
- LightweightCodeGenerator.cs
- DataGridViewCellPaintingEventArgs.cs
- SslStreamSecurityBindingElement.cs
- UrlRoutingHandler.cs
- StorageAssociationSetMapping.cs
- FunctionNode.cs
- PermissionToken.cs
- BoundField.cs
- WebBaseEventKeyComparer.cs
- UnsafeNativeMethods.cs
- Header.cs
- TagPrefixCollection.cs
- WindowsFormsHelpers.cs
- PassportIdentity.cs
- ContainerParagraph.cs
- FixedElement.cs
- cookiecollection.cs
- EntryWrittenEventArgs.cs
- ImmutableObjectAttribute.cs
- ResXFileRef.cs
- LogicalExpr.cs
- LoggedException.cs
- SocketElement.cs
- StringConverter.cs
- SortExpressionBuilder.cs
- TransformerConfigurationWizardBase.cs
- FilterFactory.cs
- ExtendedTransformFactory.cs
- BackStopAuthenticationModule.cs
- DataGridViewImageColumn.cs
- TdsParameterSetter.cs
- GestureRecognizer.cs
- MsmqIntegrationBindingElement.cs
- SqlCacheDependencyDatabase.cs
- BamlRecordHelper.cs
- ConfigurationHelpers.cs
- CollectionsUtil.cs
- FontNamesConverter.cs
- WebBrowserHelper.cs
- ImageListStreamer.cs
- HitTestWithPointDrawingContextWalker.cs
- Soap12FormatExtensions.cs
- PointConverter.cs
- PageTheme.cs
- Choices.cs
- SoapExtensionImporter.cs
- TypedElement.cs
- OnOperation.cs
- PageAsyncTask.cs
- DataGridViewRowPostPaintEventArgs.cs
- RemoteHelper.cs
- CalendarTable.cs
- AbandonedMutexException.cs
- Privilege.cs
- ToolTip.cs
- TaskForm.cs