|
//---------------------------------------------------------------------------
//
// File: DocumentGridContextMenu.cs
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Description: Context menu for DocumentGrid
//
//---------------------------------------------------------------------------
namespace MS.Internal.Documents
{
using MS.Internal;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Runtime.InteropServices;
using System.Security;
using MS.Internal.Documents;
using System.Security.Permissions;
using MS.Win32;
using System.Windows.Interop;
// A Component of DocumentViewer supporting the default ContextMenu.
internal static class DocumentGridContextMenu
{
//------------------------------------------------------
//
// Class Internal Methods
//
//------------------------------------------------------
#region Class Internal Methods
// Registers the event handler for DocumentGrid.
/// <SecurityNote>
/// Critical: This code hooks up a call back to context menu opening event which has the ability to spoof copy
/// TreatAsSafe: This code does not expose the callback and does not drive any input into it
/// </SecurityNote>
[SecurityCritical,SecurityTreatAsSafe]
internal static void RegisterClassHandler()
{
EventManager.RegisterClassHandler(typeof(DocumentGrid), FrameworkElement.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpening));
EventManager.RegisterClassHandler(typeof(DocumentApplicationDocumentViewer), FrameworkElement.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnDocumentViewerContextMenuOpening));
}
#endregion Class Internal Methods
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
/// <summary>
/// Callback for FrameworkElement.ContextMenuOpeningEvent, when fired from DocumentViewer. This is
/// here to catch the event when it is fired by the keyboard rather than the mouse.
/// </summary>
/// <SecurityNote>
/// Critical - forwards user-initiated information to OnContextMenuOpening, which is also SecurityCritical
/// </SecurityNote>
[SecurityCritical]
private static void OnDocumentViewerContextMenuOpening(object sender, ContextMenuEventArgs e)
{
if (e.CursorLeft == KeyboardInvokedSentinel)
{
DocumentViewer dv = sender as DocumentViewer;
if (dv != null && dv.ScrollViewer != null)
{
OnContextMenuOpening(dv.ScrollViewer.Content, e);
}
}
}
// Callback for FrameworkElement.ContextMenuOpeningEvent.
// If the control is using the default ContextMenu, we initialize it
// here.
/// <SecurityNote>
/// Critical - accepts a parameter which may be used to set the userInitiated
/// bit on a command, which is used for security purposes later.
/// </SecurityNote>
[SecurityCritical]
private static void OnContextMenuOpening(object sender, ContextMenuEventArgs e)
{
DocumentGrid documentGrid = sender as DocumentGrid;
ContextMenu contextMenu;
if (documentGrid == null)
{
return;
}
// We only want to programmatically generate the menu for Mongoose
if (!(documentGrid.DocumentViewerOwner is DocumentApplicationDocumentViewer))
return;
// If the DocumentViewer or ScrollViewer has a ContextMenu set, the DocumentGrid menu should be ignored
if (documentGrid.DocumentViewerOwner.ContextMenu != null || documentGrid.DocumentViewerOwner.ScrollViewer.ContextMenu != null)
return;
// Start by grabbing whatever's set to the UiScope's ContextMenu property.
contextMenu = documentGrid.ContextMenu;
// If someone explicitly set it null -- don't mess with it.
if (documentGrid.ReadLocalValue(FrameworkElement.ContextMenuProperty) == null)
return;
// If it's not null, someone's overriding our default -- don't mess with it.
if (contextMenu != null)
return;
// It's a default null, so spin up a temporary ContextMenu now.
contextMenu = new ViewerContextMenu();
contextMenu.Placement = PlacementMode.RelativePoint;
contextMenu.PlacementTarget = documentGrid;
((ViewerContextMenu)contextMenu).AddMenuItems(documentGrid, e.UserInitiated);
Point uiScopeMouseDownPoint;
if (e.CursorLeft == KeyboardInvokedSentinel)
{
uiScopeMouseDownPoint = new Point(.5 * documentGrid.ViewportWidth, .5 * documentGrid.ViewportHeight);
}
else
{
uiScopeMouseDownPoint = Mouse.GetPosition(documentGrid);
}
contextMenu.HorizontalOffset = uiScopeMouseDownPoint.X;
contextMenu.VerticalOffset = uiScopeMouseDownPoint.Y;
// This line raises a public event.
contextMenu.IsOpen = true;
e.Handled = true;
}
#endregion Private methods
//------------------------------------------------------
//
// Private Constants
//
//------------------------------------------------------
#region Private Constants
private const double KeyboardInvokedSentinel = -1.0; // e.CursorLeft has this value when the menu is invoked with the keyboard.
#endregion
//------------------------------------------------------
//
// Private Types
//
//------------------------------------------------------
#region Private Types
// Default ContextMenu for TextBox and RichTextBox.
private class ViewerContextMenu : ContextMenu
{
// Initialize the context menu.
// Creates a new instance.
/// <SecurityNote>
/// Critical - accepts a parameter which may be used to set the userInitiated
/// bit on a command, which is used for security purposes later.
/// Although there is a demand here to prevent non userinitiated
/// code paths to be blocked this function is not TreatAsSafe because
/// we want to track any new callers to this call
/// </SecurityNote>
[SecurityCritical]
internal void AddMenuItems(DocumentGrid dg, bool userInitiated)
{
// create a special menu item for paste which only works for user initiated copy
// within the confines of partial trust this cannot be done programmatically
if (userInitiated == false)
{
SecurityHelper.DemandAllClipboardPermission();
}
this.Name = "ViewerContextMenu";
SetMenuProperties(new EditorMenuItem(), dg, ApplicationCommands.Copy); // Copy will be marked as user initiated
// build menu for XPSViewer
SetMenuProperties(new MenuItem(), dg, ApplicationCommands.SelectAll);
AddSeparator();
SetMenuProperties(
new MenuItem(),
dg,
NavigationCommands.PreviousPage,
SR.Get(SRID.DocumentApplicationContextMenuPreviousPageHeader),
SR.Get(SRID.DocumentApplicationContextMenuPreviousPageInputGesture));
SetMenuProperties(
new MenuItem(),
dg,
NavigationCommands.NextPage,
SR.Get(SRID.DocumentApplicationContextMenuNextPageHeader),
SR.Get(SRID.DocumentApplicationContextMenuNextPageInputGesture));
SetMenuProperties(
new MenuItem(),
dg,
NavigationCommands.FirstPage,
null, //menu header
SR.Get(SRID.DocumentApplicationContextMenuFirstPageInputGesture));
SetMenuProperties(
new MenuItem(),
dg,
NavigationCommands.LastPage,
null, //menu header
SR.Get(SRID.DocumentApplicationContextMenuLastPageInputGesture));
AddSeparator();
SetMenuProperties(new MenuItem(), dg, ApplicationCommands.Print);
}
private void AddSeparator()
{
this.Items.Add(new Separator());
}
//Helper to set properties on the menu items based on the command
private void SetMenuProperties(MenuItem menuItem, DocumentGrid dg, RoutedUICommand command)
{
SetMenuProperties(menuItem, dg, command, null, null);
}
private void SetMenuProperties(MenuItem menuItem, DocumentGrid dg, RoutedUICommand command, string header, string inputGestureText)
{
menuItem.Command = command;
menuItem.CommandTarget = dg.DocumentViewerOwner; // the text editor expects the commands to come from the DocumentViewer
if (header == null)
{
menuItem.Header = command.Text; // use default menu text for this command
}
else
{
menuItem.Header = header;
}
if (inputGestureText != null)
{
menuItem.InputGestureText = inputGestureText;
}
menuItem.Name = "ViewerContextMenu_" + command.Name; // does not require localization
this.Items.Add(menuItem);
}
}
// Default EditorContextMenu item base class.
// Used to distinguish our items from anything an application
// may have added.
private class EditorMenuItem : MenuItem
{
internal EditorMenuItem() : base() {}
/// <SecurityNote>
/// Critical - accepts a parameter which may be used to set the userInitiated
/// bit on a command, which is used for security purposes later.
/// </SecurityNote>
[SecurityCritical]
internal override void OnClickCore(bool userInitiated)
{
OnClickImpl(userInitiated);
}
}
#endregion Private Types
}
}
|