Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLluis Sanchez <lluis@novell.com>2010-03-17 15:35:28 +0300
committerLluis Sanchez <lluis@novell.com>2010-03-17 15:35:28 +0300
commit8fa37870e9cc22ffccdd494fa951b2c3807d7978 (patch)
treebda14806802947c51676c183b08f166878964c40 /main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands
parentf1a8582658af8aeb0f6fa459965a2e4d0684c347 (diff)
parent585086f0ea0a49166046bb8f48d2def87907d0e0 (diff)
Merged MD.Projects into MD.Core, and MD.Projects.Gui, MD.Core.Gui and MD.Components into MD.Ide.
svn path=/trunk/monodevelop/; revision=153730
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands')
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs134
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionType.cs39
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/Command.cs127
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandArrayInfo.cs124
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandCheckMenuItem.cs178
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntry.cs165
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntrySet.cs148
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandErrorHandler.cs60
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs74
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandler.cs73
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandlerAttribute.cs63
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfo.cs136
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfoSet.cs47
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs1448
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenu.cs145
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs46
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuItem.cs239
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandRouterContainer.cs64
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSelectedEventArgs.cs45
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSystemCommands.cs38
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToggleToolButton.cs91
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolButton.cs104
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolbar.cs78
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommand.cs56
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandTargetAttribute.cs75
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandUpdaterAttribute.cs78
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomItem.cs43
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomMenuItem.cs46
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandBar.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandDelegatorRouter.cs41
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandMenuItem.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandRouter.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandTargetVisitor.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUpdateHandler.cs55
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUserItem.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/IMultiCastCommandRouter.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs910
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingScheme.cs38
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingService.cs210
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingSet.cs276
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/LinkCommandEntry.cs106
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/MenuToolButton.cs64
42 files changed, 5886 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs
new file mode 100644
index 0000000000..0f06f0cd38
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs
@@ -0,0 +1,134 @@
+//
+// ActionCommand.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using Mono.Addins;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class ActionCommand: Command
+ {
+ ActionType type;
+ bool commandArray;
+ Type defaultHandlerType;
+ CommandHandler defaultHandler;
+
+ RuntimeAddin defaultHandlerAddin;
+ string defaultHandlerTypeName;
+
+ public ActionCommand ()
+ {
+ }
+
+ public ActionCommand (object id, string text): base (id, text)
+ {
+ }
+
+ public ActionCommand (object id, string text, IconId icon): base (id, text)
+ {
+ Icon = icon;
+ }
+
+ public ActionCommand (object id, string text, IconId icon, string accelKey, ActionType type): base (id, text)
+ {
+ Icon = icon;
+ AccelKey = accelKey;
+ this.type = type;
+ }
+
+ public ActionType ActionType {
+ get { return type; }
+ set { type = value; }
+ }
+
+ public bool CommandArray {
+ get { return commandArray; }
+ set { commandArray = value; }
+ }
+
+ public Type DefaultHandlerType {
+ get {
+ if (defaultHandlerType != null)
+ return defaultHandlerType;
+ if (defaultHandlerAddin != null)
+ return defaultHandlerType = defaultHandlerAddin.GetType (defaultHandlerTypeName, true);
+ return null;
+ }
+ set {
+ if (!typeof (CommandHandler).IsAssignableFrom (value))
+ throw new ArgumentException ("Value must be a subclass of CommandHandler (" + value + ")");
+
+ defaultHandlerType = value;
+ }
+ }
+
+ public void SetDefaultHandlerTypeInfo (RuntimeAddin addin, string typeName)
+ {
+ defaultHandlerAddin = addin;
+ defaultHandlerTypeName = typeName;
+ }
+
+ public CommandHandler DefaultHandler {
+ get { return defaultHandler; }
+ set { defaultHandler = value; }
+ }
+
+ public virtual bool DispatchCommand (object dataItem)
+ {
+ if (defaultHandler == null) {
+ if (DefaultHandlerType == null)
+ return false;
+ defaultHandler = (CommandHandler) Activator.CreateInstance (DefaultHandlerType);
+ }
+ defaultHandler.InternalRun (dataItem);
+ return true;
+ }
+
+ public virtual void UpdateCommandInfo (CommandInfo info)
+ {
+ if (defaultHandler == null) {
+ if (DefaultHandlerType == null) {
+ info.Enabled = false;
+ if (!DisabledVisible)
+ info.Visible = false;
+ return;
+ }
+ defaultHandler = (CommandHandler) Activator.CreateInstance (DefaultHandlerType);
+ }
+ if (commandArray) {
+ info.ArrayInfo = new CommandArrayInfo (info);
+ defaultHandler.InternalUpdate (info.ArrayInfo);
+ }
+ else
+ defaultHandler.InternalUpdate (info);
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionType.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionType.cs
new file mode 100644
index 0000000000..40c3d71491
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionType.cs
@@ -0,0 +1,39 @@
+//
+// ActionType.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public enum ActionType
+ {
+ Normal,
+ Check,
+ Radio
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/Command.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/Command.cs
new file mode 100644
index 0000000000..bc1079bca4
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/Command.cs
@@ -0,0 +1,127 @@
+//
+// Command.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public delegate void KeyBindingChangedEventHandler (object s, KeyBindingChangedEventArgs args);
+
+ public abstract class Command
+ {
+ public static readonly object Separator = new Object ();
+
+ object id;
+ string text;
+ string description;
+ IconId icon;
+ string accelKey;
+ string category;
+ bool disabledVisible;
+ internal string AccelPath;
+ internal object HandlerData; // Used internally when dispatching the command
+
+ public Command ()
+ {
+ }
+
+ public Command (object id, string text)
+ {
+ this.id = CommandManager.ToCommandId (id);
+ this.text = text;
+ }
+
+ public object Id {
+ get { return id; }
+ set { id = CommandManager.ToCommandId (value); }
+ }
+
+ public string Text {
+ get { return text; }
+ set { text = value; }
+ }
+
+ public IconId Icon {
+ get { return icon; }
+ set { icon = value; }
+ }
+
+ public string AccelKey {
+ get { return accelKey; }
+ set {
+ string binding = accelKey;
+ accelKey = value == String.Empty ? null : value;
+
+ if (KeyBindingChanged != null && accelKey != binding)
+ KeyBindingChanged (this, new KeyBindingChangedEventArgs (this, binding));
+ }
+ }
+
+ public bool DisabledVisible {
+ get { return disabledVisible; }
+ set { disabledVisible = value; }
+ }
+
+ public string Description {
+ get { return description; }
+ set { description = value; }
+ }
+
+ public string Category {
+ get { return category == null ? string.Empty : category; }
+ set { category = value; }
+ }
+
+ public event KeyBindingChangedEventHandler KeyBindingChanged;
+ }
+
+ public class KeyBindingChangedEventArgs {
+ Command command;
+ string binding;
+
+ public KeyBindingChangedEventArgs (Command command, string oldBinding)
+ {
+ this.command = command;
+ this.binding = oldBinding;
+ }
+
+ public Command Command {
+ get { return command; }
+ }
+
+ public string OldKeyBinding {
+ get { return binding; }
+ }
+
+ public string NewKeyBinding {
+ get { return command.AccelKey; }
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandArrayInfo.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandArrayInfo.cs
new file mode 100644
index 0000000000..d81d07e729
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandArrayInfo.cs
@@ -0,0 +1,124 @@
+//
+// CommandArrayInfo.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandArrayInfo: IEnumerable
+ {
+ List<CommandInfo> list = new List<CommandInfo> ();
+ CommandInfo defaultInfo;
+ bool bypass;
+ internal object UpdateHandlerData;
+
+ internal CommandArrayInfo (CommandInfo defaultInfo)
+ {
+ this.defaultInfo = defaultInfo;
+ }
+
+ public void Clear ()
+ {
+ list.Clear ();
+ }
+
+ public void Insert (int index, CommandInfoSet infoSet)
+ {
+ Insert (index, infoSet, null);
+ }
+
+ public void Insert (int index, CommandInfo info, object dataItem)
+ {
+ info.DataItem = dataItem;
+ if (info.Text == null) info.Text = defaultInfo.Text;
+ if (info.Icon.IsNull) info.Icon = defaultInfo.Icon;
+ list.Insert (index, info);
+ }
+
+ public CommandInfo Insert (int index, string text, object dataItem)
+ {
+ CommandInfo info = new CommandInfo (text);
+ Insert (index, info, dataItem);
+ return info;
+ }
+
+ public void Add (CommandInfoSet infoSet)
+ {
+ Add (infoSet, null);
+ }
+
+ public void Add (CommandInfo info, object dataItem)
+ {
+ info.DataItem = dataItem;
+ if (info.Text == null) info.Text = defaultInfo.Text;
+ if (info.Icon.IsNull) info.Icon = defaultInfo.Icon;
+ list.Add (info);
+ }
+
+ public CommandInfo Add (string text, object dataItem)
+ {
+ CommandInfo info = new CommandInfo (text);
+ Add (info, dataItem);
+ return info;
+ }
+
+ public CommandInfo this [int n] {
+ get { return list [n]; }
+ }
+
+ public int Count {
+ get { return list.Count; }
+ }
+
+
+ public void AddSeparator ()
+ {
+ CommandInfo info = new CommandInfo ("-");
+ info.IsArraySeparator = true;
+ Add (info, null);
+ }
+
+ public CommandInfo DefaultCommandInfo {
+ get { return defaultInfo; }
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+
+ // When set in an update handler, the command manager will ignore this handler method
+ // and will keep looking in the command route.
+ public bool Bypass {
+ get { return bypass; }
+ set { bypass = value; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandCheckMenuItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandCheckMenuItem.cs
new file mode 100644
index 0000000000..cf14ea9cb6
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandCheckMenuItem.cs
@@ -0,0 +1,178 @@
+//
+// CommandCheckMenuItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ internal class CommandCheckMenuItem: Gtk.CheckMenuItem, ICommandMenuItem
+ {
+ CommandManager commandManager;
+ object commandId;
+ bool updating;
+ bool isArrayItem;
+ object arrayDataItem;
+ object initialTarget;
+ string overrideLabel;
+ CommandInfo lastCmdInfo;
+ bool disabledVisible = true;
+
+ public CommandCheckMenuItem (object commandId, CommandManager commandManager, string overrideLabel, bool disabledVisible): base ("")
+ {
+ this.commandId = commandId;
+ this.commandManager = commandManager;
+ this.overrideLabel = overrideLabel;
+ this.disabledVisible = disabledVisible;
+
+ ActionCommand cmd = commandManager.GetCommand (commandId) as ActionCommand;
+ if (cmd != null && cmd.ActionType == ActionType.Radio)
+ this.DrawAsRadio = true;
+ }
+
+ public CommandCheckMenuItem (object commandId, CommandManager commandManager): this (commandId, commandManager, null, true)
+ {
+ }
+
+ void ICommandUserItem.Update (object initialTarget)
+ {
+ if (commandManager != null && !isArrayItem) {
+ CommandInfo cinfo = commandManager.GetCommandInfo (commandId, initialTarget);
+ this.initialTarget = initialTarget;
+ Update (cinfo);
+ }
+ }
+
+ void ICommandMenuItem.SetUpdateInfo (CommandInfo cmdInfo, object initialTarget)
+ {
+ isArrayItem = true;
+ arrayDataItem = cmdInfo.DataItem;
+ this.initialTarget = initialTarget;
+ Update (cmdInfo);
+ }
+
+ protected override void OnParentSet (Gtk.Widget parent)
+ {
+ base.OnParentSet (parent);
+ if (Parent == null)
+ return;
+
+ ((ICommandUserItem)this).Update (null);
+
+ // Make sure the accelerators always work for this item
+ // while the menu is hidden
+ Sensitive = true;
+ Visible = true;
+ }
+
+ protected override void OnActivated ()
+ {
+ base.OnActivated ();
+
+ if (updating)
+ return;
+
+ if (commandManager == null)
+ throw new InvalidOperationException ();
+
+ commandManager.DispatchCommand (commandId, arrayDataItem, initialTarget);
+ }
+
+ protected override void OnSelected ()
+ {
+ if (commandManager != null)
+ commandManager.NotifySelected (lastCmdInfo);
+ base.OnSelected ();
+ }
+
+ protected override void OnDeselected ()
+ {
+ if (commandManager != null)
+ commandManager.NotifyDeselected ();
+ base.OnDeselected ();
+ }
+
+ void Update (CommandInfo cmdInfo)
+ {
+ lastCmdInfo = cmdInfo;
+
+ Gtk.Widget child = Child;
+ if (child == null)
+ return;
+
+ updating = true;
+
+ Gtk.Label accel_label = null;
+ Gtk.Label label = null;
+
+ if (!(child is Gtk.HBox)) {
+ child = new Gtk.HBox (false, 0);
+ accel_label = new Gtk.Label ("");
+ accel_label.UseUnderline = false;
+ accel_label.Xalign = 1.0f;
+ accel_label.Show ();
+
+ label = new Gtk.Label ("");
+ label.UseUnderline = true;
+ label.Xalign = 0.0f;
+ label.Show ();
+
+ ((Gtk.Box) child).PackStart (label);
+ ((Gtk.Box) child).PackStart (accel_label);
+ child.Show ();
+
+ this.Remove (Child);
+ this.Add (child);
+ } else {
+ accel_label = (Gtk.Label) ((Gtk.Box) child).Children[1];
+ label = (Gtk.Label) ((Gtk.Box) child).Children[0];
+ }
+
+ if (cmdInfo.AccelKey != null)
+ accel_label.Text = " " + KeyBindingManager.BindingToDisplayLabel (cmdInfo.AccelKey, true);
+ else
+ accel_label.Text = String.Empty;
+
+ if (cmdInfo.UseMarkup) {
+ label.Markup = overrideLabel ?? cmdInfo.Text;
+ label.UseMarkup = true;
+ } else {
+ label.Text = overrideLabel ?? cmdInfo.Text;
+ label.UseMarkup = false;
+ }
+
+ label.UseUnderline = true;
+
+ Sensitive = cmdInfo.Enabled;
+ Visible = cmdInfo.Visible && (disabledVisible || cmdInfo.Enabled);
+ Active = cmdInfo.Checked;
+ Inconsistent = cmdInfo.CheckedInconsistent;
+
+ updating = false;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntry.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntry.cs
new file mode 100644
index 0000000000..28e4bb4d3f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntry.cs
@@ -0,0 +1,165 @@
+//
+// CommandEntry.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandEntry
+ {
+ object cmdId;
+ string overrideLabel;
+ bool disabledVisible = true;
+ Command localCmd;
+
+ public CommandEntry (object cmdId, string overrideLabel, bool disabledVisible)
+ {
+ this.cmdId = CommandManager.ToCommandId (cmdId);
+ this.overrideLabel = overrideLabel;
+ this.disabledVisible = disabledVisible;
+ }
+
+ public CommandEntry (object cmdId, string overrideLabel): this (cmdId, overrideLabel, true)
+ {
+ }
+
+ public CommandEntry (object cmdId) : this (cmdId, null, true)
+ {
+ }
+
+ public CommandEntry (Command localCmd) : this (localCmd.Id, null, true)
+ {
+ this.localCmd = localCmd;
+ }
+
+ public object CommandId {
+ get { return cmdId; }
+ set { cmdId = CommandManager.ToCommandId (value); }
+ }
+
+ public bool DisabledVisible {
+ get { return disabledVisible; }
+ set { disabledVisible = value; }
+ }
+
+ public virtual Command GetCommand (CommandManager manager)
+ {
+ if (localCmd != null) {
+ if (manager.GetCommand (localCmd.Id) == null)
+ manager.RegisterCommand (localCmd);
+ localCmd = null;
+ }
+
+ return manager.GetCommand (cmdId);
+ }
+
+ internal protected virtual Gtk.MenuItem CreateMenuItem (CommandManager manager)
+ {
+ return CreateMenuItem (manager, GetCommand (manager), cmdId, true, overrideLabel, disabledVisible);
+ }
+
+ internal protected virtual Gtk.ToolItem CreateToolItem (CommandManager manager)
+ {
+ if (cmdId == CommandManager.ToCommandId (Command.Separator))
+ return new Gtk.SeparatorToolItem ();
+
+ Command cmd = GetCommand (manager);
+ if (cmd == null)
+ return new Gtk.ToolItem ();
+
+ if (cmd is CustomCommand) {
+ Gtk.Widget child = (Gtk.Widget) Activator.CreateInstance (((CustomCommand)cmd).WidgetType);
+ Gtk.ToolItem ti;
+ if (child is Gtk.ToolItem)
+ ti = (Gtk.ToolItem) child;
+ else {
+ ti = new Gtk.ToolItem ();
+ ti.Child = child;
+ }
+ if (cmd.Text != null && cmd.Text.Length > 0) {
+ //strip "_" accelerators from tooltips
+ string text = cmd.Text;
+ while (true) {
+ int underscoreIndex = text.IndexOf ('_');
+ if (underscoreIndex > -1)
+ text = text.Remove (underscoreIndex, 1);
+ else
+ break;
+ }
+ ti.TooltipText = text;
+ }
+ return ti;
+ }
+
+ ActionCommand acmd = cmd as ActionCommand;
+ if (acmd == null)
+ throw new InvalidOperationException ("Unknown cmd type.");
+
+ if (acmd.CommandArray) {
+ CommandMenu menu = new CommandMenu (manager);
+ menu.Append (CreateMenuItem (manager));
+ return new MenuToolButton (menu, acmd.Icon);
+ }
+ else if (acmd.ActionType == ActionType.Normal)
+ return new CommandToolButton (cmdId, manager);
+ else
+ return new CommandToggleToolButton (cmdId, manager);
+ }
+
+ internal static Gtk.MenuItem CreateMenuItem (CommandManager manager, object cmdId, bool isArrayMaster)
+ {
+ return CreateMenuItem (manager, null, cmdId, isArrayMaster, null, true);
+ }
+
+ static Gtk.MenuItem CreateMenuItem (CommandManager manager, Command cmd, object cmdId, bool isArrayMaster, string overrideLabel, bool disabledVisible)
+ {
+ cmdId = CommandManager.ToCommandId (cmdId);
+ if (cmdId == CommandManager.ToCommandId (Command.Separator))
+ return new Gtk.SeparatorMenuItem ();
+
+ if (cmd == null)
+ cmd = manager.GetCommand (cmdId);
+ if (cmd == null)
+ return new Gtk.MenuItem ("<Unknown Command>");
+
+ if (cmd is CustomCommand) {
+ Gtk.Widget child = (Gtk.Widget) Activator.CreateInstance (((CustomCommand)cmd).WidgetType);
+ CustomMenuItem ti = new CustomMenuItem ();
+ ti.Child = child;
+ return ti;
+ }
+
+ ActionCommand acmd = cmd as ActionCommand;
+ if (acmd.ActionType == ActionType.Normal || (isArrayMaster && acmd.CommandArray))
+ return new CommandMenuItem (cmdId, manager, overrideLabel, disabledVisible);
+ else
+ return new CommandCheckMenuItem (cmdId, manager, overrideLabel, disabledVisible);
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntrySet.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntrySet.cs
new file mode 100644
index 0000000000..999fae15fc
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandEntrySet.cs
@@ -0,0 +1,148 @@
+//
+// CommandEntrySet.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandEntrySet: CommandEntry, IEnumerable
+ {
+ ArrayList cmds = new ArrayList ();
+ string name;
+ IconId icon;
+ bool autoHide;
+
+ public CommandEntrySet (): base ((object)null)
+ {
+ }
+
+ public CommandEntrySet (string name, IconId icon): base ((object)null)
+ {
+ this.name = name;
+ this.icon = icon;
+ }
+
+ public string Name {
+ get { return name; }
+ set { name = value; }
+ }
+
+ public IconId Icon {
+ get { return icon; }
+ set { icon = value; }
+ }
+
+ public bool AutoHide {
+ get { return autoHide; }
+ set { autoHide = value; }
+ }
+
+ public void Add (CommandEntry entry)
+ {
+ cmds.Add (entry);
+ }
+
+ public void Add (Command cmd)
+ {
+ cmds.Add (new CommandEntry (cmd));
+ }
+
+ public CommandEntry AddItem (object cmdId)
+ {
+ CommandEntry cmd = new CommandEntry (cmdId);
+ cmds.Add (cmd);
+ return cmd;
+ }
+
+ public void AddSeparator ()
+ {
+ AddItem (Command.Separator);
+ }
+
+ public CommandEntrySet AddItemSet (string name)
+ {
+ return AddItemSet (name, "");
+ }
+
+ public CommandEntrySet AddItemSet (string name, IconId icon)
+ {
+ CommandEntrySet cmdset = new CommandEntrySet (name, icon);
+ cmds.Add (cmdset);
+ return cmdset;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return cmds.GetEnumerator ();
+ }
+
+ public int Count {
+ get { return cmds.Count; }
+ }
+
+ internal protected override Gtk.MenuItem CreateMenuItem (CommandManager manager)
+ {
+ Gtk.MenuItem mi;
+ if (autoHide)
+ mi = new AutoHideMenuItem (name != null ? name : "");
+ else
+ mi = new Gtk.MenuItem (name != null ? name : "");
+ mi.Submenu = new CommandMenu (manager, this);
+ return mi;
+ }
+
+ internal protected override Gtk.ToolItem CreateToolItem (CommandManager manager)
+ {
+ Gtk.Menu menu = manager.CreateMenu (this);
+ return new MenuToolButton (menu, icon);
+ }
+ }
+
+ class AutoHideMenuItem: Gtk.ImageMenuItem
+ {
+ public AutoHideMenuItem (string name): base (name)
+ {
+ ShowAll ();
+ }
+
+ public bool HasVisibleChildren {
+ get {
+ if (!Submenu.IsRealized)
+ Submenu.Realize ();
+ foreach (Gtk.Widget item in ((Gtk.Menu)Submenu).Children) {
+ if (item.Visible)
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandErrorHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandErrorHandler.cs
new file mode 100644
index 0000000000..af685bf593
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandErrorHandler.cs
@@ -0,0 +1,60 @@
+//
+// CommandErrorHandler.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public delegate void CommandErrorHandler (object sender, CommandErrorArgs args);
+
+ public class CommandErrorArgs
+ {
+ object commandId;
+ string errorMessage;
+ Exception ex;
+
+ public CommandErrorArgs (object commandId, string errorMessage, Exception ex)
+ {
+ this.errorMessage = errorMessage;
+ this.commandId = commandId;
+ this.ex = ex;
+ }
+
+ public object CommandId {
+ get { return commandId; }
+ }
+
+ public string ErrorMessage {
+ get { return errorMessage; }
+ }
+
+ public Exception Exception {
+ get { return ex; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs
new file mode 100644
index 0000000000..218a1d75f6
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs
@@ -0,0 +1,74 @@
+//
+// CommandFrame.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using Mono.Addins;
+using MonoDevelop.Components.DockToolbars;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandFrame: DockToolbarFrame
+ {
+ CommandManager manager;
+
+ public CommandFrame (CommandManager manager)
+ {
+ this.manager = manager;
+ manager.RegisterGlobalHandler (this);
+ }
+
+ [CommandHandler (CommandSystemCommands.ToolbarList)]
+ protected void OnViewToolbar (object ob)
+ {
+ IDockToolbar bar = (IDockToolbar) ob;
+ bar.Visible = !bar.Visible;
+ }
+
+ [CommandUpdateHandler (CommandSystemCommands.ToolbarList)]
+ protected void OnUpdateViewToolbar (CommandArrayInfo info)
+ {
+ foreach (IDockToolbar bar in Toolbars) {
+ CommandInfo cmd = new CommandInfo (bar.Title);
+ cmd.Checked = bar.Visible;
+ cmd.Description = AddinManager.CurrentLocalizer.GetString ("Show toolbar '{0}'", bar.Title);
+ info.Add (cmd, bar);
+ }
+ }
+
+ protected override void OnPanelClick (Gdk.EventButton e, Placement placement)
+ {
+ if (e.Button == 3) {
+ CommandEntrySet opset = new CommandEntrySet ();
+ opset.AddItem (CommandSystemCommands.ToolbarList);
+ Gtk.Menu menu = manager.CreateMenu (opset);
+ menu.Popup (null, null, null, 0, e.Time);
+ }
+ base.OnPanelClick (e, placement);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandler.cs
new file mode 100644
index 0000000000..af0bb209c3
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandler.cs
@@ -0,0 +1,73 @@
+//
+// CommandHandler.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public abstract class CommandHandler
+ {
+ internal void InternalRun ()
+ {
+ Run ();
+ }
+
+ internal void InternalRun (object dataItem)
+ {
+ Run (dataItem);
+ }
+
+ internal void InternalUpdate (CommandInfo info)
+ {
+ Update (info);
+ }
+
+ internal void InternalUpdate (CommandArrayInfo info)
+ {
+ Update (info);
+ }
+
+ protected virtual void Run ()
+ {
+ }
+
+ protected virtual void Run (object dataItem)
+ {
+ Run ();
+ }
+
+ protected virtual void Update (CommandInfo info)
+ {
+ }
+
+ protected virtual void Update (CommandArrayInfo info)
+ {
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandlerAttribute.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandlerAttribute.cs
new file mode 100644
index 0000000000..68d5ce6b37
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandHandlerAttribute.cs
@@ -0,0 +1,63 @@
+//
+// CommandMethodAttribute.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandMethodAttribute: Attribute
+ {
+ object commandId;
+
+ internal CommandMethodAttribute (object commandId)
+ {
+ this.commandId = commandId;
+ }
+
+ public object CommandId {
+ get { return commandId; }
+ set { commandId = value; }
+ }
+ }
+
+ [AttributeUsage (AttributeTargets.Method, AllowMultiple=true)]
+ public class CommandHandlerAttribute: CommandMethodAttribute
+ {
+ public CommandHandlerAttribute (object commandId): base (commandId)
+ {
+ }
+ }
+
+ [AttributeUsage (AttributeTargets.Method, AllowMultiple=true)]
+ public class CommandUpdateHandlerAttribute: CommandMethodAttribute
+ {
+ public CommandUpdateHandlerAttribute (object commandId): base (commandId)
+ {
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfo.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfo.cs
new file mode 100644
index 0000000000..a62b8bc5e5
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfo.cs
@@ -0,0 +1,136 @@
+//
+// CommandInfo.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandInfo
+ {
+ string text;
+ IconId icon;
+ string accelKey;
+ string description;
+ bool enabled = true;
+ bool visible = true;
+ bool checkd;
+ bool useMarkup;
+ bool bypass;
+ bool checkedInconsistent;
+ internal object UpdateHandlerData;
+
+ internal CommandInfo (Command cmd)
+ {
+ text = cmd.Text;
+ icon = cmd.Icon;
+ accelKey = cmd.AccelKey;
+ description = cmd.Description;
+ }
+
+ public CommandInfo ()
+ {
+ }
+
+ public CommandInfo (string text)
+ {
+ Text = text;
+ }
+
+ public CommandInfo (string text, bool enabled, bool checkd)
+ {
+ Text = text;
+ this.enabled = enabled;
+ this.checkd = checkd;
+ }
+
+ public string Text {
+ get { return text; }
+ set { text = value; }
+ }
+
+ public IconId Icon {
+ get { return icon; }
+ set { icon = value; }
+ }
+
+ public string AccelKey {
+ get { return accelKey; }
+ set { accelKey = value; }
+ }
+
+ public string Description {
+ get { return description; }
+ set { description = value; }
+ }
+
+ public bool Enabled {
+ get { return enabled; }
+ set { enabled = value; }
+ }
+
+ public bool Visible {
+ get { return visible; }
+ set { visible = value; }
+ }
+
+ public bool Checked {
+ get { return checkd; }
+ set { checkd = value; }
+ }
+
+ public bool CheckedInconsistent {
+ get { return checkedInconsistent; }
+ set { checkedInconsistent = value; }
+ }
+
+ public bool UseMarkup {
+ get { return useMarkup; }
+ set { useMarkup = value; }
+ }
+
+ // When set in an update handler, the command manager will ignore this handler method
+ // and will keep looking in the command route.
+ public bool Bypass {
+ get { return bypass; }
+ set { bypass = value; }
+ }
+
+ public CommandArrayInfo ArrayInfo {
+ get; internal set;
+ }
+
+ public object DataItem {
+ get; internal set;
+ }
+
+ public bool IsArraySeparator {
+ get; internal set;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfoSet.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfoSet.cs
new file mode 100644
index 0000000000..80ebafe453
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandInfoSet.cs
@@ -0,0 +1,47 @@
+//
+// CommandInfoSet.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandInfoSet: CommandInfo
+ {
+ CommandArrayInfo commandInfos;
+
+ public CommandInfoSet ()
+ {
+ commandInfos = new CommandArrayInfo (this);
+ }
+
+ public CommandArrayInfo CommandInfos {
+ get { return commandInfos; }
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs
new file mode 100644
index 0000000000..96ce6e241f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs
@@ -0,0 +1,1448 @@
+//
+// CommandManager.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+using System;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+using MonoDevelop.Components.Commands.ExtensionNodes;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandManager: IDisposable
+ {
+ public static CommandManager Main = new CommandManager ();
+ Gtk.Window rootWidget;
+ KeyBindingManager bindings;
+ Gtk.AccelGroup accelGroup;
+ string mode;
+
+ Dictionary<object,Command> cmds = new Dictionary<object,Command> ();
+ Hashtable handlerInfo = new Hashtable ();
+ List<ICommandBar> toolbars = new List<ICommandBar> ();
+ CommandTargetChain globalHandlerChain;
+ ArrayList commandUpdateErrors = new ArrayList ();
+ ArrayList visitors = new ArrayList ();
+ Dictionary<Gtk.Window,Gtk.Window> topLevelWindows = new Dictionary<Gtk.Window,Gtk.Window> ();
+ Stack delegatorStack = new Stack ();
+
+ HashSet<object> visitedTargets = new HashSet<object> ();
+
+ bool disposed;
+ bool toolbarUpdaterRunning;
+ bool enableToolbarUpdate;
+ int guiLock;
+
+ // Fields used to keep track of the application focus
+ bool appHasFocus;
+ Gtk.Window lastFocused;
+ DateTime focusCheckDelayTimeout = DateTime.MinValue;
+
+ internal static readonly object CommandRouteTerminator = new object ();
+
+ internal bool handlerFoundInMulticast;
+
+ public CommandManager (): this (null)
+ {
+ }
+
+ public CommandManager (Gtk.Window root)
+ {
+ rootWidget = root;
+ bindings = new KeyBindingManager ();
+ ActionCommand c = new ActionCommand (CommandSystemCommands.ToolbarList, "Toolbar List", null, null, ActionType.Check);
+ c.CommandArray = true;
+ RegisterCommand (c);
+ }
+
+ public void LoadCommands (string addinPath)
+ {
+ AddinManager.AddExtensionNodeHandler (addinPath, OnExtensionChange);
+ }
+
+ public void LoadKeyBindingSchemes (string addinPath)
+ {
+ KeyBindingService.LoadBindingsFromExtensionPath (addinPath);
+ }
+
+ void OnExtensionChange (object s, ExtensionNodeEventArgs args)
+ {
+ if (args.Change == ExtensionChange.Add) {
+ if (args.ExtensionNode is CommandCodon)
+ RegisterCommand ((Command) args.ExtensionObject);
+ else
+ // It's a category node. Track changes in the category.
+ args.ExtensionNode.ExtensionNodeChanged += OnExtensionChange;
+ }
+ else {
+ if (args.ExtensionNode is CommandCodon)
+ UnregisterCommand ((Command)args.ExtensionObject);
+ else
+ args.ExtensionNode.ExtensionNodeChanged -= OnExtensionChange;
+ }
+ }
+
+ public Gtk.MenuBar CreateMenuBar (string addinPath)
+ {
+ CommandEntrySet cset = CreateCommandEntrySet (addinPath);
+ return CreateMenuBar (addinPath, cset);
+ }
+
+ public Gtk.Toolbar[] CreateToolbarSet (string addinPath)
+ {
+ ArrayList bars = new ArrayList ();
+
+ CommandEntrySet cset = CreateCommandEntrySet (addinPath);
+ foreach (CommandEntry ce in cset) {
+ CommandEntrySet ces = ce as CommandEntrySet;
+ if (ces != null)
+ bars.Add (CreateToolbar (addinPath + "/" + ces.CommandId, ces));
+ }
+ return (Gtk.Toolbar[]) bars.ToArray (typeof(Gtk.Toolbar));
+ }
+
+ public Gtk.Toolbar CreateToolbar (string addinPath)
+ {
+ CommandEntrySet cset = CreateCommandEntrySet (addinPath);
+ return CreateToolbar (addinPath, cset);
+ }
+
+ public Gtk.Menu CreateMenu (string addinPath)
+ {
+ CommandEntrySet cset = CreateCommandEntrySet (addinPath);
+ return CreateMenu (cset);
+ }
+
+ public void ShowContextMenu (string addinPath)
+ {
+ ShowContextMenu (CreateCommandEntrySet (addinPath));
+ }
+
+ public CommandEntrySet CreateCommandEntrySet (ExtensionContext ctx, string addinPath)
+ {
+ CommandEntrySet cset = new CommandEntrySet ();
+ object[] items = ctx.GetExtensionObjects (addinPath, false);
+ foreach (CommandEntry e in items)
+ cset.Add (e);
+ return cset;
+ }
+
+ public CommandEntrySet CreateCommandEntrySet (string addinPath)
+ {
+ CommandEntrySet cset = new CommandEntrySet ();
+ object[] items = AddinManager.GetExtensionObjects (addinPath, false);
+ foreach (CommandEntry e in items)
+ cset.Add (e);
+ return cset;
+ }
+
+ bool CanUseBinding (string mode, string binding)
+ {
+ if (!bindings.BindingExists (binding))
+ return false;
+
+ if (mode == null && bindings.ModeExists (binding)) {
+ // binding is a simple accel and is registered as a mode... modes take precedence
+ return false;
+ }
+
+ return true;
+ }
+
+ [GLib.ConnectBefore]
+ void OnKeyPressed (object o, Gtk.KeyPressEventArgs e)
+ {
+ bool complete;
+ string accel = KeyBindingManager.AccelFromKey (e.Event, out complete);
+
+ if (!complete) {
+ // incomplete accel
+ e.RetVal = true;
+ return;
+ }
+
+ string binding = KeyBindingManager.Binding (mode, accel);
+ List<Command> commands = null;
+
+ if (CanUseBinding (mode, binding)) {
+ commands = bindings.Commands (binding);
+ e.RetVal = true;
+ mode = null;
+ } else if (mode != null && CanUseBinding (null, accel)) {
+ // fall back to accel
+ commands = bindings.Commands (accel);
+ e.RetVal = true;
+ mode = null;
+ } else if (bindings.ModeExists (accel)) {
+ e.RetVal = true;
+ mode = accel;
+ return;
+ } else {
+ e.RetVal = false;
+ mode = null;
+ return;
+ }
+
+ for (int i = 0; i < commands.Count; i++) {
+ CommandInfo cinfo = GetCommandInfo (commands[i].Id, null);
+ if (cinfo.Enabled && cinfo.Visible && DispatchCommand (commands[i].Id))
+ return;
+ }
+
+ e.RetVal = false;
+ mode = null;
+ }
+
+ public void SetRootWindow (Gtk.Window root)
+ {
+ if (rootWidget != null)
+ rootWidget.KeyPressEvent -= OnKeyPressed;
+
+ rootWidget = root;
+ rootWidget.AddAccelGroup (AccelGroup);
+ RegisterTopWindow (rootWidget);
+ }
+
+ void RegisterTopWindow (Gtk.Window win)
+ {
+ if (!topLevelWindows.ContainsKey (win)) {
+ topLevelWindows.Add (win, win);
+ win.KeyPressEvent += OnKeyPressed;
+ win.Destroyed += TopLevelDestroyed;
+ }
+ }
+
+ void TopLevelDestroyed (object o, EventArgs args)
+ {
+ Gtk.Window w = (Gtk.Window) o;
+ w.Destroyed -= TopLevelDestroyed;
+ w.KeyPressEvent -= OnKeyPressed;
+ topLevelWindows.Remove (w);
+ if (w == lastFocused)
+ lastFocused = null;
+ }
+
+ public void Dispose ()
+ {
+ disposed = true;
+ bindings.Dispose ();
+ lastFocused = null;
+ }
+
+ public void LockAll ()
+ {
+ guiLock++;
+ if (guiLock == 1) {
+ foreach (ICommandBar toolbar in toolbars)
+ toolbar.SetEnabled (false);
+ }
+ }
+
+ public void UnlockAll ()
+ {
+ if (guiLock == 1) {
+ foreach (ICommandBar toolbar in toolbars)
+ toolbar.SetEnabled (true);
+ }
+
+ if (guiLock > 0)
+ guiLock--;
+ }
+
+ public bool EnableIdleUpdate {
+ get { return enableToolbarUpdate; }
+ set {
+ if (enableToolbarUpdate != value) {
+ if (value) {
+ if (toolbars.Count > 0 || visitors.Count > 0) {
+ if (!toolbarUpdaterRunning) {
+ GLib.Timeout.Add (500, new GLib.TimeoutHandler (UpdateStatus));
+ toolbarUpdaterRunning = true;
+ }
+ }
+ } else {
+ toolbarUpdaterRunning = false;
+ }
+ enableToolbarUpdate = value;
+ }
+ }
+ }
+
+ public void RegisterCommand (Command cmd)
+ {
+ KeyBindingService.StoreDefaultBinding (cmd);
+ KeyBindingService.LoadBinding (cmd);
+
+ cmds[cmd.Id] = cmd;
+ bindings.RegisterCommand (cmd);
+ }
+
+ public void UnregisterCommand (Command cmd)
+ {
+ bindings.UnregisterCommand (cmd);
+ cmds.Remove (cmd.Id);
+ }
+
+ public void LoadUserBindings ()
+ {
+ foreach (Command cmd in cmds.Values)
+ KeyBindingService.LoadBinding (cmd);
+ }
+
+ public void RegisterGlobalHandler (object handler)
+ {
+ globalHandlerChain = CommandTargetChain.AddTarget (globalHandlerChain, handler);
+ }
+
+ public void UnregisterGlobalHandler (object handler)
+ {
+ globalHandlerChain = CommandTargetChain.RemoveTarget (globalHandlerChain, handler);
+ }
+
+ public void RegisterCommandTargetVisitor (ICommandTargetVisitor visitor)
+ {
+ visitors.Add (visitor);
+ if (enableToolbarUpdate && !toolbarUpdaterRunning) {
+ GLib.Timeout.Add (500, new GLib.TimeoutHandler (UpdateStatus));
+ toolbarUpdaterRunning = true;
+ }
+ }
+
+ public void UnregisterCommandTargetVisitor (ICommandTargetVisitor visitor)
+ {
+ visitors.Remove (visitor);
+ }
+
+ public Command GetCommand (object cmdId)
+ {
+ // Include the type name when converting enum members to ids.
+ cmdId = ToCommandId (cmdId);
+
+ Command cmd;
+ if (cmds.TryGetValue (cmdId, out cmd))
+ return cmd;
+ else
+ return null;
+ }
+
+ public IEnumerable<Command> GetCommands ()
+ {
+ return cmds.Values;
+ }
+
+ public ActionCommand GetActionCommand (object cmdId)
+ {
+ return GetCommand (cmdId) as ActionCommand;
+ }
+
+ public Gtk.MenuBar CreateMenuBar (string name, CommandEntrySet entrySet)
+ {
+ Gtk.MenuBar topMenu = new CommandMenuBar (this);
+ foreach (CommandEntry entry in entrySet) {
+ Gtk.MenuItem mi = entry.CreateMenuItem (this);
+ CustomItem ci = mi.Child as CustomItem;
+ if (ci != null)
+ ci.SetMenuStyle (topMenu);
+ topMenu.Append (mi);
+ }
+ return topMenu;
+ }
+
+/* public Gtk.Toolbar CreateToolbar (CommandEntrySet entrySet)
+ {
+ return CreateToolbar ("", entrySet);
+ }
+
+*/
+ public Gtk.Menu CreateMenu (CommandEntrySet entrySet, CommandMenu menu)
+ {
+ foreach (CommandEntry entry in entrySet) {
+ Gtk.MenuItem mi = entry.CreateMenuItem (this);
+ CustomItem ci = mi.Child as CustomItem;
+ if (ci != null)
+ ci.SetMenuStyle (menu);
+ menu.Append (mi);
+ }
+ return menu;
+ }
+
+ public Gtk.Menu CreateMenu (CommandEntrySet entrySet)
+ {
+ return CreateMenu (entrySet, new CommandMenu (this));
+ }
+
+ public void InsertOptions (Gtk.Menu menu, CommandEntrySet entrySet, int index)
+ {
+ foreach (CommandEntry entry in entrySet) {
+ Gtk.MenuItem item = entry.CreateMenuItem (this);
+ CustomItem ci = item.Child as CustomItem;
+ if (ci != null)
+ ci.SetMenuStyle (menu);
+ int n = menu.Children.Length;
+ menu.Insert (item, index);
+ if (item is ICommandUserItem)
+ ((ICommandUserItem)item).Update (null);
+ else
+ item.Show ();
+ index += menu.Children.Length - n;
+ }
+ }
+
+ public void ShowContextMenu (CommandEntrySet entrySet)
+ {
+ ShowContextMenu (entrySet, null);
+ }
+
+ public void ShowContextMenu (CommandEntrySet entrySet, object initialTarget)
+ {
+ CommandMenu menu = (CommandMenu) CreateMenu (entrySet);
+ ShowContextMenu (menu, initialTarget);
+ }
+
+ public void ShowContextMenu (Gtk.Menu menu)
+ {
+ menu.Popup (null, null, null, 0, Gtk.Global.CurrentEventTime);
+ }
+
+ public void ShowContextMenu (Gtk.Menu menu, object initialCommandTarget)
+ {
+ if (menu is CommandMenu) {
+ ((CommandMenu)menu).InitialCommandTarget = initialCommandTarget;
+ }
+ ShowContextMenu (menu);
+ }
+
+ public Gtk.Toolbar CreateToolbar (CommandEntrySet entrySet)
+ {
+ return CreateToolbar ("", entrySet, null);
+ }
+
+ public Gtk.Toolbar CreateToolbar (CommandEntrySet entrySet, object initialTarget)
+ {
+ return CreateToolbar ("", entrySet, initialTarget);
+ }
+
+ public Gtk.Toolbar CreateToolbar (string id, CommandEntrySet entrySet)
+ {
+ return CreateToolbar (id, entrySet, null);
+ }
+
+ public Gtk.Toolbar CreateToolbar (string id, CommandEntrySet entrySet, object initialTarget)
+ {
+ CommandToolbar toolbar = new CommandToolbar (this, id, entrySet.Name);
+ toolbar.InitialCommandTarget = initialTarget;
+
+ foreach (CommandEntry entry in entrySet) {
+ Gtk.ToolItem ti = entry.CreateToolItem (this);
+ CustomItem ci = ti.Child as CustomItem;
+ if (ci != null)
+ ci.SetToolbarStyle (toolbar);
+ toolbar.Add (ti);
+ }
+ ToolbarTracker tt = new ToolbarTracker ();
+ tt.Track (toolbar);
+ return toolbar;
+ }
+
+ public bool DispatchCommand (object commandId)
+ {
+ return DispatchCommand (commandId, null, null);
+ }
+
+ public bool DispatchCommand (object commandId, object dataItem)
+ {
+ return DispatchCommand (commandId, dataItem, null);
+ }
+
+ public bool DispatchCommand (object commandId, object dataItem, object initialTarget)
+ {
+ if (guiLock > 0)
+ return false;
+
+ List<HandlerCallback> handlers = new List<HandlerCallback> ();
+ ActionCommand cmd = null;
+ try {
+ cmd = GetActionCommand (commandId);
+ if (cmd == null)
+ return false;
+
+ object cmdTarget = GetFirstCommandTarget (initialTarget);
+ CommandInfo info = new CommandInfo (cmd);
+
+ while (cmdTarget != null)
+ {
+ HandlerTypeInfo typeInfo = GetTypeHandlerInfo (cmdTarget);
+
+ bool bypass = false;
+
+ CommandUpdaterInfo cui = typeInfo.GetCommandUpdater (commandId);
+ if (cui != null) {
+ if (cmd.CommandArray) {
+ // Make sure that the option is still active
+ CommandArrayInfo ainfo = new CommandArrayInfo (info);
+ cui.Run (cmdTarget, ainfo);
+ if (!ainfo.Bypass) {
+ bool found = false;
+ foreach (CommandInfo ci in ainfo) {
+ if (dataItem == ci.DataItem || Object.Equals (dataItem, ci.DataItem)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+ } else
+ bypass = true;
+ } else {
+ info.Bypass = false;
+ cui.Run (cmdTarget, info);
+ bypass = info.Bypass;
+
+ if (!bypass && (!info.Enabled || !info.Visible))
+ return false;
+ }
+ }
+
+ if (!bypass) {
+ CommandHandlerInfo chi = typeInfo.GetCommandHandler (commandId);
+ if (chi != null) {
+ object localTarget = cmdTarget;
+ if (cmd.CommandArray) {
+ handlers.Add (delegate {
+ chi.Run (localTarget, cmd, dataItem);
+ });
+ }
+ else {
+ handlers.Add (delegate {
+ chi.Run (localTarget, cmd);
+ });
+ }
+ handlerFoundInMulticast = true;
+ cmdTarget = NextMulticastTarget ();
+ if (cmdTarget == null)
+ break;
+ else
+ continue;
+ }
+ }
+ cmdTarget = GetNextCommandTarget (cmdTarget);
+ }
+
+ if (handlers.Count > 0) {
+ foreach (HandlerCallback c in handlers)
+ c ();
+ UpdateToolbars ();
+ return true;
+ }
+
+ if (cmd.DispatchCommand (dataItem)) {
+ UpdateToolbars ();
+ return true;
+ }
+ }
+ catch (Exception ex) {
+ string name = (cmd != null && cmd.Text != null && cmd.Text.Length > 0) ? cmd.Text : commandId.ToString ();
+ name = name.Replace ("_","");
+ ReportError (commandId, "Error while executing command: " + name, ex);
+ }
+ return false;
+ }
+
+ public CommandInfo GetCommandInfo (object commandId, object initialTarget)
+ {
+ ActionCommand cmd = GetActionCommand (commandId);
+ if (cmd == null)
+ throw new InvalidOperationException ("Invalid action command id: " + commandId);
+
+ NotifyCommandTargetScanStarted ();
+ CommandInfo info = new CommandInfo (cmd);
+
+ try {
+ object cmdTarget = GetFirstCommandTarget (initialTarget);
+ bool multiCastEnabled = true;
+ bool multiCastVisible = false;
+
+ while (cmdTarget != null)
+ {
+ HandlerTypeInfo typeInfo = GetTypeHandlerInfo (cmdTarget);
+ CommandUpdaterInfo cui = typeInfo.GetCommandUpdater (commandId);
+
+ bool bypass = false;
+ bool handlerFound = false;
+
+ if (cui != null) {
+ if (cmd.CommandArray) {
+ info.ArrayInfo = new CommandArrayInfo (info);
+ cui.Run (cmdTarget, info.ArrayInfo);
+ if (!info.ArrayInfo.Bypass) {
+ if (guiLock > 0)
+ info.Enabled = false;
+ handlerFound = true;
+ }
+ }
+ else {
+ info.Bypass = false;
+ cui.Run (cmdTarget, info);
+ if (!info.Bypass) {
+ if (guiLock > 0)
+ info.Enabled = false;
+ handlerFound = true;
+ }
+ }
+ if (!handlerFound)
+ bypass = true;
+ }
+
+ if (handlerFound) {
+ handlerFoundInMulticast = true;
+ if (!info.Enabled || !info.Visible)
+ multiCastEnabled = false;
+ if (info.Visible)
+ multiCastVisible = true;
+ cmdTarget = NextMulticastTarget ();
+ if (cmdTarget == null) {
+ if (!multiCastEnabled)
+ info.Enabled = false;
+ if (multiCastVisible)
+ info.Visible = true;
+ return info;
+ }
+ continue;
+ }
+ else if (!bypass && typeInfo.GetCommandHandler (commandId) != null) {
+ info.Enabled = guiLock == 0;
+ info.Visible = true;
+ return info;
+ }
+
+ cmdTarget = GetNextCommandTarget (cmdTarget);
+ }
+
+ cmd.UpdateCommandInfo (info);
+ }
+ catch (Exception ex) {
+ if (!commandUpdateErrors.Contains (commandId)) {
+ commandUpdateErrors.Add (commandId);
+ ReportError (commandId, "Error while updating status of command: " + commandId, ex);
+ }
+ info.Enabled = false;
+ info.Visible = true;
+ } finally {
+ NotifyCommandTargetScanFinished ();
+ }
+
+ if (guiLock > 0)
+ info.Enabled = false;
+ return info;
+ }
+
+ public object VisitCommandTargets (ICommandTargetVisitor visitor, object initialTarget)
+ {
+ object cmdTarget = GetFirstCommandTarget (initialTarget);
+
+ while (cmdTarget != null)
+ {
+ if (visitor.Visit (cmdTarget))
+ return cmdTarget;
+
+ cmdTarget = GetNextCommandTarget (cmdTarget);
+ }
+
+ visitor.Visit (null);
+ return null;
+ }
+
+ internal bool DispatchCommandFromAccel (object commandId, object dataItem, object initialTarget)
+ {
+ // Dispatches a command that has been fired by an accelerator.
+ // The difference from a normal dispatch is that there may
+ // be several commands bound to the same accelerator, and in
+ // this case it will execute the one that is enabled.
+
+ // If the original key has been modified
+ // by a CommandUpdate handler, it won't work. That's a limitation,
+ // but checking all possible commands would be too slow.
+
+ Command cmd = GetCommand (commandId);
+ if (cmd == null)
+ return false;
+
+ string accel = cmd.AccelKey;
+ if (accel == null)
+ return DispatchCommand (commandId, dataItem, initialTarget);
+
+ List<Command> list = bindings.Commands (accel);
+ if (list == null || list.Count == 1)
+ // The command is not overloaded, so it can be handled normally.
+ return DispatchCommand (commandId, dataItem, initialTarget);
+
+ // Get the accelerator used to fire the command and make sure it has not changed.
+ CommandInfo accelInfo = GetCommandInfo (commandId, initialTarget);
+ bool res = DispatchCommand (commandId, accelInfo.DataItem, initialTarget);
+
+ // If the accelerator has changed, we can't handle overloading.
+ if (res || accel != accelInfo.AccelKey)
+ return res;
+
+ // Execution failed. Now try to execute alternate commands
+ // bound to the same key.
+
+ for (int i = 0; i < list.Count; i++) {
+ if (list[i].Id == commandId) // already handled above.
+ continue;
+
+ CommandInfo cinfo = GetCommandInfo (list[i].Id, initialTarget);
+ if (cinfo.AccelKey != accel) // Key changed by a handler, just ignore the command.
+ continue;
+
+ if (DispatchCommand (list[i].Id, cinfo.DataItem, initialTarget))
+ return true;
+ }
+
+ return false;
+ }
+
+ internal Gtk.AccelGroup AccelGroup {
+ get {
+ if (accelGroup == null) {
+ accelGroup = new Gtk.AccelGroup ();
+ }
+ return accelGroup;
+ }
+ }
+
+ internal void NotifySelected (CommandInfo cmdInfo)
+ {
+ if (CommandSelected != null) {
+ CommandSelectedEventArgs args = new CommandSelectedEventArgs (cmdInfo);
+ CommandSelected (this, args);
+ }
+ }
+
+ internal void NotifyDeselected ()
+ {
+ if (CommandDeselected != null)
+ CommandDeselected (this, EventArgs.Empty);
+ }
+
+ HandlerTypeInfo GetTypeHandlerInfo (object cmdTarget)
+ {
+ HandlerTypeInfo typeInfo = (HandlerTypeInfo) handlerInfo [cmdTarget.GetType ()];
+ if (typeInfo != null) return typeInfo;
+
+ Type type = cmdTarget.GetType ();
+ typeInfo = new HandlerTypeInfo ();
+
+ List<CommandHandlerInfo> handlers = new List<CommandHandlerInfo> ();
+ List<CommandUpdaterInfo> updaters = new List<CommandUpdaterInfo> ();
+
+ Type curType = type;
+ while (curType != null && curType.Assembly != typeof(Gtk.Widget).Assembly && curType.Assembly != typeof(object).Assembly) {
+ MethodInfo[] methods = curType.GetMethods (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
+ foreach (MethodInfo method in methods) {
+
+ ICommandUpdateHandler customHandlerChain = null;
+ ICommandArrayUpdateHandler customArrayHandlerChain = null;
+ ICommandTargetHandler customTargetHandlerChain = null;
+ ICommandArrayTargetHandler customArrayTargetHandlerChain = null;
+ List<CommandHandlerInfo> methodHandlers = new List<CommandHandlerInfo> ();
+
+ foreach (object attr in method.GetCustomAttributes (true)) {
+ if (attr is CommandHandlerAttribute)
+ methodHandlers.Add (new CommandHandlerInfo (method, (CommandHandlerAttribute) attr));
+ else if (attr is CommandUpdateHandlerAttribute)
+ AddUpdater (updaters, method, (CommandUpdateHandlerAttribute) attr);
+ else {
+ customHandlerChain = ChainHandler (customHandlerChain, attr);
+ customArrayHandlerChain = ChainHandler (customArrayHandlerChain, attr);
+ customTargetHandlerChain = ChainHandler (customTargetHandlerChain, attr);
+ customArrayTargetHandlerChain = ChainHandler (customArrayTargetHandlerChain, attr);
+ }
+ }
+
+ foreach (object attr in type.GetCustomAttributes (true)) {
+ customHandlerChain = ChainHandler (customHandlerChain, attr);
+ customArrayHandlerChain = ChainHandler (customArrayHandlerChain, attr);
+ customTargetHandlerChain = ChainHandler (customTargetHandlerChain, attr);
+ customArrayTargetHandlerChain = ChainHandler (customArrayTargetHandlerChain, attr);
+ }
+
+ if (methodHandlers.Count > 0) {
+ if (customHandlerChain != null || customArrayHandlerChain != null) {
+ // There are custom handlers. Create update handlers for all commands
+ // that the method handles so the custom update handlers can be chained
+ foreach (CommandHandlerInfo ci in methodHandlers) {
+ CommandUpdaterInfo c = AddUpdateHandler (updaters, ci.CommandId);
+ c.AddCustomHandlers (customHandlerChain, customArrayHandlerChain);
+ }
+ }
+ if (customTargetHandlerChain != null || customArrayTargetHandlerChain != null) {
+ foreach (CommandHandlerInfo ci in methodHandlers)
+ ci.AddCustomHandlers (customTargetHandlerChain, customArrayTargetHandlerChain);
+ }
+ }
+ handlers.AddRange (methodHandlers);
+ }
+ curType = curType.BaseType;
+ }
+
+ if (handlers.Count > 0)
+ typeInfo.CommandHandlers = handlers.ToArray ();
+ if (updaters.Count > 0)
+ typeInfo.CommandUpdaters = updaters.ToArray ();
+
+ handlerInfo [type] = typeInfo;
+ return typeInfo;
+ }
+
+ CommandUpdaterInfo AddUpdateHandler (List<CommandUpdaterInfo> methodUpdaters, object cmdId)
+ {
+ foreach (CommandUpdaterInfo ci in methodUpdaters) {
+ if (ci.CommandId.Equals (cmdId))
+ return ci;
+ }
+ // Not found, it needs to be added
+ CommandUpdaterInfo cinfo = new CommandUpdaterInfo (cmdId);
+ methodUpdaters.Add (cinfo);
+ return cinfo;
+ }
+
+ void AddUpdater (List<CommandUpdaterInfo> methodUpdaters, MethodInfo method, CommandUpdateHandlerAttribute attr)
+ {
+ foreach (CommandUpdaterInfo ci in methodUpdaters) {
+ if (ci.CommandId.Equals (CommandManager.ToCommandId (attr.CommandId))) {
+ ci.Init (method, attr);
+ return;
+ }
+ }
+ // Not found, it needs to be added
+ CommandUpdaterInfo cinfo = new CommandUpdaterInfo (method, attr);
+ methodUpdaters.Add (cinfo);
+ }
+
+ ICommandArrayUpdateHandler ChainHandler (ICommandArrayUpdateHandler chain, object attr)
+ {
+ ICommandArrayUpdateHandler h = attr as ICommandArrayUpdateHandler;
+ if (h == null) return chain;
+ h.Next = chain ?? DefaultCommandHandler.Instance;
+ return h;
+ }
+
+ ICommandUpdateHandler ChainHandler (ICommandUpdateHandler chain, object attr)
+ {
+ ICommandUpdateHandler h = attr as ICommandUpdateHandler;
+ if (h == null) return chain;
+ h.Next = chain ?? DefaultCommandHandler.Instance;
+ return h;
+ }
+
+ ICommandTargetHandler ChainHandler (ICommandTargetHandler chain, object attr)
+ {
+ ICommandTargetHandler h = attr as ICommandTargetHandler;
+ if (h == null) return chain;
+ h.Next = chain ?? DefaultCommandHandler.Instance;
+ return h;
+ }
+
+ ICommandArrayTargetHandler ChainHandler (ICommandArrayTargetHandler chain, object attr)
+ {
+ ICommandArrayTargetHandler h = attr as ICommandArrayTargetHandler;
+ if (h == null) return chain;
+ h.Next = chain ?? DefaultCommandHandler.Instance;
+ return h;
+ }
+
+ object GetFirstCommandTarget (object initialTarget)
+ {
+ delegatorStack.Clear ();
+ visitedTargets.Clear ();
+ handlerFoundInMulticast = false;
+ object cmdTarget;
+ if (initialTarget != null)
+ cmdTarget = initialTarget;
+ else {
+ cmdTarget = GetActiveWidget (rootWidget);
+ if (cmdTarget == null) {
+ cmdTarget = globalHandlerChain;
+ }
+ }
+ visitedTargets.Add (cmdTarget);
+ return cmdTarget;
+ }
+
+ object GetNextCommandTarget (object cmdTarget)
+ {
+ if (cmdTarget is IMultiCastCommandRouter)
+ cmdTarget = new MultiCastDelegator (this, (IMultiCastCommandRouter)cmdTarget);
+
+ if (cmdTarget is ICommandDelegatorRouter) {
+ object oldCmdTarget = cmdTarget;
+ cmdTarget = ((ICommandDelegatorRouter)oldCmdTarget).GetDelegatedCommandTarget ();
+ if (cmdTarget != null)
+ delegatorStack.Push (oldCmdTarget);
+ else
+ cmdTarget = ((ICommandDelegatorRouter)oldCmdTarget).GetNextCommandTarget ();
+ }
+ else if (cmdTarget is ICommandRouter)
+ cmdTarget = ((ICommandRouter)cmdTarget).GetNextCommandTarget ();
+ else if (cmdTarget is Gtk.Widget)
+ cmdTarget = ((Gtk.Widget)cmdTarget).Parent;
+ else
+ cmdTarget = null;
+
+ if (cmdTarget == null || !visitedTargets.Add (cmdTarget)) {
+ if (delegatorStack.Count > 0) {
+ ICommandDelegatorRouter del = (ICommandDelegatorRouter) delegatorStack.Pop ();
+ cmdTarget = del.GetNextCommandTarget ();
+ if (cmdTarget == CommandManager.CommandRouteTerminator)
+ return null;
+ if (cmdTarget != null)
+ return cmdTarget;
+ }
+ return globalHandlerChain;
+ } else
+ return cmdTarget;
+ }
+
+ internal object NextMulticastTarget ()
+ {
+ while (delegatorStack.Count > 0) {
+ MultiCastDelegator del = delegatorStack.Pop () as MultiCastDelegator;
+ if (del != null) {
+ object cmdTarget = GetNextCommandTarget (del);
+ return cmdTarget == globalHandlerChain ? null : cmdTarget;
+ }
+ }
+ return null;
+ }
+
+ Gtk.Widget GetActiveWidget (Gtk.Window win)
+ {
+ Gtk.Window[] wins = Gtk.Window.ListToplevels ();
+
+ bool hasFocus = false;
+ bool lastFocusedExists = lastFocused == null;
+ Gtk.Window newFocused = null;
+ foreach (Gtk.Window w in wins) {
+ if (w.Visible) {
+ if (w.HasToplevelFocus) {
+ hasFocus = true;
+ newFocused = w;
+ }
+ if (w.IsActive && w.Type == Gtk.WindowType.Toplevel && !(w is Gtk.Dialog)) {
+ win = w;
+ break;
+ }
+ if (lastFocused == w) {
+ lastFocusedExists = true;
+ }
+ }
+ }
+
+ lastFocused = newFocused;
+ UpdateAppFocusStatus (hasFocus, lastFocusedExists);
+
+ if (win != null) {
+ RegisterTopWindow (win);
+ Gtk.Widget widget = win;
+ while (widget is Gtk.Container) {
+ Gtk.Widget child = ((Gtk.Container)widget).FocusChild;
+ if (child != null)
+ widget = child;
+ else
+ break;
+ }
+ return widget;
+ }
+ return win;
+ }
+
+ bool UpdateStatus ()
+ {
+ if (!disposed)
+ UpdateToolbars ();
+ else
+ toolbarUpdaterRunning = false;
+
+ return toolbarUpdaterRunning;
+ }
+
+ public void RegisterCommandBar (ICommandBar commandBar)
+ {
+ if (toolbars.Contains (commandBar))
+ return;
+
+ toolbars.Add (commandBar);
+ if (enableToolbarUpdate && !toolbarUpdaterRunning) {
+ GLib.Timeout.Add (500, new GLib.TimeoutHandler (UpdateStatus));
+ toolbarUpdaterRunning = true;
+ }
+ commandBar.SetEnabled (guiLock == 0);
+
+ object activeWidget = GetActiveWidget (rootWidget);
+ commandBar.Update (activeWidget);
+ }
+
+ public void UnregisterCommandBar (ICommandBar commandBar)
+ {
+ toolbars.Remove (commandBar);
+ }
+
+ void UpdateToolbars ()
+ {
+ // This might get called after the app has exited, e.g. after executing the quit command
+ // It then queries widgets, which resurrects widget wrappers, which breaks on managed widgets
+ if (this.disposed)
+ return;
+
+ object activeWidget = GetActiveWidget (rootWidget);
+ foreach (ICommandBar toolbar in toolbars) {
+ toolbar.Update (activeWidget);
+ }
+ foreach (ICommandTargetVisitor v in visitors)
+ VisitCommandTargets (v, null);
+ }
+
+ void UpdateAppFocusStatus (bool hasFocus, bool lastFocusedExists)
+ {
+ if (hasFocus != appHasFocus) {
+ // The last focused window has been destroyed. Wait a few ms since another app's window
+ // may gain focus again
+ DateTime now = DateTime.Now;
+ if (now < focusCheckDelayTimeout)
+ return;
+ if (!hasFocus && !lastFocusedExists) {
+ focusCheckDelayTimeout = now.AddMilliseconds (100);
+ return;
+ }
+ focusCheckDelayTimeout = DateTime.MinValue;
+
+ appHasFocus = hasFocus;
+ if (appHasFocus) {
+ if (ApplicationFocusIn != null)
+ ApplicationFocusIn (this, EventArgs.Empty);
+ } else {
+ if (ApplicationFocusOut != null)
+ ApplicationFocusOut (this, EventArgs.Empty);
+ }
+ }
+ }
+
+ public void ReportError (object commandId, string message, Exception ex)
+ {
+ if (CommandError != null) {
+ CommandErrorArgs args = new CommandErrorArgs (commandId, message, ex);
+ CommandError (this, args);
+ }
+ }
+
+ internal static object ToCommandId (object ob)
+ {
+ // Include the type name when converting enum members to ids.
+ if (ob == null)
+ return null;
+ else if (ob.GetType ().IsEnum)
+ return ob.GetType ().FullName + "." + ob;
+ else
+ return ob;
+ }
+
+ void NotifyCommandTargetScanStarted ()
+ {
+ if (CommandTargetScanStarted != null)
+ CommandTargetScanStarted (this, EventArgs.Empty);
+ }
+
+ void NotifyCommandTargetScanFinished ()
+ {
+ if (CommandTargetScanFinished != null)
+ CommandTargetScanFinished (this, EventArgs.Empty);
+ }
+
+ public event CommandErrorHandler CommandError;
+ public event EventHandler<CommandSelectedEventArgs> CommandSelected;
+ public event EventHandler CommandDeselected;
+ public event EventHandler ApplicationFocusIn; // Fired when the application gets the focus
+ public event EventHandler ApplicationFocusOut; // Fired when the application loses the focus
+ public event EventHandler CommandTargetScanStarted;
+ public event EventHandler CommandTargetScanFinished;
+ }
+
+ internal class HandlerTypeInfo
+ {
+ public CommandHandlerInfo[] CommandHandlers;
+ public CommandUpdaterInfo[] CommandUpdaters;
+
+ public CommandHandlerInfo GetCommandHandler (object commandId)
+ {
+ if (CommandHandlers == null) return null;
+ foreach (CommandHandlerInfo cui in CommandHandlers)
+ if (cui.CommandId.Equals (commandId))
+ return cui;
+ return null;
+ }
+
+ public CommandUpdaterInfo GetCommandUpdater (object commandId)
+ {
+ if (CommandUpdaters == null) return null;
+ foreach (CommandUpdaterInfo cui in CommandUpdaters)
+ if (cui.CommandId.Equals (commandId))
+ return cui;
+ return null;
+ }
+ }
+
+
+ internal class CommandMethodInfo
+ {
+ public object CommandId;
+ protected MethodInfo Method;
+
+ public CommandMethodInfo (MethodInfo method, CommandMethodAttribute attr)
+ {
+ Init (method, attr);
+ }
+
+ protected void Init (MethodInfo method, CommandMethodAttribute attr)
+ {
+ // Don't assign the method if there is already one assigned (maybe from a subclass)
+ if (this.Method == null) {
+ this.Method = method;
+ CommandId = CommandManager.ToCommandId (attr.CommandId);
+ }
+ }
+
+ public CommandMethodInfo (object commandId)
+ {
+ CommandId = CommandManager.ToCommandId (commandId);
+ }
+ }
+
+ internal class CommandHandlerInfo: CommandMethodInfo
+ {
+ ICommandTargetHandler customHandlerChain;
+ ICommandArrayTargetHandler customArrayHandlerChain;
+
+ public CommandHandlerInfo (MethodInfo method, CommandHandlerAttribute attr): base (method, attr)
+ {
+ ParameterInfo[] pars = method.GetParameters ();
+ if (pars.Length > 1)
+ throw new InvalidOperationException ("Invalid signature for command handler: " + method.DeclaringType + "." + method.Name + "()");
+ }
+
+ public void Run (object cmdTarget, Command cmd)
+ {
+ if (customHandlerChain != null) {
+ cmd.HandlerData = Method;
+ customHandlerChain.Run (cmdTarget, cmd);
+ }
+ else
+ Method.Invoke (cmdTarget, null);
+ }
+
+ public void Run (object cmdTarget, Command cmd, object dataItem)
+ {
+ if (customArrayHandlerChain != null) {
+ cmd.HandlerData = Method;
+ customArrayHandlerChain.Run (cmdTarget, cmd, dataItem);
+ }
+ else
+ Method.Invoke (cmdTarget, new object[] {dataItem});
+ }
+
+ public void AddCustomHandlers (ICommandTargetHandler handlerChain, ICommandArrayTargetHandler arrayHandlerChain)
+ {
+ this.customHandlerChain = handlerChain;
+ this.customArrayHandlerChain = arrayHandlerChain;
+ }
+ }
+
+ internal class CommandUpdaterInfo: CommandMethodInfo
+ {
+ ICommandUpdateHandler customHandlerChain;
+ ICommandArrayUpdateHandler customArrayHandlerChain;
+
+ bool isArray;
+
+ public CommandUpdaterInfo (object commandId): base (commandId)
+ {
+ }
+
+ public CommandUpdaterInfo (MethodInfo method, CommandUpdateHandlerAttribute attr): base (method, attr)
+ {
+ Init (method, attr);
+ }
+
+ public void Init (MethodInfo method, CommandUpdateHandlerAttribute attr)
+ {
+ base.Init (method, attr);
+ ParameterInfo[] pars = method.GetParameters ();
+ if (pars.Length == 1) {
+ Type t = pars[0].ParameterType;
+
+ if (t == typeof(CommandArrayInfo)) {
+ isArray = true;
+ return;
+ } else if (t == typeof(CommandInfo))
+ return;
+ }
+ throw new InvalidOperationException ("Invalid signature for command update handler: " + method.DeclaringType + "." + method.Name + "()");
+ }
+
+ public void AddCustomHandlers (ICommandUpdateHandler handlerChain, ICommandArrayUpdateHandler arrayHandlerChain)
+ {
+ this.customHandlerChain = handlerChain;
+ this.customArrayHandlerChain = arrayHandlerChain;
+ }
+
+ public void Run (object cmdTarget, CommandInfo info)
+ {
+ if (customHandlerChain != null) {
+ info.UpdateHandlerData = Method;
+ customHandlerChain.CommandUpdate (cmdTarget, info);
+ } else {
+ if (Method == null)
+ throw new InvalidOperationException ("Invalid custom update handler. An implementation of ICommandUpdateHandler was expected.");
+ if (isArray)
+ throw new InvalidOperationException ("Invalid signature for command update handler: " + Method.DeclaringType + "." + Method.Name + "()");
+ Method.Invoke (cmdTarget, new object[] {info} );
+ }
+ }
+
+ public void Run (object cmdTarget, CommandArrayInfo info)
+ {
+ if (customArrayHandlerChain != null) {
+ info.UpdateHandlerData = Method;
+ customArrayHandlerChain.CommandUpdate (cmdTarget, info);
+ } else {
+ if (Method == null)
+ throw new InvalidOperationException ("Invalid custom update handler. An implementation of ICommandArrayUpdateHandler was expected.");
+ if (!isArray)
+ throw new InvalidOperationException ("Invalid signature for command update handler: " + Method.DeclaringType + "." + Method.Name + "()");
+ Method.Invoke (cmdTarget, new object[] {info} );
+ }
+ }
+ }
+
+ class DefaultCommandHandler: ICommandUpdateHandler, ICommandArrayUpdateHandler, ICommandTargetHandler, ICommandArrayTargetHandler
+ {
+ public static DefaultCommandHandler Instance = new DefaultCommandHandler ();
+
+ public void CommandUpdate (object target, CommandInfo info)
+ {
+ MethodInfo mi = (MethodInfo) info.UpdateHandlerData;
+ if (mi != null)
+ mi.Invoke (target, new object[] {info} );
+ }
+
+ public void CommandUpdate (object target, CommandArrayInfo info)
+ {
+ MethodInfo mi = (MethodInfo) info.UpdateHandlerData;
+ if (mi != null)
+ mi.Invoke (target, new object[] {info} );
+ }
+
+ public void Run (object target, Command cmd)
+ {
+ MethodInfo mi = (MethodInfo) cmd.HandlerData;
+ if (mi != null)
+ mi.Invoke (target, new object[0] );
+ }
+
+ public void Run (object target, Command cmd, object data)
+ {
+ MethodInfo mi = (MethodInfo) cmd.HandlerData;
+ if (mi != null)
+ mi.Invoke (target, new object[] {data} );
+ }
+
+ ICommandArrayTargetHandler ICommandArrayTargetHandler.Next {
+ get {
+ return null;
+ }
+ set {
+ }
+ }
+
+ ICommandTargetHandler ICommandTargetHandler.Next {
+ get {
+ return null;
+ }
+ set {
+ }
+ }
+
+ ICommandArrayUpdateHandler ICommandArrayUpdateHandler.Next {
+ get {
+ // Last one in the chain
+ return null;
+ }
+ set {
+ }
+ }
+
+ public ICommandUpdateHandler Next {
+ get {
+ // Last one in the chain
+ return null;
+ }
+ set {
+ }
+ }
+ }
+
+ internal class ToolbarTracker
+ {
+ Gtk.IconSize lastSize;
+
+ public void Track (Gtk.Toolbar toolbar)
+ {
+ lastSize = toolbar.IconSize;
+ toolbar.AddNotification (OnToolbarPropChanged);
+ }
+
+ void OnToolbarPropChanged (object ob, GLib.NotifyArgs args)
+ {
+ Gtk.Toolbar t = (Gtk.Toolbar) ob;
+ if (lastSize != t.IconSize || args.Property == "orientation" || args.Property == "toolbar-style")
+ UpdateCustomItems (t);
+ lastSize = t.IconSize;
+ }
+
+ void UpdateCustomItems (Gtk.Toolbar t)
+ {
+ foreach (Gtk.ToolItem ti in t.Children) {
+ CustomItem ci = ti.Child as CustomItem;
+ if (ci != null)
+ ci.SetToolbarStyle (t);
+ }
+ }
+ }
+
+ class MultiCastDelegator: ICommandDelegatorRouter
+ {
+ IEnumerator enumerator;
+ object nextTarget;
+ CommandManager manager;
+ bool done;
+
+ public MultiCastDelegator (CommandManager manager, IMultiCastCommandRouter mcr)
+ {
+ this.manager = manager;
+ enumerator = mcr.GetCommandTargets ().GetEnumerator ();
+ }
+
+ public object GetNextCommandTarget ()
+ {
+ if (nextTarget != null)
+ return this;
+ else {
+ if (manager.handlerFoundInMulticast)
+ return manager.NextMulticastTarget ();
+ else
+ return null;
+ }
+ }
+
+ public object GetDelegatedCommandTarget ()
+ {
+ object currentTarget;
+ if (done)
+ return null;
+ if (nextTarget != null) {
+ currentTarget = nextTarget;
+ nextTarget = null;
+ } else {
+ if (enumerator.MoveNext ())
+ currentTarget = enumerator.Current;
+ else
+ return null;
+ }
+
+ if (enumerator.MoveNext ())
+ nextTarget = enumerator.Current;
+ else {
+ done = true;
+ nextTarget = null;
+ }
+
+ return currentTarget;
+ }
+ }
+
+ class CommandTargetChain: ICommandDelegatorRouter
+ {
+ object target;
+ internal CommandTargetChain Next;
+
+ public CommandTargetChain (object target)
+ {
+ this.target = target;
+ }
+
+ public object GetNextCommandTarget ()
+ {
+ if (Next == null)
+ return CommandManager.CommandRouteTerminator;
+ else
+ return Next;
+ }
+
+ public object GetDelegatedCommandTarget ()
+ {
+ return target;
+ }
+
+ public static CommandTargetChain RemoveTarget (CommandTargetChain chain, object target)
+ {
+ if (chain == null)
+ return null;
+ if (chain.target == target)
+ return chain.Next;
+ else if (chain.Next != null)
+ chain.Next = CommandTargetChain.RemoveTarget (chain.Next, target);
+ return chain;
+ }
+
+ public static CommandTargetChain AddTarget (CommandTargetChain chain, object target)
+ {
+ if (chain == null)
+ return new CommandTargetChain (target);
+ else {
+ chain.Next = AddTarget (chain.Next, target);
+ return chain;
+ }
+ }
+ }
+
+ delegate void HandlerCallback ();
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenu.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenu.cs
new file mode 100644
index 0000000000..c921b21e86
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenu.cs
@@ -0,0 +1,145 @@
+//
+// CommandMenu.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandMenu: Gtk.Menu
+ {
+ CommandEntrySet commandEntrySet;
+ CommandManager manager;
+ object initialCommandTarget;
+
+ public CommandMenu (CommandManager manager)
+ {
+ this.manager = manager;
+ this.AccelGroup = manager.AccelGroup;
+ }
+
+ public CommandMenu (CommandManager manager, CommandEntrySet commandEntrySet) : this (manager)
+ {
+ this.commandEntrySet = commandEntrySet;
+ }
+
+
+ public object InitialCommandTarget {
+ get { return initialCommandTarget; }
+ set { initialCommandTarget = value; }
+ }
+
+ internal CommandManager CommandManager {
+ get {
+ if (manager == null) {
+ Gtk.Widget w = Parent;
+ while (w != null && !(w is CommandMenu) && !(w is CommandMenuBar)) {
+ w = w.Parent;
+ }
+ if (w is CommandMenu)
+ manager = ((CommandMenu)w).CommandManager;
+ else if (w is CommandMenuBar)
+ manager = ((CommandMenuBar)w).CommandManager;
+ else
+ throw new InvalidOperationException ("Menu not bound to a CommandManager.");
+ }
+ return manager;
+ }
+ }
+
+ protected CommandMenu (IntPtr ptr): base (ptr)
+ {
+ }
+
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ Update ();
+ }
+ protected override void OnRealized ()
+ {
+ base.OnRealized ();
+ if (commandEntrySet != null) {
+ manager.CreateMenu (commandEntrySet, this);
+ Update ();
+ commandEntrySet = null;
+ }
+ }
+
+ internal void Update ()
+ {
+ foreach (Gtk.Widget item in Children) {
+ if (item is ICommandUserItem)
+ ((ICommandUserItem)item).Update (initialCommandTarget);
+ else if (item is Gtk.MenuItem) {
+ Gtk.MenuItem mitem = (Gtk.MenuItem) item;
+ CommandMenu men = mitem.Submenu as CommandMenu;
+ if (men != null)
+ men.InitialCommandTarget = initialCommandTarget;
+ item.Show ();
+ if (item is AutoHideMenuItem) {
+ men.Update ();
+ if (!((AutoHideMenuItem)item).HasVisibleChildren)
+ item.Hide ();
+ }
+ }
+ else
+ item.Show ();
+ }
+
+ // After updating the menu, hide the separators which don't actually
+ // separate items.
+ bool prevWasItem = false;
+ Gtk.Widget lastSeparator = null;
+ foreach (Gtk.Widget item in Children) {
+ if (item is Gtk.SeparatorMenuItem) {
+ if (!prevWasItem)
+ item.Hide ();
+ else {
+ prevWasItem = false;
+ lastSeparator = item;
+ }
+ } else if (item.Visible)
+ prevWasItem = true;
+ }
+ if (!prevWasItem && lastSeparator != null)
+ lastSeparator.Hide ();
+ }
+
+ protected override void OnHidden ()
+ {
+ base.OnHidden ();
+
+ // Make sure the accelerators allways work for this item
+ // while the menu is hidden
+ foreach (Gtk.MenuItem item in Children) {
+ item.Sensitive = true;
+ item.Visible = true;
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs
new file mode 100644
index 0000000000..e5e4724b7f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs
@@ -0,0 +1,46 @@
+//
+// CommandMenuBar.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandMenuBar: Gtk.MenuBar
+ {
+ CommandManager manager;
+
+ public CommandMenuBar (CommandManager manager)
+ {
+ this.manager = manager;
+ }
+
+ internal CommandManager CommandManager {
+ get { return manager; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuItem.cs
new file mode 100644
index 0000000000..131cb88243
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuItem.cs
@@ -0,0 +1,239 @@
+//
+// CommandMenuItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandMenuItem: Gtk.ImageMenuItem, ICommandMenuItem
+ {
+ CommandManager commandManager;
+ object commandId;
+ bool isArray;
+ bool isArrayItem;
+ object arrayDataItem;
+ ArrayList itemArray;
+ IconId lastIcon;
+ string overrideLabel;
+ bool wasButtonActivation;
+ object initialTarget;
+ bool disabledVisible = true;
+ CommandInfo lastCmdInfo;
+
+ public CommandMenuItem (object commandId, CommandManager commandManager, string overrideLabel, bool disabledVisible): base ("")
+ {
+ this.commandId = commandId;
+ this.commandManager = commandManager;
+ this.overrideLabel = overrideLabel;
+ this.disabledVisible = disabledVisible;
+ ActionCommand cmd = commandManager.GetCommand (commandId) as ActionCommand;
+ if (cmd != null)
+ isArray = cmd.CommandArray;
+ }
+
+ public CommandMenuItem (object commandId, CommandManager commandManager): this (commandId, commandManager, null, true)
+ {
+ }
+
+ void ICommandUserItem.Update (object initialTarget)
+ {
+ if (commandManager != null && !isArrayItem) {
+ CommandInfo cinfo = commandManager.GetCommandInfo (commandId, initialTarget);
+ this.initialTarget = initialTarget;
+ Update (cinfo);
+ }
+ }
+
+ void ICommandMenuItem.SetUpdateInfo (CommandInfo cmdInfo, object initialTarget)
+ {
+ isArrayItem = true;
+ this.initialTarget = initialTarget;
+ arrayDataItem = cmdInfo.DataItem;
+ Update (cmdInfo);
+ }
+
+ protected override void OnParentSet (Gtk.Widget parent)
+ {
+ base.OnParentSet (parent);
+ if (Parent == null)
+ return;
+
+ ((ICommandUserItem)this).Update (null);
+
+ if (!isArrayItem) {
+ // Make sure the accelerators always work for this item
+ // while the menu is hidden
+ Sensitive = true;
+ Visible = true;
+ }
+ }
+
+ protected override bool OnButtonReleaseEvent (Gdk.EventButton ev)
+ {
+ wasButtonActivation = true;
+ return base.OnButtonReleaseEvent (ev);
+ }
+
+ protected override void OnActivated ()
+ {
+ base.OnActivated ();
+
+ if (commandManager == null)
+ throw new InvalidOperationException ();
+
+ if (!wasButtonActivation) {
+ // It's being activated by an accelerator.
+ if (Submenu == null)
+ commandManager.DispatchCommandFromAccel (commandId, arrayDataItem, initialTarget);
+ } else {
+ wasButtonActivation = false;
+ commandManager.DispatchCommand (commandId, arrayDataItem, initialTarget);
+ }
+ }
+
+ protected override void OnSelected ()
+ {
+ if (commandManager != null)
+ commandManager.NotifySelected (lastCmdInfo);
+ base.OnSelected ();
+ }
+
+ protected override void OnDeselected ()
+ {
+ if (commandManager != null)
+ commandManager.NotifyDeselected ();
+ base.OnDeselected ();
+ }
+
+ void Update (CommandInfo cmdInfo)
+ {
+ lastCmdInfo = cmdInfo;
+ if (isArray && !isArrayItem) {
+ this.Visible = false;
+ Gtk.Menu menu = (Gtk.Menu) Parent;
+
+ if (itemArray != null) {
+ foreach (Gtk.MenuItem item in itemArray)
+ menu.Remove (item);
+ }
+
+ itemArray = new ArrayList ();
+ int i = Array.IndexOf (menu.Children, this);
+
+ if (cmdInfo.ArrayInfo != null) {
+ foreach (CommandInfo info in cmdInfo.ArrayInfo) {
+ Gtk.MenuItem item;
+ if (info.IsArraySeparator) {
+ item = new Gtk.SeparatorMenuItem ();
+ item.Show ();
+ } else {
+ item = CommandEntry.CreateMenuItem (commandManager, commandId, false);
+ ICommandMenuItem mi = (ICommandMenuItem) item;
+ mi.SetUpdateInfo (info, initialTarget);
+ }
+ menu.Insert (item, ++i);
+ itemArray.Add (item);
+ }
+ }
+ } else {
+ Gtk.Widget child = Child;
+ if (child == null)
+ return;
+
+ Gtk.Label accel_label = null;
+ Gtk.Label label = null;
+
+ if (!(child is Gtk.HBox)) {
+ child = new Gtk.HBox (false, 0);
+ accel_label = new Gtk.Label ("");
+ accel_label.UseUnderline = false;
+ accel_label.Xalign = 1.0f;
+ accel_label.Show ();
+
+ label = new Gtk.Label ("");
+ label.UseUnderline = true;
+ label.Xalign = 0.0f;
+ label.Show ();
+
+ ((Gtk.Box) child).PackStart (label);
+ ((Gtk.Box) child).PackStart (accel_label);
+ child.Show ();
+
+ this.Remove (Child);
+ this.Add (child);
+ } else {
+ accel_label = (Gtk.Label) ((Gtk.Box) child).Children[1];
+ label = (Gtk.Label) ((Gtk.Box) child).Children[0];
+ }
+
+ if (cmdInfo.AccelKey != null)
+ accel_label.Text = " " + KeyBindingManager.BindingToDisplayLabel (cmdInfo.AccelKey, true);
+ else
+ accel_label.Text = String.Empty;
+
+ if (cmdInfo.UseMarkup) {
+ label.Markup = overrideLabel ?? cmdInfo.Text;
+ label.UseMarkup = true;
+ } else {
+ label.Text = overrideLabel ?? cmdInfo.Text;
+ label.UseMarkup = false;
+ }
+
+ label.UseUnderline = true;
+
+ this.Sensitive = cmdInfo.Enabled;
+ this.Visible = cmdInfo.Visible && (disabledVisible || cmdInfo.Enabled);
+
+ if (!cmdInfo.Icon.IsNull && cmdInfo.Icon != lastIcon) {
+ Image = new Gtk.Image (cmdInfo.Icon, Gtk.IconSize.Menu);
+ lastIcon = cmdInfo.Icon;
+ }
+
+ if (cmdInfo is CommandInfoSet) {
+ CommandInfoSet ciset = (CommandInfoSet) cmdInfo;
+ Gtk.Menu smenu = new Gtk.Menu ();
+ Submenu = smenu;
+ foreach (CommandInfo info in ciset.CommandInfos) {
+ Gtk.MenuItem item;
+ if (info.IsArraySeparator) {
+ item = new Gtk.SeparatorMenuItem ();
+ item.Show ();
+ } else {
+ item = CommandEntry.CreateMenuItem (commandManager, commandId, false);
+ ICommandMenuItem mi = (ICommandMenuItem) item;
+ mi.SetUpdateInfo (info, initialTarget);
+ }
+ smenu.Add (item);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandRouterContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandRouterContainer.cs
new file mode 100644
index 0000000000..53612e944b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandRouterContainer.cs
@@ -0,0 +1,64 @@
+//
+// CommandRouterContainer.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandRouterContainer: Gtk.HBox, ICommandDelegatorRouter
+ {
+ bool continueToParent;
+
+ protected object Delegated {
+ get;
+ set;
+ }
+
+ protected CommandRouterContainer (bool continueToParent)
+ {
+ this.continueToParent = continueToParent;
+ }
+
+ public CommandRouterContainer (Gtk.Widget child, object target, bool continueToParent) : this (continueToParent)
+ {
+ if (child != null)
+ PackStart (child, true, true, 0);
+ Delegated = target;
+ }
+
+ public virtual object GetNextCommandTarget ()
+ {
+ return continueToParent ? Parent : null;
+ }
+
+ public virtual object GetDelegatedCommandTarget ()
+ {
+ return Delegated;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSelectedEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSelectedEventArgs.cs
new file mode 100644
index 0000000000..4d87a0198b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSelectedEventArgs.cs
@@ -0,0 +1,45 @@
+// CommandSelectedArgs.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandSelectedEventArgs: EventArgs
+ {
+ CommandInfo cmd;
+
+ public CommandSelectedEventArgs (CommandInfo cmd)
+ {
+ this.cmd = cmd;
+ }
+
+ public CommandInfo CommandInfo {
+ get { return cmd; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSystemCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSystemCommands.cs
new file mode 100644
index 0000000000..4b2895ad3d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandSystemCommands.cs
@@ -0,0 +1,38 @@
+//
+// CommandSystemCommands.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public enum CommandSystemCommands
+ {
+ ToolbarList
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToggleToolButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToggleToolButton.cs
new file mode 100644
index 0000000000..1f8f765f5e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToggleToolButton.cs
@@ -0,0 +1,91 @@
+//
+// CommandToggleToolButton.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandToggleToolButton: Gtk.ToggleToolButton, ICommandUserItem
+ {
+ CommandManager commandManager;
+ object commandId;
+ bool updating;
+ object initialTarget;
+
+ public CommandToggleToolButton (object commandId, CommandManager commandManager): base ("")
+ {
+ this.commandId = commandId;
+ this.commandManager = commandManager;
+ }
+
+ protected override void OnParentSet (Gtk.Widget parent)
+ {
+ base.OnParentSet (parent);
+ if (Parent == null) return;
+
+ ((ICommandUserItem)this).Update (null);
+ }
+
+ void ICommandUserItem.Update (object initialTarget)
+ {
+ if (commandManager != null) {
+ CommandInfo cinfo = commandManager.GetCommandInfo (commandId, initialTarget);
+ this.initialTarget = initialTarget;
+ Update (cinfo);
+ }
+ }
+
+ protected override void OnClicked ()
+ {
+ base.OnClicked ();
+ if (updating) return;
+
+ if (commandManager == null)
+ throw new InvalidOperationException ();
+
+ commandManager.DispatchCommand (commandId, null, initialTarget);
+ }
+
+ IconId stockId = null;
+
+ void Update (CommandInfo cmdInfo)
+ {
+ updating = true;
+ Label = cmdInfo.Text;
+ if (cmdInfo.Icon != stockId) {
+ stockId = cmdInfo.Icon;
+ this.IconWidget = new Gtk.Image (cmdInfo.Icon, Gtk.IconSize.Menu);
+ }
+ Sensitive = cmdInfo.Enabled;
+ Visible = cmdInfo.Visible;
+ Active = cmdInfo.Checked;
+ updating = false;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolButton.cs
new file mode 100644
index 0000000000..7509b0b67f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolButton.cs
@@ -0,0 +1,104 @@
+//
+// CommandToolButton.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandToolButton: Gtk.ToolButton, ICommandUserItem
+ {
+ CommandManager commandManager;
+ object commandId;
+ string lastDesc;
+ object initialTarget;
+
+ public CommandToolButton (object commandId, CommandManager commandManager): base ("")
+ {
+ this.commandId = commandId;
+ this.commandManager = commandManager;
+ UseUnderline = true;
+ }
+
+ protected override void OnParentSet (Gtk.Widget parent)
+ {
+ base.OnParentSet (parent);
+ if (Parent == null) return;
+
+ ((ICommandUserItem)this).Update (null);
+ }
+
+ void ICommandUserItem.Update (object initialTarget)
+ {
+ if (commandManager != null) {
+ CommandInfo cinfo = commandManager.GetCommandInfo (commandId, initialTarget);
+ this.initialTarget = initialTarget;
+ Update (cinfo);
+ }
+ }
+
+ protected override void OnClicked ()
+ {
+ base.OnClicked ();
+
+ if (commandManager == null)
+ throw new InvalidOperationException ();
+
+ commandManager.DispatchCommand (commandId, null, initialTarget);
+ }
+
+ IconId stockId = null;
+
+ void Update (CommandInfo cmdInfo)
+ {
+ if (lastDesc != cmdInfo.Description) {
+ string toolTip;
+ if (string.IsNullOrEmpty (cmdInfo.AccelKey)) {
+ toolTip = cmdInfo.Description;
+ } else {
+ toolTip = cmdInfo.Description + " (" + KeyBindingManager.BindingToDisplayLabel (cmdInfo.AccelKey, false) + ")";
+ }
+ TooltipText = toolTip;
+ lastDesc = cmdInfo.Description;
+ }
+
+ if (Label != cmdInfo.Text)
+ Label = cmdInfo.Text;
+ if (cmdInfo.Icon != stockId) {
+ stockId = cmdInfo.Icon;
+ this.IconWidget = new Gtk.Image (cmdInfo.Icon, Gtk.IconSize.Menu);
+ }
+ if (cmdInfo.Enabled != Sensitive)
+ Sensitive = cmdInfo.Enabled;
+ if (cmdInfo.Visible != Visible)
+ Visible = cmdInfo.Visible;
+ if (cmdInfo.Icon.IsNull)
+ IsImportant = true;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolbar.cs
new file mode 100644
index 0000000000..c3518642a0
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandToolbar.cs
@@ -0,0 +1,78 @@
+//
+// CommandToolbar.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using MonoDevelop.Components.DockToolbars;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CommandToolbar: DockToolbar, ICommandBar
+ {
+ object initialCommandTarget;
+
+ public CommandToolbar (CommandManager manager, string id, string title): base (id, title)
+ {
+ manager.RegisterCommandBar (this);
+ }
+
+ internal object InitialCommandTarget {
+ get { return initialCommandTarget; }
+ set { initialCommandTarget = value; }
+ }
+
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ ((ICommandBar)this).Update (null);
+ }
+
+ void ICommandBar.Update (object defaultTarget)
+ {
+ if (!Visible)
+ return;
+
+ if (initialCommandTarget != null)
+ defaultTarget = initialCommandTarget;
+
+ foreach (Gtk.Widget item in Children) {
+ if (item is ICommandUserItem)
+ ((ICommandUserItem)item).Update (defaultTarget);
+ else
+ item.Show ();
+ }
+ }
+
+ public void SetEnabled (bool enabled)
+ {
+ foreach (Gtk.Widget item in Children) {
+ if (item.GetType () == typeof(Gtk.ToolItem))
+ item.Sensitive = enabled;
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommand.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommand.cs
new file mode 100644
index 0000000000..d30cd57beb
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommand.cs
@@ -0,0 +1,56 @@
+//
+// CustomCommand.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CustomCommand: Command
+ {
+ Type widgetType;
+
+ public CustomCommand ()
+ {
+ }
+
+ public CustomCommand (object id, string text, Type widgetType): base (id, text)
+ {
+ WidgetType = widgetType;
+ }
+
+ public Type WidgetType {
+ get { return widgetType; }
+ set {
+ if (!typeof(Gtk.Widget).IsAssignableFrom (value))
+ throw new ArgumentException ("Value is not a subclass of Gtk.Widget.");
+ widgetType = value;
+ }
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandTargetAttribute.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandTargetAttribute.cs
new file mode 100644
index 0000000000..289dab82ec
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandTargetAttribute.cs
@@ -0,0 +1,75 @@
+// CustomCommandTargetAttribute.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CustomCommandTargetAttribute: Attribute, ICommandTargetHandler, ICommandArrayTargetHandler
+ {
+ ICommandTargetHandler next;
+ ICommandArrayTargetHandler nextArray;
+
+ void ICommandArrayTargetHandler.Run (object target, Command cmd, object data)
+ {
+ Run (target, cmd, data);
+ }
+
+ void ICommandTargetHandler.Run (object target, Command cmd)
+ {
+ Run (target, cmd);
+ }
+
+ protected virtual void Run (object target, Command cmd, object data)
+ {
+ nextArray.Run (target, cmd, data);
+ }
+
+ protected virtual void Run (object target, Command cmd)
+ {
+ next.Run (target, cmd);
+ }
+
+ ICommandTargetHandler ICommandTargetHandler.Next {
+ get {
+ return next;
+ }
+ set {
+ next = value;
+ }
+ }
+
+ ICommandArrayTargetHandler ICommandArrayTargetHandler.Next {
+ get {
+ return nextArray;
+ }
+ set {
+ nextArray = value;
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandUpdaterAttribute.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandUpdaterAttribute.cs
new file mode 100644
index 0000000000..ba8f4adfe2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomCommandUpdaterAttribute.cs
@@ -0,0 +1,78 @@
+// CustomCommandUpdaterAttribute.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ // This base class can be used to create attribute classes which provide custom
+ // command updating behavior. When applied to a method, the overriden CommandUpdate method
+ // will be called to update the command status.
+ public abstract class CustomCommandUpdaterAttribute: Attribute, ICommandUpdateHandler, ICommandArrayUpdateHandler
+ {
+ ICommandUpdateHandler next;
+ ICommandArrayUpdateHandler nextArray;
+
+ void ICommandUpdateHandler.CommandUpdate (object target, CommandInfo cinfo)
+ {
+ CommandUpdate (target, cinfo);
+ }
+
+ ICommandUpdateHandler ICommandUpdateHandler.Next {
+ get {
+ return next;
+ }
+ set {
+ next = value;
+ }
+ }
+
+ void ICommandArrayUpdateHandler.CommandUpdate (object target, CommandArrayInfo cinfo)
+ {
+ CommandUpdate (target, cinfo);
+ }
+
+ ICommandArrayUpdateHandler ICommandArrayUpdateHandler.Next {
+ get {
+ return nextArray;
+ }
+ set {
+ nextArray = value;
+ }
+ }
+
+ protected virtual void CommandUpdate (object target, CommandInfo cinfo)
+ {
+ next.CommandUpdate (target, cinfo);
+ }
+
+ protected virtual void CommandUpdate (object target, CommandArrayInfo cinfo)
+ {
+ nextArray.CommandUpdate (target, cinfo);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomItem.cs
new file mode 100644
index 0000000000..a49432879f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomItem.cs
@@ -0,0 +1,43 @@
+// CustomItem.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+using Gtk;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class CustomItem: HBox
+ {
+ public virtual void SetToolbarStyle (Gtk.Toolbar toolbar)
+ {
+ }
+
+ public virtual void SetMenuStyle (Gtk.MenuShell menu)
+ {
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomMenuItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomMenuItem.cs
new file mode 100644
index 0000000000..e70bf24edd
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CustomMenuItem.cs
@@ -0,0 +1,46 @@
+//
+// CustomMenuItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+
+namespace MonoDevelop.Components.Commands
+{
+ class CustomMenuItem: Gtk.MenuItem
+ {
+ protected override bool OnButtonPressEvent (Gdk.EventButton e)
+ {
+ return true;
+ }
+
+ protected override bool OnButtonReleaseEvent (Gdk.EventButton e)
+ {
+ return true;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandBar.cs
new file mode 100644
index 0000000000..f3a7ea3e51
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandBar.cs
@@ -0,0 +1,37 @@
+//
+// ICommandUserInterface.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2010 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface ICommandBar
+ {
+ void Update (object activeTarget);
+ void SetEnabled (bool enabled);
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandDelegatorRouter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandDelegatorRouter.cs
new file mode 100644
index 0000000000..31600c9d74
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandDelegatorRouter.cs
@@ -0,0 +1,41 @@
+//
+// ICommandDelegatorRouter.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ // Redirects the command route to the object returned by
+ // GetDelegatedCommandTarget and when done, continues with
+ // GetNextCommandTarget.
+ public interface ICommandDelegatorRouter
+ {
+ object GetNextCommandTarget ();
+ object GetDelegatedCommandTarget ();
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandMenuItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandMenuItem.cs
new file mode 100644
index 0000000000..37bac948e5
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandMenuItem.cs
@@ -0,0 +1,37 @@
+//
+// ICommandMenuItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ internal interface ICommandMenuItem: ICommandUserItem
+ {
+ void SetUpdateInfo (CommandInfo cmdInfo, object initialTarget);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandRouter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandRouter.cs
new file mode 100644
index 0000000000..6ac9e6be51
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandRouter.cs
@@ -0,0 +1,37 @@
+//
+// ICommandRouter.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface ICommandRouter
+ {
+ object GetNextCommandTarget ();
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandTargetVisitor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandTargetVisitor.cs
new file mode 100644
index 0000000000..c3fe21d5ff
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandTargetVisitor.cs
@@ -0,0 +1,37 @@
+// ICommandTargetVisitor.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface ICommandTargetVisitor
+ {
+ bool Visit (object ob);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUpdateHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUpdateHandler.cs
new file mode 100644
index 0000000000..88056cf28b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUpdateHandler.cs
@@ -0,0 +1,55 @@
+// ICommandUpdateHandler.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface ICommandUpdateHandler
+ {
+ ICommandUpdateHandler Next { get; set; }
+ void CommandUpdate (object target, CommandInfo cinfo);
+ }
+
+ public interface ICommandArrayUpdateHandler
+ {
+ ICommandArrayUpdateHandler Next { get; set; }
+ void CommandUpdate (object target, CommandArrayInfo cinfo);
+ }
+
+ public interface ICommandTargetHandler
+ {
+ ICommandTargetHandler Next { get; set; }
+ void Run (object target, Command cmd);
+ }
+
+ public interface ICommandArrayTargetHandler
+ {
+ ICommandArrayTargetHandler Next { get; set; }
+ void Run (object target, Command cmd, object data);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUserItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUserItem.cs
new file mode 100644
index 0000000000..212cae9bd6
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ICommandUserItem.cs
@@ -0,0 +1,37 @@
+//
+// ICommandUserItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface ICommandUserItem
+ {
+ void Update (object initialTarget);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/IMultiCastCommandRouter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/IMultiCastCommandRouter.cs
new file mode 100644
index 0000000000..725514fda9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/IMultiCastCommandRouter.cs
@@ -0,0 +1,37 @@
+// IMultiCastCommandRouter.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+using System.Collections;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface IMultiCastCommandRouter
+ {
+ IEnumerable GetCommandTargets ();
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs
new file mode 100644
index 0000000000..2790be19aa
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs
@@ -0,0 +1,910 @@
+//
+// KeyBindingManager.cs
+//
+// Author: Jeffrey Stedfast <fejj@novell.com>
+//
+// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+// Terminology:
+// mode: A 'mode' is a key binding prefix / modifier meant to allow a
+// wider set of key bindings to exist. If you are familiar with
+// GNU/Emacs, you might be familiar with the key binding for
+// 'Save'. This key binding would have a 'mode' of 'Control+x'
+// and an accel of 'Control+s' - the full binding being:
+// 'Control+x Control+s'.
+//
+// accel: A (possibly partial) key binding for some command/action.
+// When combined with the mode prefix, results in the full key
+// binding.
+//
+// binding: The full key binding for a command/action.
+//
+
+namespace MonoDevelop.Components.Commands
+{
+ public class KeyBindingManager : IDisposable
+ {
+ static bool isX11 = false;
+
+ Dictionary<string, List<Command>> bindings = new Dictionary<string, List<Command>> ();
+ Dictionary<string, int> modes = new Dictionary<string, int> ();
+ List<Command> commands = new List<Command> ();
+
+ static KeyBindingManager ()
+ {
+ isX11 = !isMac && System.Environment.OSVersion.Platform == PlatformID.Unix;
+
+ if (isMac) {
+ SelectionModifierAlt = Gdk.ModifierType.Mod5Mask;
+ SelectionModifierControl = Gdk.ModifierType.Mod1Mask | Gdk.ModifierType.MetaMask;
+ SelectionModifierSuper = Gdk.ModifierType.ControlMask;
+ } else {
+ SelectionModifierAlt = Gdk.ModifierType.Mod1Mask;
+ SelectionModifierControl = Gdk.ModifierType.ControlMask;
+ SelectionModifierSuper = Gdk.ModifierType.SuperMask;
+ }
+ }
+
+ static bool isMac {
+ get { return MonoDevelop.Core.PropertyService.IsMac; }
+ }
+
+ public void Dispose ()
+ {
+ if (commands == null)
+ return;
+
+ for (int i = 0; i < commands.Count; i++)
+ commands[i].KeyBindingChanged -= OnKeyBindingChanged;
+ commands = null;
+ }
+
+ #region Platform-dependent selection modifiers
+ //FIXME: these should be named for what they do, not what they are
+
+ public static Gdk.ModifierType SelectionModifierAlt {
+ get; private set;
+ }
+
+ public static Gdk.ModifierType SelectionModifierControl {
+ get; private set;
+ }
+
+ public static Gdk.ModifierType SelectionModifierSuper {
+ get; private set;
+ }
+
+ #endregion
+
+ #region Static Public Helpers For Building Key-Binding Strings
+
+ static string KeyToString (Gdk.Key key)
+ {
+ char c = (char) Gdk.Keyval.ToUnicode ((uint) key);
+
+ if (c != 0) {
+ if (c == ' ')
+ return "Space";
+
+ return Char.ToUpper (c).ToString ();
+ }
+
+ //HACK: Page_Down and Next are synonyms for the same enum value, but alphabetically, Next gets returned
+ // first by enum ToString(). Similarly, Page_Up and Prior are synonyms, but Page_Up is returned. Hence the
+ // default pairing is Next and Page_Up, which is confusingly inconsistent, so we fix this here.
+ //
+ //The same problem applies to some other common keys, so we ensure certain values are mapped
+ // to the most common name.
+ switch (key) {
+ case Gdk.Key.Next:
+ return "Page_Down";
+ case Gdk.Key.L1:
+ return "F11";
+ case Gdk.Key.L2:
+ return "F12";
+ }
+
+ return key.ToString ();
+ }
+
+ //USED INTERNALLY ONLY
+ static string AccelFromKey (Gdk.Key key, Gdk.ModifierType modifier)
+ {
+ bool complete;
+ return AccelFromKey (key, modifier, out complete);
+ }
+
+ public static string AccelFromKey (Gdk.EventKey raw, out bool complete)
+ {
+ Gdk.Key key;
+ Gdk.ModifierType modifier;
+ MapRawKeys (raw, out key, out modifier);
+ return AccelFromKey (key, modifier, out complete);
+ }
+
+ static string AccelFromKey (Gdk.Key key, Gdk.ModifierType modifier, out bool complete)
+ {
+ bool keyIsModifier;
+ string modifierlabel = ModifierToPartialAccel (modifier, key, out keyIsModifier);
+ complete = !keyIsModifier;
+
+ if (keyIsModifier) {
+ return modifierlabel;
+ } else {
+ return modifierlabel + KeyToString (key);
+ }
+ }
+
+ static bool AccelToKeyPartial (string accel, ref int i, out uint key, out Gdk.ModifierType modifier)
+ {
+ Gdk.ModifierType mod, mask = 0;
+ string str;
+ uint k = 0;
+ int j = i;
+
+ modifier = Gdk.ModifierType.None;
+ key = 0;
+
+ if (accel == null || i >= accel.Length)
+ return false;
+
+ while (i < accel.Length) {
+ for (j = i + 1; j < accel.Length; j++) {
+ if (accel[j] == '+' || accel[j] == '|')
+ break;
+ }
+
+ str = accel.Substring (i, j - i);
+ if ((mod = ModifierMask (str)) == Gdk.ModifierType.None) {
+ if (str == "Space")
+ k = (uint) Gdk.Key.space;
+ else if (str.Length > 1)
+ k = Gdk.Keyval.FromName (str);
+ else
+ k = (uint) str[0];
+
+ break;
+ }
+
+ mask |= mod;
+ i = j + 1;
+ }
+
+ i = j;
+
+ modifier = mask;
+ key = k;
+
+ return k != 0;
+ }
+
+ static bool AccelToKeyPartial (string accel, out uint key, out Gdk.ModifierType modifier)
+ {
+ int i = 0;
+
+ modifier = Gdk.ModifierType.None;
+ key = 0;
+
+ if (accel == null || accel == String.Empty)
+ return false;
+
+ if (AccelToKeyPartial (accel, ref i, out key, out modifier) && i == accel.Length)
+ return true;
+
+ return false;
+ }
+
+ public static bool AccelToKey (string accel, out uint key, out Gdk.ModifierType modifier)
+ {
+ if (AccelToKeyPartial (accel, out key, out modifier))
+ return true;
+ modifier = Gdk.ModifierType.None;
+ key = 0;
+ return false;
+ }
+
+ static bool BindingToKeysPartial (string binding, out uint modeKey, out Gdk.ModifierType modeModifier, out uint key, out Gdk.ModifierType modifier)
+ {
+ int i = 0;
+
+ modeModifier = Gdk.ModifierType.None;
+ modeKey = 0;
+ modifier = Gdk.ModifierType.None;
+ key = 0;
+
+ if (binding == null || binding == String.Empty)
+ return false;
+
+ if (!AccelToKeyPartial (binding, ref i, out key, out modifier))
+ return false;
+
+ if (i == binding.Length) {
+ // no mode in this binding, we're done
+ return true;
+ }
+
+ if (binding[i] != '|') {
+ // bad format
+ return false;
+ }
+
+ modeModifier = modifier;
+ modeKey = key;
+ i++;
+
+ return AccelToKeyPartial (binding, ref i, out key, out modifier) && i == binding.Length;
+ }
+
+ public static bool BindingToKeys (string binding, out uint modeKey, out Gdk.ModifierType modeModifier, out uint key, out Gdk.ModifierType modifier)
+ {
+ if (BindingToKeysPartial (binding, out modeKey, out modeModifier, out key, out modifier))
+ return true;
+
+ modeModifier = modifier = Gdk.ModifierType.None;
+ modeKey = key = 0;
+ return false;
+ }
+
+ public static string Binding (string mode, string accel)
+ {
+ if (mode == null) {
+ if (accel == String.Empty)
+ return null;
+
+ return accel;
+ }
+
+ if (accel == String.Empty)
+ return mode;
+
+ return mode + "|" + accel;
+ }
+
+
+ static void MapRawKeys (Gdk.EventKey evt, out Gdk.Key key, out Gdk.ModifierType mod)
+ {
+ key = evt.Key;
+ mod = evt.State;
+
+ if (isX11) {
+ //this is a workaround for a common X mapping issue
+ //where the alt key is mapped to the meta key when the shift modifier is active
+ if (key.Equals (Gdk.Key.Meta_L) || key.Equals (Gdk.Key.Meta_R))
+ key = Gdk.Key.Alt_L;
+ }
+
+ //HACK: the MAC GTK+ port currently does some horrible, un-GTK-ish key mappings
+ // so we work around them by playing some tricks to remap and decompose modifiers.
+ // We also decompose keys to the root physical key so that the Mac command
+ // combinations appear as expected, e.g. shift-{ is treated as shift-[.
+ if (isMac && !isX11) {
+ // Mac GTK+ maps the command key to the Mod1 modifier, which usually means alt/
+ // We map this instead to meta, because the Mac GTK+ has mapped the cmd key
+ // to the meta key (yay inconsistency!). IMO super would have been saner.
+ if ((mod & Gdk.ModifierType.Mod1Mask) != 0) {
+ mod ^= Gdk.ModifierType.Mod1Mask;
+ mod |= Gdk.ModifierType.MetaMask;
+ }
+
+ // If Mod5 is active it *might* mean that opt/alt is active,
+ // so we can unset this and map it back to the normal modifier.
+ if ((mod & Gdk.ModifierType.Mod5Mask) != 0) {
+ mod ^= Gdk.ModifierType.Mod5Mask;
+ mod |= Gdk.ModifierType.Mod1Mask;
+ }
+
+ // When shift or opt modifiers are active, we need to decompose keys down to the
+ // "root" physical key to make the command appear correct for Mac.
+ if (evt.Group == (byte) 1 || (mod & Gdk.ModifierType.ShiftMask) != 0)
+ key = GetRootKey (evt);
+
+ // In addition, we can only inspect whether the opt/alt key is pressed by examining
+ // the key's "group", because the Mac GTK+ treats opt as a group modifier and does
+ // not expose it as an actual GDK modifier.
+ if (evt.Group == (byte) 1)
+ mod |= Gdk.ModifierType.Mod1Mask;
+ }
+ }
+
+ static Dictionary<Gdk.Key,Gdk.Key> hardwareMappings;
+ static Gdk.Keymap keymap;
+
+ static void InitKeymaps ()
+ {
+ if (keymap != null)
+ return;
+
+ keymap = Gdk.Keymap.Default;
+ keymap.KeysChanged += delegate {
+ hardwareMappings.Clear ();
+ };
+ hardwareMappings = new Dictionary<Gdk.Key,Gdk.Key> ();
+ }
+
+ static Gdk.Key GetRootKey (Gdk.EventKey evt)
+ {
+ InitKeymaps ();
+ Gdk.Key ret;
+ if (hardwareMappings.TryGetValue (evt.Key, out ret))
+ return ret;
+
+ //FIXME: LookupKey isn't implemented on Mac, so we have to use this workaround
+ uint[] keyvals;
+ Gdk.KeymapKey [] keys;
+ keymap.GetEntriesForKeycode (evt.HardwareKeycode, out keys, out keyvals);
+
+ for (uint i = 0; i < keys.Length; i++) {
+ if (keys[i].Group == 0 && keys[i].Level == 0) {
+ ret = (Gdk.Key)keyvals[i];
+ foreach (Gdk.Key key in keyvals)
+ hardwareMappings[key] = ret;
+ return ret;
+ }
+ }
+
+ //failed, but avoid looking it up again
+ return hardwareMappings[evt.Key] = evt.Key;
+ }
+
+ static string ModifierToPartialAccel (Gdk.ModifierType mod, Gdk.Key key, out bool keyIsModifier)
+ {
+ string label = String.Empty;
+
+ if ((mod & Gdk.ModifierType.ControlMask) != 0)
+ label += "Control+";
+ if ((mod & Gdk.ModifierType.Mod1Mask) != 0)
+ label += "Alt+";
+ if ((mod & Gdk.ModifierType.ShiftMask) != 0)
+ label += "Shift+";
+ if ((mod & Gdk.ModifierType.MetaMask) != 0)
+ label += "Meta+";
+ if ((mod & Gdk.ModifierType.SuperMask) != 0)
+ label += "Super+";
+
+ keyIsModifier = true;
+ if (key.Equals (Gdk.Key.Control_L) || key.Equals (Gdk.Key.Control_R))
+ label += "Control+";
+ else if (key.Equals (Gdk.Key.Alt_L) || key.Equals (Gdk.Key.Alt_R))
+ label += "Alt+";
+ else if (key.Equals (Gdk.Key.Shift_L) || key.Equals (Gdk.Key.Shift_R))
+ label += "Shift+";
+ else if (key.Equals (Gdk.Key.Meta_L) || key.Equals (Gdk.Key.Meta_R))
+ label += "Meta+";
+ else if (key.Equals (Gdk.Key.Super_L) || key.Equals (Gdk.Key.Super_L))
+ label += "Super+";
+ else
+ keyIsModifier = false;
+
+ return label;
+ }
+
+ #endregion
+
+ #region Display label formatting
+
+ public static string BindingToDisplayLabel (string binding, bool concise)
+ {
+ return BindingToDisplayLabel (binding, concise, false);
+ }
+
+ public static string BindingToDisplayLabel (string binding, bool concise, bool includeIncomplete)
+ {
+ Gdk.ModifierType modeMod, mod;
+ string label = String.Empty;
+ uint modeKey, key;
+
+ if (includeIncomplete) {
+ BindingToKeysPartial (binding, out modeKey, out modeMod, out key, out mod);
+ } else if (!BindingToKeys (binding, out modeKey, out modeMod, out key, out mod)) {
+ return null;
+ }
+
+ if (modeKey != 0) {
+ label += ModifierToDisplayLabel (modeMod, concise) + KeyToDisplayLabel ((Gdk.Key)modeKey) + (isMac? " " : "|");
+ }
+ label += ModifierToDisplayLabel (mod, concise);
+ if (key != 0)
+ label += KeyToDisplayLabel ((Gdk.Key)key);
+
+ return label;
+ }
+
+ static string KeyToDisplayLabel (Gdk.Key key)
+ {
+ if (isMac) {
+ char appl = AppleMapKeyToSymbol (key);
+ if (appl != '\0') {
+ return new string (appl, 1);
+ }
+ }
+ return KeyToString (key);
+ }
+
+ static string ModifierToDisplayLabel (Gdk.ModifierType mod, bool concise)
+ {
+ if (isMac) {
+ return AppleMapModifierToSymbols (mod);
+ }
+
+ string label = String.Empty;
+
+ if ((mod & Gdk.ModifierType.ControlMask) != 0)
+ label += concise? "Ctrl+" : "Control+";
+ if ((mod & Gdk.ModifierType.Mod1Mask) != 0)
+ label += "Alt+";
+ if ((mod & Gdk.ModifierType.ShiftMask) != 0)
+ label += "Shift+";
+ if ((mod & Gdk.ModifierType.MetaMask) != 0)
+ label += "Meta+";
+ if ((mod & Gdk.ModifierType.SuperMask) != 0)
+ label += "Super+";
+
+ return label;
+ }
+
+ static Gdk.ModifierType ModifierMask (string name)
+ {
+ switch (name) {
+ case "Alt":
+ return Gdk.ModifierType.Mod1Mask;
+ case "Shift":
+ return Gdk.ModifierType.ShiftMask;
+ case "Control":
+ case "Ctrl":
+ return Gdk.ModifierType.ControlMask;
+ case "Meta":
+ return Gdk.ModifierType.MetaMask;
+ case "Super":
+ return Gdk.ModifierType.SuperMask;
+ default:
+ return Gdk.ModifierType.None;
+ }
+ }
+
+ ///
+ /// Converts the old shortcut format into the new "key binding" format
+ ///
+ static string ShortcutToBinding (string shortcut)
+ {
+ Gdk.ModifierType mod, mask = 0;
+ int i = 0, j;
+ Gdk.Key key;
+ string str;
+
+ if (shortcut == null || shortcut == String.Empty)
+ return null;
+
+ while (i < shortcut.Length) {
+ if ((j = shortcut.IndexOf ('|', i + 1)) == -1) {
+ // we are at the last part of the shortcut, the Gdk.Key
+ j = shortcut.Length;
+ }
+
+ str = shortcut.Substring (i, j - i);
+ if ((mod = ModifierMask (str)) == Gdk.ModifierType.None) {
+ if (str.Length > 1)
+ key = (Gdk.Key) Gdk.Key.Parse (typeof (Gdk.Key), str);
+ else
+ key = (Gdk.Key) str[0];
+
+ if (j < shortcut.Length)
+ Console.WriteLine ("WARNING: trailing data after Gdk.Key portion of shortcut {0}", shortcut);
+
+ return AccelFromKey (key, mask);
+ }
+
+ mask |= mod;
+ i = j + 1;
+ }
+
+ Console.WriteLine ("WARNING: Incomplete shortcut '{0}'?", shortcut);
+
+ return null;
+ }
+
+ ///
+ /// Returns true if @shortcut is in the old string format or false if it is the newer string format.
+ ///
+ static bool IsShortcutFormat (string shortcut)
+ {
+ int i = 0;
+
+ if (shortcut == null || shortcut == String.Empty)
+ return false;
+
+ if (shortcut.Length == 1 && (shortcut[0] == '|' || shortcut[0] == '+')) {
+ // single-key binding
+ return false;
+ }
+
+ while (i < shortcut.Length && (shortcut[i] != '|' && shortcut[i] != '+'))
+ i++;
+
+ return i < shortcut.Length && shortcut[i] == '|' && ModifierMask (shortcut.Substring (0, i)) != Gdk.ModifierType.None;
+ }
+
+ public static string CanonicalizeBinding (string binding)
+ {
+ Gdk.ModifierType modeMod, mod;
+ string accel, mode = null;
+ uint modeKey, key;
+
+ if (binding == null || binding == String.Empty)
+ return null;
+
+ if (IsShortcutFormat (binding))
+ return ShortcutToBinding (binding);
+
+ if (!BindingToKeys (binding, out modeKey, out modeMod, out key, out mod)) {
+ Console.WriteLine ("WARNING: failed to canonicalize binding {0}", binding);
+ return null;
+ }
+
+ if (modeKey != 0)
+ mode = AccelFromKey ((Gdk.Key) modeKey, modeMod);
+
+ accel = AccelFromKey ((Gdk.Key) key, mod);
+
+ return Binding (mode, accel);
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public bool BindingExists (string binding)
+ {
+ return bindings.ContainsKey (binding);
+ }
+
+ public bool BindingExists (string mode, string accel)
+ {
+ return BindingExists (Binding (mode, accel));
+ }
+
+ public bool ModeExists (string mode)
+ {
+ return modes.ContainsKey (mode);
+ }
+
+ string BindingMode (string binding)
+ {
+ bool plus = false;
+ int i;
+
+ if (binding == null)
+ return null;
+
+ for (i = 0; i < binding.Length; i++) {
+ if (!plus && binding[i] == '|')
+ return binding.Substring (0, i);
+
+ if (binding[i] == '+')
+ plus = !plus;
+ else
+ plus = false;
+ }
+
+ return null;
+ }
+
+ void SetBinding (Command command, string oldMode, string oldBinding, string newMode, string newBinding)
+ {
+ List<Command> list;
+ int refs;
+
+ if (newMode != oldMode) {
+ // keep track of valid modes
+ if (oldMode != null) {
+ if ((refs = modes[oldMode] - 1) == 0)
+ modes.Remove (oldMode);
+ else
+ modes[oldMode] = refs;
+ }
+
+ if (newMode != null) {
+ if (modes.ContainsKey (newMode)) {
+ modes[newMode]++;
+ } else
+ modes.Add (newMode, 1);
+ }
+ }
+
+ if (oldBinding != null) {
+ list = bindings[oldBinding];
+ list.Remove (command);
+
+ if (list.Count == 0)
+ bindings.Remove (oldBinding);
+ }
+
+ if (newBinding != null) {
+ if (!bindings.ContainsKey (newBinding)) {
+ list = new List<Command> ();
+ list.Add (command);
+
+ bindings.Add (newBinding, list);
+ } else {
+ list = bindings[newBinding];
+ list.Add (command);
+ }
+ }
+ }
+
+ void SetBinding (Command command, string oldBinding, string newBinding)
+ {
+ SetBinding (command, BindingMode (oldBinding), oldBinding, BindingMode (newBinding), newBinding);
+ }
+
+ void OnKeyBindingChanged (object o, KeyBindingChangedEventArgs args)
+ {
+ SetBinding (args.Command, args.OldKeyBinding, args.NewKeyBinding);
+ }
+
+ public bool CommandIsRegistered (Command command)
+ {
+ return commands.Contains (command);
+ }
+
+ public void RegisterCommand (Command command)
+ {
+ if (commands.Contains (command)) {
+ Console.WriteLine ("WARNING: trying to re-register command {0}", command);
+ return;
+ }
+
+ SetBinding (command, null, null, BindingMode (command.AccelKey), command.AccelKey);
+ command.KeyBindingChanged += new KeyBindingChangedEventHandler (OnKeyBindingChanged);
+ commands.Add (command);
+ }
+
+ public void UnregisterCommand (Command command)
+ {
+ List<Command> list;
+ string mode;
+ int refs;
+
+ if (!commands.Contains (command)) {
+ Console.WriteLine ("WARNING: trying to unregister unknown command {0}", command);
+ return;
+ }
+
+ command.KeyBindingChanged -= OnKeyBindingChanged;
+ commands.Remove (command);
+
+ if (string.IsNullOrEmpty (command.AccelKey))
+ return;
+
+ list = bindings[command.AccelKey];
+ list.Remove (command);
+ if (list.Count == 0)
+ bindings.Remove (command.AccelKey);
+
+ mode = BindingMode (command.AccelKey);
+ if (mode != null && modes.ContainsKey (mode)) {
+ if ((refs = modes[mode] - 1) == 0)
+ modes.Remove (mode);
+ else
+ modes[mode] = refs;
+ }
+ }
+
+ ///
+ /// Returns the oldest registered command with the specified key binding.
+ ///
+ public Command Command (string binding)
+ {
+ List<Command> list;
+
+ if (!BindingExists (binding))
+ return null;
+
+ list = bindings[binding];
+
+ return list[0];
+ }
+
+ ///
+ /// Returns the list of commands registered for the specified key binding.
+ ///
+ public List<Command> Commands (string binding)
+ {
+ if (!BindingExists (binding))
+ return null;
+
+ return bindings[binding];
+ }
+
+ #endregion
+
+ #region Apple symbol mappings
+
+ static string AppleMapModifierToSymbols (Gdk.ModifierType mod)
+ {
+ string ret = "";
+
+ if ((mod & Gdk.ModifierType.ControlMask) != 0) {
+ ret += "⌃";
+ mod ^= Gdk.ModifierType.ControlMask;
+ }
+ if ((mod & Gdk.ModifierType.Mod1Mask) != 0) {
+ ret += "⌥";
+ mod ^= Gdk.ModifierType.Mod1Mask;
+ }
+ if ((mod & Gdk.ModifierType.ShiftMask) != 0) {
+ ret += "⇧";
+ mod ^= Gdk.ModifierType.ShiftMask;
+ }
+ if ((mod & Gdk.ModifierType.MetaMask) != 0) {
+ ret += "⌘";
+ mod ^= Gdk.ModifierType.MetaMask;
+ }
+ if (mod != 0)
+ throw new InvalidOperationException ("Unexpected modifiers: " + mod.ToString ());
+ return ret;
+ }
+
+ static char AppleMapKeyToSymbol (Gdk.Key key)
+ {
+ // unicode codes from http://macbiblioblog.blogspot.com/2005/05/special-key-symbols.html
+ // unmapped:
+ // ⇤ tab back
+ // ⌤ enter
+
+ switch (key) {
+ case Gdk.Key.Escape:
+ return '⎋';
+ case Gdk.Key.Tab:
+ return '⇥';
+ case Gdk.Key.Caps_Lock:
+ return '⇪';
+ case Gdk.Key.Shift_L:
+ case Gdk.Key.Shift_R:
+ return '⇧';
+ case Gdk.Key.Control_L:
+ case Gdk.Key.Control_R:
+ return '⌃';
+ case Gdk.Key.BackSpace:
+ return '⌫';
+ case Gdk.Key.Delete:
+ return '⌦';
+ case Gdk.Key.Home:
+ return '⇱';
+ case Gdk.Key.End:
+ return '⇲';
+ case Gdk.Key.Page_Up:
+ return '⇞';
+ case Gdk.Key.Page_Down:
+ return '⇟';
+ case Gdk.Key.Up:
+ return '↑';
+ case Gdk.Key.Down:
+ return '↓';
+ case Gdk.Key.Left:
+ return '←';
+ case Gdk.Key.Right:
+ return '→';
+ case Gdk.Key.Clear:
+ return '⌧';
+ case Gdk.Key.Num_Lock:
+ return '⇭';
+ case Gdk.Key.Return:
+ return '⏎';
+ case Gdk.Key.space:
+ return '␣';
+ case Gdk.Key.Meta_L:
+ case Gdk.Key.Meta_R:
+ return '⌘';
+ case Gdk.Key.Alt_L:
+ case Gdk.Key.Alt_R:
+ return '⌥';
+ }
+ return (char)0;
+ }
+
+/* static Gdk.Key AppleMapSymbolToKey (char ch)
+ {
+ // unicode codes from http://macbiblioblog.blogspot.com/2005/05/special-key-symbols.html
+ // unmapped:
+ // ⇤ tab back
+ // ⌥ option (alt, alternative)
+ // ⌘ command
+ // ⌤ enter
+
+ switch (ch) {
+ case '⎋':
+ return Gdk.Key.Escape;
+ case '⇥':
+ return Gdk.Key.Tab;
+ case '⇪':
+ return Gdk.Key.Caps_Lock;
+ case '⇧':
+ return Gdk.Key.Shift_L;
+ case '⌃':
+ return Gdk.Key.Control_L;
+ case '⌫':
+ return Gdk.Key.BackSpace;
+ case '⌦':
+ return Gdk.Key.Delete;
+ case '⇱':
+ return Gdk.Key.Home;
+ case '⇲':
+ return Gdk.Key.End;
+ case '⇞':
+ return Gdk.Key.Page_Up;
+ case '⇟':
+ return Gdk.Key.Page_Down;
+ case '↑':
+ return Gdk.Key.Up;
+ case '↓':
+ return Gdk.Key.Down;
+ case '←':
+ return Gdk.Key.Left;
+ case '→':
+ return Gdk.Key.Right;
+ case '⌧':
+ return Gdk.Key.Clear;
+ case '⇭':
+ return Gdk.Key.Num_Lock;
+ case '⏎':
+ return Gdk.Key.Return;
+ case '␣':
+ return Gdk.Key.space;
+ case '⌘':
+ return Gdk.Key.Meta_L;
+ case '⌥':
+ return Gdk.Key.Alt_L;
+ }
+ return (Gdk.Key)0;
+ }
+ */
+ #endregion
+ }
+
+//#region KeyBindingException
+// public class KeyBindingConflictException : Exception {
+// Command command;
+// string binding;
+//
+// public KeyBindingConflictException (Command command, string binding)
+// : base (String.Format ("An existing command, {0}, is already bound to {1}",
+// command, binding))
+// {
+// this.command = command;
+// this.binding = binding;
+// }
+//
+// public Command Command {
+// get { return command; }
+// }
+//
+// public string Binding {
+// get { return binding; }
+// }
+// }
+//#endregion
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingScheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingScheme.cs
new file mode 100644
index 0000000000..8b2ab77989
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingScheme.cs
@@ -0,0 +1,38 @@
+// KeyBindingScheme.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public interface KeyBindingScheme
+ {
+ string Id { get; }
+ string Name { get; }
+ KeyBindingSet GetKeyBindingSet ();
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingService.cs
new file mode 100644
index 0000000000..a698f7d97a
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingService.cs
@@ -0,0 +1,210 @@
+// KeyBindingService.cs
+//
+// Author: Jeffrey Stedfast <fejj@novell.com>
+//
+// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Xml;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+using Mono.Addins;
+
+using Unix = Mono.Unix.Native;
+
+using MonoDevelop.Core;
+using MonoDevelop.Components.Commands.ExtensionNodes;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class KeyBindingService
+ {
+ const string configFileName = "KeyBindings.xml";
+ const string configFileNameMac = "KeyBindingsMac.xml";
+
+ static KeyBindingSet current;
+ static SortedDictionary<string, KeyBindingScheme> schemes;
+ static KeyBindingSet defaultSchemeBindings;
+ static DefaultScheme defaultScheme;
+
+ static KeyBindingService ()
+ {
+ schemes = new SortedDictionary<string,KeyBindingScheme> ();
+
+ // Initialize the default scheme
+ defaultSchemeBindings = new KeyBindingSet ();
+ defaultScheme = new DefaultScheme (defaultSchemeBindings);
+ schemes.Add (defaultScheme.Id, defaultScheme);
+
+ // Initialize the current bindings
+ current = new KeyBindingSet (defaultSchemeBindings);
+ }
+
+ static string ConfigFileName {
+ get {
+ return Path.Combine (PropertyService.ConfigPath, PropertyService.IsMac? configFileNameMac : configFileName);
+ }
+ }
+
+ internal static KeyBindingSet DefaultKeyBindingSet {
+ get { return defaultSchemeBindings; }
+ }
+
+ public static KeyBindingSet CurrentKeyBindingSet {
+ get { return current; }
+ }
+
+ public static KeyBindingScheme GetScheme (string id)
+ {
+ KeyBindingScheme scheme;
+ if (schemes.TryGetValue (id, out scheme))
+ return scheme;
+ else
+ return null;
+ }
+
+ public static KeyBindingScheme GetSchemeByName (string name)
+ {
+ foreach (KeyBindingScheme scheme in schemes.Values)
+ if (scheme.Name == name)
+ return scheme;
+ return null;
+ }
+
+ public static IEnumerable<KeyBindingScheme> Schemes {
+ get {
+ foreach (KeyBindingScheme s in schemes.Values)
+ yield return s;
+ }
+ }
+
+ public static void LoadBindingsFromExtensionPath (string path)
+ {
+ AddinManager.AddExtensionNodeHandler (path, OnBindingExtensionChanged);
+ }
+
+ static void OnBindingExtensionChanged (object s, ExtensionNodeEventArgs args)
+ {
+ if (args.Change == ExtensionChange.Add) {
+ SchemeExtensionNode node = (SchemeExtensionNode) args.ExtensionNode;
+ if (node.IsForMac == PropertyService.IsMac)
+ schemes.Add (node.Id, node);
+ }
+ else {
+ SchemeExtensionNode node = (SchemeExtensionNode) args.ExtensionNode;
+ if (node.IsForMac == PropertyService.IsMac)
+ schemes.Remove (node.Name);
+ }
+ }
+
+ public static void LoadBinding (Command cmd)
+ {
+ current.LoadBinding (cmd);
+ }
+
+ public static void StoreDefaultBinding (Command cmd)
+ {
+ defaultSchemeBindings.StoreBinding (cmd);
+ }
+
+ public static void ResetCurrent (KeyBindingSet kbset)
+ {
+ current = kbset.Clone ();
+ }
+
+ public static void ResetCurrent ()
+ {
+ ResetCurrent ((string)null);
+ }
+
+ public static void ResetCurrent (string schemeId)
+ {
+ if (schemeId != null) {
+ KeyBindingScheme scheme = GetScheme (schemeId);
+ if (scheme != null) {
+ current = scheme.GetKeyBindingSet ().Clone ();
+ return;
+ }
+ }
+
+ current.ClearBindings ();
+ }
+
+ public static void StoreBinding (Command cmd)
+ {
+ current.StoreBinding (cmd);
+ }
+
+ internal static string GetCommandKey (Command cmd)
+ {
+ if (cmd.Id is Enum)
+ return cmd.Id.GetType () + "." + cmd.Id;
+ else
+ return cmd.Id.ToString ();
+ }
+
+ public static void LoadCurrentBindings (string defaultSchemaId)
+ {
+ XmlTextReader reader = null;
+
+ try {
+ reader = new XmlTextReader (ConfigFileName);
+ current.LoadScheme (reader, "current");
+ } catch {
+ ResetCurrent (defaultSchemaId);
+ } finally {
+ if (reader != null)
+ reader.Close ();
+ }
+ }
+
+ public static void SaveCurrentBindings ()
+ {
+ current.Save (ConfigFileName, "current");
+ }
+ }
+
+ class DefaultScheme: KeyBindingScheme
+ {
+ KeyBindingSet bindings;
+
+ public DefaultScheme (KeyBindingSet bindings)
+ {
+ this.bindings = bindings;
+ }
+
+ public string Id {
+ get { return "Default"; }
+ }
+
+ public string Name {
+ get { return AddinManager.CurrentLocalizer.GetString ("Default"); }
+ }
+
+ public KeyBindingSet GetKeyBindingSet ()
+ {
+ return bindings;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingSet.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingSet.cs
new file mode 100644
index 0000000000..039064767b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingSet.cs
@@ -0,0 +1,276 @@
+// KeyBindingSet.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+// Jeffrey Stedfast <fejj@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Xml;
+using System.Text;
+using MonoDevelop.Core;
+using Unix = Mono.Unix.Native;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class KeyBindingSet
+ {
+ const string version = "1.0";
+ Dictionary<string, string> bindings = new Dictionary<string, string> ();
+ KeyBindingSet parent;
+
+ public KeyBindingSet()
+ {
+ }
+
+ public KeyBindingSet (KeyBindingSet parent)
+ {
+ this.parent = parent;
+ }
+
+ public KeyBindingConflict[] CheckKeyBindingConflicts (IEnumerable<Command> commands)
+ {
+ Dictionary<string, List<Command>> bindings = new Dictionary<string, List<Command>> ();
+ List<KeyBindingConflict> conflicts = new List<KeyBindingConflict> ();
+
+ // Load all bindings
+ foreach (Command cmd in commands) {
+ string key = GetBinding (cmd);
+ if (key.Length == 0)
+ continue;
+ List<Command> clist;
+ if (!bindings.TryGetValue (key, out clist)) {
+ clist = new List<Command> ();
+ bindings.Add (key, clist);
+ }
+ clist.Add (cmd);
+ if (clist.Count == 2)
+ // We have a conflict
+ conflicts.Add (new KeyBindingConflict (key, clist));
+ }
+
+ // Check if there are conflicts with key combinations prefixes.
+ // For example, ctrl+X would conflict with ctrl+X|A
+ foreach (Command cmd in commands) {
+ string key = GetBinding (cmd);
+ int i = key.IndexOf ('|');
+ if (i != -1) {
+ key = key.Substring (0, i);
+ List<Command> clist;
+ if (bindings.TryGetValue (key, out clist)) {
+ clist.Add (cmd);
+ if (clist.Count == 2)
+ // We have a conflict
+ conflicts.Add (new KeyBindingConflict (key, clist));
+ }
+ }
+ }
+ return conflicts.ToArray ();
+ }
+
+ public KeyBindingSet Clone ()
+ {
+ KeyBindingSet kset = new KeyBindingSet (parent);
+ kset.bindings = new Dictionary<string, string> (bindings);
+ return kset;
+ }
+
+ public bool Equals (KeyBindingSet other)
+ {
+ if (bindings.Count != other.bindings.Count)
+ return false;
+ foreach (KeyValuePair<string, string> binding in bindings) {
+ string accel;
+ if (!other.bindings.TryGetValue (binding.Key, out accel) || accel != binding.Value)
+ return false;
+ }
+ return true;
+ }
+
+ public void ClearBindings ()
+ {
+ bindings.Clear ();
+ }
+
+ public void StoreBinding (Command cmd)
+ {
+ SetBinding (cmd, cmd.AccelKey);
+ }
+
+ public void SetBinding (Command cmd, string accelKey)
+ {
+ string key = KeyBindingService.GetCommandKey (cmd);
+ if (accelKey == null) {
+ bindings.Remove (key);
+ return;
+ }
+
+ if (parent == null)
+ bindings [key] = accelKey;
+ else {
+ // If the key is the same as the default, remove it from the scheme
+ string pbind = parent.GetBinding (cmd);
+ if ((accelKey == pbind) || (string.IsNullOrEmpty (accelKey) && string.IsNullOrEmpty (pbind)))
+ bindings.Remove (key);
+ else
+ bindings[key] = accelKey;
+ }
+ }
+
+ public void LoadBinding (Command cmd)
+ {
+ cmd.AccelKey = GetBinding (cmd);
+ }
+
+ public string GetBinding (Command cmd)
+ {
+ string key = KeyBindingService.GetCommandKey (cmd);
+
+ // Schemes use default key bindings when not explicitly overriden
+ string accel;
+ if (bindings.TryGetValue (key, out accel))
+ return accel;
+ else if (parent != null)
+ return parent.GetBinding (cmd);
+ else
+ return string.Empty;
+ }
+
+ const string commandAttr = "command";
+ const string shortcutAttr = "shortcut";
+
+ internal bool LoadScheme (XmlTextReader reader, string id)
+ {
+ bool foundSchemes = false;
+ bool foundScheme = false;
+ string command;
+ string binding;
+
+ bindings.Clear ();
+
+ while (reader.Read ()) {
+ if (reader.IsStartElement ("schemes") || reader.IsStartElement ("scheme")) {
+ foundSchemes = true;
+ break;
+ }
+ }
+
+ if (!foundSchemes || reader.GetAttribute ("version") != version)
+ return false;
+
+ if (reader.IsStartElement ("schemes")) {
+ while (reader.Read ()) {
+ if (reader.IsStartElement ("scheme") && reader.GetAttribute ("name") == id) {
+ foundScheme = true;
+ break;
+ }
+ }
+ if (!foundScheme)
+ return false;
+ }
+
+ while (reader.Read ()) {
+ if (reader.IsStartElement ()) {
+ switch (reader.LocalName) {
+ case "scheme":
+ // this is the beginning of the next scheme
+ return true;
+ case "binding":
+ command = reader.GetAttribute (commandAttr);
+ binding = reader.GetAttribute (shortcutAttr);
+
+ if (command != null && command != String.Empty)
+ bindings.Add (command, binding);
+
+ break;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void Save (XmlTextWriter writer, string id)
+ {
+ writer.WriteStartElement ("scheme");
+ writer.WriteAttributeString ("name", id);
+
+ foreach (KeyValuePair<string, string> binding in bindings) {
+ writer.WriteStartElement ("binding");
+ writer.WriteAttributeString (commandAttr, binding.Key);
+ writer.WriteAttributeString (shortcutAttr, binding.Value);
+ writer.WriteEndElement ();
+ }
+
+ writer.WriteEndElement ();
+ }
+
+ public void Save (string fileName, string id)
+ {
+ XmlTextWriter writer = null;
+ Stream stream = null;
+ bool success = false;
+
+ try {
+ stream = new FileStream (fileName + '~', FileMode.Create);
+ writer = new XmlTextWriter (stream, Encoding.UTF8);
+ writer.Formatting = Formatting.Indented;
+ writer.IndentChar = ' ';
+ writer.Indentation = 2;
+
+ writer.WriteStartElement ("schemes");
+ writer.WriteAttributeString ("version", version);
+ Save (writer, id);
+ writer.WriteEndElement ();
+
+ success = true;
+ } catch (Exception e) {
+ LoggingService.LogError (e.ToString ());
+ } finally {
+ if (writer != null)
+ writer.Close ();
+
+ if (stream != null)
+ stream.Close ();
+ }
+
+ if (success)
+ FileService.SystemRename (fileName + '~', fileName);
+ }
+ }
+
+ public class KeyBindingConflict
+ {
+ internal KeyBindingConflict (string key, IEnumerable<Command> cmds)
+ {
+ Key = key;
+ Commands = cmds;
+ }
+
+ public string Key { get; internal set; }
+ public IEnumerable<Command> Commands { get; internal set; }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/LinkCommandEntry.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/LinkCommandEntry.cs
new file mode 100644
index 0000000000..8aa306233d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/LinkCommandEntry.cs
@@ -0,0 +1,106 @@
+//
+// LinkCommandEntry.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics;
+using Mono.Addins;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class LinkCommandEntry: CommandEntry
+ {
+ static object Id = new object ();
+ string text;
+ string url;
+ IconId icon = Gtk.Stock.JumpTo;
+
+ public LinkCommandEntry (string text, string url): base (Id)
+ {
+ this.text = text;
+ this.url = url;
+ }
+
+ public LinkCommandEntry (string text, string url, IconId icon): base (Id)
+ {
+ this.text = text;
+ this.url = url;
+ this.icon = icon;
+ }
+
+ public string Text {
+ get { return text; }
+ set { text = value; }
+ }
+
+ public string Url {
+ get { return url; }
+ set { url = value; }
+ }
+
+ internal void HandleActivation (object sender, EventArgs e)
+ {
+ try {
+ System.Diagnostics.Process.Start (url);
+ } catch (Exception) {
+ string msg = AddinManager.CurrentLocalizer.GetString ("Could not open the url {0}", url);
+ Gtk.MessageDialog md = new Gtk.MessageDialog (null, Gtk.DialogFlags.Modal | Gtk.DialogFlags.DestroyWithParent, Gtk.MessageType.Error, Gtk.ButtonsType.Ok, msg);
+ if (sender != null && sender is Gtk.Widget)
+ md.TransientFor = (sender as Gtk.Widget).Toplevel as Gtk.Window;
+ md.Run ();
+ md.Hide ();
+ }
+ }
+
+ internal protected override Gtk.MenuItem CreateMenuItem (CommandManager manager)
+ {
+ Gtk.ImageMenuItem item = new Gtk.ImageMenuItem (text != null ? text : url);
+ item.Image = new Gtk.Image (icon, Gtk.IconSize.Menu);
+ item.Activated += new EventHandler (HandleActivation);
+ item.Selected += delegate {
+ CommandInfo ci = new CommandInfo (Text);
+ ci.Icon = icon;
+ ci.Description = AddinManager.CurrentLocalizer.GetString ("Open {0}", Url);
+ manager.NotifySelected (ci);
+ };
+ item.Deselected += delegate {
+ manager.NotifyDeselected ();
+ };
+ return item;
+ }
+
+ internal protected override Gtk.ToolItem CreateToolItem (CommandManager manager)
+ {
+ Gtk.ToolButton item = new Gtk.ToolButton (text);
+ item.StockId = icon;
+ item.Clicked += new EventHandler (HandleActivation);
+ return item;
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/MenuToolButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/MenuToolButton.cs
new file mode 100644
index 0000000000..1524a03214
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/MenuToolButton.cs
@@ -0,0 +1,64 @@
+//
+// MenuToolButton.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace MonoDevelop.Components.Commands
+{
+ public class MenuToolButton: Gtk.ToolButton
+ {
+ Gtk.Menu menu;
+
+ public MenuToolButton (Gtk.Menu menu, string icon): base (icon)
+ {
+ this.menu = menu;
+ Child.ButtonPressEvent += new Gtk.ButtonPressEventHandler (OnButtonPress);
+
+ if (string.IsNullOrEmpty (icon)) {
+ this.Expand = false;
+ this.Homogeneous = false;
+ this.IconWidget = new Gtk.Arrow (Gtk.ArrowType.Down, Gtk.ShadowType.None);
+ }
+ }
+
+ [GLib.ConnectBeforeAttribute]
+ void OnButtonPress (object sender, Gtk.ButtonPressEventArgs e)
+ {
+ menu.Popup (null, null, new Gtk.MenuPositionFunc (OnPosition), 3, Gtk.Global.CurrentEventTime);
+ e.RetVal = true;
+ }
+
+ void OnPosition (Gtk.Menu menu, out int x, out int y, out bool pushIn)
+ {
+ this.ParentWindow.GetOrigin (out x, out y);
+ x += this.Allocation.X;
+ y += this.Allocation.Y + this.Allocation.Height;
+ pushIn = true;
+ }
+ }
+}