Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / WinForms / Managed / System / WinForms / control.ime.cs / 1407647 / control.ime.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using Accessibility; using Microsoft.Win32; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; using System.Configuration.Assemblies; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Drawing2D; using System.Globalization; using System.Security.Permissions; using System.Security; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Runtime.Remoting; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading; using System.Windows.Forms.Design; using System.Windows.Forms.Internal; using Encoding = System.Text.Encoding; using System.Drawing.Imaging; using System.Windows.Forms.Layout; ////// Control's IME feature. /// public partial class Control : Component, UnsafeNativeMethods.IOleControl, UnsafeNativeMethods.IOleObject, UnsafeNativeMethods.IOleInPlaceObject, UnsafeNativeMethods.IOleInPlaceActiveObject, UnsafeNativeMethods.IOleWindow, UnsafeNativeMethods.IViewObject, UnsafeNativeMethods.IViewObject2, UnsafeNativeMethods.IPersist, UnsafeNativeMethods.IPersistStreamInit, UnsafeNativeMethods.IPersistPropertyBag, UnsafeNativeMethods.IPersistStorage, UnsafeNativeMethods.IQuickActivate, ISupportOleDropSource, IDropTarget, ISynchronizeInvoke, IWin32Window, IArrangedElement, IBindableComponent { // See IME feature spec at http://dotnetclient/Whidbey/dev/General%20Documentation/[....].Ime.doc ////// Constants starting/ending the WM_CHAR messages to ignore count. See ImeWmCharsToIgnore property. /// private const int ImeCharsToIgnoreDisabled = -1; private const int ImeCharsToIgnoreEnabled = 0; ////// The ImeMode value for controls with ImeMode = ImeMode.NoControl. See PropagatingImeMode property. /// private static ImeMode propagatingImeMode = ImeMode.Inherit; // Inherit means uninitialized. ////// This flag prevents resetting ImeMode value of the focused control. See IgnoreWmImeNotify property. /// private static bool ignoreWmImeNotify; ////// The ImeMode in the property store. /// internal ImeMode CachedImeMode { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CachedImeMode(), this = " + this ); Debug.Indent(); // Get the ImeMode from the property store // bool found; ImeMode cachedImeMode = (ImeMode) Properties.GetInteger( PropImeMode, out found ); if( !found ) { cachedImeMode = DefaultImeMode; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "using DefaultImeMode == " + cachedImeMode ); } else { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "using property store ImeMode == " + cachedImeMode ); } // If inherited, get the mode from this control's parent // if( cachedImeMode == ImeMode.Inherit ) { Control parent = ParentInternal; if( parent != null ) { cachedImeMode = parent.CachedImeMode; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "inherited from parent = " + parent.GetType() ); } else { cachedImeMode = ImeMode.NoControl; } } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "returning CachedImeMode == " + cachedImeMode ); Debug.Unindent(); return cachedImeMode; } set { // WARNING: When the control is in restricted mode (!CanEnableIme) the CachedImeMode should be changed only programatically, // calls generated by user interaction should be wrapped with a check for CanEnableIme. // Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_CachedImeMode(), this = " + this ); Debug.Indent(); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Warning, "Setting cached Ime == " + value ); Properties.SetInteger( PropImeMode, (int) value ); Debug.Unindent(); } } ////// Specifies whether the ImeMode property value can be changed to an active value. /// Added to support Password & ReadOnly (and maybe other) properties, which when set, should force disabling /// the IME if using one. /// protected virtual bool CanEnableIme { get { // Note: If overriding this property make sure to add the Debug tracing code and call this method (base.CanEnableIme). Debug.Indent(); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format(CultureInfo.CurrentCulture, "Inside get_CanEnableIme(), value = {0}, this = {1}", ImeSupported, this ) ); Debug.Unindent(); return ImeSupported; } } ////// Gets the current IME context mode. If no IME associated, ImeMode.Inherit is returned. /// internal ImeMode CurrentImeContextMode { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CurrentImeContextMode(), this = " + this ); if( this.IsHandleCreated ) { return ImeContext.GetImeMode( this.Handle ); } else { // window is not yet created hence no IME associated yet. return ImeMode.Inherit; } } } ////// protected virtual ImeMode DefaultImeMode { get { return ImeMode.Inherit; } } ////// Flag used to avoid re-entrancy during WM_IME_NOTFIY message processing - see WmImeNotify(). /// Also to avoid raising the ImeModeChanged event more than once during the process of changing the ImeMode. /// internal int DisableImeModeChangedCount { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_DisableImeModeChangedCount()" ); Debug.Indent(); bool dummy; int val = (int) Properties.GetInteger( PropDisableImeModeChangedCount, out dummy ); Debug.Assert( val >= 0, "Counter underflow." ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value: " + val ); Debug.Unindent(); return val; } set { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_DisableImeModeChangedCount(): " + value ); Properties.SetInteger(PropDisableImeModeChangedCount, value); } } ////// Flag used to prevent setting ImeMode in focused control when loosing focus and hosted in a non-Form shell. /// See WmImeKillFocus() for more info. /// private static bool IgnoreWmImeNotify { get { Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_IgnoreWmImeNotify()"); Debug.Indent(); bool val = Control.ignoreWmImeNotify; Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value: " + val); Debug.Unindent(); return val; } set { Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_IgnoreWmImeNotify(): " + value); Control.ignoreWmImeNotify = value; } } ////// /// Specifies a value that determines the IME (Input Method Editor) status of the /// object when that object is selected. /// [ SRCategory( SR.CatBehavior ), Localizable( true ), AmbientValue( ImeMode.Inherit ), SRDescription( SR.ControlIMEModeDescr ) ] public ImeMode ImeMode { get { ImeMode imeMode = ImeModeBase; if (imeMode == ImeMode.OnHalf) // This is for compatibility. See QFE#4448. { imeMode = ImeMode.On; } return imeMode; } set { ImeModeBase = value; } } ////// Internal version of ImeMode property. This is provided for controls that override CanEnableIme and that /// return ImeMode.Disable for the ImeMode property when CanEnableIme is false - See TextBoxBase controls. /// protected virtual ImeMode ImeModeBase { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_ImeModeBase(), this = " + this ); Debug.Indent(); ImeMode imeMode = CachedImeMode; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + imeMode ); Debug.Unindent(); return imeMode; } set { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside set_ImeModeBase({0}), this = {1}", value, this ) ); Debug.Indent(); //valid values are -1 to 0xb if( !ClientUtils.IsEnumValid( value, (int) value, (int) ImeMode.Inherit, (int) ImeMode.OnHalf ) ) { throw new InvalidEnumArgumentException( "ImeMode", (int) value, typeof( ImeMode ) ); } ImeMode oldImeMode = CachedImeMode; CachedImeMode = value; if( oldImeMode != value ) { // Cache current value to determine whether we need to raise the ImeModeChanged. Control ctl = null; if( !DesignMode && ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { // Set the context to the new value if control is focused. if( Focused ) { ctl = this; } else if( ContainsFocus ) { ctl = FromChildHandleInternal( UnsafeNativeMethods.GetFocus() ); } if( ctl != null && ctl.CanEnableIme ) { // Block ImeModeChanged since we are checking for it below. DisableImeModeChangedCount++; try { ctl.UpdateImeContextMode(); } finally { DisableImeModeChangedCount--; } } } VerifyImeModeChanged( oldImeMode, CachedImeMode ); } ImeContext.TraceImeStatus( this ); Debug.Unindent(); } } ////// Determines whether the Control supports IME handling by default. /// private bool ImeSupported { get { return DefaultImeMode != ImeMode.Disable; } } ////// /// [WinCategory( "Behavior" ), SRDescription( SR.ControlOnImeModeChangedDescr )] public event EventHandler ImeModeChanged { add { Events.AddHandler( EventImeModeChanged, value ); } remove { Events.RemoveHandler( EventImeModeChanged, value ); } } ///[To be supplied.] ////// Returns the current number of WM_CHAR messages to ignore after processing corresponding WM_IME_CHAR msgs. /// internal int ImeWmCharsToIgnore { // The IME sends WM_IME_CHAR messages for each character in the composition string, and then // after all messages are sent, corresponding WM_CHAR messages are also sent. (in non-unicode // windows two WM_CHAR messages are sent per char in the IME). We need to keep a counter // not to process each character twice or more. // Marshal.SystemDefaultCharSize represents the default character size on the system; the default // is 2 for Unicode systems and 1 for ANSI systems. This is how it is implemented in Control. get { return Properties.GetInteger( PropImeWmCharsToIgnore ); } set { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside set_ImeWmCharToIgnore(value={0}), this = {1}", value, this ) ); Debug.Indent(); // WM_CHAR is not send after WM_IME_CHAR when the composition has been closed by either, changing the conversion mode or // dissasociating the IME (for instance when loosing focus and conversion is forced to complete). if( ImeWmCharsToIgnore != ImeCharsToIgnoreDisabled ) { Properties.SetInteger( PropImeWmCharsToIgnore, value ); } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImeWmCharsToIgnore on leaving setter: " + ImeWmCharsToIgnore ); Debug.Unindent(); } } ////// Gets the last value CanEnableIme property when it was last checked for ensuring IME context restriction mode. /// This is used by controls that implement some sort of IME restriction mode (like TextBox on Password/ReadOnly mode). /// See the VerifyImeRestrictedModeChanged() method. /// private bool LastCanEnableIme { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_LastCanEnableIme()" ); Debug.Indent(); bool valueFound; int val = (int) Properties.GetInteger( PropLastCanEnableIme, out valueFound ); if( valueFound ) { valueFound = val == 1; } else { valueFound = true; } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value: " + valueFound ); Debug.Unindent(); return valueFound; } set { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_LastCanEnableIme(): " + value ); Properties.SetInteger( PropLastCanEnableIme, value ? 1 : 0 ); } } ////// Represents the internal ImeMode value for controls with ImeMode = ImeMode.NoControl. This property is changed /// only by user interaction and is required to set the IME context appropriately while keeping the ImeMode property /// unchanged. /// protected static ImeMode PropagatingImeMode { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_PropagatingImeMode()" ); Debug.Indent(); if( Control.propagatingImeMode == ImeMode.Inherit ) { // Initialize the propagating IME mode to the value the IME associated to the focused window currently has, // this enables propagating the IME mode from/to unmanaged applications hosting [....] controls. Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Initializing PropagatingImeMode" ); ImeMode imeMode = ImeMode.Inherit; IntPtr focusHandle = UnsafeNativeMethods.GetFocus(); if( focusHandle != IntPtr.Zero ) { imeMode = ImeContext.GetImeMode(focusHandle); // If focused control is disabled we won't be able to get the app ime context mode, try the top window. // this is the case of a disabled [....] control hosted in a non-Form shell. if( imeMode == ImeMode.Disable ) { focusHandle = UnsafeNativeMethods.GetAncestor(new HandleRef(null, focusHandle), NativeMethods.GA_ROOT); if( focusHandle != IntPtr.Zero ) { imeMode = ImeContext.GetImeMode(focusHandle); } } } // If IME is disabled the PropagatingImeMode will not be initialized, see property setter below. PropagatingImeMode = imeMode; } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Value: " + Control.propagatingImeMode ); Debug.Unindent(); return Control.propagatingImeMode; } private set { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside set_PropagatingImeMode()" ); Debug.Indent(); if (Control.propagatingImeMode != value) { switch( value ) { case ImeMode.NoControl: case ImeMode.Disable: // Cannot set propagating ImeMode to one of these values. Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Cannot change PropagatingImeMode to " + value); return; default: Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Warning, string.Format( CultureInfo.CurrentCulture, "Setting PropagatingImeMode: Current value = {0}, New value = {1}", propagatingImeMode, value ) ); Control.propagatingImeMode = value; break; } } Debug.Unindent(); } } ////// Sets the IME context to the appropriate ImeMode according to the control's ImeMode state. /// This method is commonly used when attaching the IME to the control's window. /// internal void UpdateImeContextMode() { ImeMode[] inputLanguageTable = ImeModeConversion.InputLanguageTable; if (!DesignMode && (inputLanguageTable != ImeModeConversion.UnsupportedTable) && Focused) { // Note: CHN IME won't send WM_IME_NOTIFY msg when getting associated, setting the IME context mode // forces the message to be sent as a side effect. Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside UpdateImeContextMode(), this = " + this ); Debug.Indent(); // If the value is not supported by the Ime, it will be mapped to a corresponding one, we need // to update the cached ImeMode to the actual value. ImeMode newImeContextMode = ImeMode.Disable; ImeMode currentImeMode = CachedImeMode; if( ImeSupported && CanEnableIme ) { newImeContextMode = currentImeMode == ImeMode.NoControl ? PropagatingImeMode : currentImeMode; } // If PropagatingImeMode has not been initialized it will return ImeMode.Inherit above, need to check newImeContextMode for this. if (CurrentImeContextMode != newImeContextMode && newImeContextMode != ImeMode.Inherit) { // If the context changes the window will receive one or more WM_IME_NOTIFY messages and as part of its // processing it will raise the ImeModeChanged event if needed. We need to prevent the event from been // raised here from here. DisableImeModeChangedCount++; // Setting IME status to Disable will first close the IME and then disable it. For CHN IME, the first action will // update the PropagatingImeMode to ImeMode.Close which is incorrect. We need to save the PropagatingImeMode in // this case and restore it after the context has been changed. See VSW#530471 // Also this call here is very important since it will initialize the PropagatingImeMode if not already initialized // before setting the IME context to the control's ImeMode value which could be different from the propagating value. ImeMode savedPropagatingImeMode = PropagatingImeMode; try { ImeContext.SetImeStatus( newImeContextMode, this.Handle ); } finally { DisableImeModeChangedCount--; if (newImeContextMode == ImeMode.Disable && inputLanguageTable == ImeModeConversion.ChineseTable) { // Restore saved propagating mode. PropagatingImeMode = savedPropagatingImeMode; } } // Get mapped value from the context. if( currentImeMode == ImeMode.NoControl ) { if( CanEnableIme ) { PropagatingImeMode = CurrentImeContextMode; } } else { if( CanEnableIme ) { CachedImeMode = CurrentImeContextMode; } // Need to raise the ImeModeChanged event? VerifyImeModeChanged( newImeContextMode, CachedImeMode ); } } ImeContext.TraceImeStatus( this ); Debug.Unindent(); } } ////// Checks if specified ImeMode values are different and raise the event if true. /// private void VerifyImeModeChanged( ImeMode oldMode, ImeMode newMode ) { if( ImeSupported && (DisableImeModeChangedCount == 0) && (newMode != ImeMode.NoControl) ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside VerifyImeModeChanged(oldMode={0}, newMode={1}), this = {2}", oldMode, newMode, this ) ); Debug.Indent(); if( oldMode != newMode ) { OnImeModeChanged( EventArgs.Empty ); } Debug.Unindent(); } } ////// Verifies whether the IME context mode is correct based on the control's Ime restriction mode (CanEnableIme) /// and updates the IME context if needed. /// internal void VerifyImeRestrictedModeChanged() { Debug.Assert( ImeSupported, "This method should not be called from controls that don't support IME input." ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside VerifyImeRestrictedModeChanged(), this = " + this ); Debug.Indent(); bool currentCanEnableIme = this.CanEnableIme; if( LastCanEnableIme != currentCanEnableIme ) { if( Focused ) { // Disable ImeModeChanged from the following call since we'll raise it here if needed. DisableImeModeChangedCount++; try { UpdateImeContextMode(); } finally { DisableImeModeChangedCount--; } } // Assume for a moment the control is getting restricted; ImeMode oldImeMode = CachedImeMode; ImeMode newImeMode = ImeMode.Disable; if( currentCanEnableIme ) { // Control is actually getting unrestricted, swap values. newImeMode = oldImeMode; oldImeMode = ImeMode.Disable; } // Do we need to raise the ImeModeChanged event? VerifyImeModeChanged( oldImeMode, newImeMode ); // Finally update the saved CanEnableIme value. LastCanEnableIme = currentCanEnableIme; } Debug.Unindent(); } ////// Update internal ImeMode properties (PropagatingImeMode/CachedImeMode) with actual IME context mode if needed. /// This method can be used with a child control when the IME mode is more relevant to it than to the control itself, /// for instance ComboBox and its native ListBox/Edit controls. /// internal void OnImeContextStatusChanged( IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside OnImeContextStatusChanged(), this = " + this ); Debug.Indent(); Debug.Assert( ImeSupported, "WARNING: Attempting to update ImeMode properties on IME-Unaware control!" ); Debug.Assert( !DesignMode, "Shouldn't be updating cached ime mode at design-time" ); ImeMode fromContext = ImeContext.GetImeMode( handle ); if( fromContext != ImeMode.Inherit ) { ImeMode oldImeMode = CachedImeMode; if( CanEnableIme ) { // Cache or Propagating ImeMode should not be updated by interaction when the control is in restricted mode. if( oldImeMode != ImeMode.NoControl ) { CachedImeMode = fromContext; // This could end up in the same value due to ImeMode language mapping. // ImeMode may be changing by user interaction. VerifyImeModeChanged( oldImeMode, CachedImeMode ); } else { PropagatingImeMode = fromContext; } } } Debug.Unindent(); } ////// /// protected virtual void OnImeModeChanged( EventArgs e ) { Debug.Assert( ImeSupported, "ImeModeChanged should not be raised on an Ime-Unaware control." ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside OnImeModeChanged(), this = " + this ); EventHandler handler = (EventHandler) Events[EventImeModeChanged]; if( handler != null ) handler( this, e ); } ///Raises the ////// event. /// Resets the Ime mode. /// [EditorBrowsable( EditorBrowsableState.Never )] public void ResetImeMode() { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ResetImeMode(), this = " + this ); ImeMode = DefaultImeMode; } ////// Returns true if the ImeMode should be persisted in code gen. /// [EditorBrowsable( EditorBrowsableState.Never )] internal virtual bool ShouldSerializeImeMode() { // This method is for designer support. If the ImeMode has not been changed or it is the same as the // default value it should not be serialized. bool found; int imeMode = Properties.GetInteger( PropImeMode, out found ); return ( found && imeMode != (int) DefaultImeMode ); } ////// Handles the WM_INPUTLANGCHANGE message /// ///private void WmInputLangChange( ref Message m ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmInputLangChange(), this = " + this ); Debug.Indent(); // Make sure the IME context is associated with the correct (mapped) mode. UpdateImeContextMode(); // If detaching IME (setting to English) reset propagating IME mode so when reattaching the IME is set to direct input again. if( ImeModeConversion.InputLanguageTable == ImeModeConversion.UnsupportedTable ) { PropagatingImeMode = ImeMode.Off; } Form form = FindFormInternal(); if( form != null ) { InputLanguageChangedEventArgs e = InputLanguage.CreateInputLanguageChangedEventArgs( m ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Culture=" + e.Culture ); form.PerformOnInputLanguageChanged( e ); } DefWndProc( ref m ); ImeContext.TraceImeStatus( this ); Debug.Unindent(); } /// /// Handles the WM_INPUTLANGCHANGEREQUEST message /// ///private void WmInputLangChangeRequest( ref Message m ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmInputLangChangeRequest(), this=" + this ); Debug.Indent(); InputLanguageChangingEventArgs e = InputLanguage.CreateInputLanguageChangingEventArgs( m ); Form form = FindFormInternal(); if( form != null ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Culture=" + e.Culture ); form.PerformOnInputLanguageChanging( e ); } if( !e.Cancel ) { DefWndProc( ref m ); } else { m.Result = IntPtr.Zero; } Debug.Unindent(); } /// /// Handles the WM_IME_CHAR message /// private void WmImeChar( ref Message m ) { if( ProcessKeyEventArgs( ref m ) ) { return; } DefWndProc( ref m ); } ////// Handles the WM_IME_ENDCOMPOSITION message /// private void WmImeEndComposition( ref Message m ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeEndComposition() - Disabling ImeWmCharToIgnore, this=" + this ); this.ImeWmCharsToIgnore = ImeCharsToIgnoreDisabled; DefWndProc( ref m ); } ////// Handles the WM_IME_NOTIFY message /// private void WmImeNotify( ref Message m ) { if( ImeSupported && ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable && !IgnoreWmImeNotify) { int wparam = (int) m.WParam; // The WM_IME_NOTIFY message is not consistent across the different IMEs, particularly the notification type // we care about (IMN_SETCONVERSIONMODE & IMN_SETOPENSTATUS). // The IMN_SETOPENSTATUS command is sent when the open status of the input context is updated. // The IMN_SETCONVERSIONMODE command is sent when the conversion mode of the input context is updated. // - The Korean IME sents both msg notifications when changing the conversion mode (From/To Hangul/Alpha). // - The Chinese IMEs sends the IMN_SETCONVERSIONMODE when changing mode (On/Close, Full Shape/Half Shape) // and IMN_SETOPENSTATUS when getting disabled/enabled or closing/opening as well, but it does not send any // WM_IME_NOTIFY when associating an IME to the app for the first time; setting the IME mode to direct input // during WM_INPUTLANGCHANGED forces the IMN_SETOPENSTATUS message to be sent. // - The Japanese IME sends IMN_SETCONVERSIONMODE when changing from Off to one of the active modes (Katakana..) // and IMN_SETOPENSTATUS when changing beteween the active modes or when enabling/disabling the IME. // In any case we update the cache. // Warning: // Attempting to change the IME mode from here will cause re-entrancy - WM_IME_NOTIFY is resent. // We guard against re-entrancy since the ImeModeChanged event can be raised and any changes from the handler could // lead to another WM_IME_NOTIFY loop. if( wparam == NativeMethods.IMN_SETCONVERSIONMODE || wparam == NativeMethods.IMN_SETOPENSTATUS ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside WmImeNotify(m.wparam=[{0}]), this={1}", m.WParam, this ) ); Debug.Indent(); // Synchronize internal properties with the IME context mode. OnImeContextStatusChanged( this.Handle ); Debug.Unindent(); } } DefWndProc( ref m ); } ////// Handles the WM_SETFOCUS message for IME related stuff. /// internal void WmImeSetFocus() { if (ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable) { Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeSetFocus(), this=" + this); Debug.Indent(); // Make sure the IME context is set to the correct value. // Consider - Perf improvement: ContainerControl controls should update the IME context only when they don't contain // a focusable control since it will be updated by that control. UpdateImeContextMode(); Debug.Unindent(); } } ////// Handles the WM_IME_STARTCOMPOSITION message /// private void WmImeStartComposition( ref Message m ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeStartComposition() - Enabling ImeWmCharToIgnore, this=" + this ); // Need to call the property store directly because the WmImeCharsToIgnore property is locked when ImeCharsToIgnoreDisabled. Properties.SetInteger( PropImeWmCharsToIgnore, ImeCharsToIgnoreEnabled ); DefWndProc( ref m ); } ////// Handles the WM_KILLFOCUS message /// ///private void WmImeKillFocus() { Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside WmImeKillFocus(), this=" + this); Debug.Indent(); Control topMostWinformsParent = TopMostParent; Form appForm = topMostWinformsParent as Form; if( (appForm == null || appForm.Modal) && !topMostWinformsParent.ContainsFocus ) { // This means the [....] component container is not a [....] host and it is no longer focused. // Or it is not the main app host. See VSW#531520,604741 (VSU QFE#5055), DDB#95889, TFS VSTSDEVDIV463760 Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Unfocused TopMostParent = " + topMostWinformsParent); // We need to reset the PropagatingImeMode to force reinitialization when the [....] component gets focused again; // this enables inheritting the propagating mode from an unmanaged application hosting a [....] component. // But before leaving the [....] container we need to set the IME to the propagating IME mode since the focused control // may not support IME which would leave the IME disabled. // See the PropagatingImeMode property and VSW#531520. // Note: We need to check the static field here directly to avoid initialization of the property. if (Control.propagatingImeMode != ImeMode.Inherit) { // Setting the ime context of the top window will generate a WM_IME_NOTIFY on the focused control which will // update its ImeMode, we need to prevent this temporarily. IgnoreWmImeNotify = true; try { Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Setting IME context to PropagatingImeMode (leaving [....] container). this = " + this); ImeContext.SetImeStatus(PropagatingImeMode, topMostWinformsParent.Handle); Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "Resetting PropagatingImeMode. this = " + this); PropagatingImeMode = ImeMode.Inherit; } finally { IgnoreWmImeNotify = false; } } } Debug.Unindent(); } } // end class Control ///////////////////////////////////////////////////////// ImeContext class ///////////////////////////////////////////////////////// /// /// Represents the native IME context. /// public static class ImeContext { ////// The IME context handle obtained when first associating an IME. /// private static IntPtr originalImeContext; ////// Disable the IME /// public static void Disable( IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ImeContext.Disable(" + handle + ")" ); Debug.Indent(); if( ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { // Close the IME if necessary // if( ImeContext.IsOpen( handle ) ) { ImeContext.SetOpenStatus( false, handle ); } // Disable the IME by disassociating the context from the window. // Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmAssociateContext(" + handle + ", null)" ); IntPtr oldContext = UnsafeNativeMethods.ImmAssociateContext( new HandleRef( null, handle ), NativeMethods.NullHandleRef ); if( oldContext != IntPtr.Zero ) { originalImeContext = oldContext; } } ImeContext.TraceImeStatus( handle ); Debug.Unindent(); } ////// Enable the IME /// public static void Enable( IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ImeContext.Enable(" + handle + ")" ); Debug.Indent(); if( ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); // Enable IME by associating the IME context to the window. if( inputContext == IntPtr.Zero ) { if( originalImeContext == IntPtr.Zero ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmCreateContext()" ); inputContext = UnsafeNativeMethods.ImmCreateContext(); if( inputContext != IntPtr.Zero ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmAssociateContext(" + handle + ", " + inputContext + ")" ); UnsafeNativeMethods.ImmAssociateContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); } } else { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmAssociateContext()" ); UnsafeNativeMethods.ImmAssociateContext( new HandleRef( null, handle ), new HandleRef( null, originalImeContext ) ); } } else { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); } // Make sure the IME is opened. if( !ImeContext.IsOpen( handle ) ) { ImeContext.SetOpenStatus( true, handle ); } } ImeContext.TraceImeStatus( handle ); Debug.Unindent(); } ////// Gets the ImeMode that corresponds to ImeMode.Disable based on the current input language ImeMode table. /// public static ImeMode GetImeMode( IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Insise ImeContext.GetImeMode(" + handle + ")" ); Debug.Indent(); IntPtr inputContext = IntPtr.Zero; ImeMode retval = ImeMode.NoControl; // Get the right table for the current keyboard layout // ImeMode[] countryTable = ImeModeConversion.InputLanguageTable; if( countryTable == ImeModeConversion.UnsupportedTable ) { // No IME associated with current culture. retval = ImeMode.Inherit; goto cleanup; } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); if( inputContext == IntPtr.Zero ) { // No IME context attached - The Ime has been disabled. retval = ImeMode.Disable; goto cleanup; } if( !ImeContext.IsOpen( handle ) ) { // There's an IME associated with the window but is closed - the input is taken from the keyboard as is (English). retval = countryTable[ImeModeConversion.ImeClosed]; goto cleanup; } // Determine the IME mode from the conversion status // int conversion = 0; // Combination of conversion mode values int sentence = 0; // Sentence mode value Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetConversionStatus(" + inputContext + ", conversion, sentence)" ); UnsafeNativeMethods.ImmGetConversionStatus( new HandleRef( null, inputContext ), ref conversion, ref sentence ); Debug.Assert( countryTable != null, "countryTable is null" ); if( ( conversion & NativeMethods.IME_CMODE_NATIVE ) != 0 ) { if( ( conversion & NativeMethods.IME_CMODE_KATAKANA ) != 0 ) { retval = ( ( conversion & NativeMethods.IME_CMODE_FULLSHAPE ) != 0 ) ? countryTable[ImeModeConversion.ImeNativeFullKatakana] : countryTable[ImeModeConversion.ImeNativeHalfKatakana]; goto cleanup; } else { // ! Katakana retval = ( ( conversion & NativeMethods.IME_CMODE_FULLSHAPE ) != 0 ) ? countryTable[ImeModeConversion.ImeNativeFullHiragana] : countryTable[ImeModeConversion.ImeNativeHalfHiragana]; goto cleanup; } } else { // ! IME_CMODE_NATIVE retval = ( ( conversion & NativeMethods.IME_CMODE_FULLSHAPE ) != 0 ) ? countryTable[ImeModeConversion.ImeAlphaFull] : countryTable[ImeModeConversion.ImeAlphaHalf]; } cleanup: if( inputContext != IntPtr.Zero ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); } ImeContext.TraceImeStatus( handle ); Debug.Unindent(); return retval; } ////// Get the actual IME status - This method is for debugging purposes only. /// [Conditional( "DEBUG" )] internal static void TraceImeStatus( Control ctl ) { #if DEBUG if ( ctl.IsHandleCreated ){ TraceImeStatus( ctl.Handle ); } #endif } [Conditional( "DEBUG" )] private static void TraceImeStatus( IntPtr handle ) { #if DEBUG if (CompModSwitches.ImeMode.Level >= TraceLevel.Info) { string status = "?"; IntPtr inputContext = IntPtr.Zero; ImeMode[] countryTable = ImeModeConversion.InputLanguageTable; if (countryTable == ImeModeConversion.UnsupportedTable) { status = "IME not supported in current language."; goto cleanup; } inputContext = UnsafeNativeMethods.ImmGetContext(new HandleRef(null, handle)); if (inputContext == IntPtr.Zero) { status = string.Format(CultureInfo.CurrentCulture, "No ime context for handle=[{0}]", handle); goto cleanup; } if (!UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(null, inputContext))) { status = string.Format(CultureInfo.CurrentCulture, "Ime closed for handle=[{0}]", handle); goto cleanup; } int conversion = 0; // Combination of conversion mode values int sentence = 0; // Sentence mode value UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(null, inputContext), ref conversion, ref sentence); ImeMode retval; if ((conversion & NativeMethods.IME_CMODE_NATIVE) != 0) { if ((conversion & NativeMethods.IME_CMODE_KATAKANA) != 0) { retval = ((conversion & NativeMethods.IME_CMODE_FULLSHAPE) != 0) ? countryTable[ImeModeConversion.ImeNativeFullKatakana] : countryTable[ImeModeConversion.ImeNativeHalfKatakana]; } else { // ! Katakana retval = ((conversion & NativeMethods.IME_CMODE_FULLSHAPE) != 0) ? countryTable[ImeModeConversion.ImeNativeFullHiragana] : countryTable[ImeModeConversion.ImeNativeHalfHiragana]; } } else { // ! IME_CMODE_NATIVE retval = ((conversion & NativeMethods.IME_CMODE_FULLSHAPE) != 0) ? countryTable[ImeModeConversion.ImeAlphaFull] : countryTable[ImeModeConversion.ImeAlphaHalf]; } status = string.Format(CultureInfo.CurrentCulture, "Ime conversion status mode for handle=[{0}]: {1}", handle, retval); cleanup: if (inputContext != IntPtr.Zero) { UnsafeNativeMethods.ImmReleaseContext(new HandleRef(null, handle), new HandleRef(null, inputContext)); } Debug.WriteLine(status); } #endif } ////// Returns true if the IME is currently open /// public static bool IsOpen( IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside ImeContext.IsOpen(" + handle + ")" ); Debug.Indent(); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); bool retval = false; if( inputContext != IntPtr.Zero ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetOpenStatus(" + inputContext + ")" ); retval = UnsafeNativeMethods.ImmGetOpenStatus( new HandleRef( null, inputContext ) ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, " IsOpen = " + retval ); Debug.Unindent(); return retval; } ////// Sets the actual IME context value. /// public static void SetImeStatus( ImeMode imeMode, IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside ImeContext.SetImeStatus(ImeMode=[{0}, handle=[{1}])", imeMode, handle ) ); Debug.Indent(); Debug.Assert( imeMode != ImeMode.Inherit, "ImeMode.Inherit is an invalid argument to ImeContext.SetImeStatus" ); if( imeMode == ImeMode.Inherit || imeMode == ImeMode.NoControl ) { goto cleanup; // No action required } ImeMode[] inputLanguageTable = ImeModeConversion.InputLanguageTable; if (inputLanguageTable == ImeModeConversion.UnsupportedTable) { goto cleanup; // We only support Japanese, Korean and Chinese IME. } int conversion = 0; // Combination of conversion mode values int sentence = 0; // Sentence mode value Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Warning, "\tSetting IME context to " + imeMode ); if( imeMode == ImeMode.Disable ) { ImeContext.Disable( handle ); } else { // This will make sure the IME is opened. ImeContext.Enable( handle ); } switch( imeMode ) { case ImeMode.NoControl: case ImeMode.Disable: break; // No action required case ImeMode.On: // IME active mode (CHN = On, JPN = Hiragana, KOR = Hangul). // Setting ImeMode to Hiragana (or any other active value) will force the IME to get to an active value // independent on the language. imeMode = ImeMode.Hiragana; goto default; case ImeMode.Off: // IME direct input (CHN = Off, JPN = Off, KOR = Alpha). if (inputLanguageTable == ImeModeConversion.JapaneseTable) { // Japanese IME interprets Close as Off. goto case ImeMode.Close; } // CHN: to differentiate between Close and Off we set the ImeMode to Alpha. imeMode = ImeMode.Alpha; goto default; case ImeMode.Close: if (inputLanguageTable == ImeModeConversion.KoreanTable) { // Korean IME has no idea what Close means. imeMode = ImeMode.Alpha; goto default; } ImeContext.SetOpenStatus( false, handle ); break; default: if( ImeModeConversion.ImeModeConversionBits.ContainsKey( imeMode ) ) { // Update the conversion status // ImeModeConversion conversionEntry = (ImeModeConversion) ImeModeConversion.ImeModeConversionBits[imeMode]; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetConversionStatus(" + inputContext + ", conversion, sentence)" ); UnsafeNativeMethods.ImmGetConversionStatus( new HandleRef( null, inputContext ), ref conversion, ref sentence ); conversion |= conversionEntry.setBits; conversion &= ~conversionEntry.clearBits; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmSetConversionStatus(" + inputContext + ", conversion, sentence)" ); bool retval = UnsafeNativeMethods.ImmSetConversionStatus( new HandleRef( null, inputContext ), conversion, sentence ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); } break; } cleanup: ImeContext.TraceImeStatus( handle ); Debug.Unindent(); } ////// Opens or closes the IME context. /// public static void SetOpenStatus( bool open, IntPtr handle ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, string.Format( CultureInfo.CurrentCulture, "Inside SetOpenStatus(open=[{0}], handle=[{1}])", open, handle ) ); Debug.Indent(); if( ImeModeConversion.InputLanguageTable != ImeModeConversion.UnsupportedTable ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmGetContext(" + handle + ")" ); IntPtr inputContext = UnsafeNativeMethods.ImmGetContext( new HandleRef( null, handle ) ); Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "context = " + inputContext ); if( inputContext != IntPtr.Zero ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmSetOpenStatus(" + inputContext + ", " + open + ")" ); bool succeeded = UnsafeNativeMethods.ImmSetOpenStatus( new HandleRef( null, inputContext ), open ); Debug.Assert( succeeded, "Could not set the IME open status." ); if( succeeded ) { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Verbose, "ImmReleaseContext(" + handle + ", " + inputContext + ")" ); succeeded = UnsafeNativeMethods.ImmReleaseContext( new HandleRef( null, handle ), new HandleRef( null, inputContext ) ); Debug.Assert( succeeded, "Could not release IME context." ); } } } ImeContext.TraceImeStatus( handle ); Debug.Unindent(); } }// end ImeContext class ///////////////////////////////////////////////////////// ImeModeConversion structure ///////////////////////////////////////////////////////// ////// Helper class that provides information about IME convertion mode. Convertion mode refers to how IME interprets input like /// ALPHANUMERIC or HIRAGANA and depending on its value the IME enables/disables the IME convertion window appropriately. /// [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] // this class has no public instance memebers. public struct ImeModeConversion { private static DictionaryimeModeConversionBits; internal int setBits; internal int clearBits; // Tables of conversions from IME context bits to IME mode // //internal const int ImeNotAvailable = 0; internal const int ImeDisabled = 1; internal const int ImeDirectInput = 2; internal const int ImeClosed = 3; internal const int ImeNativeInput = 4; internal const int ImeNativeFullHiragana = 4; // Index of Native Input Mode. internal const int ImeNativeHalfHiragana = 5; internal const int ImeNativeFullKatakana = 6; internal const int ImeNativeHalfKatakana = 7; internal const int ImeAlphaFull = 8; internal const int ImeAlphaHalf = 9; /// /// Supported input language ImeMode tables. /// WARNING: Do not try to map 'active' IME modes from one table to another since they can have a different /// meaning depending on the language; for instance ImeMode.Off means 'disable' or 'alpha' to Chinese /// but to Japanese it is 'alpha' and to Korean it has no meaning. /// private static ImeMode[] japaneseTable = { ImeMode.Inherit, ImeMode.Disable, ImeMode.Off, ImeMode.Off, ImeMode.Hiragana, ImeMode.Hiragana, ImeMode.Katakana, ImeMode.KatakanaHalf, ImeMode.AlphaFull, ImeMode.Alpha }; private static ImeMode[] koreanTable = { ImeMode.Inherit, ImeMode.Disable, ImeMode.Alpha, ImeMode.Alpha, ImeMode.HangulFull, ImeMode.Hangul, ImeMode.HangulFull, ImeMode.Hangul, ImeMode.AlphaFull, ImeMode.Alpha }; private static ImeMode[] chineseTable = { ImeMode.Inherit, ImeMode.Disable, ImeMode.Off, ImeMode.Close, ImeMode.On, ImeMode.OnHalf, ImeMode.On, ImeMode.OnHalf, ImeMode.Off, ImeMode.Off }; private static ImeMode[] unsupportedTable = { }; internal static ImeMode[] ChineseTable { get { return chineseTable; } } internal static ImeMode[] JapaneseTable { get { return japaneseTable; } } internal static ImeMode[] KoreanTable { get { return koreanTable; } } internal static ImeMode[] UnsupportedTable { get { return unsupportedTable; } } ////// Gets the ImeMode table of the current input language. /// Although this property is per-thread based we cannot cache it and share it among controls running in the same thread /// for two main reasons: we still have some controls that don't handle IME properly (TabControl, ComboBox, TreeView...) /// and would render it invalid and since the IME API is not public third party controls would not have a way to update /// the cached value. See VSW#541650/541728. /// internal static ImeMode[] InputLanguageTable { get { Debug.WriteLineIf(CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_InputLanguageTable(), Input Language = " + InputLanguage.CurrentInputLanguage.Culture.DisplayName + ", Thread = " + System.Threading.Thread.CurrentThread.ManagedThreadId); InputLanguage inputLanguage = InputLanguage.CurrentInputLanguage; int lcid = (int)((long)inputLanguage.Handle & (long)0xFFFF); switch (lcid) { case 0x0404: // Chinese (----) case 0x0804: // Chinese (PRC) case 0x0c04: // Chinese (Hong Kong SAR, PRC) case 0x1004: // Chinese (Singapore) case 0x1404: // Chinese (Macau SAR) return chineseTable; case 0x0412: // Korean case 0x0812: // Korean (Johab) return koreanTable; case 0x0411: // Japanese return japaneseTable; default: return unsupportedTable; } } } ////// Dictionary of ImeMode and corresponding convertion flags. /// public static DictionaryImeModeConversionBits { get { if( imeModeConversionBits == null ) { // Create ImeModeConversionBits dictionary imeModeConversionBits = new Dictionary ( 7 ); ImeModeConversion conversion; // Hiragana, On // conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_NATIVE; conversion.clearBits = NativeMethods.IME_CMODE_KATAKANA; imeModeConversionBits.Add( ImeMode.Hiragana, conversion ); // Katakana // conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; conversion.clearBits = 0; imeModeConversionBits.Add( ImeMode.Katakana, conversion ); // KatakanaHalf // conversion.setBits = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; conversion.clearBits = NativeMethods.IME_CMODE_FULLSHAPE; imeModeConversionBits.Add( ImeMode.KatakanaHalf, conversion ); // AlphaFull // conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE; conversion.clearBits = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; imeModeConversionBits.Add( ImeMode.AlphaFull, conversion ); // Alpha // conversion.setBits = 0; conversion.clearBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_NATIVE; imeModeConversionBits.Add( ImeMode.Alpha, conversion ); // HangulFull // conversion.setBits = NativeMethods.IME_CMODE_FULLSHAPE | NativeMethods.IME_CMODE_NATIVE; conversion.clearBits = 0; imeModeConversionBits.Add( ImeMode.HangulFull, conversion ); // Hangul // conversion.setBits = NativeMethods.IME_CMODE_NATIVE; conversion.clearBits = NativeMethods.IME_CMODE_FULLSHAPE; imeModeConversionBits.Add( ImeMode.Hangul, conversion ); // OnHalf // conversion.setBits = NativeMethods.IME_CMODE_NATIVE; conversion.clearBits = NativeMethods.IME_CMODE_KATAKANA | NativeMethods.IME_CMODE_FULLSHAPE; imeModeConversionBits.Add(ImeMode.OnHalf, conversion); } return imeModeConversionBits; } } public static bool IsCurrentConversionTableSupported{ get { return InputLanguageTable != UnsupportedTable; } } }// end ImeModeConversion struct. } // end namespace System.Windows.Forms // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- KerberosSecurityTokenProvider.cs
- HttpDebugHandler.cs
- SourceFilter.cs
- MimeMapping.cs
- EventWaitHandle.cs
- PlainXmlWriter.cs
- PropertySourceInfo.cs
- StreamGeometryContext.cs
- EditingCommands.cs
- PreservationFileReader.cs
- XmlHierarchicalDataSourceView.cs
- MarginsConverter.cs
- EnumUnknown.cs
- ScrollItemPattern.cs
- BasicCellRelation.cs
- RegisteredHiddenField.cs
- SqlReorderer.cs
- KeyValuePairs.cs
- AnnotationService.cs
- DateTimeAutomationPeer.cs
- Switch.cs
- EventSetterHandlerConverter.cs
- WorkflowInstanceUnhandledExceptionRecord.cs
- TableRow.cs
- XmlSerializerFormatAttribute.cs
- FlowLayout.cs
- ProjectionPlanCompiler.cs
- XmlRawWriter.cs
- LongValidator.cs
- VectorCollectionConverter.cs
- namescope.cs
- ErasingStroke.cs
- BufferedReadStream.cs
- ExclusiveTcpTransportManager.cs
- EntityDesignerUtils.cs
- VisualProxy.cs
- SqlBulkCopy.cs
- PasswordRecovery.cs
- TextContainerChangeEventArgs.cs
- MenuStrip.cs
- _NetRes.cs
- SqlDataSourceView.cs
- ObjectSet.cs
- FullTextState.cs
- WebPart.cs
- OptimisticConcurrencyException.cs
- CompoundFileStorageReference.cs
- PreApplicationStartMethodAttribute.cs
- TimeZone.cs
- HwndMouseInputProvider.cs
- BaseTemplateCodeDomTreeGenerator.cs
- JsonByteArrayDataContract.cs
- OperatingSystem.cs
- AsymmetricKeyExchangeDeformatter.cs
- CodeConstructor.cs
- MissingMethodException.cs
- SizeAnimationUsingKeyFrames.cs
- PenThreadWorker.cs
- RecordsAffectedEventArgs.cs
- XNameConverter.cs
- HMACRIPEMD160.cs
- ApplicationCommands.cs
- OleDbParameterCollection.cs
- UserControl.cs
- XmlDigitalSignatureProcessor.cs
- ResourceAssociationTypeEnd.cs
- EntityProxyFactory.cs
- RawAppCommandInputReport.cs
- NetWebProxyFinder.cs
- DataGridViewCellStyleChangedEventArgs.cs
- BamlTreeMap.cs
- Int32CollectionConverter.cs
- GridViewHeaderRowPresenter.cs
- CodeAttributeArgumentCollection.cs
- CrossContextChannel.cs
- HwndAppCommandInputProvider.cs
- PageContentAsyncResult.cs
- PassportAuthentication.cs
- WebPartDescriptionCollection.cs
- CompressStream.cs
- PassportPrincipal.cs
- SystemDiagnosticsSection.cs
- OledbConnectionStringbuilder.cs
- MobileControlBuilder.cs
- DropDownList.cs
- SkipStoryboardToFill.cs
- DbProviderFactoriesConfigurationHandler.cs
- Parser.cs
- FormsAuthenticationUser.cs
- MultiAsyncResult.cs
- JumpPath.cs
- PathTooLongException.cs
- VectorConverter.cs
- ConfigurationPermission.cs
- DetailsViewUpdateEventArgs.cs
- Span.cs
- MailBnfHelper.cs
- DataBindingHandlerAttribute.cs
- DateTimeOffsetConverter.cs
- UriSection.cs