File: Core\CSharp\System\Windows\Input\Command\CommandDevice.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.Windows;
using System.Windows.Media;
using System.Security;
using System.Security.Permissions;
using MS.Internal;
using MS.Internal.PresentationCore;                        // SecurityHelper
using MS.Win32; // VK translation.
 
namespace System.Windows.Input
{
    /// <summary>
    /// The CommandDevice class represents the mouse device to the
    /// members of a context.
    /// </summary>
    internal sealed class CommandDevice : InputDevice
    {
        /// <SecurityNote>
        /// Critical - This code stores a reference to inputmanager which is critical data
        /// TreatAsSafe: This constructor handles critical data but does not expose it
        ///             It stores instance but there are demands on the instances.
        /// </SecurityNote>
        [SecurityCritical,SecurityTreatAsSafe]
        internal CommandDevice( InputManager inputManager )
        {
            _inputManager = new SecurityCriticalData<InputManager>(inputManager);
            _inputManager.Value.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput);
            _inputManager.Value.PostProcessInput += new ProcessInputEventHandler(PostProcessInput);
        }
 
        /// <summary>
        /// Returns the element that input from this device is sent to.
        /// </summary>
        public override IInputElement Target
        {
            get
            {
                VerifyAccess();
                return Keyboard.FocusedElement;
            }
        }
 
        /// <summary>
        /// Returns the PresentationSource that is reporting input for this device.
        /// </summary>
        ///<SecurityNote>
        ///     Critical - critical hands out presentation source. 
        ///     TreatAsSafe - Right now presentation source is null. 
        ///     However there's a demand in place as a defense-in-depth measure ( just in case someone implements this). 
        ///</SecurityNote> 
        public override PresentationSource ActiveSource
        {
            [SecurityCritical, SecurityTreatAsSafe ]
            get
            {  
                SecurityHelper.DemandUnrestrictedUIPermission();
                
                return null;
            }
        }
 
        ///<SecurityNote>
        /// Critical: accesses e.StagingItem.Input
        ///</SecurityNote>
        [SecurityCritical]
        private void PreProcessInput( object sender, PreProcessInputEventArgs e )
        {
            InputReportEventArgs input = e.StagingItem.Input as InputReportEventArgs;
            if (input != null)
            {
                if (input.Report.Type == InputType.Command)
                {
                    RawAppCommandInputReport rawAppCommandInputReport = input.Report as RawAppCommandInputReport;
                    if (rawAppCommandInputReport != null)
                    {
                        // Claim the input for the Command.
                        input.Device = this;
 
                        // Set the proper source
                        input.Source = GetSourceFromDevice(rawAppCommandInputReport.Device);
                    }
                }
            }
        }
 
        // Used by CommandDevice to send AppCommands to the tree.
        internal static readonly RoutedEvent CommandDeviceEvent =
            EventManager.RegisterRoutedEvent("CommandDevice",
                                             RoutingStrategy.Bubble,
                                             typeof(CommandDeviceEventHandler),
                                             typeof(CommandDevice));
 
