From 3f16aa470adabfcfe7f20a326bf18853dff6335a Mon Sep 17 00:00:00 2001 From: Sergej Andrejev Date: Sun, 24 May 2009 12:43:56 +0000 Subject: [PATCH] Missing files from initial commands registry implementation git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4128 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Command/CommandBindingDescriptor.cs | 68 ++++ .../Command/InputBindingDescriptor.cs | 50 +++ .../CommandsService/CommandBindingInfo.cs | 118 +++++++ .../CommandsService/CommandsRegistry.cs | 326 ++++++++++++++++++ .../CommandsService/ManagedCommandBinding.cs | 35 ++ .../CommandsService/ManagedInputBinding.cs | 29 ++ 6 files changed, 626 insertions(+) create mode 100644 src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/CommandBindingDescriptor.cs create mode 100644 src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/InputBindingDescriptor.cs create mode 100644 src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandBindingInfo.cs create mode 100644 src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs create mode 100644 src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedCommandBinding.cs create mode 100644 src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedInputBinding.cs diff --git a/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/CommandBindingDescriptor.cs b/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/CommandBindingDescriptor.cs new file mode 100644 index 0000000000..a118d3e08c --- /dev/null +++ b/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/CommandBindingDescriptor.cs @@ -0,0 +1,68 @@ +using System; + +namespace ICSharpCode.Core +{ + /// + /// Stores information about command binding loaded from add-in tree + /// + public class CommandBindingDescriptor + { + /// + /// Codon used to create this descriptor + /// + public Codon Codon { + get; private set; + } + + /// + /// Full name of the command class which will be executed when this + /// binding is triggered + /// + public string Class { + get { + return Codon.Properties["class"]; + } + } + + /// + /// Full name of routed UI command which will trigger this binding + /// + public string Command { + get { + return Codon.Properties["command"]; + } + } + + /// + /// Full name of context class. + /// + /// UI element in which this binding will be valid + /// + public string Context { + get { + return Codon.Properties["context"]; + } + } + + /// + /// Lazy loading + /// + /// If true add-in referenced assemblies are loaded when command is invoked. + /// Otherwise command can't be invoked until addin is loaded + /// + public bool Lazy { + get { + return Codon.Properties["lazy"] == "1" || Codon.Properties["lazy"] == "true"; + } + } + + /// + /// Constructor + /// + /// Reference to codon used to create this descriptor + public CommandBindingDescriptor(Codon codon) + { + Codon = codon; + } + } +} diff --git a/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/InputBindingDescriptor.cs b/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/InputBindingDescriptor.cs new file mode 100644 index 0000000000..46716e330a --- /dev/null +++ b/src/Main/Core/Project/Src/AddInTree/AddIn/DefaultDoozers/Command/InputBindingDescriptor.cs @@ -0,0 +1,50 @@ +using System; + +namespace ICSharpCode.Core +{ + /// + /// Stores information about input binding loaded from add-in tree + /// + public class InputBindingDescriptor + { + private Codon codon; + + /// + /// Full name of routed UI command which will be invoked when this binding is triggered + /// + public string Command { + get { + return codon.Properties["command"]; + } + } + + /// + /// Full name of context class. + /// + /// UI element in which this binding will be valid + /// + public string Context { + get { + return codon.Properties["context"]; + } + } + + /// + /// Description of gesture which will trigger this bindin + /// + public string Gesture { + get { + return codon.Properties["gesture"]; + } + } + + /// + /// Constructor + /// + /// Reference to codon used to create this descriptor + public InputBindingDescriptor(Codon codon) + { + this.codon = codon; + } + } +} diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandBindingInfo.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandBindingInfo.cs new file mode 100644 index 0000000000..914dda0eba --- /dev/null +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandBindingInfo.cs @@ -0,0 +1,118 @@ +using System; +using System.Windows; +using System.Windows.Input; + +namespace ICSharpCode.Core.Presentation +{ + /// + /// Stores details about command binding + /// + public class CommandBindingInfo + { + /// + /// Constructor + /// + /// Context full name + /// Name of routed UI command which triggers this binding + /// Command full name + /// Add-in where command is registered + /// Lazy load command + public CommandBindingInfo(string contextName, string routedCommandName, string className, AddIn addIn, bool isLazy) { + RoutedCommandName = routedCommandName; + ContextName = contextName; + ClassName = className; + IsLazy = isLazy; + AddIn = addIn; + } + + /// + /// Routed command name + /// + /// Described binding is triggered by this routed command + /// + /// + public string RoutedCommandName { + get; private set; + } + + /// + /// Routed command instance + /// + /// Described binding is triggered by this routed command + /// + /// + public RoutedUICommand RoutedCommand { + get { + return CommandsRegistry.GetRoutedUICommand(RoutedCommandName); + } + } + + /// + /// Add-in to which binded command belongs + /// + public AddIn AddIn { + get; private set; + } + + /// + /// Binded command full name + /// + /// This command is invoke when this binding is triggered + /// + /// + public string ClassName { + get; private set; + } + + /// + /// Binded command instance + /// + /// This command is invoke when this binding is triggered. If this value is equal + /// to null then add-in is not loaded yet, see IsLazy attribute + /// for details + /// + /// + public System.Windows.Input.ICommand Class { + get { + System.Windows.Input.ICommand command; + CommandsRegistry.commands.TryGetValue(ClassName, out command); + + return command; + } + } + + /// + /// Context class full name + /// + /// Described binding will be valid in this context + /// + public string ContextName{ + get; private set; + } + + /// + /// Context class instance + /// + /// Described binding will be valid in this context + /// + public UIElement Context { + get { + UIElement context; + CommandsRegistry.contexts.TryGetValue(ContextName, out context); + + return context; + } + } + + /// + /// Lazy load + /// + /// If lazy load is enabled then all add-in references are loaded when this + /// command is invoked. Otherwice if add-in is not loaded and IsLazy is set + /// to false then this binding can't be triggered until add-in is loaded. + /// + public bool IsLazy{ + get; private set; + } + } +} diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs new file mode 100644 index 0000000000..7bca48b18e --- /dev/null +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Windows.Input; +using System.Windows; +using ICSharpCode.Core; +using System.Threading; + +namespace ICSharpCode.Core.Presentation +{ + public delegate void BindingsUpdatedHandler(); + + /// + /// Global registry to store and access commands, command bindings and input bindings + /// + public static class CommandsRegistry + { + /// + /// Default application context. + /// + /// This should be set to the root UI element + /// + public static string DefaultContext { + get; set; + } + + private static List commandBindings = new List(); + private static List inputBidnings = new List(); + + private static Dictionary routedCommands = new Dictionary(); + internal static Dictionary commands = new Dictionary(); + internal static Dictionary contexts = new Dictionary(); + + private static Dictionary> commandBindingsUpdateHandlers = new Dictionary>(); + private static Dictionary> inputBindingsUpdateHandlers = new Dictionary>(); + + /// + /// Get reference to routed UI command by name + /// + /// Routed command name + /// Routed command instance + public static RoutedUICommand GetRoutedUICommand(string routedCommandName) { + RoutedUICommand routedUICommand; + routedCommands.TryGetValue(routedCommandName, out routedUICommand); + + return routedUICommand; + } + + /// + /// Register new routed command in the global registry + /// + /// Routed command name should be uniq in SharpDevelop scope. + /// Use "." to emulate namespaces + /// + /// Routed command name + /// Short text describing command functionality + public static void RegisterRoutedUICommand(string routedCommandName, string text) { + var routedCommand = new RoutedUICommand(text, routedCommandName, typeof(UIElement)); + + if(!routedCommands.ContainsKey(routedCommandName)) { + routedCommands.Add(routedCommandName, routedCommand); + } else { + throw new IndexOutOfRangeException("Routed 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 + /// + /// Registering input binding means that when provided gesture is met in specified + /// context routed command will be invoked + /// + /// Context class full name + /// Routed UI command invoked on gesture run + /// Gesture + public static void RegisterInputBinding(string contextName, string routedCommandName, KeyGesture gesture) { + var inputBindingInfo = new InputBindingInfo(contextName, routedCommandName, gesture); + inputBidnings.Add(inputBindingInfo); + } + + /// + /// Remove input binding from global registry + /// + /// Null attributes are ignored and oly bindings which satisfy not null arguments are removed + /// + /// Context class full name + /// Routed UI command name + /// Gesture + public static void UnregisterInputBindings(string contextName, string routedCommandName, KeyGesture gesture) { + for(var i = inputBidnings.Count - 1; i >= 0; i--) { + if((contextName == null || inputBidnings[i].ContextName == contextName) + && (routedCommandName == null || inputBidnings[i].RoutedCommandName == routedCommandName) + && (gesture == null || inputBidnings[i].Gesture == gesture)) { + inputBidnings.RemoveAt(i); + } + } + } + + /// + /// Register delegate which will be invoked on change in input bindings in specified context + /// + /// Context class full name + /// Update handler delegate + public static void RegisterInputBindingUpdateHandler(string contextName, BindingsUpdatedHandler handler) { + if(!inputBindingsUpdateHandlers.ContainsKey(contextName)) { + inputBindingsUpdateHandlers.Add(contextName, new List()); + } + + inputBindingsUpdateHandlers[contextName].Add(handler); + } + + /// + /// Invoke registered input bindings update handlers registered in specified context + /// + /// Context class full name + public static void InvokeInputBindingUpdateHandlers(string contextName) { + if(contextName != null) { + if(inputBindingsUpdateHandlers.ContainsKey(contextName)) { + foreach(var handler in inputBindingsUpdateHandlers[contextName]) { + handler.Invoke(); + } + } + } else { + foreach(var contextHandlers in inputBindingsUpdateHandlers) { + foreach(var handler in contextHandlers.Value) { + handler.Invoke(); + } + } + } + } + + /// + /// Remove all managed input bindings from input bindings collection + /// + /// + public static void RemoveManagedInputBindings(InputBindingCollection inputBindingCollection) { + for(var i = inputBindingCollection.Count - 1; i >= 0; i--) { + if(inputBindingCollection[i] is ManagedInputBinding) { + inputBindingCollection.RemoveAt(i); + } + } + } + + /// + /// Register command binding + /// + /// Registering command binding means that when provided routed command is invoked + /// in specified context event is routed to specified command (implementing ICommand class) + /// + /// Context class full name + /// Routed UI command name + /// Command full name to which invokation event is routed + /// Add-in in which hosts the command + /// Load add-in referenced assemblies on command invocation + public static void RegisterCommandBinding(string contextName, string routedCommandName, string className, AddIn addIn, bool isLazy) { + var commandBindingInfo = new CommandBindingInfo(contextName, routedCommandName, className, addIn, isLazy); + commandBindings.Add(commandBindingInfo); + } + + /// + /// Remove registered command bindnig from global registry + /// + /// + /// + /// + public static void UnregisterCommandBindings(string contextName, string routedCommandName, string className) { + for(var i = commandBindings.Count - 1; i >= 0; i--) { + if((contextName == null || commandBindings[i].ContextName == contextName) + && (routedCommandName == null || commandBindings[i].RoutedCommandName == routedCommandName) + && (className == null || commandBindings[i].ClassName == className)) { + inputBidnings.RemoveAt(i); + } + } + } + + /// + /// Register delegate which will be invoked on any chage in command bindings of specified context + /// + /// Context class full name + /// Update handler delegate + public static void RegisterCommandBindingsUpdateHandler(string contextName, BindingsUpdatedHandler handler) { + if(!commandBindingsUpdateHandlers.ContainsKey(contextName)) { + commandBindingsUpdateHandlers.Add(contextName, new List()); + } + + commandBindingsUpdateHandlers[contextName].Add(handler); + } + + /// + /// Invoke registered command bindings update handlers registered in specified context + /// + /// Context class full name + public static void InvokeCommandBindingUpdateHandlers(string contextName) { + if(contextName != null) { + if(commandBindingsUpdateHandlers.ContainsKey(contextName)) { + foreach(var handler in commandBindingsUpdateHandlers[contextName]) { + handler.Invoke(); + } + } + } else { + foreach(var contextHandlers in commandBindingsUpdateHandlers) { + foreach(var handler in contextHandlers.Value) { + handler.Invoke(); + } + } + } + } + + /// + /// Remove all managed command bindungs from command bindings collection + /// + /// + public static void RemoveManagedCommandBindings(CommandBindingCollection commandBindingsCollection) { + for(var i = commandBindingsCollection.Count - 1; i >= 0; i--) { + if(commandBindingsCollection[i] is ManagedCommandBinding) { + commandBindingsCollection.RemoveAt(i); + } + } + } + + /// + /// Load all registered commands in addin + /// + /// Addin + public static void LoadAddinCommands(AddIn addIn) { + foreach(var binding in commandBindings) { + if(binding.AddIn != addIn) continue; + + LoadCommand(binding.ClassName, addIn.CreateObject(binding.ClassName)); + } + } + + /// + /// Load command + /// + /// 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); + } + + commands.Add(commandName, wpfCommand); + } + + /// + /// Load context + /// + /// Context class full name + /// Context class instance + public static void LoadContext(string contextName, UIElement context) { + contexts[contextName] = context; + } + + + /// + /// Get all commands bindings registered in provided context + /// + /// Context class full name + /// Collection of managed command bindings + public static CommandBindingCollection GetCommandBindings(string contextName) { + var bindings = new CommandBindingCollection(); + + foreach(var binding in commandBindings) { + if(binding.ContextName != contextName) continue; + + var managedCommandBinding = new ManagedCommandBinding(binding.RoutedCommand); + + managedCommandBinding.CanExecute += delegate(Object sender, CanExecuteRoutedEventArgs e) { + if(binding.IsLazy && binding.Class == null) { + e.CanExecute = true; + } else if(binding.Class == null) { + e.CanExecute = false; + } else { + e.CanExecute = binding.Class.CanExecute(e.Parameter); + } + }; + + managedCommandBinding.Executed += delegate(Object sender, ExecutedRoutedEventArgs e) { + if(binding.IsLazy && binding.Class == null) { + binding.AddIn.LoadRuntimeAssemblies(); + + var command = (ICommand)binding.AddIn.CreateObject(binding.ClassName); + CommandsRegistry.LoadCommand(binding.ClassName, command); + } + + if(binding.Class != null) { + binding.Class.Execute(e.Parameter); + } + }; + + bindings.Add(managedCommandBinding); + } + + return bindings; + } + + /// + /// Get list of all input bindings registered in provided context + /// + /// Context class full name + /// Collection of managed command bindings + public static InputBindingCollection GetInputBindings(string contextName) { + var bindings = new InputBindingCollection(); + + foreach(var binding in inputBidnings) { + if(binding.ContextName != contextName) continue; + + bindings.Add(new ManagedInputBinding(binding.RoutedCommand, binding.Gesture)); + } + + return bindings; + } + } +} diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedCommandBinding.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedCommandBinding.cs new file mode 100644 index 0000000000..cf9ea7e9d5 --- /dev/null +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedCommandBinding.cs @@ -0,0 +1,35 @@ +using System; +using System.Windows.Input; + +namespace ICSharpCode.Core.Presentation +{ + /// + /// ManagedCommandBinding is used to distinguish command bindings managed + /// by CommandsRegistry from other command bindings not managed by CommandsRegistry + /// + /// If command binding is not managed then CommandsRegistry ignores it when + /// performing any action + /// + public class ManagedCommandBinding : System.Windows.Input.CommandBinding + { + /// + public ManagedCommandBinding() + : base() + { } + + /// + public ManagedCommandBinding(System.Windows.Input.ICommand command) + : base(command) + { } + + /// + public ManagedCommandBinding(System.Windows.Input.ICommand command, System.Windows.Input.ExecutedRoutedEventHandler executed) + : base(command, executed) + { } + + /// + public ManagedCommandBinding(System.Windows.Input.ICommand command, System.Windows.Input.ExecutedRoutedEventHandler executed, System.Windows.Input.CanExecuteRoutedEventHandler canExecute) + : base(command, executed, canExecute) + { } + } +} diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedInputBinding.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedInputBinding.cs new file mode 100644 index 0000000000..487ee57658 --- /dev/null +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/ManagedInputBinding.cs @@ -0,0 +1,29 @@ +using System; +using System.Windows.Input; + +namespace ICSharpCode.Core.Presentation +{ + /// + /// ManagedInputBinding is used to distinguish input bindings managed + /// by CommandsRegistry from other input bindings not managed by CommandsRegistry. + /// + /// If input binding is not managed then CommansRegistry ignores it when + /// performing any action + /// + public class ManagedInputBinding : System.Windows.Input.KeyBinding + { + /// + public ManagedInputBinding() : base() + { } + + /// + public ManagedInputBinding(System.Windows.Input.ICommand command, System.Windows.Input.KeyGesture gesture) + : base(command, gesture) + { } + + /// + public ManagedInputBinding(System.Windows.Input.ICommand command, System.Windows.Input.Key key, System.Windows.Input.ModifierKeys modifiers) + : base(command, key, modifiers) + { } + } +}