Files
DynamicDataDisplay/Backup/Charts/Navigation/DefaultContextMenu.cs
2024-02-23 09:29:44 -05:00

332 lines
9.8 KiB
C#

using System.Collections.Generic;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Input;
using System.Linq;
using System.Diagnostics;
using System;
using System.Collections.ObjectModel;
using System.Collections;
using System.ComponentModel;
using System.Windows.Data;
using Microsoft.Research.DynamicDataDisplay.Charts.Navigation;
namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
/// <summary>
/// Is responsible for displaying and populating default context menu of ChartPlotter
/// </summary>
public class DefaultContextMenu : IPlotterElement
{
private static readonly BitmapImage helpIcon;
private static readonly BitmapImage copyScreenshotIcon;
private static readonly BitmapImage saveScreenshotIcon;
private static readonly BitmapImage fitToViewIcon;
static DefaultContextMenu()
{
helpIcon = LoadIcon("HelpIcon");
saveScreenshotIcon = LoadIcon("SaveIcon");
copyScreenshotIcon = LoadIcon("CopyScreenshotIcon");
fitToViewIcon = LoadIcon("FitToViewIcon");
}
private static BitmapImage LoadIcon(string name)
{
Assembly currentAssembly = typeof(DefaultContextMenu).Assembly;
BitmapImage icon = new BitmapImage();
icon.BeginInit();
icon.StreamSource = currentAssembly.GetManifestResourceStream("Microsoft.Research.DynamicDataDisplay.Resources." + name + ".png");
icon.EndInit();
icon.Freeze();
return icon;
}
/// <summary>
/// Initializes a new instance of the <see cref="DefaultContextMenu"/> class.
/// </summary>
public DefaultContextMenu() { }
protected ContextMenu PopulateContextMenu(Plotter target)
{
ContextMenu menu = new ContextMenu();
MenuItem fitToViewMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuFitToView,
ToolTip = Strings.UIResources.ContextMenuFitToViewTooltip,
Icon = new Image { Source = fitToViewIcon },
Command = ChartCommands.FitToView,
CommandTarget = target
};
MenuItem savePictureMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuSaveScreenshot,
ToolTip = Strings.UIResources.ContextMenuSaveScreenshotTooltip,
Icon = new Image { Source = saveScreenshotIcon },
Command = ChartCommands.SaveScreenshot,
CommandTarget = target
};
MenuItem copyPictureMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuCopyScreenshot,
ToolTip = Strings.UIResources.ContextMenuCopyScreenshotTooltip,
Icon = new Image { Source = copyScreenshotIcon },
Command = ChartCommands.CopyScreenshot,
CommandTarget = target
};
MenuItem quickHelpMenuItem = new MenuItem
{
Header = Strings.UIResources.ContextMenuQuickHelp,
ToolTip = Strings.UIResources.ContextMenuQuickHelpTooltip,
Command = ChartCommands.ShowHelp,
Icon = new Image { Source = helpIcon },
CommandTarget = target
};
MenuItem reportFeedback = new MenuItem
{
Header = Strings.UIResources.ContextMenuReportFeedback,
ToolTip = Strings.UIResources.ContextMenuReportFeedbackTooltip,
Icon = (Image)plotter.Resources["SendFeedbackIcon"]
};
reportFeedback.Click += reportFeedback_Click;
staticMenuItems.Add(fitToViewMenuItem);
staticMenuItems.Add(copyPictureMenuItem);
staticMenuItems.Add(savePictureMenuItem);
staticMenuItems.Add(quickHelpMenuItem);
staticMenuItems.Add(reportFeedback);
menu.ItemsSource = staticMenuItems;
return menu;
}
private void reportFeedback_Click(object sender, RoutedEventArgs e)
{
try
{
using (Process.Start("mailto:" + Strings.UIResources.SendFeedbackEmail + "?Subject=[D3]%20" + typeof(DefaultContextMenu).Assembly.GetName().Version)) { }
}
catch (Exception)
{
MessageBox.Show(Strings.UIResources.SendFeedbackError + Strings.UIResources.SendFeedbackEmail, "Error while sending feedback", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private readonly ObservableCollection<object> staticMenuItems = new ObservableCollection<object>();
// hidden because default menu items' command target is plotter, and serializing this will
// cause circular reference
/// <summary>
/// Gets the static menu items.
/// </summary>
/// <value>The static menu items.</value>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ObservableCollection<object> StaticMenuItems
{
get { return staticMenuItems; }
}
#region IPlotterElement Members
private Plotter2D plotter;
void IPlotterElement.OnPlotterAttached(Plotter plotter)
{
this.plotter = (Plotter2D)plotter;
ContextMenu menu = PopulateContextMenu(plotter);
plotter.ContextMenu = menu;
plotter.PreviewMouseRightButtonDown += plotter_PreviewMouseRightButtonDown;
plotter.PreviewMouseRightButtonUp += plotter_PreviewMouseRightButtonUp;
plotter.PreviewMouseLeftButtonDown += plotter_PreviewMouseLeftButtonDown;
plotter.ContextMenu.Closed += ContextMenu_Closed;
}
private void plotter_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// this will prevent other tools like PointSelector from
// wrong actuations
if (contextMenuOpen)
{
plotter.Focus();
e.Handled = true;
}
}
void IPlotterElement.OnPlotterDetaching(Plotter plotter)
{
plotter.ContextMenu.Closed -= ContextMenu_Closed;
plotter.ContextMenu = null;
plotter.PreviewMouseRightButtonDown -= plotter_PreviewMouseRightButtonDown;
plotter.PreviewMouseRightButtonUp -= plotter_PreviewMouseRightButtonUp;
this.plotter = null;
}
private void ContextMenu_Closed(object sender, RoutedEventArgs e)
{
contextMenuOpen = false;
foreach (var item in dynamicMenuItems)
{
staticMenuItems.Remove(item);
}
}
private Point mousePos;
/// <summary>
/// Gets the mouse position when right mouse button was clicked.
/// </summary>
/// <value>The mouse position on click.</value>
public Point MousePositionOnClick
{
get { return mousePos; }
}
private void plotter_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
contextMenuOpen = false;
mousePos = e.GetPosition(plotter);
}
private bool contextMenuOpen = false;
private readonly ObservableCollection<object> dynamicMenuItems = new ObservableCollection<object>();
private void plotter_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
Point position = e.GetPosition(plotter);
if (mousePos == position)
{
hitResults.Clear();
VisualTreeHelper.HitTest(plotter, null, CollectAllVisuals_Callback, new PointHitTestParameters(position));
foreach (var item in dynamicMenuItems)
{
staticMenuItems.Remove(item);
}
dynamicMenuItems.Clear();
var dynamicItems = (hitResults.Where(r =>
{
IPlotterContextMenuSource menuSource = r as IPlotterContextMenuSource;
if (menuSource != null)
{
menuSource.BuildMenu();
}
var items = GetPlotterContextMenu(r);
return items != null && items.Count > 0;
}).SelectMany(r =>
{
var menuItems = GetPlotterContextMenu(r);
FrameworkElement chart = r as FrameworkElement;
if (chart != null)
{
foreach (var menuItem in menuItems.OfType<MenuItem>())
{
menuItem.DataContext = chart.DataContext;
}
}
return menuItems;
})).ToList();
foreach (var item in dynamicItems)
{
dynamicMenuItems.Add(item);
//MenuItem menuItem = item as MenuItem;
//if (menuItem != null)
//{
// menuItem.CommandTarget = plotter;
//}
}
staticMenuItems.AddMany(dynamicMenuItems);
plotter.Focus();
contextMenuOpen = true;
// in XBAP applications these lines throws a SecurityException, as (as I think)
// we are opening "new window" here. So in XBAP we are not opening context menu manually, but
// it will be opened by itself as this is right click.
#if !RELEASEXBAP
plotter.ContextMenu.IsOpen = true;
e.Handled = true;
#endif
}
else
{
// this is to prevent showing menu when RMB was pressed, then moved and now is releasing.
e.Handled = true;
}
}
#region PlotterContextMenu property
/// <summary>
/// Gets the plotter context menu.
/// </summary>
/// <param name="obj">The obj.</param>
/// <returns></returns>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public static ObjectCollection GetPlotterContextMenu(DependencyObject obj)
{
return (ObjectCollection)obj.GetValue(PlotterContextMenuProperty);
}
/// <summary>
/// Sets the plotter context menu.
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="value">The value.</param>
public static void SetPlotterContextMenu(DependencyObject obj, ObjectCollection value)
{
obj.SetValue(PlotterContextMenuProperty, value);
}
/// <summary>
/// Identifies the PlotterContextMenu attached property.
/// </summary>
public static readonly DependencyProperty PlotterContextMenuProperty = DependencyProperty.RegisterAttached(
"PlotterContextMenu",
typeof(ObjectCollection),
typeof(DefaultContextMenu),
new FrameworkPropertyMetadata(null));
#endregion
private List<DependencyObject> hitResults = new List<DependencyObject>();
private HitTestResultBehavior CollectAllVisuals_Callback(HitTestResult result)
{
if (result == null || result.VisualHit == null)
return HitTestResultBehavior.Stop;
hitResults.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
Plotter IPlotterElement.Plotter
{
get { return plotter; }
}
#endregion
}
/// <summary>
/// Represents a collection of additional menu items in ChartPlotter's context menu.
/// </summary>
public sealed class ObjectCollection : ObservableCollection<Object> { }
}