        ///<SecurityNote>
        /// Critical: Calls a critical function (PushInput)
        ///           Accesses e.StagingItem.Input
        ///</SecurityNote>
        [SecurityCritical]
        private void PostProcessInput( object sender, ProcessInputEventArgs e )
        {
            if (e.StagingItem.Input.RoutedEvent == InputManager.InputReportEvent)
            {
                if (!e.StagingItem.Input.Handled)
                {
                    InputReportEventArgs inputReportEventArgs = e.StagingItem.Input as InputReportEventArgs;
                    if (inputReportEventArgs != null)
                    {
                        RawAppCommandInputReport rawAppCommandInputReport = inputReportEventArgs.Report as RawAppCommandInputReport;
                        if (rawAppCommandInputReport != null)
                        {
                            IInputElement commandTarget = e.StagingItem.Input.OriginalSource as IInputElement;
                            if (commandTarget != null)
                            {
                                RoutedCommand command = GetRoutedCommand(rawAppCommandInputReport.AppCommand);
                                if (command != null)
                                {
                                    // Send the app command to the tree to be handled by UIElements and ContentElements
                                    // that will forward the event to CommandManager.
                                    CommandDeviceEventArgs args = new CommandDeviceEventArgs(this, rawAppCommandInputReport.Timestamp, command);
                                    args.RoutedEvent = CommandDeviceEvent;
                                    args.Source = commandTarget;
                                    e.PushInput(args, e.StagingItem);
                                }
                            }
                        }
                    }
                }
            }
            else if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent ||
                     e.StagingItem.Input.RoutedEvent == Mouse.MouseUpEvent ||
                     e.StagingItem.Input.RoutedEvent == Keyboard.GotKeyboardFocusEvent ||
                     e.StagingItem.Input.RoutedEvent == Keyboard.LostKeyboardFocusEvent)
            {
                CommandManager.InvalidateRequerySuggested();
            }
        }
 
        // Maps the Windows WM_APPCOMMANDS to regular Commands.
        private RoutedCommand GetRoutedCommand( int appCommandId )
        {
            RoutedCommand appCommand = null;
            switch (appCommandId)
            {                            
                case NativeMethods.APPCOMMAND_BROWSER_BACKWARD:
                    appCommand = NavigationCommands.BrowseBack;
                    break;
                case NativeMethods.APPCOMMAND_BROWSER_FORWARD:
                    appCommand = NavigationCommands.BrowseForward;
                    break;
                case NativeMethods.APPCOMMAND_BROWSER_REFRESH:
                    appCommand = NavigationCommands.Refresh;
                    break;
                case NativeMethods.APPCOMMAND_BROWSER_STOP:
                    appCommand = NavigationCommands.BrowseStop;
                    break;
                case NativeMethods.APPCOMMAND_BROWSER_SEARCH:
                    appCommand = NavigationCommands.Search;
                    break;
                case NativeMethods.APPCOMMAND_BROWSER_FAVORITES:
                    appCommand = NavigationCommands.Favorites;
                    break;
                case NativeMethods.APPCOMMAND_BROWSER_HOME:
                    appCommand = NavigationCommands.BrowseHome;
                    break;                     
                case NativeMethods.APPCOMMAND_VOLUME_MUTE:
                    appCommand = MediaCommands.MuteVolume;
                    break;
                case NativeMethods.APPCOMMAND_VOLUME_DOWN:
                    appCommand = MediaCommands.DecreaseVolume;
                    break;
                case NativeMethods.APPCOMMAND_VOLUME_UP:
                    appCommand = MediaCommands.IncreaseVolume;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_NEXTTRACK:
                    appCommand = MediaCommands.NextTrack;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_PREVIOUSTRACK:
                    appCommand = MediaCommands.PreviousTrack;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_STOP:
                    appCommand = MediaCommands.Stop;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_PLAY_PAUSE:
                    appCommand = MediaCommands.TogglePlayPause;
                    break;
                case NativeMethods.APPCOMMAND_LAUNCH_APP1:
                case NativeMethods.APPCOMMAND_LAUNCH_APP2:
                case NativeMethods.APPCOMMAND_LAUNCH_MAIL:
                    break;
                case NativeMethods.APPCOMMAND_LAUNCH_MEDIA_SELECT:
                    appCommand = MediaCommands.Select;
                    break;
                case NativeMethods.APPCOMMAND_BASS_DOWN:
                    appCommand = MediaCommands.DecreaseBass;
                    break;
                case NativeMethods.APPCOMMAND_BASS_BOOST:
                    appCommand = MediaCommands.BoostBass;
                    break;
                case NativeMethods.APPCOMMAND_BASS_UP:
                    appCommand = MediaCommands.IncreaseBass;
                    break;
                case NativeMethods.APPCOMMAND_TREBLE_DOWN:
                    appCommand = MediaCommands.DecreaseTreble;
                    break;
                case NativeMethods.APPCOMMAND_TREBLE_UP:
                    appCommand = MediaCommands.IncreaseTreble;
                    break;
                case NativeMethods.APPCOMMAND_MICROPHONE_VOLUME_MUTE:
                    appCommand = MediaCommands.MuteMicrophoneVolume;
                    break;
                case NativeMethods.APPCOMMAND_MICROPHONE_VOLUME_DOWN:
                    appCommand = MediaCommands.DecreaseMicrophoneVolume;
                    break;
                case NativeMethods.APPCOMMAND_MICROPHONE_VOLUME_UP:
                    appCommand = MediaCommands.IncreaseMicrophoneVolume;
                    break;
                case NativeMethods.APPCOMMAND_HELP:
                    appCommand = ApplicationCommands.Help;
                    break;
                case NativeMethods.APPCOMMAND_FIND:
                    appCommand = ApplicationCommands.Find;
                    break;
                case NativeMethods.APPCOMMAND_NEW:
                    appCommand = ApplicationCommands.New;
                    break;
                case NativeMethods.APPCOMMAND_OPEN:
                    appCommand = ApplicationCommands.Open;
                    break;
                case NativeMethods.APPCOMMAND_CLOSE:
                    appCommand = ApplicationCommands.Close;
                    break;
                case NativeMethods.APPCOMMAND_SAVE:
                    appCommand = ApplicationCommands.Save;
                    break;
                case NativeMethods.APPCOMMAND_PRINT:
                    appCommand = ApplicationCommands.Print;
                    break;
                case NativeMethods.APPCOMMAND_UNDO:
                    appCommand = ApplicationCommands.Undo;
                    break;
                case NativeMethods.APPCOMMAND_REDO:
                    appCommand = ApplicationCommands.Redo;
                    break;
                case NativeMethods.APPCOMMAND_COPY:
                    appCommand = ApplicationCommands.Copy;
                    break;
                case NativeMethods.APPCOMMAND_CUT:
                    appCommand = ApplicationCommands.Cut;
                    break;
                case NativeMethods.APPCOMMAND_PASTE:
                    appCommand = ApplicationCommands.Paste;
                    break;
                case NativeMethods.APPCOMMAND_REPLY_TO_MAIL:
                case NativeMethods.APPCOMMAND_FORWARD_MAIL:
                case NativeMethods.APPCOMMAND_SEND_MAIL:
                case NativeMethods.APPCOMMAND_SPELL_CHECK:
                case NativeMethods.APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE:
                    break;
                case NativeMethods.APPCOMMAND_MIC_ON_OFF_TOGGLE:
                    appCommand = MediaCommands.ToggleMicrophoneOnOff;
                    break;
                case NativeMethods.APPCOMMAND_CORRECTION_LIST:
                    appCommand = ApplicationCommands.CorrectionList;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_PLAY:
                    appCommand = MediaCommands.Play;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_PAUSE:
                    appCommand = MediaCommands.Pause;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_RECORD:
                    appCommand = MediaCommands.Record;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_FAST_FORWARD:
                    appCommand = MediaCommands.FastForward;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_REWIND:
                    appCommand = MediaCommands.Rewind;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_CHANNEL_UP:
                    appCommand = MediaCommands.ChannelUp;
                    break;
                case NativeMethods.APPCOMMAND_MEDIA_CHANNEL_DOWN:
                    appCommand = MediaCommands.ChannelDown;
                    break;
            } 
            return appCommand;
        }
 
        /// <summary>
        /// Takes an InputType enum representing the device and returns the element that
        /// should be the source of the command.
        /// </summary>
        /// <param name="device"></param>
        /// <returns>Returns either Mouse.DirectlyOver or Keyboard.FocusedElement</returns>
        private IInputElement GetSourceFromDevice(InputType device)
        {
            if (device == InputType.Mouse)
            {
                return Mouse.DirectlyOver;
            }
            else
            {
                // All other devices route to the keyboard.
                return Keyboard.FocusedElement;
            }
        }
 
        private SecurityCriticalData<InputManager> _inputManager;
    }
 
    /// <summary>
    ///     Used by CommandDevice to send AppCommands to the tree.
    /// </summary>
    internal class CommandDeviceEventArgs : InputEventArgs
    {
        /// <summary>
        ///     Initializes a new instance of this class.
        /// </summary>
        /// <param name="commandDevice">The logical CommandDevice associated with this event.</param>
        /// <param name="timestamp">The time when the input occured.</param>
        /// <param name="command">Command associated with this event.</param>
        internal CommandDeviceEventArgs(CommandDevice commandDevice, int timestamp, ICommand command)
            : base(commandDevice, timestamp)
        {
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }
 
            _command = command;
        }
 
        /// <summary>
        ///     The command that will be executed.
        /// </summary>
        internal ICommand Command
        {
            get { return _command; }
        }
 
        /// <summary>
        ///     Invokes the handler.
        /// </summary>
        /// <param name="execHandler">delegate</param>
        /// <param name="target">element</param>
        protected override void InvokeEventHandler(Delegate execHandler, object target)
        {
            CommandDeviceEventHandler handler = (CommandDeviceEventHandler)execHandler;
            handler(target, this);
        }
 
        private ICommand _command;
    }
 
    /// <summary>
    ///     Used by CommandDevice to send AppCommands to the tree.
    /// </summary>
    internal delegate void CommandDeviceEventHandler(object sender, CommandDeviceEventArgs e);
}