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)
+ { }
+ }
+}