File: Microsoft\Windows\Controls\KeyTipService.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon_dxtfdo3u_wpftmp.csproj (System.Windows.Controls.Ribbon)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#region Using declarations
 
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Threading;
#if RIBBON_IN_FRAMEWORK
using MS.Internal;
using Microsoft.Windows.Controls;
using System.Windows.Controls.Ribbon;
 
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls
#else
namespace Microsoft.Windows.Controls
#endif
{
#else
    using Microsoft.Windows.Controls.Ribbon;
#endif
 
    #endregion
 
    /// <summary>
    ///     The service class which enables and manages KeyTips.
    /// </summary>
    public class KeyTipService
    {
        #region Constructor
 
        private KeyTipService()
        {
            InputManager.Current.PostProcessInput += new ProcessInputEventHandler(PostProcessInput);
            InputManager.Current.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput);
            State = KeyTipState.None;
        }
 
        #endregion
 
        #region KeyTip Properties
 
        /// <summary>
        ///     DependencyProperty for KeyTip string.
        /// </summary>
        public static readonly DependencyProperty KeyTipProperty =
            DependencyProperty.RegisterAttached("KeyTip", typeof(string), typeof(KeyTipService), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnKeyTipChanged)));
 
        /// <summary>
        ///     Gets the value of KeyTip on the specified object
        /// </summary>
        public static string GetKeyTip(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (string)element.GetValue(KeyTipProperty);
        }
 
        /// <summary>
        ///     Sets the value of KeyTip on the specified object
        /// </summary>
        public static void SetKeyTip(DependencyObject element, string value)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(KeyTipProperty, value);
        }
 
        private static void OnKeyTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            string newKeyTip = (string)e.NewValue;
            string oldKeyTip = (string)e.OldValue;
 
            bool isNewEmpty = string.IsNullOrEmpty(newKeyTip);
 
            if (isNewEmpty != string.IsNullOrEmpty(oldKeyTip))
            {
                if (isNewEmpty)
                {
                    if (CanUnregisterKeyTipElement(d))
                    {
                        UnregisterKeyTipElement(d);
                    }
                }
                else
                {
                    RegisterKeyTipElement(d);
                }
            }
        }
 
        public static bool GetIsKeyTipScope(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (bool)element.GetValue(IsKeyTipScopeProperty);
        }
 
        public static void SetIsKeyTipScope(DependencyObject element, bool value)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(IsKeyTipScopeProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for IsKeyTipScope.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsKeyTipScopeProperty =
            DependencyProperty.RegisterAttached("IsKeyTipScope", typeof(bool), typeof(KeyTipService), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsKeyTipScopeChanged)));
 
        private static void OnIsKeyTipScopeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ProcessKeyTipScopeChange(d, (bool)e.NewValue);
        }
 
        public static Style GetKeyTipStyle(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (Style)element.GetValue(KeyTipStyleProperty);
        }
 
        public static void SetKeyTipStyle(DependencyObject element, Style value)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(KeyTipStyleProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for KeyTipStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty KeyTipStyleProperty =
            DependencyProperty.RegisterAttached("KeyTipStyle", typeof(Style), typeof(KeyTipService), new FrameworkPropertyMetadata(null));
 
        #endregion
 
        #region Registration
 
        private static readonly DependencyPropertyKey KeyTipScopePropertyKey =
            DependencyProperty.RegisterAttachedReadOnly("KeyTipScope", typeof(DependencyObject), typeof(KeyTipService),
                new UIPropertyMetadata(null, new PropertyChangedCallback(OnKeyTipScopeChanged)));
 
        private static void OnKeyTipScopeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DependencyObject oldScope = e.OldValue as DependencyObject;
            DependencyObject newScope = e.NewValue as DependencyObject;
            KeyTipService current = Current;
 
            if (oldScope != null)
            {
                // Remove the element from the old scope data sets.
                WeakHashSet<DependencyObject> oldElementSet = null;
                if (current._scopeToElementMap.TryGetValue(oldScope, out oldElementSet))
                {
                    if (oldElementSet != null)
                    {
                        oldElementSet.Remove(d);
                    }
                }
            }
            if (newScope != null)
            {
                // Add the element to the new scope data sets.
                if (!current._scopeToElementMap.ContainsKey(newScope) ||
                    current._scopeToElementMap[newScope] == null)
                {
                    current._scopeToElementMap[newScope] = new WeakHashSet<DependencyObject>();
                }
                current._scopeToElementMap[newScope].Add(d);
            }
        }
 
        private static void HookKeyTipElementEventHandlers(DependencyObject element)
        {
            FrameworkElement fe = element as FrameworkElement;
            if (fe == null)
            {
                FrameworkContentElement fce = element as FrameworkContentElement;
                if (fce != null)
                {
                    fce.Loaded += new RoutedEventHandler(OnKeyTipElementLoaded);
                    fce.Unloaded += new RoutedEventHandler(OnKeyTipElementUnLoaded);
                }
            }
            else
            {
                fe.Loaded += new RoutedEventHandler(OnKeyTipElementLoaded);
                fe.Unloaded += new RoutedEventHandler(OnKeyTipElementUnLoaded);
            }
        }
 
        private static void UnhookKeyTipElementEventHandlers(DependencyObject element)
        {
            FrameworkElement fe = element as FrameworkElement;
            if (fe == null)
            {
                FrameworkContentElement fce = element as FrameworkContentElement;
                if (fce != null)
                {
                    fce.Loaded -= new RoutedEventHandler(OnKeyTipElementLoaded);
                    fce.Unloaded -= new RoutedEventHandler(OnKeyTipElementUnLoaded);
                }
            }
            else
            {
                fe.Loaded -= new RoutedEventHandler(OnKeyTipElementLoaded);
                fe.Unloaded -= new RoutedEventHandler(OnKeyTipElementUnLoaded);
            }
        }
 
        private static void RegisterKeyTipElement(DependencyObject element)
        {
            AddElementForKeyTipScoping(element);
            HookKeyTipElementEventHandlers(element);
        }
 
        private static void UnregisterKeyTipElement(DependencyObject element)
        {
            KeyTipService current = Current;
            current._toBeScopedElements.Remove(element);
            element.ClearValue(KeyTipScopePropertyKey);
            UnhookKeyTipElementEventHandlers(element);
        }
 
        private static bool CanUnregisterKeyTipElement(DependencyObject element)
        {
            return (string.IsNullOrEmpty(GetKeyTip(element)) &&
                GetKeyTipAutoGenerationElements(element) == null &&
                GetCustomSiblingKeyTipElements(element) == null);
        }
 
        private static void OnKeyTipElementLoaded(object sender, RoutedEventArgs e)
        {
            AddElementForKeyTipScoping(sender as DependencyObject);
        }
 
        private static void OnKeyTipElementUnLoaded(object sender, RoutedEventArgs e)
        {
            AddElementForKeyTipScoping(sender as DependencyObject);
        }
 
        private static void AddElementForKeyTipScoping(DependencyObject element)
        {
            Debug.Assert(element != null);
            KeyTipService current = Current;
            if (!current._toBeScopedElements.Contains(element))
            {
                current._toBeScopedElements.Add(element);
            }
        }
 
        private static void ProcessKeyTipScopeChange(DependencyObject scopeElement, bool newIsScope)
        {
            KeyTipService current = Current;
            if (current._unprocessedScopes.ContainsKey(scopeElement))
            {
                if (current._unprocessedScopes[scopeElement] != newIsScope)
                {
                    // Remove the scope element from unprocess scopes list
                    // if its IsKeyTipScope value changed from T->F->T or
                    // F->T->F
                    current._unprocessedScopes.Remove(scopeElement);
                }
            }
            else
            {
                current._unprocessedScopes[scopeElement] = newIsScope;
            }
        }
 
        private void ProcessScoping()
        {
            Dictionary<DependencyObject, bool> processedElements = new Dictionary<DependencyObject, bool>();
 
            // Process the all the scopes elements
            foreach (DependencyObject scope in _unprocessedScopes.Keys)
            {
                if (_unprocessedScopes[scope])
                {
                    // If an element newly became scope then process
                    // all the keytip elements under its parent scope.
                    DependencyObject scopedParent = FindScope(scope);
                    if (scopedParent == null)
                    {
                        scopedParent = scope;
                    }
                    ReevaluateScopes(scopedParent, processedElements);
                }
                else
                {
                    // Else process all the elements currently under the scope.
                    ReevaluateScopes(scope, processedElements);
                }
            }
            _unprocessedScopes.Clear();
 
            // Process all the one of elements which are queued for
            // scope processing.
            foreach (DependencyObject element in _toBeScopedElements)
            {
                if (!processedElements.ContainsKey(element))
                {
                    DependencyObject newScope = FindScope(element);
                    AddElementToScope(newScope, element);
                    processedElements[element] = true;
                }
            }
            _toBeScopedElements.Clear();
        }
 
        private static void AddItemToArrayListRef(object item, ref ArrayList list)
        {
            if (list == null)
            {
                list = new ArrayList(2);
            }
            list.Add(item);
        }
 
        /// <summary>
        ///     Reevalutates the scoping for all elements under
        ///     the given scope.
        /// </summary>
        private void ReevaluateScopes(DependencyObject scopedParent, Dictionary<DependencyObject, bool> processedElements)
        {
            if (scopedParent == null)
            {
                return;
            }
            if (_scopeToElementMap.ContainsKey(scopedParent))
            {
                WeakHashSet<DependencyObject> elementSet = _scopeToElementMap[scopedParent];
                if (elementSet != null && elementSet.Count > 0)
                {
                    Dictionary<DependencyObject, DependencyObject> elementToScopeMap = null;
                    foreach (DependencyObject element in elementSet)
                    {
                        if (!processedElements.ContainsKey(element))
                        {
                            DependencyObject newScope = FindScope(element);
                            if (newScope != scopedParent)
                            {
                                if (elementToScopeMap == null)
                                {
                                    elementToScopeMap = new Dictionary<DependencyObject, DependencyObject>();
                                }
                                elementToScopeMap[element] = newScope;
                            }
                            processedElements[element] = true;
                        }
                    }
                    if (elementToScopeMap != null)
                    {
                        foreach (DependencyObject element in elementToScopeMap.Keys)
                        {
                            // Add the element to the new scope.
                            AddElementToScope(elementToScopeMap[element], element);
                        }
                    }
                }
            }
        }
 
        private static void AddElementToScope(DependencyObject scopedParent, DependencyObject element)
        {
            element.SetValue(KeyTipScopePropertyKey, scopedParent);
        }
 
        private static DependencyObject FindScope(DependencyObject element)
        {
            return FindScope(element, true);
        }
 
        private static DependencyObject FindScope(DependencyObject element, bool searchVisualTreeOnly)
        {
            FrameworkElement fe = element as FrameworkElement;
            if (fe == null)
            {
                FrameworkContentElement fce = element as FrameworkContentElement;
                if (fce == null || !fce.IsLoaded)
                {
                    return null;
                }
            }
            else if (!fe.IsLoaded)
            {
                return null;
            }
 
            // If an ancestor has IsKeyTipScope set to true or
            // if it does not have niether logical nor visual parent
            // then it is considered to be a scope.
            if (searchVisualTreeOnly)
            {
                return TreeHelper.FindVisualAncestor(element,
                    delegate(DependencyObject d)
                    {
                        return ((KeyTipService.GetIsKeyTipScope(d) == true) || (TreeHelper.GetParent(d) == null));
                    });
            }
            else
            {
                return TreeHelper.FindAncestor(element, delegate(DependencyObject d)
                {
                    return ((KeyTipService.GetIsKeyTipScope(d) == true) || (TreeHelper.GetParent(d) == null));
                });
            }
        }
 
        #endregion
 
        #region Custom KeyTip Siblings
 
        internal static IEnumerable<DependencyObject> GetCustomSiblingKeyTipElements(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (IEnumerable<DependencyObject>)element.GetValue(CustomSiblingKeyTipElementsProperty);
        }
 
        internal static void SetCustomSiblingKeyTipElements(DependencyObject element, IEnumerable<DependencyObject> value)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(CustomSiblingKeyTipElementsProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for CustomSiblingKeyTipElements.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty CustomSiblingKeyTipElementsProperty =
            DependencyProperty.RegisterAttached("CustomSiblingKeyTipElements",
                typeof(IEnumerable<DependencyObject>),
                typeof(KeyTipService),
                new UIPropertyMetadata(null, new PropertyChangedCallback(OnCustomSiblingKeyTipElementsChanged)));
 
        private static void OnCustomSiblingKeyTipElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                RegisterKeyTipElement(d);
            }
            else
            {
                if (CanUnregisterKeyTipElement(d))
                {
                    UnregisterKeyTipElement(d);
                }
            }
        }
 
        #endregion
 
        #region KeyTip AutoGeneration
 
        internal static IEnumerable<DependencyObject> GetKeyTipAutoGenerationElements(DependencyObject obj)
        {
            return (IEnumerable<DependencyObject>)obj.GetValue(KeyTipAutoGenerationElementsProperty);
        }
 
        internal static void SetKeyTipAutoGenerationElements(DependencyObject obj, IEnumerable<DependencyObject> value)
        {
            obj.SetValue(KeyTipAutoGenerationElementsProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for KeyTipAutoGenerationElements.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty KeyTipAutoGenerationElementsProperty =
            DependencyProperty.RegisterAttached("KeyTipAutoGenerationElements",
                typeof(IEnumerable<DependencyObject>),
                typeof(KeyTipService),
                new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnKeyTipAutoGenerationElementsChanged)));
 
        private static void OnKeyTipAutoGenerationElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                // Register for scoping
                RegisterKeyTipElement(d);
            }
            else
            {
                if (CanUnregisterKeyTipElement(d))
                {
                    // Unregister from scope.
                    UnregisterKeyTipElement(d);
                }
            }
        }
 
        /// <summary>
        ///     Helper method which determines if the keytip
        ///     was auto generated or not.
        /// </summary>
        private static bool IsAutoGeneratedKeyTip(string keyTip)
        {
            int length = keyTip.Length;
            if (length == 0)
            {
                return false;
            }
 
            if (length == 1)
            {
                int keyTipValue = 0;
                if (Int32.TryParse(keyTip, out keyTipValue))
                {
                    if (keyTipValue > 0 && keyTipValue <= NonZeroDigitCount)
                    {
                        return true;
                    }
                }
            }
            else
            {
                string keyTipEnd = keyTip.TrimStart('0');
                if (keyTipEnd != null && keyTipEnd.Length == 1)
                {
                    return true;
                }
            }
 
            return false;
        }
 
        /// <summary>
        ///     Helper method to return next keytip string
        ///     in the auto generation sequence.
        /// </summary>
        private string GetNextAutoGenerationKeyTip(DependencyObject targetAutoGenerationScope)
        {
            // The first nine are 1 to 9
            CultureInfo culture = GetCultureForElement(targetAutoGenerationScope);
            if (string.IsNullOrEmpty(_autoGenerationKeyTipPrefix))
            {
                if (_nextAutoGenerationIndex < NonZeroDigitCount)
                {
                    _nextAutoGenerationIndex++;
                    return _nextAutoGenerationIndex.ToString(culture);
                }
                else
                {
                    _nextAutoGenerationIndex = 0;
                    _autoGenerationKeyTipPrefix = _nextAutoGenerationIndex.ToString(culture);
                }
            }
 
            if (_nextAutoGenerationIndex < NonZeroDigitCount)
            {
                // 9 to 1 precedeeded by appropriate repetitions of '0'
                return _autoGenerationKeyTipPrefix + (NonZeroDigitCount - _nextAutoGenerationIndex++).ToString(culture);
            }
            else
            {
                // A to Z preceeded by appropriate repetitions of '0'
                int alphaIndex = _nextAutoGenerationIndex++ - 9;
                if (alphaIndex < QatKeyTipCharacters.Length)
                {
                    return _autoGenerationKeyTipPrefix + QatKeyTipCharacters[alphaIndex];
                }
 
                _nextAutoGenerationIndex = 1;
 
                int i = 0;
                _autoGenerationKeyTipPrefix += i.ToString(culture);
                i = 9;
                return _autoGenerationKeyTipPrefix + i.ToString(culture);
            }
        }
 
        /// <summary>
        ///     Method which generates keytips for elements provided by
        ///     KeyTipAutoGenerationElements enumerables in current scope.
        /// </summary>
        private void AutoGenerateKeyTips(WeakHashSet<DependencyObject> currentScopeElements)
        {
            if (currentScopeElements != null && currentScopeElements.Count > 0)
            {
                bool reprocessScoping = false;
                foreach (DependencyObject element in currentScopeElements)
                {
                    IEnumerable<DependencyObject> keyTipAutoGenerationElements = GetKeyTipAutoGenerationElements(element);
                    if (keyTipAutoGenerationElements != null)
                    {
                        foreach (DependencyObject autoGenerationElement in keyTipAutoGenerationElements)
                        {
                            if (!_keyTipAutoGeneratedElements.Contains(autoGenerationElement))
                            {
                                reprocessScoping = true;
                                SetKeyTip(autoGenerationElement, GetNextAutoGenerationKeyTip(element));
                                _keyTipAutoGeneratedElements.Add(autoGenerationElement);
                            }
                        }
                    }
                }
                if (reprocessScoping)
                {
                    ProcessScoping();
                }
            }
        }
 
        private void ResetAutoGeneratedKeyTips()
        {
            if (_keyTipAutoGeneratedElements != null && _keyTipAutoGeneratedElements.Count > 0)
            {
                foreach (DependencyObject d in _keyTipAutoGeneratedElements)
                {
                    string keyTip = GetKeyTip(d);
                    if (!string.IsNullOrEmpty(keyTip) &&
                        IsAutoGeneratedKeyTip(keyTip))
                    {
                        d.ClearValue(KeyTipProperty);
                    }
                }
            }
        }
 
        #endregion
 
        #region Input
 
        internal enum KeyTipState
        {
            None,
            Pending,
            Enabled
        }
 
        internal KeyTipState State
        {
            get;
            private set;
        }
 
        private static bool IsKeyTipKey(KeyEventArgs e)
        {
            return ((e.Key == Key.System) &&
                (e.SystemKey == Key.RightAlt ||
                    e.SystemKey == Key.LeftAlt ||
                    e.SystemKey == Key.F10) &&
                (Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift &&
                (Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control);
        }
 
        private static bool IsKeyTipClosingKey(KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Space:
                case Key.Enter:
                case Key.Left:
                case Key.Right:
                case Key.Up:
                case Key.Down:
                case Key.PageDown:
                case Key.PageUp:
                case Key.Home:
                case Key.End:
                case Key.Tab:
                    return true;
 
                case Key.System:
                    {
                        switch (e.SystemKey)
                        {
                            case Key.NumLock:
                            case Key.NumPad0:
                            case Key.NumPad1:
                            case Key.NumPad2:
                            case Key.NumPad3:
                            case Key.NumPad4:
                            case Key.NumPad5:
                            case Key.NumPad6:
                            case Key.NumPad7:
                            case Key.NumPad8:
                            case Key.NumPad9:
                            case Key.LeftShift:
                            case Key.RightShift:
                                return true;
                        }
                    }
                    break;
            }
            return false;
        }
 
        void PreProcessInput(object sender, PreProcessInputEventArgs e)
        {
            if (State != KeyTipState.None)
            {
                RoutedEvent routedEvent = e.StagingItem.Input.RoutedEvent;
                if (routedEvent == Keyboard.PreviewKeyUpEvent)
                {
                    KeyEventArgs keyArgs = (KeyEventArgs)e.StagingItem.Input;
                    if (IsKeyTipKey(keyArgs))
                    {
                        OnPreviewKeyTipKeyUp(keyArgs);
                    }
                }
                else if (routedEvent == Mouse.PreviewMouseDownEvent ||
                        routedEvent == Stylus.PreviewStylusDownEvent)
                        // || routedEvent == UIElement.PreviewTouchDownEvent)
                {
                    LeaveKeyTipMode(false);
                }
                else if (routedEvent == TextCompositionManager.PreviewTextInputEvent)
                {
                    OnPreviewTextInput((TextCompositionEventArgs)e.StagingItem.Input);
                }
                else if (routedEvent == Keyboard.PreviewKeyDownEvent)
                {
                    OnPreviewKeyDown((KeyEventArgs)e.StagingItem.Input);
                }
                else if (routedEvent == Mouse.PreviewMouseWheelEvent)
                {
                    e.StagingItem.Input.Handled = true;
                }
            }
        }
 
        void PostProcessInput(object sender, ProcessInputEventArgs e)
        {
            if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent)
            {
                KeyEventArgs keyArgs = (KeyEventArgs)e.StagingItem.Input;
                if (!keyArgs.Handled && IsKeyTipKey(keyArgs) && State == KeyTipState.None)
                {
                    OnKeyTipKeyDown(keyArgs);
                }
                else if (State != KeyTipState.None)
                {
                    if (keyArgs.Key == Key.Escape)
                    {
                        OnEscapeKeyDown(keyArgs);
                    }
                }
            }
        }
 
        private void OnPreviewKeyDown(KeyEventArgs keyArgs)
        {
            if (_modeEnterKey == Key.None &&
                _probableModeEnterKey == Key.None &&
                keyArgs.Key == Key.System &&
                (keyArgs.SystemKey == Key.LeftAlt || keyArgs.SystemKey == Key.RightAlt))
            {
                // set _probableModeEnterKey
                _probableModeEnterKey = keyArgs.SystemKey;
            }
            else if (IsKeyTipClosingKey(keyArgs))
            {
                LeaveKeyTipMode(false);
            }
        }
 
        private void OnKeyTipKeyDown(KeyEventArgs keyArgs)
        {
            // Tries to enter keytip mode and set various
            // members accordingly.
            DependencyObject globalScope = GetGlobalScopeForElement(keyArgs.OriginalSource as DependencyObject);
            if (globalScope != null)
            {
                if (EnterKeyTipMode(globalScope, (keyArgs.SystemKey == Key.F10 ? false : true), true))
                {
                    keyArgs.Handled = true;
                    _focusRibbonOnKeyTipKeyUp = true;
                    if (keyArgs.SystemKey != Key.F10)
                    {
                        _modeEnterKey = keyArgs.SystemKey;
                    }
                }
            }
        }
 
        private void OnPreviewKeyTipKeyUp(KeyEventArgs keyArgs)
        {
            if (State == KeyTipState.Pending &&
                ((_modeEnterKey == Key.None &&
                 keyArgs.SystemKey == Key.F10) ||
                 keyArgs.SystemKey == Key.LeftAlt ||
                 keyArgs.SystemKey == Key.RightAlt))
            {
                ShowKeyTips();
            }
            else if (_modeEnterKey == Key.None)
            {
                LeaveKeyTipMode();
                keyArgs.Handled = true;
            }
 
            if (_modeEnterKey == keyArgs.SystemKey)
            {
                _modeEnterKey = Key.None;
            }
 
            if (_probableModeEnterKey == keyArgs.SystemKey)
            {
                _probableModeEnterKey = Key.None;
            }
 
            if (_focusRibbonOnKeyTipKeyUp)
            {
                // Raise KeyTipEnterFocus event.
                if (State == KeyTipState.Enabled)
                {
                    PresentationSource targetSource = RibbonHelper.GetPresentationSourceFromVisual(_currentGlobalScope as Visual);
                    if (targetSource != null)
                    {
                        RaiseKeyTipFocusEvent(targetSource, EventArgs.Empty, _keyTipEnterFocusHandlers);
                    }
                }
                _focusRibbonOnKeyTipKeyUp = false;
            }
        }
 
        private void OnEscapeKeyDown(KeyEventArgs keyArgs)
        {
            if (IsAltPressed)
            {
                LeaveKeyTipMode();
            }
            else
            {
                PopKeyTipScope();
            }
            keyArgs.Handled = true;
        }
 
        private void OnPreviewTextInput(TextCompositionEventArgs textArgs)
        {
            string text = textArgs.Text;
            if (string.IsNullOrEmpty(text))
            {
                text = textArgs.SystemText;
            }
 
            if (string.IsNullOrEmpty(text))
            {
                return;
            }
 
            if (text == " ")
            {
                // Do nothing.
            }
            else
            {
                if (State == KeyTipState.Pending)
                {
                    ShowKeyTips();
                }
 
                if (_currentActiveKeyTipElements.Count > 0)
                {
                    if (_probableModeEnterKey != Key.None &&
                        _modeEnterKey == Key.None)
                    {
                        // When text is pressed, probableModeEnterKey becomes
                        // modeEnterKey. KeyTips are not dismissed on up of
                        // modeEnterKey for once. This is useful when multiple
                        // text is pressed with alt pressed.
                        _modeEnterKey = _probableModeEnterKey;
                        _probableModeEnterKey = Key.None;
                    }
 
                    textArgs.Handled = true;
                    text = _prefixText + text;
 
                    DependencyObject exactMatchElement = null;
                    List<DependencyObject> activeKeyTipElements = FindKeyTipMatches(text, out exactMatchElement);
                    if (exactMatchElement != null)
                    {
                        OnKeyTipExactMatch(exactMatchElement);
                    }
                    else
                    {
                        OnKeyTipPartialMatch(activeKeyTipElements, text);
                    }
                }
            }
        }
 
        private List<DependencyObject> FindKeyTipMatches(string text, out DependencyObject exactMatchElement)
        {
            exactMatchElement = null;
            List<DependencyObject> activeKeyTipElements = null;
            foreach (DependencyObject element in _currentActiveKeyTipElements)
            {
                string keyTip = GetKeyTip(element);
                CultureInfo culture = GetCultureForElement(element);
                if (string.Compare(keyTip, text, true, culture) == 0)
                {
                    exactMatchElement = element;
                    return null;
                }
                else if (keyTip.StartsWith(text, true, culture))
                {
                    if (activeKeyTipElements == null)
                    {
                        activeKeyTipElements = new List<DependencyObject>();
                    }
                    activeKeyTipElements.Add(element);
                }
            }
            return activeKeyTipElements;
        }
 
        internal static CultureInfo GetCultureForElement(DependencyObject element)
        {
            CultureInfo culture = CultureInfo.CurrentCulture;
            if (DependencyPropertyHelper.GetValueSource(element, FrameworkElement.LanguageProperty).BaseValueSource != BaseValueSource.Default)
            {
                XmlLanguage language = (XmlLanguage)element.GetValue(FrameworkElement.LanguageProperty);
                if (language != null && language != XmlLanguage.Empty)
                {
                    Dictionary<XmlLanguage, CultureInfo> cultureCache = Current._cultureCache;
                    if (cultureCache != null && cultureCache.ContainsKey(language))
                    {
                        culture = cultureCache[language];
                    }
                    else
                    {
                        CultureInfo computedCulture = RibbonHelper.GetCultureInfo(element);
                        if (computedCulture != null)
                        {
                            culture = computedCulture;
                            if (cultureCache == null)
                            {
                                Current._cultureCache = cultureCache = new Dictionary<XmlLanguage, CultureInfo>();
                            }
                            cultureCache[language] = culture;
                        }
                    }
                }
            }
            return culture;
        }
 
        private void OnKeyTipExactMatch(DependencyObject exactMatchElement)
        {
            if (!((bool)(exactMatchElement.GetValue(UIElement.IsEnabledProperty))))
            {
                RibbonHelper.Beep();
                return;
            }
 
            HideCurrentShowingKeyTips();
            _prefixText = string.Empty;
            KeyTipAccessedEventArgs eventArgs = new KeyTipAccessedEventArgs();
            eventArgs.RoutedEvent = PreviewKeyTipAccessedEvent;
            object oldFocusedElement = Keyboard.FocusedElement;
            IInputElement inputElement = exactMatchElement as IInputElement;
            if (inputElement != null)
            {
                inputElement.RaiseEvent(eventArgs);
                eventArgs.RoutedEvent = KeyTipAccessedEvent;
                inputElement.RaiseEvent(eventArgs);
            }
 
            // KeyTips might have been dismissed by one of the event handlers
            // hence check again.
            if (State != KeyTipState.None)
            {
                object newFocusedElement = Keyboard.FocusedElement;
                DependencyObject newScope = eventArgs.TargetKeyTipScope;
                if (newScope != null &&
                    !KeyTipService.GetIsKeyTipScope(newScope) &&
                    newScope != _currentGlobalScope)
                {
                    throw new InvalidOperationException(Microsoft.Windows.Controls.SR.ElementNotKeyTipScope);
                }
                if (newScope == null &&
                    KeyTipService.GetIsKeyTipScope(exactMatchElement))
                {
                    newScope = exactMatchElement;
                }
 
                if (newScope != null)
                {
                    // Show keytips for new scope in a dispatcher operation.
                    Dispatcher.CurrentDispatcher.BeginInvoke(
                        (Action)delegate()
                        {
                            ShowKeyTipsForScope(newScope, true);
                        },
                        DispatcherPriority.Loaded);
                }
                else
                {
                    LeaveKeyTipMode(oldFocusedElement == newFocusedElement /*restoreFocus*/);
                }
            }
        }
 
        private void OnKeyTipPartialMatch(List<DependencyObject> activeKeyTipElements, string text)
        {
            if (activeKeyTipElements == null ||
                activeKeyTipElements.Count == 0)
            {
                // Beep when there are no matches.
                RibbonHelper.Beep();
                return;
            }
 
            int currentActiveCount = _currentActiveKeyTipElements.Count;
            int newActiveCount = activeKeyTipElements.Count;
            int newActiveElementIndex = 0;
            DependencyObject newActiveElement = activeKeyTipElements[newActiveElementIndex++];
 
            // Hide keytips for all the elements which do not
            // match with the new prefix.
            for (int i = 0; i < currentActiveCount; i++)
            {
                DependencyObject currentActiveElement = _currentActiveKeyTipElements[i];
                if (currentActiveElement == newActiveElement)
                {
                    if (newActiveElementIndex < newActiveCount)
                    {
                        newActiveElement = activeKeyTipElements[newActiveElementIndex++];
                    }
                    else
                    {
                        newActiveElement = null;
                    }
                }
                else
                {
                    HideKeyTipForElement(currentActiveElement);
                }
            }
            _currentActiveKeyTipElements = activeKeyTipElements;
            _prefixText = text;
        }
 
        private void OnWindowDeactivated(object sender, EventArgs e)
        {
            LeaveKeyTipMode();
        }
 
        private void OnWindowLocationChanged(object sender, EventArgs e)
        {
            LeaveKeyTipMode();
        }
 
        private void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
        {
            LeaveKeyTipMode();
        }
 
        private DependencyObject GetGlobalScopeForElement(DependencyObject element)
        {
            if (element == null)
            {
                return null;
            }
 
            if (_scopeToElementMap.Count == 0 && _toBeScopedElements.Count == 0 && _unprocessedScopes.Count == 0)
            {
                return null;
            }
 
            return TreeHelper.FindRoot(element);
        }
 
        private bool EnterKeyTipMode(DependencyObject scope, bool showKeyTips, bool showAsync)
        {
            ProcessScoping();
 
            if (!_scopeToElementMap.ContainsKey(scope))
            {
                return false;
            }
 
            _currentGlobalScope = scope;
            _currentWindow = Window.GetWindow(scope);
            if (_currentWindow != null)
            {
                _currentWindow.Deactivated += new EventHandler(OnWindowDeactivated);
                _currentWindow.SizeChanged += new SizeChangedEventHandler(OnWindowSizeChanged);
                _currentWindow.LocationChanged += new EventHandler(OnWindowLocationChanged);
            }
            State = KeyTipState.Pending;
 
            if (showKeyTips)
            {
                if (showAsync)
                {
                    StartShowKeyTipsTimer();
                }
                else
                {
                    ShowKeyTips();
                }
            }
 
            return true;
        }
 
        private void StartShowKeyTipsTimer()
        {
            if (_showKeyTipsTimer == null)
            {
                _showKeyTipsTimer = new DispatcherTimer(DispatcherPriority.Normal);
                _showKeyTipsTimer.Interval = TimeSpan.FromMilliseconds(ShowKeyTipsWaitTime);
                _showKeyTipsTimer.Tick += delegate(object sender, EventArgs e) { ShowKeyTips(); };
            }
            _showKeyTipsTimer.Start();
        }
 
        private void ShowKeyTips()
        {
            if (_showKeyTipsTimer != null)
            {
                _showKeyTipsTimer.Stop();
            }
            if (State == KeyTipState.Pending)
            {
                Debug.Assert(_currentGlobalScope != null);
                if (ShowKeyTipsForScope(_currentGlobalScope))
                {
                    // KeyTips might have been dismissed during
                    // show, hence check again.
                    if (State == KeyTipState.Pending)
                    {
                        State = KeyTipState.Enabled;
                    }
                }
                else
                {
                    LeaveKeyTipMode(true);
                }
            }
        }
 
        private bool ShowKeyTipsForScope(DependencyObject scopeElement)
        {
            return ShowKeyTipsForScope(scopeElement, false);
        }
 
        private bool ShowKeyTipsForScope(DependencyObject scopeElement, bool pushOnEmpty)
        {
            bool returnValue = false;
            ProcessScoping();
            if (_scopeToElementMap.ContainsKey(scopeElement))
            {
                WeakHashSet<DependencyObject> elementSet = _scopeToElementMap[scopeElement];
                AutoGenerateKeyTips(elementSet);
                if (elementSet != null && elementSet.Count > 0)
                {
                    foreach (DependencyObject element in elementSet)
                    {
                        returnValue |= ShowKeyTipForElement(element);
                    }
                }
 
                // KeyTips might have been dismissed during
                // show, hence check again.
                if (State != KeyTipState.None)
                {
                    if (_scopeStack.Count == 0 ||
                        _scopeStack.Peek() != scopeElement)
                    {
                        _scopeStack.Push(scopeElement);
                    }
                }
            }
            else if (pushOnEmpty)
            {
                // Push the scope even if it is empty.
                // Used for any non-global scope.
                if (_scopeStack.Count == 0 ||
                    _scopeStack.Peek() != scopeElement)
                {
                    _scopeStack.Push(scopeElement);
                }
            }
            return returnValue;
        }
 
        private void PopKeyTipScope()
        {
            Debug.Assert(_scopeStack.Count > 0);
            DependencyObject currentScope = _scopeStack.Pop();
            DependencyObject parentScope = FindScope(currentScope, false);
            DependencyObject stackParentScope = (_scopeStack.Count > 0 ? _scopeStack.Peek() : null);
            if (stackParentScope != null &&
                parentScope != null &&
                RibbonHelper.IsAncestorOf(stackParentScope, parentScope))
            {
                // If there is any intermediate ancestral scope between current scope
                // and the next scope in stack, then push it onto stack.
                _scopeStack.Push(parentScope);
            }
            HideCurrentShowingKeyTips();
            _prefixText = string.Empty;
 
            if (_scopeStack.Count > 0)
            {
                // Dispatcher operation to show keytips for topmost
                // scope on the stack.
                Dispatcher.CurrentDispatcher.BeginInvoke(
                    (Action)delegate()
                    {
                        if (_scopeStack.Count > 0)
                        {
                            ShowKeyTipsForScope(_scopeStack.Peek(), true);
                        }
                    },
                    DispatcherPriority.Loaded);
            }
            else
            {
                LeaveKeyTipMode();
            }
        }
 
        private void HideCurrentShowingKeyTips()
        {
            foreach (DependencyObject element in _currentActiveKeyTipElements)
            {
                HideKeyTipForElement(element);
            }
            _currentActiveKeyTipElements.Clear();
        }
 
        private void Reset()
        {
            _keyTipAutoGeneratedElements.Clear();
            _autoGenerationKeyTipPrefix = string.Empty;
            _nextAutoGenerationIndex = 0;
            _cultureCache = null;
            _cachedKeyTipControls = null;
            _currentGlobalScope = null;
            if (_currentWindow != null)
            {
                _currentWindow.Deactivated -= new EventHandler(OnWindowDeactivated);
                _currentWindow.SizeChanged -= new SizeChangedEventHandler(OnWindowSizeChanged);
                _currentWindow.LocationChanged -= new EventHandler(OnWindowLocationChanged);
            }
            _currentWindow = null;
            if (_showKeyTipsTimer != null)
            {
                _showKeyTipsTimer.Stop();
            }
            _focusRibbonOnKeyTipKeyUp = false;
            _modeEnterKey = Key.None;
            _probableModeEnterKey = Key.None;
            _prefixText = string.Empty;
            _currentActiveKeyTipElements.Clear();
            _scopeStack.Clear();
            State = KeyTipState.None;
        }
 
        private void LeaveKeyTipMode()
        {
            LeaveKeyTipMode(true);
        }
 
        private void LeaveKeyTipMode(bool restoreFocus)
        {
            HideCurrentShowingKeyTips();
            ResetAutoGeneratedKeyTips();
            if (restoreFocus)
            {
                PresentationSource targetSource = RibbonHelper.GetPresentationSourceFromVisual(_currentGlobalScope as Visual);
                if (targetSource != null)
                {
                    RaiseKeyTipFocusEvent(targetSource, EventArgs.Empty, _keyTipExitRestoreFocusHandlers);
                }
            }
            Reset();
        }
 
        private static bool IsAltPressed
        {
            get
            {
                return ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt);
            }
        }
 
        #endregion
 
        #region KeyTip Creation
 
        internal static bool GetCanClipKeyTip(DependencyObject element)
        {
            ArgumentNullException.ThrowIfNull(element);
            return (bool)element.GetValue(CanClipKeyTipProperty);
        }
 
        internal static void SetCanClipKeyTip(DependencyObject element, bool value)
        {
            ArgumentNullException.ThrowIfNull(element);
            element.SetValue(CanClipKeyTipProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for CanClipKeyTip.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty CanClipKeyTipProperty =
            DependencyProperty.RegisterAttached("CanClipKeyTip", typeof(bool), typeof(KeyTipService), new UIPropertyMetadata(true));
 
        /// <summary>
        ///     Returns the adorner layer to be used
        ///     for keytips. The implementation is similar
        ///     to that of AdornerLayer's except we ignore
        ///     AdornerLayer of ScrollContentPresenter if its
        ///     ScrollViewer has CanClipKeyTip set to false.
        /// </summary>
        static private AdornerLayer GetAdornerLayer(Visual visual, out bool isScrollAdornerLayer)
        {
            ArgumentNullException.ThrowIfNull(visual);
 
            isScrollAdornerLayer = false;
            Visual parent = VisualTreeHelper.GetParent(visual) as Visual;
            ScrollContentPresenter lastScp = null;
 
            while (parent != null)
            {
                AdornerDecorator adornerDecorator = parent as AdornerDecorator;
                if (adornerDecorator != null)
                {
                    return adornerDecorator.AdornerLayer;
                }
 
                ScrollContentPresenter scp = parent as ScrollContentPresenter;
                if (scp != null)
                {
                    lastScp = scp;
                }
 
                ScrollViewer sv = parent as ScrollViewer;
                if (sv != null && lastScp != null)
                {
                    if (GetCanClipKeyTip(sv))
                    {
                        isScrollAdornerLayer = true;
                        return lastScp.AdornerLayer;
                    }
                    lastScp = null;
                }
 
                parent = VisualTreeHelper.GetParent(parent) as Visual;
            }
 
            return null;
        }
 
        private static readonly DependencyProperty KeyTipAdornerProperty =
            DependencyProperty.RegisterAttached("KeyTipAdorner", typeof(KeyTipAdorner), typeof(KeyTipService), new UIPropertyMetadata(null));
 
        private static readonly DependencyProperty KeyTipAdornerHolderProperty =
            DependencyProperty.RegisterAttached("KeyTipAdornerHolder", typeof(UIElement), typeof(KeyTipService), new UIPropertyMetadata(null));
 
        private static readonly DependencyProperty ShowingKeyTipProperty =
            DependencyProperty.RegisterAttached("ShowingKeyTip", typeof(bool), typeof(KeyTipService),
                new UIPropertyMetadata(false, new PropertyChangedCallback(OnShowingKeyTipChanged)));
 
        private static void OnShowingKeyTipChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                UIElement uie = RibbonHelper.GetContainingUIElement(element);
                if (uie != null &&
                    uie.Visibility != Visibility.Visible)
                {
                    // revert the value if the element is not visible
                    element.SetValue(ShowingKeyTipProperty, false);
                    return;
                }
 
                // Raise the ActivatingKeyTip event.
                ActivatingKeyTipEventArgs activatingEventArgs = new ActivatingKeyTipEventArgs();
                IInputElement inputElement = element as IInputElement;
                if (inputElement != null)
                {
                    inputElement.RaiseEvent(activatingEventArgs);
                }
 
                // KeyTips could have been dismissed due to one
                // of the event handler, hence check again.
                KeyTipService current = Current;
                if (current.State != KeyTipState.None)
                {
                    if (activatingEventArgs.KeyTipVisibility == Visibility.Visible)
                    {
                        // Create the keytip and add it as the adorner.
                        UIElement adornedElement = RibbonHelper.GetContainingUIElement(activatingEventArgs.PlacementTarget == null ? element : activatingEventArgs.PlacementTarget);
                        if (adornedElement != null && adornedElement.IsVisible)
                        {
                            bool isScrollAdornerLayer = false;
                            AdornerLayer adornerLayer = GetAdornerLayer(adornedElement, out isScrollAdornerLayer);
                            if (adornerLayer != null)
                            {
                                KeyTipAdorner adorner = new KeyTipAdorner(adornedElement,
                                    activatingEventArgs.PlacementTarget,
                                    activatingEventArgs.KeyTipHorizontalPlacement,
                                    activatingEventArgs.KeyTipVerticalPlacement,
                                    activatingEventArgs.KeyTipHorizontalOffset,
                                    activatingEventArgs.KeyTipVerticalOffset,
                                    activatingEventArgs.Handled ? null : activatingEventArgs.OwnerRibbonGroup);
                                LinkKeyTipControlToAdorner(adorner, element);
                                adornerLayer.Add(adorner);
                                element.SetValue(KeyTipAdornerProperty, adorner);
                                element.SetValue(KeyTipAdornerHolderProperty, adornedElement);
 
                                // Begin invode an operation to nudge all the keytips into the
                                // adorner layer boundary unless the layer belongs to a scroll viewer.
                                if (!isScrollAdornerLayer)
                                {
                                    current.EnqueueAdornerLayerForPlacementProcessing(adornerLayer);
                                }
                            }
                        }
                    }
 
                    if (activatingEventArgs.KeyTipVisibility != Visibility.Collapsed)
                    {
                        // add the element to currentActiveKeyTipElement list.
                        current._currentActiveKeyTipElements.Add(element);
                    }
                    else
                    {
                        // Revert the value if it is asked by event handlers
                        // (i.e, by setting KeyTipVisibility to collapsed.
                        element.SetValue(ShowingKeyTipProperty, false);
                    }
                }
                else
                {
                    // Revert the value if we already dismissed keytips.
                    element.SetValue(ShowingKeyTipProperty, false);
                }
            }
            else
            {
                // Remove keytip from adorner.
                KeyTipAdorner adorner = (KeyTipAdorner)element.GetValue(KeyTipAdornerProperty);
                UIElement adornedElement = (UIElement)element.GetValue(KeyTipAdornerHolderProperty);
                if (adornedElement != null && adorner != null)
                {
                    UnlinkKeyTipControlFromAdorner(adorner);
                    bool isScrollAdornerLayer = false;
                    AdornerLayer adornerLayer = GetAdornerLayer(adornedElement, out isScrollAdornerLayer);
                    if (adornerLayer != null)
                    {
                        adornerLayer.Remove(adorner);
                    }
                }
                element.ClearValue(KeyTipAdornerProperty);
                element.ClearValue(KeyTipAdornerHolderProperty);
            }
        }
 
        private void EnqueueAdornerLayerForPlacementProcessing(AdornerLayer adornerLayer)
        {
            if (!_placementProcessingAdornerLayers.Contains(adornerLayer))
            {
                _placementProcessingAdornerLayers.Add(adornerLayer);
            }
 
            if (!_adornerLayerPlacementProcessingQueued)
            {
                _adornerLayerPlacementProcessingQueued = true;
                adornerLayer.Dispatcher.BeginInvoke(
                    (Action)delegate()
                    {
                        // Iterate through all the enqueued adorner layers and nudge all
                        // the keytip adorners belonging to them if needed.
                        foreach (AdornerLayer currentAdornerLayer in _placementProcessingAdornerLayers)
                        {
                            foreach (object child in LogicalTreeHelper.GetChildren(currentAdornerLayer))
                            {
                                KeyTipAdorner keyTipAdorner = child as KeyTipAdorner;
                                if (keyTipAdorner != null)
                                {
                                    keyTipAdorner.NudgeIntoAdornerLayerBoundary(currentAdornerLayer);
                                }
                            }
                        }
                        _placementProcessingAdornerLayers.Clear();
                        _adornerLayerPlacementProcessingQueued = false;
                    },
                    DispatcherPriority.Input,
                    null);
            }
        }
 
        private bool ShowKeyTipForElement(DependencyObject element)
        {
            if (State == KeyTipState.None)
            {
                return false;
            }
 
            bool returnValue = false;
            if (!string.IsNullOrEmpty(GetKeyTip(element)))
            {
                // Show keytip on the element if it has one.
                returnValue = true;
                element.SetValue(ShowingKeyTipProperty, true);
            }
 
            // If the element can provide custome keytip siblings
            // then show keytips on all such siblings.
            IEnumerable<DependencyObject> customSiblings = GetCustomSiblingKeyTipElements(element);
            if (customSiblings != null)
            {
                foreach (DependencyObject siblingElement in customSiblings)
                {
                    returnValue |= ShowKeyTipForElement(siblingElement);
                }
            }
            return returnValue;
        }
 
        private static void HideKeyTipForElement(DependencyObject element)
        {
            element.ClearValue(ShowingKeyTipProperty);
        }
 
        private static void LinkKeyTipControlToAdorner(KeyTipAdorner adorner, DependencyObject keyTipElement)
        {
            KeyTipService current = Current;
            KeyTipControl keyTipControl = null;
            if (current._cachedKeyTipControls != null &&
                current._cachedKeyTipControls.Count > 0)
            {
                // Retrieve a KeyTipControl from cache for reuse.
                int count = current._cachedKeyTipControls.Count;
                keyTipControl = current._cachedKeyTipControls[count - 1];
                current._cachedKeyTipControls.RemoveAt(count - 1);
            }
            if (keyTipControl == null)
            {
                keyTipControl = new KeyTipControl();
            }
            adorner.LinkKeyTipControl(keyTipElement, keyTipControl);
        }
 
        private static void UnlinkKeyTipControlFromAdorner(KeyTipAdorner adorner)
        {
            KeyTipControl keyTipControl = adorner.KeyTipControl;
            adorner.UnlinkKeyTipControl();
            if (keyTipControl != null)
            {
                // Save the unlinked KeyTipControl to cache for reuse.
                KeyTipService current = Current;
                if (current._cachedKeyTipControls == null)
                {
                    current._cachedKeyTipControls = new List<KeyTipControl>();
                }
                current._cachedKeyTipControls.Add(keyTipControl);
            }
        }
 
        #endregion
 
        #region Events
 
        public static readonly RoutedEvent ActivatingKeyTipEvent = EventManager.RegisterRoutedEvent("ActivatingKeyTip", RoutingStrategy.Bubble, typeof(ActivatingKeyTipEventHandler), typeof(KeyTipService));
 
        public static void AddActivatingKeyTipHandler(DependencyObject element, ActivatingKeyTipEventHandler handler)
        {
            RibbonHelper.AddHandler(element, ActivatingKeyTipEvent, handler);
        }
 
        public static void RemoveActivatingKeyTipHandler(DependencyObject element, ActivatingKeyTipEventHandler handler)
        {
            RibbonHelper.RemoveHandler(element, ActivatingKeyTipEvent, handler);
        }
 
        public static readonly RoutedEvent PreviewKeyTipAccessedEvent = EventManager.RegisterRoutedEvent("PreviewKeyTipAccessed", RoutingStrategy.Tunnel, typeof(KeyTipAccessedEventHandler), typeof(KeyTipService));
 
        public static void AddPreviewKeyTipAccessedHandler(DependencyObject element, KeyTipAccessedEventHandler handler)
        {
            RibbonHelper.AddHandler(element, PreviewKeyTipAccessedEvent, handler);
        }
 
        public static void RemovePreviewKeyTipAccessedHandler(DependencyObject element, KeyTipAccessedEventHandler handler)
        {
            RibbonHelper.RemoveHandler(element, PreviewKeyTipAccessedEvent, handler);
        }
 
        public static readonly RoutedEvent KeyTipAccessedEvent = EventManager.RegisterRoutedEvent("KeyTipAccessed", RoutingStrategy.Bubble, typeof(KeyTipAccessedEventHandler), typeof(KeyTipService));
 
        public static void AddKeyTipAccessedHandler(DependencyObject element, KeyTipAccessedEventHandler handler)
        {
            RibbonHelper.AddHandler(element, KeyTipAccessedEvent, handler);
        }
 
        public static void RemoveKeyTipAccessedHandler(DependencyObject element, KeyTipAccessedEventHandler handler)
        {
            RibbonHelper.RemoveHandler(element, KeyTipAccessedEvent, handler);
        }
 
        #endregion
 
        #region Focus Events
 
        internal delegate bool KeyTipFocusEventHandler(object sender, EventArgs e);
 
        /// <summary>
        ///     Event used to notify ribbon to obtain focus
        ///     while entering keytip mode.
        /// </summary>
        internal event KeyTipFocusEventHandler KeyTipEnterFocus
        {
            add
            {
                AddKeyTipFocusEventHandler(value, ref _keyTipEnterFocusHandlers);
            }
            remove
            {
                RemoveKeyTipFocusEventHandler(value, _keyTipEnterFocusHandlers);
            }
        }
 
        /// <summary>
        ///     Event used to notify ribbon to restore focus
        ///     while exiting keytip mode.
        /// </summary>
        internal event KeyTipFocusEventHandler KeyTipExitRestoreFocus
        {
            add
            {
                AddKeyTipFocusEventHandler(value, ref _keyTipExitRestoreFocusHandlers);
            }
            remove
            {
                RemoveKeyTipFocusEventHandler(value, _keyTipExitRestoreFocusHandlers);
            }
        }
 
        /// <summary>
        ///     Helper method to add weakhandler to the list
        /// </summary>
        private static void AddKeyTipFocusEventHandler(KeyTipFocusEventHandler handler, ref List<WeakReference> handlerList)
        {
            if (handlerList == null)
                handlerList = new List<WeakReference>(1);
 
            lock (handlerList)
            {
                // Cleanup the list in case some of the weakEnterMenuModeHandlers is disposed
                for (int i = 0; i < handlerList.Count; i++)
                {
                    if (!handlerList[i].IsAlive)
                    {
                        handlerList.RemoveAt(i);
                        i--;
                    }
                }
                handlerList.Add(new WeakReference(handler));
            }
        }
 
        /// <summary>
        ///     Helper method to remove weakhandler from the list.
        /// </summary>
        private static void RemoveKeyTipFocusEventHandler(KeyTipFocusEventHandler handler, List<WeakReference> handlerList)
        {
            if (handlerList != null)
            {
                lock (handlerList)
                {
                    for (int i = 0; i < handlerList.Count; i++)
                    {
                        KeyTipFocusEventHandler current = handlerList[i].Target as KeyTipFocusEventHandler;
 
                        if (current == null || current == handler)
                        {
                            handlerList.RemoveAt(i);
                            i--;
                        }
                    }
                }
            }
        }
 
        /// <summary>
        ///     Helper method to iterate through list of weak
        ///     event handlers and call them until the event is
        ///     handled.
        /// </summary>
        private static void RaiseKeyTipFocusEvent(object sender, EventArgs e, List<WeakReference> handlerList)
        {
            if (handlerList != null)
            {
                lock (handlerList)
                {
                    for (int i = 0; i < handlerList.Count; i++)
                    {
                        KeyTipFocusEventHandler current = handlerList[i].Target as KeyTipFocusEventHandler;
 
                        if (current == null)
                        {
                            handlerList.RemoveAt(i);
                            i--;
                        }
                        else
                        {
                            if (current(sender, e))
                            {
                                return;
                            }
                        }
                    }
                }
            }
        }
 
        #endregion
 
        #region Public Methods
 
        public static void DismissKeyTips()
        {
            Current.LeaveKeyTipMode(false);
        }
 
        #endregion
 
        #region Instance
 
        internal static KeyTipService Current
        {
            get
            {
                if (_current == null)
                    _current = new KeyTipService();
                return _current;
            }
        }
 
        [ThreadStatic]
        private static KeyTipService _current;
 
        #endregion
 
        #region Private Data
 
        // Data members which exist across multiple keytip life times.
        WeakHashSet<DependencyObject> _toBeScopedElements = new WeakHashSet<DependencyObject>(); // List of one-off elements to be scoped.
        WeakDictionary<DependencyObject, bool> _unprocessedScopes = new WeakDictionary<DependencyObject, bool>(); // List of scopes which are to processed
        WeakDictionary<DependencyObject, WeakHashSet<DependencyObject>> _scopeToElementMap = new WeakDictionary<DependencyObject, WeakHashSet<DependencyObject>>();
        private List<WeakReference> _keyTipEnterFocusHandlers = null;
        private List<WeakReference> _keyTipExitRestoreFocusHandlers = null;
        private bool _adornerLayerPlacementProcessingQueued = false;
        private List<AdornerLayer> _placementProcessingAdornerLayers = new List<AdornerLayer>();
 
        // Data members which exist with in one keytip life time.
        private DependencyObject _currentGlobalScope = null;
        private Window _currentWindow = null;
        private DispatcherTimer _showKeyTipsTimer = null;
        private bool _focusRibbonOnKeyTipKeyUp = false;
        private Key _modeEnterKey = Key.None; // Next keyup of this key will not dimiss keytips.
        private Key _probableModeEnterKey = Key.None; // Key which is probable to become _modeEnterKey.
        private string _prefixText = string.Empty;
        private List<DependencyObject> _currentActiveKeyTipElements = new List<DependencyObject>(); // List of elements whose keytips are active.
        private Stack<DependencyObject> _scopeStack = new Stack<DependencyObject>();
        private List<KeyTipControl> _cachedKeyTipControls = null; // cache of keytip controls for re-use.
        private Dictionary<XmlLanguage, CultureInfo> _cultureCache = null; // cache of culture infos.
        private WeakHashSet<DependencyObject> _keyTipAutoGeneratedElements = new WeakHashSet<DependencyObject>();
        private int _nextAutoGenerationIndex = 0;
        private string _autoGenerationKeyTipPrefix = string.Empty;
 
        private const int ShowKeyTipsWaitTime = 500;
        private const int NonZeroDigitCount = 9;
 
        private static String QatKeyTipCharacters = Microsoft.Windows.Controls.SR.QATKeyTipCharacters;
 
        #endregion
    }
}