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); } } }