using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Xml.Serialization;
using ICSharpCode.Core;
namespace ICSharpCode.Core.Presentation
{
///
/// Global registry to store and access commands, command bindings and input bindings
///
public static class CommandManager
{
///
/// Default application context.
///
/// This should be set to the root UI element
///
public static string DefaultContextName {
get; set;
}
// Binding infos
private static HashSet commandBindings = new HashSet();
private static HashSet inputBidnings = new HashSet();
// Commands
private static Dictionary routedCommands = new Dictionary();
internal static Dictionary commands = new Dictionary();
// Binding update handlers
private static Dictionary> inputBindingUpdatedHandlers = new Dictionary>();
private static Dictionary> commandBindingUpdatedHandlers = new Dictionary>();
// Named instances and types
private static Dictionary> namedUIInstances = new Dictionary>();
private static Dictionary> namedUITypes = new Dictionary>();
// Reverse named instances and types (used to search for instance name by type)
private static Dictionary> reverseNamedUIInstances = new Dictionary>();
private static Dictionary> reverseNamedUITypes = new Dictionary>();
// Categories
public static List InputBindingCategories = new List();
///
/// Register UI element instance accessible by unique name
///
/// Instance name
/// Instance
public static void RegisterNamedUIElement(string instanceName, UIElement element)
{
if(!namedUIInstances.ContainsKey(instanceName)){
namedUIInstances.Add(instanceName, new HashSet());
}
if(!reverseNamedUIInstances.ContainsKey(element)) {
reverseNamedUIInstances.Add(element, new HashSet());
}
if(namedUIInstances[instanceName].Add(element)) {
reverseNamedUIInstances[element].Add(instanceName);
// If there are some bindings and update handlers already registered,
// but owner is not loaded then invoke those bindings
InvokeCommandBindingUpdateHandlers(new BindingInfoTemplate { OwnerInstanceName = instanceName });
InvokeInputBindingUpdateHandlers(new BindingInfoTemplate { OwnerInstanceName = instanceName });
}
}
///
/// Get instance by unique instance name
///
/// Instance name
///
public static ICollection GetNamedUIElementCollection(string instanceName)
{
HashSet instances;
namedUIInstances.TryGetValue(instanceName, out instances);
return instances ?? new HashSet();
}
public static ICollection GetUIElementNameCollection(UIElement instance)
{
HashSet names;
reverseNamedUIInstances.TryGetValue(instance, out names);
return names ?? new HashSet();
}
///
/// Register UI type which can be accessible by name
///
/// Type name
/// Type
public static void RegisterNamedUIType(string typeName, Type type)
{
if(!namedUITypes.ContainsKey(typeName)){
namedUITypes.Add(typeName, new HashSet());
}
if(!reverseNamedUITypes.ContainsKey(type)) {
reverseNamedUITypes.Add(type, new HashSet());
}
if(namedUITypes[typeName].Add(type)) {
reverseNamedUITypes[type].Add(typeName);
// If any update handlers where assigned to the type and type was not
// loaded yet then invoke update handlers
InvokeCommandBindingUpdateHandlers(new BindingInfoTemplate { OwnerTypeName = typeName });
InvokeInputBindingUpdateHandlers(new BindingInfoTemplate { OwnerTypeName = typeName });
}
}
///
/// Get type by uniqe type name
///
/// Type name
/// Type
public static ICollection GetNamedUITypeCollection(string typeName)
{
HashSet instance;
namedUITypes.TryGetValue(typeName, out instance);
return instance ?? new HashSet();
}
public static ICollection GetUITypeNameCollection(Type type)
{
HashSet typeNames;
reverseNamedUITypes.TryGetValue(type, out typeNames);
return typeNames ?? new HashSet();
}
///
/// Get reference to routed UI command by name
///
/// Routed command name
/// Routed command instance
public static RoutedUICommand GetRoutedUICommand(string routedCommandName) {
if(routedCommands.ContainsKey(routedCommandName)) {
return routedCommands[routedCommandName];
}
return null;
}
///
/// Checks whether routed UI command registered
///
/// Routed command name
/// Returns value specifting whether routed UI command is registered
public static bool IsRoutedUICommandRegistered(string routedCommandName) {
return GetRoutedUICommand(routedCommandName) != null;
}
///
/// Register new routed command in the global registry
///
/// Routed command name should be unique in SharpDevelop scope.
/// Use "." to organize commands into groups
///
/// Routed command name
/// Short text describing command functionality
public static RoutedUICommand RegisterRoutedUICommand(string routedCommandName, string text) {
var routedCommand = new RoutedUICommand(text, routedCommandName, typeof(CommandManager));
if(!routedCommands.ContainsKey(routedCommandName)) {
routedCommands.Add(routedCommandName, routedCommand);
} else {
var test = routedCommands[routedCommandName];
throw new IndexOutOfRangeException("Routed UI command with name " + routedCommandName + " is already registered");
}
return routedCommand;
}
///
/// Register existing routed command in the global registry
///
/// Routed command then can be accessed
/// Routed command name should be uniq in SharpDevelop scope.
/// Use "." to organize commands into groups
///
/// Existing routed command
public static void RegisterRoutedUICommand(RoutedUICommand existingRoutedUICommand) {
string routedCommandName = existingRoutedUICommand.OwnerType.Name + "." + existingRoutedUICommand.Name;
if(!routedCommands.ContainsKey(routedCommandName)) {
routedCommands.Add(routedCommandName, existingRoutedUICommand);
} else {
throw new IndexOutOfRangeException("Routed UI command with name " + routedCommandName + " is already registered");
}
}
///
/// Remove routed command from global registry
///
/// Routed command name
public static void UnregisterRoutedUICommand(string routedCommandName) {
if(routedCommands.ContainsKey(routedCommandName)) {
routedCommands.Remove(routedCommandName);
}
}
///
/// Register input binding by specifying this binding parameters
///
/// Input binding parameters
public static void RegisterInputBinding(InputBindingInfo inputBindingInfo)
{
var similarBindingTemplate = new BindingInfoTemplate();
similarBindingTemplate.OwnerTypeName = inputBindingInfo.OwnerTypeName;
similarBindingTemplate.OwnerInstanceName = inputBindingInfo.OwnerInstanceName;
similarBindingTemplate.RoutedCommandName = inputBindingInfo.RoutedCommandName;
var similarInputBinding = FindInputBindingInfos(similarBindingTemplate).FirstOrDefault();
if(similarInputBinding != null) {
foreach(InputGesture gesture in inputBindingInfo.DefaultGestures) {
var existingGesture = new InputGestureCollection(similarInputBinding.DefaultGestures.ToList());
if(!existingGesture.ContainsTemplateFor(gesture, GestureCompareMode.ExactlyMatches)) {
similarInputBinding.DefaultGestures.Add(gesture);
}
}
similarInputBinding.Categories.AddRange(inputBindingInfo.Categories);
similarInputBinding.Groups.AddRange(inputBindingInfo.Groups);
} else {
if(inputBindingInfo.OwnerInstanceName == null && inputBindingInfo.OwnerTypeName == null) {
throw new ArgumentException("Binding owner must be specified");
}
var registeredBindingTemplate = new BindingInfoTemplate();
registeredBindingTemplate.OwnerInstanceName = inputBindingInfo.OwnerInstanceName;
registeredBindingTemplate.OwnerTypeName = inputBindingInfo.OwnerTypeName;
registeredBindingTemplate.RoutedCommandName = inputBindingInfo.RoutedCommandName;
inputBidnings.Add(inputBindingInfo);
inputBindingInfo.IsRegistered = true;
RegisterInputBindingsUpdateHandler(registeredBindingTemplate, inputBindingInfo.DefaultInputBindingHandler);
InvokeInputBindingUpdateHandlers(registeredBindingTemplate);
}
}
///
/// Unregister input binding
///
/// Input binding parameters
public static void UnregisterInputBinding(BindingInfoTemplate template)
{
var similarInputBindingInfos = FindInputBindingInfos(template);
foreach(var similarInputBindingInfo in similarInputBindingInfos) {
inputBidnings.Remove(similarInputBindingInfo);
// Remove command bindings
if(similarInputBindingInfo.OwnerTypes != null) {
foreach(var ownerType in similarInputBindingInfo.OwnerTypes) {
foreach(InputBinding binding in similarInputBindingInfo.OldInputBindings) {
RemoveClassInputBinding(ownerType, binding);
}
foreach(InputBinding binding in similarInputBindingInfo.ActiveInputBindings) {
RemoveClassInputBinding(ownerType, binding);
}
}
} else if (similarInputBindingInfo.OwnerInstances != null) {
foreach(var ownerInstance in similarInputBindingInfo.OwnerInstances) {
foreach(InputBinding binding in similarInputBindingInfo.OldInputBindings) {
ownerInstance.InputBindings.Remove(binding);
}
foreach(InputBinding binding in similarInputBindingInfo.ActiveInputBindings) {
ownerInstance.InputBindings.Remove(binding);
}
}
}
}
}
public static IEnumerable FindInputBindingInfos(params BindingInfoTemplate[] templates)
{
return FindBindingInfos(inputBidnings, templates).Cast();
}
private static IEnumerable FindBindingInfos(ICollection bindingInfos, params BindingInfoTemplate[] templates)
{
foreach(var binding in bindingInfos) {
foreach(var template in templates) {
if(template.IsTemplateFor(binding)) {
yield return binding;
continue;
}
}
}
}
public static InputBindingCollection FindInputBindings(params BindingInfoTemplate[] templates)
{
var inputBindingInfoCollection = FindInputBindingInfos(templates);
var inputBindingCollection = new InputBindingCollection();
foreach(var bindingInfo in inputBindingInfoCollection) {
inputBindingCollection.AddRange(bindingInfo.ActiveInputBindings);
}
return inputBindingCollection;
}
///
/// Remove input binding associated with type
///
/// Owner type
/// Input binding
public static void RemoveClassInputBinding(Type ownerType, InputBinding inputBinding)
{
var fieldInfo = typeof(System.Windows.Input.CommandManager).GetField("_classInputBindings", BindingFlags.Static | BindingFlags.NonPublic);
var fieldData = (HybridDictionary)fieldInfo.GetValue(null);
var classInputBindings = (InputBindingCollection)fieldData[ownerType];
if(classInputBindings != null) {
classInputBindings.Remove(inputBinding);
}
}
internal static void OrderClassInputBindingsByChords(Type ownerType)
{
var fieldInfo = typeof(System.Windows.Input.CommandManager).GetField("_classInputBindings", BindingFlags.Static | BindingFlags.NonPublic);
var fieldData = (HybridDictionary)fieldInfo.GetValue(null);
var classInputBindings = (InputBindingCollection)fieldData[ownerType];
if(classInputBindings != null) {
classInputBindings.SortByChords();
}
}
internal static void OrderInstanceInputBindingsByChords(UIElement ownerType)
{
if(ownerType.InputBindings != null) {
ownerType.InputBindings.SortByChords();
}
}
///
/// Remove command binding associated with type
///
///
///
public static void RemoveClassCommandBinding(Type ownerType, CommandBinding commandBinding)
{
var fieldInfo = typeof(System.Windows.Input.CommandManager).GetField("_classCommandBindings", BindingFlags.Static | BindingFlags.NonPublic);
var fieldData = (HybridDictionary)fieldInfo.GetValue(null);
var classCommandBindings = (CommandBindingCollection)fieldData[ownerType];
if(classCommandBindings != null) {
classCommandBindings.Remove(commandBinding);
}
}
///
/// Register command binding by specifying command binding parameters
///
/// Command binding parameters
public static void RegisterCommandBinding(CommandBindingInfo commandBindingInfo) {
if(commandBindingInfo.OwnerInstanceName == null && commandBindingInfo.OwnerTypeName == null) {
throw new ArgumentException("Binding owner must be specified");
}
var registeredBindingTemplate = new BindingInfoTemplate();
registeredBindingTemplate.OwnerInstanceName = commandBindingInfo.OwnerInstanceName;
registeredBindingTemplate.OwnerTypeName = commandBindingInfo.OwnerTypeName;
registeredBindingTemplate.RoutedCommandName = commandBindingInfo.RoutedCommandName;
commandBindings.Add(commandBindingInfo);
commandBindingInfo.IsRegistered = true;
RegisterInputBindingsUpdateHandler(registeredBindingTemplate, commandBindingInfo.DefaultCommandBindingHandler);
InvokeInputBindingUpdateHandlers(registeredBindingTemplate);
}
///
/// Unregister command binding
///
/// Command binding parameters
public static void UnregisterCommandBinding(CommandBindingInfo commandBindingInfo) {
var template = new BindingInfoTemplate();
template.OwnerTypeName = commandBindingInfo.OwnerTypeName;
template.OwnerInstanceName = commandBindingInfo.OwnerInstanceName;
template.RoutedCommandName = commandBindingInfo.RoutedCommandName;
var similarCommandBindingInfos = FindCommandBindingInfos(template);
foreach(var similarCommandBindingInfo in similarCommandBindingInfos) {
commandBindings.Remove(similarCommandBindingInfo);
// Remove command bindings
if(similarCommandBindingInfo.OwnerTypes != null) {
foreach(var ownerType in similarCommandBindingInfo.OwnerTypes) {
foreach(CommandBinding binding in similarCommandBindingInfo.OldCommandBindings) {
RemoveClassCommandBinding(ownerType, binding);
}
foreach(CommandBinding binding in similarCommandBindingInfo.ActiveCommandBindings) {
RemoveClassCommandBinding(ownerType, binding);
}
}
} else if (similarCommandBindingInfo.OwnerInstances != null) {
foreach(var ownerInstance in similarCommandBindingInfo.OwnerInstances) {
foreach(CommandBinding binding in similarCommandBindingInfo.OldCommandBindings) {
ownerInstance.CommandBindings.Remove(binding);
}
foreach(CommandBinding binding in similarCommandBindingInfo.ActiveCommandBindings) {
ownerInstance.CommandBindings.Remove(binding);
}
}
}
}
}
#region Update handlers
private static void RegisterBindingsUpdateHandler(Dictionary> bindingUpdateHandlersDictionary, IBindingInfo template, BindingsUpdatedHandler handler)
{
if(!bindingUpdateHandlersDictionary.ContainsKey(template)) {
bindingUpdateHandlersDictionary.Add(template, new HashSet());
}
bindingUpdateHandlersDictionary[template].Add(handler);
}
///
/// Register command binding update handler which is triggered when input bindings associated
/// with specified type change
///
/// Owner type name
/// Update handler
public static void RegisterCommandBindingsUpdateHandler(IBindingInfo template, BindingsUpdatedHandler handler)
{
RegisterBindingsUpdateHandler(commandBindingUpdatedHandlers, template, handler);
}
///
/// Register command binding update handler which is triggered when input bindings associated
/// with specified type change
///
/// Owner type name
/// Update handler
public static void RegisterInputBindingsUpdateHandler(IBindingInfo template, BindingsUpdatedHandler handler)
{
RegisterBindingsUpdateHandler(inputBindingUpdatedHandlers, template, handler);
}
private static void InvokeBindingUpdateHandlers(Dictionary> updateHandlerDictionary, params BindingInfoTemplate[] templates)
{
foreach(var updateHandlerPair in updateHandlerDictionary) {
foreach(var template in templates) {
if(template.IsTemplateFor(updateHandlerPair.Key)) {
foreach(var handler in updateHandlerPair.Value) {
if(handler != null) {
handler.Invoke();
}
}
}
}
}
}
public static void InvokeCommandBindingUpdateHandlers(params BindingInfoTemplate[] templates)
{
InvokeBindingUpdateHandlers(commandBindingUpdatedHandlers, templates);
}
public static void InvokeInputBindingUpdateHandlers(params BindingInfoTemplate[] templates)
{
InvokeBindingUpdateHandlers(inputBindingUpdatedHandlers, templates);
}
#endregion
///
/// Load all registered commands in add-in
///
/// Add-in
public static void LoadAddinCommands(AddIn addIn) {
foreach(CommandBindingInfo binding in commandBindings) {
if(binding.AddIn != addIn) continue;
if(binding.CommandTypeName != null && !commands.ContainsKey(binding.CommandTypeName)){
var command = addIn.CreateObject(binding.CommandTypeName);
var wpfCommand = command as System.Windows.Input.ICommand;
if(wpfCommand == null) {
wpfCommand = new WpfCommandWrapper((ICSharpCode.Core.ICommand)command);
}
commands.Add(binding.CommandTypeName, wpfCommand);
}
}
}
///
/// Register command object (either instance of or )
/// which can be identified by command name
///
/// Command name
/// Command instance
public static void LoadCommand(string commandName, object command) {
var wpfCommand = command as System.Windows.Input.ICommand;
if(wpfCommand == null) {
wpfCommand = new WpfCommandWrapper((ICSharpCode.Core.ICommand)command);
}
if(!commands.ContainsKey(commandName)) {
commands.Add(commandName, wpfCommand);
}
}
///
/// Get list of all command bindings which satisfy provided parameters
///
/// Null arguments are ignored
///
/// Context class full name
/// Get command bindings assigned only to specific context
/// Context class full name
/// Context class full name
/// Collection of managed command bindings
public static IEnumerable FindCommandBindingInfos(params BindingInfoTemplate[] templates)
{
return FindBindingInfos(commandBindings, templates).Cast();
}
public static CommandBindingCollection FindCommandBindings(params BindingInfoTemplate[] templates)
{
var commandBindingInfoCollection = FindCommandBindingInfos(templates);
var commandBindingCollection = new CommandBindingCollection();
foreach(var bindingInfo in commandBindingInfoCollection) {
commandBindingCollection.AddRange(bindingInfo.ActiveCommandBindings);
}
return commandBindingCollection;
}
///
/// Get list of input gestures from all input bindings which satisfy provided parameters
///
/// Null arguments are ignored
///
/// Context class full name
/// Get gestures assigned only to specific context
/// Routed UI command name
/// Gesture
public static InputGestureCollection FindInputGestures(params BindingInfoTemplate[] templates) {
var bindings = FindInputBindingInfos(templates);
var gestures = new InputGestureCollection();
foreach(InputBindingInfo bindingInfo in bindings) {
if(bindingInfo.ActiveGestures != null) {
foreach(InputGesture gesture in bindingInfo.ActiveGestures) {
if(!gestures.ContainsTemplateFor(gesture, GestureCompareMode.ExactlyMatches)) {
gestures.Add(gesture);
}
}
}
}
return gestures;
}
public static InputBindingCategory GetInputBindingCategory(string categoryPath, bool throwWhenNotFound)
{
foreach(var category in InputBindingCategories) {
if(category.Path == categoryPath) {
return category;
}
}
if(throwWhenNotFound) {
throw new ApplicationException(string.Format("InputBindingCategory with path {0} was not found", categoryPath));
}
return null;
}
public static ICollection GetInputBindingCategoryCollection(string categoryPathCollectionString, bool throwWhenNotFound)
{
var categoryPathCollection = categoryPathCollectionString.Split(',');
var categories = new List();
foreach(var categoryPath in categoryPathCollection) {
var category = CommandManager.GetInputBindingCategory(categoryPath, throwWhenNotFound);
if(category != null) {
categories.Add(category);
}
}
return categories;
}
public static IEnumerable GetInputBindingCategoryChildren(string categoryPath)
{
var categoryDepth = categoryPath.Count(c => c == '/');
foreach(var currentCategory in InputBindingCategories) {
if(currentCategory.Path.StartsWith(categoryPath)) {
var currentCategoryDepth = currentCategory.Path.Count(c => c == '/');
if(currentCategoryDepth == categoryDepth + 1)
{
yield return currentCategory;
}
}
}
}
public static void RegisterInputBindingCategory(InputBindingCategory category)
{
if(string.IsNullOrEmpty(category.Path)) {
throw new ArgumentException("InputBindingCategory path can not be empty");
}
if(string.IsNullOrEmpty(category.Text)) {
throw new ArgumentException("InputBindingCategory text can not be empty");
}
InputBindingCategories.Add(category);
}
}
public static class TypeExtensions
{
public static string GetShortAssemblyQualifiedName(this Type type)
{
return string.Format("{0}, {1}", type.FullName, type.Assembly.GetName().Name);
}
}
}