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
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
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Axis.cs95
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisDimension.cs35
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisPosition.cs40
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs928
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/ChartCursor.cs89
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/DateTimeAxis.cs143
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/IntegerAxis.cs96
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Serie.cs198
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/TickEnumerator.cs46
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCategoryCodon.cs49
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCodon.cs204
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandItemCodon.cs55
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/ItemSetCodon.cs72
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LinkItemCodon.cs59
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LocalCommandItemCodon.cs50
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SchemeExtensionNode.cs109
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SeparatorItemCodon.cs48
-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
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Diff.cs639
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/DiffWidget.cs511
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Merge.cs308
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Patch.cs59
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/StructuredDiff.cs306
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/TextDiff.cs177
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/UnifiedDiff.cs157
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/ArrowWindow.cs136
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockGrip.cs72
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs408
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs391
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameLayout.cs42
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameStatus.cs45
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPanel.cs965
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPosition.cs52
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarStatus.cs71
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockedPosition.cs81
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs263
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingDock.cs65
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingPosition.cs75
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/IDockBar.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/PlaceholderWindow.cs55
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs403
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs176
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs373
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs450
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs668
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs61
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs1134
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs385
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs41
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs513
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs48
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs425
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs42
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs181
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs107
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs284
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs45
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs143
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs314
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs407
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.pngbin0 -> 211 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.pngbin0 -> 239 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.pngbin0 -> 237 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.pngbin0 -> 195 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.pngbin0 -> 191 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/ISelectFileDialog.cs127
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeNode.cs133
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeOptionsPanelNode.cs48
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsDialogSection.cs64
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsPanelNode.cs114
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/PlatformDialog.cs87
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/StockIconCodon.cs77
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/BooleanEditorCell.cs92
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CharPropertyEditor.cs73
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CollectionEditor.cs359
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ColorEditorCell.cs115
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DateTimeEditor.cs98
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DefaultEditor.cs49
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EnumerationEditorCell.cs144
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs135
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs65
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsEditorCell.cs205
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsSelectorDialog.cs117
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FloatRange.cs69
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/IntRange.cs103
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditor.cs193
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditorDialog.cs78
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TimeSpanEditor.cs93
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/DefaultPropertyTab.cs98
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs182
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EventPropertyTab.cs86
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs406
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorTypeAttribute.cs63
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs467
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTree.cs711
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventArgs.cs65
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventHandler.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/SurrogateUITypeEditorAttribute.cs61
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkColors.cs162
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs310
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/Theme.cs252
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeContext.cs78
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeEngine.cs45
144 files changed, 24978 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Axis.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Axis.cs
new file mode 100644
index 0000000000..d0e5f2d008
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Axis.cs
@@ -0,0 +1,95 @@
+//
+// Axis.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.Chart
+{
+ public abstract class Axis
+ {
+ int tickSize = 6;
+ internal BasicChart Owner;
+ AxisPosition position;
+ AxisDimension dim;
+ bool showLabels = true;
+
+ public Axis ()
+ {
+ }
+
+ public Axis (bool showLabels)
+ {
+ this.showLabels = showLabels;
+ }
+
+ public int TickSize {
+ get { return tickSize; }
+ set {
+ tickSize = value;
+ if (Owner != null) Owner.QueueDraw ();
+ }
+ }
+
+ public bool ShowLabels {
+ get { return showLabels; }
+ set {
+ showLabels = value;
+ if (Owner != null) Owner.OnLayoutChanged ();
+ }
+ }
+
+ internal AxisPosition Position {
+ get { return position; }
+ set {
+ position = value;
+ if (position == AxisPosition.Top || position == AxisPosition.Bottom)
+ dim = AxisDimension.X;
+ else
+ dim = AxisDimension.Y;
+ }
+ }
+
+ internal AxisDimension Dimension {
+ get { return dim; }
+ }
+
+ public TickEnumerator GetTickEnumerator (double minTickStep)
+ {
+ TickEnumerator e = CreateTickEnumerator (minTickStep);
+ e.axis = this;
+ return e;
+ }
+
+ protected abstract TickEnumerator CreateTickEnumerator (double minTickStep);
+
+ public virtual string GetValueLabel (double value)
+ {
+ return value.ToString ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisDimension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisDimension.cs
new file mode 100644
index 0000000000..ebf0833059
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisDimension.cs
@@ -0,0 +1,35 @@
+//
+// AxisDimension.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.
+//
+
+namespace MonoDevelop.Components.Chart
+{
+ public enum AxisDimension
+ {
+ X, Y
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisPosition.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisPosition.cs
new file mode 100644
index 0000000000..42b81826ec
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/AxisPosition.cs
@@ -0,0 +1,40 @@
+//
+// AxisPosition.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.Chart
+{
+ public enum AxisPosition
+ {
+ Left,
+ Top,
+ Right,
+ Bottom
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs
new file mode 100644
index 0000000000..52304a20f2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs
@@ -0,0 +1,928 @@
+//
+// BasicChart.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;
+using Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.Chart
+{
+ public class BasicChart: DrawingArea
+ {
+ double startX, endX;
+ double startY, endY;
+ int left, top, width, height;
+ int minTickStep = 5;
+ bool xrangeChanged = true;
+ bool yrangeChanged = true;
+
+ bool autoStartX, autoStartY;
+ bool autoEndX, autoEndY;
+
+ double originX;
+ double originY;
+ bool reverseXAxis;
+ bool reverseYAxis;
+
+ int AreaBorderWidth = 1;
+ int AutoScaleMargin = 3;
+ int MinLabelGapX = 3;
+ int MinLabelGapY = 1;
+ BackgroundDisplay backgroundDisplay = BackgroundDisplay.Gradient;
+ Cairo.Color backroundColor = new Cairo.Color (0.9, 0.9, 1);
+
+ ArrayList series = new ArrayList ();
+ ArrayList axis = new ArrayList ();
+ ArrayList cursors = new ArrayList ();
+
+ bool enableSelection;
+ bool draggingCursor;
+ ChartCursor activeCursor;
+
+ ChartCursor selectionStart;
+ ChartCursor selectionEnd;
+
+ public BasicChart ()
+ {
+ this.Events = EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask;
+ selectionStart = new ChartCursor ();
+ selectionStart.Visible = false;
+ selectionEnd = new ChartCursor ();
+ selectionEnd.Visible = false;
+ AddCursor (selectionStart, AxisDimension.X);
+ AddCursor (selectionEnd, AxisDimension.X);
+ selectionStart.ValueChanged += new EventHandler (OnSelectionCursorChanged);
+ selectionEnd.ValueChanged += new EventHandler (OnSelectionCursorChanged);
+ }
+
+ public event EventHandler SelectionChanged;
+
+ public bool AllowSelection {
+ get {
+ return enableSelection;
+ }
+ set {
+ enableSelection = value;
+ if (!enableSelection) {
+ selectionStart.Visible = false;
+ selectionEnd.Visible = false;
+ }
+ }
+ }
+
+ public ChartCursor SelectionStart {
+ get { return selectionStart; }
+ }
+
+ public ChartCursor SelectionEnd {
+ get { return selectionEnd; }
+ }
+
+ public ChartCursor ActiveCursor {
+ get { return activeCursor; }
+ }
+
+ public bool ReverseXAxis {
+ get { return reverseXAxis; }
+ set { reverseXAxis = value; QueueDraw (); }
+ }
+
+ public bool ReverseYAxis {
+ get { return reverseYAxis; }
+ set { reverseYAxis = value; QueueDraw (); }
+ }
+
+ public double OriginX {
+ get { return originX; }
+ set {
+ xrangeChanged = true;
+ originX = value;
+ OnSerieChanged ();
+ }
+ }
+
+ public double OriginY {
+ get { return originY; }
+ set {
+ yrangeChanged = true;
+ originY = value;
+ OnSerieChanged ();
+ }
+ }
+
+ public double StartX {
+ get { return startX; }
+ set {
+ xrangeChanged = true;
+ startX = value;
+ if (startX > endX)
+ endX = startX;
+ OriginX = value;
+ UpdateCursors ();
+ OnSerieChanged ();
+ }
+ }
+
+ public double EndX {
+ get { return endX; }
+ set {
+ xrangeChanged = true;
+ endX = value;
+ if (endX < startX)
+ startX = endX;
+ UpdateCursors ();
+ OnSerieChanged ();
+ }
+ }
+
+ public double StartY {
+ get { return startY; }
+ set {
+ yrangeChanged = true;
+ startY = value;
+ if (startY > endY)
+ endY = startY;
+ OriginY = value;
+ UpdateCursors ();
+ OnSerieChanged ();
+ }
+ }
+
+ public double EndY {
+ get { return endY; }
+ set {
+ yrangeChanged = true;
+ endY = value;
+ if (endY < startY)
+ startY = endY;
+ UpdateCursors ();
+ OnSerieChanged ();
+ }
+ }
+
+ void FixOrigins ()
+ {
+ if (originX < startX)
+ originX = startX;
+ else if (originX > endX)
+ originX = endX;
+ if (originY < startY)
+ originY = startY;
+ else if (originY > endY)
+ originY = endY;
+ }
+
+ public void Reset ()
+ {
+ ArrayList list = (ArrayList) series.Clone ();
+ foreach (Serie s in list)
+ RemoveSerie (s);
+
+ axis.Clear ();
+ }
+
+ public void AddAxis (Axis ax, AxisPosition position)
+ {
+ ax.Owner = this;
+ ax.Position = position;
+ axis.Add (ax);
+ QueueDraw ();
+ }
+
+ public void AddSerie (Serie serie)
+ {
+ serie.Owner = this;
+ series.Add (serie);
+ OnSerieChanged ();
+ }
+
+ public void RemoveSerie (Serie serie)
+ {
+ series.Remove (serie);
+ serie.Owner = null;
+ OnSerieChanged ();
+ }
+
+ public void AddCursor (ChartCursor cursor, AxisDimension dimension)
+ {
+ cursor.Dimension = dimension;
+ cursor.ValueChanged += new EventHandler (OnCursorChanged);
+ cursor.LayoutChanged += new EventHandler (OnCursorChanged);
+ cursors.Add (cursor);
+ xrangeChanged = yrangeChanged = true;
+ QueueDraw ();
+ }
+
+ public void RemoveCursor (ChartCursor cursor)
+ {
+ cursor.ValueChanged -= new EventHandler (OnCursorChanged);
+ cursor.LayoutChanged -= new EventHandler (OnCursorChanged);
+ cursors.Remove (cursor);
+ QueueDraw ();
+ }
+
+ public void SetAutoScale (AxisDimension ad, bool autoStart, bool autoEnd)
+ {
+ if (ad == AxisDimension.X) {
+ autoStartX = autoStart;
+ autoEndX = autoEnd;
+ } else {
+ autoStartY = autoStart;
+ autoEndY = autoEnd;
+ }
+ }
+
+ void UpdateCursors ()
+ {
+ foreach (ChartCursor c in cursors) {
+ if (c.Value < GetStart (c.Dimension)) c.Value = GetStart (c.Dimension);
+ else if (c.Value > GetEnd (c.Dimension)) c.Value = GetEnd (c.Dimension);
+ }
+ }
+
+ void OnCursorChanged (object sender, EventArgs args)
+ {
+ ChartCursor c = (ChartCursor) sender;
+ if (c.Value < GetStart (c.Dimension)) c.Value = GetStart (c.Dimension);
+ else if (c.Value > GetEnd (c.Dimension)) c.Value = GetEnd (c.Dimension);
+ QueueDraw ();
+ }
+
+ internal void OnSerieChanged ()
+ {
+ xrangeChanged = true;
+ yrangeChanged = true;
+ QueueDraw ();
+ }
+
+ public void Clear ()
+ {
+ foreach (Serie s in series)
+ s.Clear ();
+ OnSerieChanged ();
+ }
+
+ double GetOrigin (AxisDimension ad)
+ {
+ if (ad == AxisDimension.X)
+ return OriginX;
+ else
+ return OriginY;
+ }
+
+ double GetStart (AxisDimension ad)
+ {
+ if (ad == AxisDimension.X)
+ return startX;
+ else
+ return startY;
+ }
+
+ double GetEnd (AxisDimension ad)
+ {
+ if (ad == AxisDimension.X)
+ return endX;
+ else
+ return endY;
+ }
+
+ int GetAreaSize (AxisDimension ad)
+ {
+ if (ad == AxisDimension.X)
+ return width;
+ else
+ return height;
+ }
+
+ double GetMinTickStep (AxisDimension ad)
+ {
+ return (((double) minTickStep) * (GetEnd (ad) - GetStart (ad))) / (double) GetAreaSize (ad);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose args)
+ {
+ Gdk.Window win = GdkWindow;
+ int rwidth, rheight;
+
+ Cairo.Context ctx = CairoHelper.Create (win);
+
+ win.GetSize (out rwidth, out rheight);
+
+ if (autoStartY || autoEndY) {
+ double nstartY = double.MaxValue;
+ double nendY = double.MinValue;
+ GetValueRange (AxisDimension.Y, out nstartY, out nendY);
+
+ if (!autoStartY) nstartY = startY;
+ if (!autoEndY) nendY = endY;
+ if (nendY < nstartY) nendY = nstartY;
+
+ if (nstartY != startY || nendY != endY) {
+ yrangeChanged = true;
+ startY = nstartY;
+ endY = nendY;
+ }
+ }
+
+ if (autoStartX || autoEndX) {
+ double nstartX = double.MaxValue;
+ double nendX = double.MinValue;
+ GetValueRange (AxisDimension.X, out nstartX, out nendX);
+
+ if (!autoStartX) nstartX = startX;
+ if (!autoEndX) nendX = endX;
+ if (nendX < nstartX) nendX = nstartX;
+
+ if (nstartX != startX || nendX != endX) {
+ xrangeChanged = true;
+ startX = nstartX;
+ endX = nendX;
+ }
+ }
+
+ if (yrangeChanged) {
+ FixOrigins ();
+ int right = rwidth - 2 - AreaBorderWidth;
+ left = AreaBorderWidth;
+ left += MeasureAxisSize (AxisPosition.Left) + 1;
+ right -= MeasureAxisSize (AxisPosition.Right) + 1;
+ yrangeChanged = false;
+ width = right - left + 1;
+ if (width <= 0) width = 1;
+ }
+
+ if (xrangeChanged) {
+ FixOrigins ();
+ int bottom = rheight - 2 - AreaBorderWidth;
+ top = AreaBorderWidth;
+ bottom -= MeasureAxisSize (AxisPosition.Bottom);
+ top += MeasureAxisSize (AxisPosition.Top);
+
+ // Make room for cursor handles
+ foreach (ChartCursor cursor in cursors) {
+ if (cursor.Dimension == AxisDimension.X && top - AreaBorderWidth < cursor.HandleSize)
+ top = cursor.HandleSize + AreaBorderWidth;
+ }
+
+ xrangeChanged = false;
+ height = bottom - top + 1;
+ if (height <= 0) height = 1;
+ }
+
+ if (AutoScaleMargin != 0 && height > 0) {
+ double margin = (double)AutoScaleMargin * (endY - startY) / (double) height;
+ if (autoStartY) startY -= margin;
+ if (autoEndY) endY += margin;
+ }
+
+// Console.WriteLine ("L:" + left + " T:" + top + " W:" + width + " H:" + height);
+
+ // Draw the background
+
+ if (backgroundDisplay == BackgroundDisplay.Gradient) {
+ ctx.Rectangle (left - 1, top - 1, width + 2, height + 2);
+ Cairo.Gradient pat = new Cairo.LinearGradient (left - 1, top - 1, left - 1, height + 2);
+ pat.AddColorStop (0, backroundColor);
+ Cairo.Color endc = new Cairo.Color (1,1,1);
+ pat.AddColorStop (1, endc);
+ ctx.Pattern = pat;
+ ctx.Fill ();
+ } else {
+ ctx.Rectangle (left - 1, top - 1, width + 2, height + 2);
+ ctx.Color = backroundColor;
+ ctx.Fill ();
+ }
+// win.DrawRectangle (Style.WhiteGC, true, left - 1, top - 1, width + 2, height + 2);
+ win.DrawRectangle (Style.BlackGC, false, left - AreaBorderWidth, top - AreaBorderWidth, width + AreaBorderWidth*2, height + AreaBorderWidth*2);
+
+ // Draw selected area
+
+ if (enableSelection) {
+ int sx, sy, ex, ey;
+ GetPoint (selectionStart.Value, selectionStart.Value, out sx, out sy);
+ GetPoint (selectionEnd.Value, selectionEnd.Value, out ex, out ey);
+ if (sx > ex) {
+ int tmp = sx; sx = ex; ex = tmp;
+ }
+ Gdk.GC sgc = new Gdk.GC (GdkWindow);
+ sgc.RgbFgColor = new Color (225, 225, 225);
+ win.DrawRectangle (sgc, true, sx, top, ex - sx, height + 1);
+ }
+
+ // Draw axes
+
+ Gdk.GC gc = Style.BlackGC;
+
+ foreach (Axis ax in axis)
+ DrawAxis (win, gc, ax);
+
+ // Draw values
+ foreach (Serie serie in series)
+ if (serie.Visible)
+ DrawSerie (ctx, serie);
+
+ // Draw cursors
+ foreach (ChartCursor cursor in cursors)
+ DrawCursor (cursor);
+
+ // Draw cursor labels
+ foreach (ChartCursor cursor in cursors)
+ if (cursor.ShowValueLabel)
+ DrawCursorLabel (cursor);
+
+ ((IDisposable)ctx).Dispose ();
+ return true;
+ }
+
+ void GetValueRange (AxisDimension ad, out double min, out double max)
+ {
+ min = double.MaxValue;
+ max = double.MinValue;
+
+ foreach (Serie serie in series) {
+ if (!serie.HasData || !serie.Visible)
+ continue;
+
+ double lmin, lmax;
+ serie.GetRange (ad, out lmin, out lmax);
+ if (lmin < min) min = lmin;
+ if (lmax > max) max = lmax;
+ }
+ }
+
+ void DrawAxis (Gdk.Window win, Gdk.GC gc, Axis ax)
+ {
+ double minStep = GetMinTickStep (ax.Dimension);
+
+ TickEnumerator enumSmall = ax.GetTickEnumerator (minStep);
+ if (enumSmall == null)
+ return;
+
+ TickEnumerator enumBig = ax.GetTickEnumerator (minStep * 2);
+
+ if (enumBig == null) {
+ DrawTicks (win, gc, enumSmall, ax.Position, ax.Dimension, ax.TickSize, ax.ShowLabels);
+ } else {
+ DrawTicks (win, gc, enumSmall, ax.Position, ax.Dimension, ax.TickSize / 2, false);
+ DrawTicks (win, gc, enumBig, ax.Position, ax.Dimension, ax.TickSize, ax.ShowLabels);
+ }
+ }
+
+ void DrawTicks (Gdk.Window win, Gdk.GC gc, TickEnumerator e, AxisPosition pos, AxisDimension ad, int tickSize, bool showLabels)
+ {
+ int rwidth, rheight;
+ win.GetSize (out rwidth, out rheight);
+
+ Pango.Layout layout = null;
+
+ if (showLabels) {
+ layout = new Pango.Layout (this.PangoContext);
+ layout.FontDescription = Pango.FontDescription.FromString ("Tahoma 8");
+ }
+
+ bool isX = pos == AxisPosition.Top || pos == AxisPosition.Bottom;
+ bool isTop = pos == AxisPosition.Top || pos == AxisPosition.Right;
+
+ double start = GetStart (ad);
+ double end = GetEnd (ad);
+
+ e.Init (GetOrigin (ad));
+
+ while (e.CurrentValue > start)
+ e.MovePrevious ();
+
+ int lastPosLabel;
+ int lastPos;
+ int lastTw = 0;
+
+ if (isX) {
+ lastPosLabel = reverseXAxis ? left + width + MinLabelGapX : left - MinLabelGapX;
+ lastPos = left - minTickStep*2;
+ }
+ else {
+ lastPosLabel = reverseYAxis ? top - MinLabelGapY : rheight + MinLabelGapY;
+ lastPos = top + height + minTickStep*2;
+ }
+
+ for ( ; e.CurrentValue <= end; e.MoveNext ())
+ {
+ int px, py;
+ int tw = 0, th = 0;
+ int tick = tickSize;
+
+ GetPoint (e.CurrentValue, e.CurrentValue, out px, out py);
+
+ if (showLabels) {
+ layout.SetMarkup (e.CurrentLabel);
+ layout.GetPixelSize (out tw, out th);
+ }
+
+ if (isX) {
+ if (Math.Abs ((long)px - (long)lastPos) < minTickStep || px < left || px > left + width)
+ continue;
+ lastPos = px;
+
+ bool labelFits = false;
+ if ((Math.Abs (px - lastPosLabel) - (tw/2) - (lastTw/2)) >= MinLabelGapX) {
+ lastPosLabel = px;
+ lastTw = tw;
+ labelFits = true;
+ }
+
+ if (isTop) {
+ if (showLabels) {
+ if (labelFits)
+ win.DrawLayout (gc, px - (tw/2), top - AreaBorderWidth - th, layout);
+ else
+ tick = tick / 2;
+ }
+ win.DrawLine (gc, px, top, px, top + tick);
+ }
+ else {
+ if (showLabels) {
+ if (labelFits)
+ win.DrawLayout (gc, px - (tw/2), top + height + AreaBorderWidth, layout);
+ else
+ tick = tick / 2;
+ }
+ win.DrawLine (gc, px, top + height, px, top + height - tick);
+ }
+ }
+ else {
+ if (Math.Abs ((long)lastPos - (long)py) < minTickStep || py < top || py > top + height)
+ continue;
+ lastPos = py;
+
+ bool labelFits = false;
+ if ((Math.Abs (py - lastPosLabel) - (th/2) - (lastTw/2)) >= MinLabelGapY) {
+ lastPosLabel = py;
+ lastTw = th;
+ labelFits = true;
+ }
+
+ if (isTop) {
+ if (showLabels) {
+ if (labelFits)
+ win.DrawLayout (gc, left + width + AreaBorderWidth + 1, py - (th/2), layout);
+ else
+ tick = tick / 2;
+ }
+ win.DrawLine (gc, left + width, py, left + width - tick, py);
+ }
+ else {
+ if (showLabels) {
+ if (labelFits)
+ win.DrawLayout (gc, left - AreaBorderWidth - tw - 1, py - (th/2), layout);
+ else
+ tick = tick / 2;
+ }
+ win.DrawLine (gc, left, py, left + tick, py);
+ }
+ }
+ }
+ }
+
+ int MeasureAxisSize (AxisPosition pos)
+ {
+ int max = 0;
+ foreach (Axis ax in axis)
+ if (ax.Position == pos && ax.ShowLabels) {
+ int nmax = MeasureAxisSize (ax);
+ if (nmax > max) max = nmax;
+ }
+ return max;
+ }
+
+ int MeasureAxisSize (Axis ax)
+ {
+ double minStep = GetMinTickStep (ax.Dimension);
+
+ TickEnumerator enumSmall = ax.GetTickEnumerator (minStep);
+ if (enumSmall == null)
+ return 0;
+
+ TickEnumerator enumBig = ax.GetTickEnumerator (minStep * 2);
+
+ if (enumBig == null)
+ return MeasureTicksSize (enumSmall, ax.Dimension);
+ else
+ return MeasureTicksSize (enumBig, ax.Dimension);
+ }
+
+ int MeasureTicksSize (TickEnumerator e, AxisDimension ad)
+ {
+ int max = 0;
+ Pango.Layout layout = new Pango.Layout (this.PangoContext);
+ layout.FontDescription = Pango.FontDescription.FromString ("Tahoma 8");
+
+ double start = GetStart (ad);
+ double end = GetEnd (ad);
+
+ e.Init (GetOrigin (ad));
+
+ while (e.CurrentValue > start)
+ e.MovePrevious ();
+
+ for ( ; e.CurrentValue <= end; e.MoveNext ())
+ {
+ int tw = 0, th = 0;
+
+ layout.SetMarkup (e.CurrentLabel);
+ layout.GetPixelSize (out tw, out th);
+
+ if (ad == AxisDimension.X) {
+ if (th > max)
+ max = th;
+ } else {
+ if (tw > max)
+ max = tw;
+ }
+ }
+ return max;
+ }
+
+ void DrawSerie (Cairo.Context ctx, Serie serie)
+ {
+ ctx.NewPath ();
+ ctx.Rectangle (left, top, width + 1, height + 1);
+ ctx.Clip ();
+
+ ctx.NewPath ();
+ ctx.Color = serie.Color;
+ ctx.LineWidth = serie.LineWidth;
+
+ bool first = true;
+ bool blockMode = serie.DisplayMode == DisplayMode.BlockLine;
+
+ double lastY = 0;
+
+ foreach (Data d in serie.GetData (startX, endX)) {
+ double x, y;
+ GetPoint (d.X, d.Y, out x, out y);
+ if (first) {
+ ctx.MoveTo (x, y);
+ lastY = y;
+ first = false;
+ } else {
+ if (blockMode) {
+ if (lastY != y)
+ ctx.LineTo (x, lastY);
+ ctx.LineTo (x, y);
+ } else
+ ctx.LineTo (x, y);
+ }
+ lastY = y;
+ }
+
+ ctx.Stroke ();
+ }
+
+ void DrawCursor (ChartCursor cursor)
+ {
+ Gdk.GC gc = new Gdk.GC (GdkWindow);
+ gc.RgbFgColor = cursor.Color;
+
+ int x, y;
+ GetPoint (cursor.Value, cursor.Value, out x, out y);
+
+ if (cursor.Dimension == AxisDimension.X) {
+ int cy = top - AreaBorderWidth - 1;
+ Point[] ps = new Point [4];
+ ps [0] = new Point (x, cy);
+ ps [1] = new Point (x + (cursor.HandleSize/2), cy - cursor.HandleSize + 1);
+ ps [2] = new Point (x - (cursor.HandleSize/2), cy - cursor.HandleSize + 1);
+ ps [3] = ps [0];
+ GdkWindow.DrawPolygon (gc, false, ps);
+ if (activeCursor == cursor)
+ GdkWindow.DrawPolygon (gc, true, ps);
+ GdkWindow.DrawLine (gc, x, top, x, top + height);
+ } else {
+ throw new NotSupportedException ();
+ }
+ }
+
+ void DrawCursorLabel (ChartCursor cursor)
+ {
+ Gdk.GC gc = new Gdk.GC (GdkWindow);
+ gc.RgbFgColor = cursor.Color;
+
+ int x, y;
+ GetPoint (cursor.Value, cursor.Value, out x, out y);
+
+ if (cursor.Dimension == AxisDimension.X) {
+
+ string text;
+
+ if (cursor.LabelAxis != null) {
+ double minStep = GetMinTickStep (cursor.Dimension);
+ TickEnumerator tenum = cursor.LabelAxis.GetTickEnumerator (minStep);
+ tenum.Init (cursor.Value);
+ text = tenum.CurrentLabel;
+ } else {
+ text = GetValueLabel (cursor.Dimension, cursor.Value);
+ }
+
+ if (text != null && text.Length > 0) {
+ Pango.Layout layout = new Pango.Layout (this.PangoContext);
+ layout.FontDescription = Pango.FontDescription.FromString ("Tahoma 8");
+ layout.SetMarkup (text);
+
+ int tw, th;
+ layout.GetPixelSize (out tw, out th);
+ int tl = x - tw/2;
+ int tt = top + 4;
+ if (tl + tw + 2 >= left + width) tl = left + width - tw - 1;
+ if (tl < left + 1) tl = left + 1;
+ GdkWindow.DrawRectangle (Style.WhiteGC, true, tl - 1, tt - 1, tw + 2, th + 2);
+ GdkWindow.DrawRectangle (Style.BlackGC, false, tl - 2, tt - 2, tw + 3, th + 3);
+ GdkWindow.DrawLayout (gc, tl, tt, layout);
+ }
+ } else {
+ throw new NotSupportedException ();
+ }
+ }
+
+ void GetPoint (double wx, double wy, out int x, out int y)
+ {
+ double dx, dy;
+ GetPoint (wx, wy, out dx, out dy);
+ x = (int) dx;
+ y = (int) dy;
+ }
+
+ void GetPoint (double wx, double wy, out double x, out double y)
+ {
+ unchecked {
+ if (reverseXAxis)
+ x = left + width - (((wx - startX) * ((double) width)) / (endX - startX));
+ else
+ x = left + (((wx - startX) * ((double) width)) / (endX - startX));
+
+ if (reverseYAxis)
+ y = top + ((wy - startY) * ((double) height) / (endY - startY));
+ else
+ y = top + height - ((wy - startY) * ((double) height) / (endY - startY));
+ }
+ }
+
+ void GetValue (int x, int y, out double wx, out double wy)
+ {
+ unchecked {
+ if (reverseXAxis)
+ wx = startX + ((double) (left + width - 1 - x)) * (endX - startX) / (double) width;
+ else
+ wx = startX + ((double) (x - left)) * (endX - startX) / (double) width;
+
+ if (reverseYAxis)
+ wy = startY + ((double) (top + y)) * (endY - startY) / (double) height;
+ else
+ wy = startY + ((double) (top + height - y - 1)) * (endY - startY) / (double) height;
+ }
+ }
+
+ string GetValueLabel (AxisDimension ad, double value)
+ {
+ foreach (Axis ax in axis)
+ if (ax.Dimension == ad)
+ return ax.GetValueLabel (value);
+ return null;
+ }
+
+ internal void OnLayoutChanged ()
+ {
+ xrangeChanged = true;
+ yrangeChanged = true;
+ QueueDraw ();
+ }
+
+ void OnSelectionCursorChanged (object sender, EventArgs args)
+ {
+ if (enableSelection) {
+ if (selectionStart.Value > selectionEnd.Value) {
+ ChartCursor tmp = selectionStart;
+ selectionStart = selectionEnd;
+ selectionEnd = tmp;
+ }
+ OnSelectionChanged ();
+ }
+ }
+
+ protected override bool OnButtonPressEvent (Gdk.EventButton ev)
+ {
+ if (ev.Button == 1) {
+ foreach (ChartCursor cursor in cursors) {
+ int cx, cy;
+ GetPoint (cursor.Value, cursor.Value, out cx, out cy);
+ if (cursor.Dimension == AxisDimension.X) {
+ if (Math.Abs (ev.X - cx) <= 2 || (ev.Y < top && (Math.Abs (ev.X - cx) <= cursor.HandleSize/2))) {
+ activeCursor = cursor;
+ draggingCursor = true;
+ activeCursor.ShowValueLabel = true;
+ QueueDraw ();
+ break;
+ }
+ } else {
+ // Implement
+ }
+ }
+
+ if (enableSelection && !draggingCursor) {
+ selectionStart.Visible = true;
+ selectionEnd.Visible = true;
+
+ double x, y;
+ GetValue ((int)ev.X, (int)ev.Y, out x, out y);
+ // avoid cursor swaping
+ ChartCursor c1 = selectionStart;
+ ChartCursor c2 = selectionEnd;
+ c1.Value = x;
+ c2.Value = x;
+ activeCursor = selectionEnd;
+ activeCursor.ShowValueLabel = true;
+ draggingCursor = true;
+ QueueDraw ();
+ }
+
+ if (draggingCursor)
+ return true;
+ }
+ return base.OnButtonPressEvent (ev);
+ }
+
+ protected override bool OnButtonReleaseEvent (EventButton e)
+ {
+ if (draggingCursor) {
+ draggingCursor = false;
+ activeCursor.ShowValueLabel = false;
+ }
+ return base.OnButtonReleaseEvent (e);
+ }
+
+ protected override bool OnMotionNotifyEvent (EventMotion e)
+ {
+ if (draggingCursor) {
+ double x, y;
+ GetValue ((int)e.X, (int)e.Y, out x, out y);
+
+ if (activeCursor.Dimension == AxisDimension.X) {
+ if (x < startX) x = startX;
+ else if (x > endX) x = endX;
+ activeCursor.Value = x;
+ }
+ else {
+ if (y < startY) y = startY;
+ else if (y > endY) y = endY;
+ activeCursor.Value = y;
+ }
+ return true;
+ }
+ return base.OnMotionNotifyEvent (e);
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle rect)
+ {
+ xrangeChanged = true;
+ yrangeChanged = true;
+ base.OnSizeAllocated (rect);
+ }
+
+ protected virtual void OnSelectionChanged ()
+ {
+ if (SelectionChanged != null)
+ SelectionChanged (this, EventArgs.Empty);
+ }
+ }
+
+ public enum BackgroundDisplay
+ {
+ Solid,
+ Gradient
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/ChartCursor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/ChartCursor.cs
new file mode 100644
index 0000000000..11c0af94e8
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/ChartCursor.cs
@@ -0,0 +1,89 @@
+//
+// ChartCursor.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 Gdk;
+
+namespace MonoDevelop.Components.Chart
+{
+ public class ChartCursor
+ {
+ double val;
+ internal AxisDimension Dimension;
+ Gdk.Color color;
+ int handleSize = 6;
+ bool visible = true;
+ bool showValueLabel;
+ Axis labelAxis;
+
+ public double Value {
+ get { return val; }
+ set { val = value; OnValueChanged (); }
+ }
+
+ public bool Visible {
+ get { return visible; }
+ set { visible = value; OnLayoutChanged (); }
+ }
+
+ public Gdk.Color Color {
+ get { return color; }
+ set { color = value; OnLayoutChanged (); }
+ }
+
+ public int HandleSize {
+ get { return handleSize; }
+ set { handleSize = value; OnLayoutChanged (); }
+ }
+
+ public bool ShowValueLabel {
+ get { return showValueLabel; }
+ set { showValueLabel = value; OnLayoutChanged (); }
+ }
+
+ public Axis LabelAxis {
+ get { return labelAxis; }
+ set { labelAxis = value; OnLayoutChanged (); }
+ }
+
+ public virtual void OnValueChanged ()
+ {
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+
+ public virtual void OnLayoutChanged ()
+ {
+ if (LayoutChanged != null)
+ LayoutChanged (this, EventArgs.Empty);
+ }
+
+ public EventHandler ValueChanged;
+ public EventHandler LayoutChanged;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/DateTimeAxis.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/DateTimeAxis.cs
new file mode 100644
index 0000000000..e1bc83998e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/DateTimeAxis.cs
@@ -0,0 +1,143 @@
+//
+// DateTimeAxis.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.Chart
+{
+ public class DateTimeAxis: Axis
+ {
+ public DateTimeAxis ()
+ {
+ }
+
+ public DateTimeAxis (bool showLabels): base (showLabels)
+ {
+ }
+
+ protected override TickEnumerator CreateTickEnumerator (double minTickStep)
+ {
+ long val = (long) minTickStep;
+ int scale;
+
+ if (val > TimeSpan.TicksPerDay * 30 * 365)
+ return null;
+ else if (val > TimeSpan.TicksPerDay * 30)
+ scale = 7;
+ else if (val > TimeSpan.TicksPerDay)
+ scale = 6;
+ else if (val > TimeSpan.TicksPerHour)
+ scale = 5;
+ else if (val > TimeSpan.TicksPerMinute * 15)
+ scale = 4;
+ else if (val > TimeSpan.TicksPerMinute)
+ scale = 3;
+ else if (val > TimeSpan.TicksPerSecond * 15)
+ scale = 2;
+ else if (val > TimeSpan.TicksPerSecond)
+ scale = 1;
+ else
+ scale = 0;
+
+ return new DateTimeTickEnumerator (scale);
+ }
+ }
+
+ internal class DateTimeTickEnumerator: TickEnumerator
+ {
+ int scale;
+ DateTime current;
+
+ public DateTimeTickEnumerator (int scale)
+ {
+ this.scale = scale;
+ }
+
+ public override void Init (double startValue)
+ {
+ DateTime t = new DateTime ((long)startValue);
+ DateTime nt;
+ switch (scale) {
+ case 0: nt = new DateTime (t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second); break;
+ case 1: nt = new DateTime (t.Year, t.Month, t.Day, t.Hour, t.Minute, (t.Second / 15) * 15); break;
+ case 2: nt = new DateTime (t.Year, t.Month, t.Day, t.Hour, t.Minute, 0); break;
+ case 3: nt = new DateTime (t.Year, t.Month, t.Day, t.Hour, (t.Minute / 15) * 15, 0); break;
+ case 4: nt = new DateTime (t.Year, t.Month, t.Day, t.Hour, 0, 0); break;
+ case 5: nt = new DateTime (t.Year, t.Month, t.Day); break;
+ case 6: nt = new DateTime (t.Year, t.Month, 1); break;
+ default: nt = new DateTime (t.Year, 1, 1); break;
+ }
+ current = nt;
+ }
+
+ public override void MoveNext ()
+ {
+ switch (scale) {
+ case 0: current = current.AddSeconds (1); break;
+ case 1: current = current.AddSeconds (15); break;
+ case 2: current = current.AddMinutes (1); break;
+ case 3: current = current.AddMinutes (15); break;
+ case 4: current = current.AddHours (1); break;
+ case 5: current = current.AddDays (1); break;
+ case 6: current = current.AddMonths (1); break;
+ case 7: current = current.AddYears (1); break;
+ }
+ }
+
+ public override void MovePrevious ()
+ {
+ switch (scale) {
+ case 0: current = current.AddSeconds (-1); break;
+ case 1: current = current.AddSeconds (-15); break;
+ case 2: current = current.AddMinutes (-1); break;
+ case 3: current = current.AddMinutes (-15); break;
+ case 4: current = current.AddHours (-1); break;
+ case 5: current = current.AddDays (-1); break;
+ case 6: current = current.AddMonths (-1); break;
+ case 7: current = current.AddYears (-1); break;
+ }
+ }
+
+ public override double CurrentValue {
+ get { return (double) current.Ticks; }
+ }
+
+ public override string CurrentLabel {
+ get {
+ switch (scale) {
+ case 0: case 1: return string.Format ("{0}:{1:00}:{2:00}", current.Hour, current.Minute, current.Second);
+ case 2: case 3: return string.Format ("{0}:{1:00}", current.Hour, current.Minute);
+ case 4: return string.Format ("{0}:00", current.Hour);
+ case 5: return current.ToShortDateString ();
+ case 6: return string.Format ("{0}/{1}", current.Month, current.Year);
+ default: return string.Format ("{0}", current.Year);
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/IntegerAxis.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/IntegerAxis.cs
new file mode 100644
index 0000000000..e0fe4b0c1f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/IntegerAxis.cs
@@ -0,0 +1,96 @@
+//
+// IntegerAxis.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.Chart
+{
+ public class IntegerAxis: Axis
+ {
+ public IntegerAxis ()
+ {
+ }
+
+ public IntegerAxis (bool showLabels): base (showLabels)
+ {
+ }
+
+ protected override TickEnumerator CreateTickEnumerator (double minTickStep)
+ {
+ long val;
+ unchecked {
+ val = (long) minTickStep;
+ long red = 10;
+ while (val > red)
+ red = red * 10;
+
+ long scale;
+
+ if (val <= 1)
+ scale = 1;
+ else if (val > red / 2)
+ scale = red;
+ else if (val > red / 4)
+ scale = red / 2;
+ else
+ scale = red / 4;
+ return new IntegerTickEnumerator (scale);
+ }
+ }
+ }
+
+ internal class IntegerTickEnumerator: TickEnumerator
+ {
+ long scale;
+ long current;
+
+ public IntegerTickEnumerator (long scale)
+ {
+ this.scale = scale;
+ }
+
+ public override void Init (double startValue)
+ {
+ current = (((long)startValue) / scale) * scale;
+ }
+
+ public override void MoveNext ()
+ {
+ current += scale;
+ }
+
+ public override void MovePrevious ()
+ {
+ current -= scale;
+ }
+
+ public override double CurrentValue {
+ get { return (double) current; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Serie.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Serie.cs
new file mode 100644
index 0000000000..45475ee559
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/Serie.cs
@@ -0,0 +1,198 @@
+//
+// Serie.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.Generic;
+using Gdk;
+
+namespace MonoDevelop.Components.Chart
+{
+ public class Serie
+ {
+ string title;
+ List<Data> dataArray = new List<Data> ();
+ bool visible = true;
+ internal BasicChart Owner;
+ Cairo.Color color;
+ bool extendBoundingValues;
+ DisplayMode mode;
+ double initialValue;
+ bool averageData;
+ double averageSpan;
+ double averageOrigin;
+ int lineWidth = 2;
+
+ public Serie ()
+ {
+ }
+
+ public Serie (string title)
+ {
+ this.title = title;
+ }
+
+ public void AddData (double x, double y)
+ {
+ dataArray.Add (new Data (x, y));
+ OnSerieChanged ();
+ }
+
+ public void Clear ()
+ {
+ dataArray.Clear ();
+ OnSerieChanged ();
+ }
+
+ public string Title {
+ get { return title; }
+ set { title = value; OnSerieChanged (); }
+ }
+
+ public bool Visible {
+ get { return visible; }
+ set { visible = value; OnSerieChanged (); }
+ }
+
+ public bool ExtendBoundingValues {
+ get { return extendBoundingValues; }
+ set { extendBoundingValues = value; OnSerieChanged (); }
+ }
+
+ // Initial value to use when ExtendBoundingValues is set to true
+ public double InitialValue {
+ get { return initialValue; }
+ set { initialValue = value; OnSerieChanged (); }
+ }
+
+ public DisplayMode DisplayMode {
+ get { return mode; }
+ set { mode = value; OnSerieChanged (); }
+ }
+
+ public bool AverageData {
+ get { return this.averageData; }
+ set { this.averageData = value; OnSerieChanged ();}
+ }
+
+ public double AverageSpan {
+ get { return this.averageSpan; }
+ set { this.averageSpan = value; OnSerieChanged ();}
+ }
+
+ public double AverageOrigin {
+ get { return this.averageOrigin; }
+ set { this.averageOrigin = value; OnSerieChanged (); }
+ }
+
+ public int LineWidth {
+ get { return this.lineWidth; }
+ set { this.lineWidth = value; }
+ }
+
+ public bool HasData {
+ get { return dataArray.Count > 0; }
+ }
+
+ public virtual void OnSerieChanged ()
+ {
+ if (Owner != null)
+ Owner.OnSerieChanged ();
+ }
+
+ public Cairo.Color Color {
+ get { return color; }
+ set { color = value; OnSerieChanged (); }
+ }
+
+ public void GetRange (AxisDimension axis, out double min, out double max)
+ {
+ min = double.MaxValue;
+ max = double.MinValue;
+ foreach (Data d in dataArray) {
+ double v = d.GetValue (axis);
+ if (v > max) max = v;
+ if (v < min) min = v;
+ }
+ }
+
+ internal IEnumerable<Data> GetData (double startX, double endX)
+ {
+ if (dataArray.Count == 0)
+ yield break;
+
+ if (extendBoundingValues) {
+ Data dfirst = dataArray [0];
+ if (dfirst.X > startX)
+ yield return new Data (startX, initialValue);
+ }
+
+ foreach (Data d in dataArray)
+ yield return d;
+
+ if (extendBoundingValues) {
+ Data dlast = dataArray [dataArray.Count - 1];
+ if (dlast.X < endX)
+ yield return new Data (endX, dlast.Y);
+ }
+ }
+ }
+
+ public enum DisplayMode
+ {
+ Line,
+ BlockLine,
+ Bar
+ }
+
+ internal class Data
+ {
+ double x;
+ double y;
+
+ internal Data (double x, double y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public double X {
+ get { return x; }
+ set { x = value; }
+ }
+
+ public double Y {
+ get { return y; }
+ set { y = value; }
+ }
+
+ public double GetValue (AxisDimension a) {
+ if (a == AxisDimension.X) return x;
+ else return y;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/TickEnumerator.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/TickEnumerator.cs
new file mode 100644
index 0000000000..721d37910b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/TickEnumerator.cs
@@ -0,0 +1,46 @@
+//
+// TickEnumerator.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.Chart
+{
+ public abstract class TickEnumerator
+ {
+ internal Axis axis;
+
+ public abstract void Init (double startValue);
+ public abstract void MoveNext ();
+ public abstract void MovePrevious ();
+ public abstract double CurrentValue { get; }
+
+ public virtual string CurrentLabel {
+ get { return axis.GetValueLabel (CurrentValue); }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCategoryCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCategoryCodon.cs
new file mode 100644
index 0000000000..cf973ae9f0
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCategoryCodon.cs
@@ -0,0 +1,49 @@
+//
+// CommandCategoryCodon.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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 Mono.Addins;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="A command category")]
+ internal class CommandCategoryCodon : ExtensionNode
+ {
+ [NodeAttribute ("_name", true, "Name", Localizable=true)]
+ string name;
+
+ public string Name {
+ get {
+ return name;
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCodon.cs
new file mode 100644
index 0000000000..b84dc02dff
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandCodon.cs
@@ -0,0 +1,204 @@
+//
+// CommandCodon.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;
+using MonoDevelop.Components.Commands;
+using Mono.Addins;
+using System.ComponentModel;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="A user interface command. The 'id' of the command must match the full name of an existing enumeration. An arbitrary string can also be used as an id for the command by just using '@' as prefix for the string.")]
+ internal class CommandCodon : TypeExtensionNode
+ {
+ [NodeAttribute ("_label", true, "Label", Localizable=true)]
+ string label;
+
+ [NodeAttribute ("_description", "Description of the command", Localizable=true)]
+ string _description;
+
+ [NodeAttribute ("shortcut", "Key combination that triggers the command. Control, Alt, Meta, Super and Shift modifiers can be specified using '+' as a separator. Multi-state key bindings can be specified using a '|' between the mode and accel. For example 'Control+D' or 'Control+X|Control+S'")]
+ string shortcut;
+
+ [NodeAttribute ("macShortcut", "Mac version of the shortcut. Format is that same as 'shortcut', but the 'Meta' modifier corresponds to the Command key.")]
+ string macShortcut;
+
+ [NodeAttribute("icon", "Icon of the command. The provided value must be a registered stock icon. A resource icon can also be specified using 'res:' as prefix for the name, for example: 'res:customIcon.png'")]
+ string icon;
+
+ [NodeAttribute("disabledVisible", "Set to 'false' if the command has to be hidden when disabled. 'true' by default.")]
+ bool disabledVisible = true;
+
+ [NodeAttribute("type", "Type of the command. It can be: normal (the default), check, radio or array.")]
+ string type = "normal";
+
+ [NodeAttribute("widget", "Class of the widget to create when type is 'custom'.")]
+ string widget = null;
+
+ [NodeAttribute("defaultHandler", "Class that handles this command. This property is optional.")]
+ string defaultHandler;
+
+ public override object CreateInstance ()
+ {
+ ActionType ct = ActionType.Normal;
+ bool isArray = false;
+ bool custom = false;
+ bool isAction = false;
+
+ foreach (string p in type.Split ('|')) {
+ switch (p) {
+ case "check":
+ ct = ActionType.Check;
+ if (isAction)
+ throw new InvalidOperationException ("Action type specified twice.");
+ isAction = true;
+ break;
+
+ case "radio":
+ ct = ActionType.Radio;
+ if (isAction)
+ throw new InvalidOperationException ("Action type specified twice.");
+ isAction = true;
+ break;
+
+ case "normal":
+ ct = ActionType.Normal;
+ if (isAction)
+ throw new InvalidOperationException ("Action type specified twice.");
+ isAction = true;
+ break;
+
+ case "custom":
+ if (widget == null)
+ throw new InvalidOperationException ("Widget type not specified in custom command.");
+ custom = true;
+ break;
+
+ case "array":
+ isArray = true;
+ break;
+
+ default:
+ throw new InvalidOperationException ("Unknown command type: " + p);
+ }
+ }
+
+ if (isAction && custom)
+ throw new InvalidOperationException ("Invalid command type combination: " + type);
+
+ Command cmd;
+
+ if (custom) {
+ if (isArray)
+ throw new InvalidOperationException ("Array custom commands are not allowed.");
+
+ CustomCommand ccmd = new CustomCommand ();
+ ccmd.Text = label;
+ ccmd.WidgetType = Addin.GetType (widget);
+ if (ccmd.WidgetType == null)
+ throw new InvalidOperationException ("Could not find command type '" + widget + "'.");
+ cmd = ccmd;
+ } else {
+ if (widget != null)
+ throw new InvalidOperationException ("Widget type can only be specified for custom commands.");
+
+ ActionCommand acmd = new ActionCommand ();
+ acmd.ActionType = ct;
+ acmd.CommandArray = isArray;
+
+ if (defaultHandler != null)
+ acmd.SetDefaultHandlerTypeInfo (Addin, defaultHandler);
+
+ cmd = acmd;
+ }
+
+ cmd.Id = ParseCommandId (this);
+ cmd.Text = StringParserService.Parse (label);
+ if ((_description != null) && (_description.Length > 0)){
+ cmd.Description = _description;
+ }
+ cmd.Description = cmd.Description;
+
+ if (icon != null)
+ cmd.Icon = GetStockId (Addin, icon);
+
+ cmd.AccelKey = KeyBindingManager.CanonicalizeBinding (PropertyService.IsMac? macShortcut : shortcut);
+
+ cmd.DisabledVisible = disabledVisible;
+
+ // Assign the category of the command
+ CommandCategoryCodon cat = Parent as CommandCategoryCodon;
+ if (cat != null)
+ cmd.Category = cat.Name;
+
+ return cmd;
+ }
+
+ internal static object ParseCommandId (ExtensionNode codon)
+ {
+ string id = codon.Id;
+ if (id.StartsWith ("@"))
+ return id.Substring (1);
+
+ return id;
+/* Type enumType = null;
+ string typeName = id;
+
+ int i = id.LastIndexOf (".");
+ if (i != -1)
+ typeName = id.Substring (0,i);
+
+ enumType = codon.Addin.GetType (typeName);
+
+ if (enumType == null)
+ enumType = Type.GetType (typeName);
+
+ if (enumType == null)
+ enumType = typeof(Command).Assembly.GetType (typeName);
+
+ if (enumType == null || !enumType.IsEnum)
+ throw new InvalidOperationException ("Could not find an enum type for the command '" + id + "'.");
+
+ try {
+ return Enum.Parse (enumType, id.Substring (i+1));
+ } catch {
+ throw new InvalidOperationException ("Could not find an enum value for the command '" + id + "'.");
+ }*/
+ }
+
+ internal static string GetStockId (RuntimeAddin addin, string icon)
+ {
+ return icon;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandItemCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandItemCodon.cs
new file mode 100644
index 0000000000..0c2768f18d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/CommandItemCodon.cs
@@ -0,0 +1,55 @@
+//
+// CommandItemCodon.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.ComponentModel;
+using MonoDevelop.Components.Commands;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="A command menu or toolbar item. The id of the element must match the id of a registered command.")]
+ internal class CommandItemCodon : InstanceExtensionNode
+ {
+ [NodeAttribute ("_label", "Label", Localizable=true)]
+ string label;
+
+ [NodeAttribute ("disabledVisible", "Visible when disabled")]
+ bool disabledVisible = true;
+
+ public override object CreateInstance ()
+ {
+ object id = CommandCodon.ParseCommandId (this);
+ return new CommandEntry (id, label, disabledVisible);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/ItemSetCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/ItemSetCodon.cs
new file mode 100644
index 0000000000..eeaa7f74ef
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/ItemSetCodon.cs
@@ -0,0 +1,72 @@
+//
+// ItemSetCodon.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.ComponentModel;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.Core;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="A submenu")]
+ internal class ItemSetCodon : InstanceExtensionNode
+ {
+ [NodeAttribute ("_label", "Label of the submenu", Localizable=true)]
+ string label;
+
+ [NodeAttribute("icon", "Icon of the submenu. The provided value must be a registered stock icon. A resource icon can also be specified using 'res:' as prefix for the name, for example: 'res:customIcon.png'")]
+ string icon;
+
+ [NodeAttribute("autohide", "Whether the submenu should be hidden when it contains no items.")]
+ bool autohide;
+
+ public override object CreateInstance ()
+ {
+ if (label == null) label = Id;
+
+ label = StringParserService.Parse (label);
+ if (icon != null) icon = CommandCodon.GetStockId (Addin, icon);
+ CommandEntrySet cset = new CommandEntrySet (label, icon);
+ cset.CommandId = Id;
+ cset.AutoHide = autohide;
+ foreach (InstanceExtensionNode e in ChildNodes) {
+ CommandEntry ce = e.CreateInstance () as CommandEntry;
+ if (ce != null)
+ cset.Add (ce);
+ else
+ throw new InvalidOperationException ("Invalid ItemSet child: " + e);
+ }
+ return cset;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LinkItemCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LinkItemCodon.cs
new file mode 100644
index 0000000000..c4b35fde16
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LinkItemCodon.cs
@@ -0,0 +1,59 @@
+//
+// LinkItemCodon.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.ComponentModel;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.Core;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="A menu or toolbar item that opens an URL when activated")]
+ internal class LinkItemCodon : InstanceExtensionNode
+ {
+ [NodeAttribute ("_label", "Label of the item.", Localizable=true)]
+ string label;
+
+ [NodeAttribute ("link", "URL to open.")]
+ string link;
+
+// [XmlMemberAttribute("description")]
+// string description;
+
+ public override object CreateInstance ()
+ {
+ // FIXME: Use internal resource
+ return new LinkCommandEntry (label, link, "md-web-search-icon");
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LocalCommandItemCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LocalCommandItemCodon.cs
new file mode 100644
index 0000000000..38250172be
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/LocalCommandItemCodon.cs
@@ -0,0 +1,50 @@
+//
+// LocalCommandItemCodon.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.ComponentModel;
+using MonoDevelop.Core;
+using MonoDevelop.Components.Commands;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="Allows the definition of a complete command item inside a menu or toolbar description")]
+ internal class LocalCommandItemCodon : CommandCodon
+ {
+ public override object CreateInstance ()
+ {
+ Command cmd = (Command) base.CreateInstance ();
+ return new CommandEntry (cmd);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SchemeExtensionNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SchemeExtensionNode.cs
new file mode 100644
index 0000000000..4d55f72f48
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SchemeExtensionNode.cs
@@ -0,0 +1,109 @@
+// SchemeExtensionNode.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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 Mono.Addins;
+using System.Xml;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ internal class SchemeExtensionNode: ExtensionNode, KeyBindingScheme
+ {
+ [NodeAttribute ("_name", "Name of the key bindings scheme", Localizable=true)]
+ string name;
+
+ [NodeAttribute ("file", "Name of the key bindings file")]
+ string file;
+
+ [NodeAttribute ("forMac", "Whether the keybinding file is for Macs.")]
+ bool isForMac;
+
+ [NodeAttribute ("resource", "Name of the resource containing the key bindings file.")]
+ string resource;
+
+ KeyBindingSet cachedSet;
+
+ public string Name {
+ get { return name ?? Id; }
+ }
+
+ public string File {
+ get {
+ return file;
+ }
+ }
+
+ public string Resource {
+ get {
+ return resource;
+ }
+ }
+
+ public bool IsForMac {
+ get { return isForMac; }
+ }
+
+ public Stream GetKeyBindingsSchemeStream ()
+ {
+ if (!string.IsNullOrEmpty (file))
+ return System.IO.File.OpenRead (Addin.GetFilePath (file));
+ if (!string.IsNullOrEmpty (resource))
+ return Addin.GetResource (resource, true);
+ throw new InvalidOperationException ("File or resource name not specified");
+ }
+
+ public KeyBindingSet GetKeyBindingSet ()
+ {
+ if (cachedSet != null)
+ return cachedSet;
+
+ XmlTextReader reader = null;
+ Stream stream = null;
+
+ try {
+ stream = GetKeyBindingsSchemeStream ();
+ if (stream != null) {
+ reader = new XmlTextReader (stream);
+ cachedSet = new KeyBindingSet (KeyBindingService.DefaultKeyBindingSet);
+ cachedSet.LoadScheme (reader, Id);
+ return cachedSet;
+ }
+ } catch (Exception e) {
+ LoggingService.LogError ("Error reading keybindings definition '{0}' in addin '{1}'.\n {2}",
+ file ?? resource, Addin.Id, e.ToString ());
+ } finally {
+ if (reader != null)
+ reader.Close ();
+ if (stream != null)
+ stream.Close ();
+ }
+ return null;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SeparatorItemCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SeparatorItemCodon.cs
new file mode 100644
index 0000000000..c19e9fac86
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands.ExtensionNodes/SeparatorItemCodon.cs
@@ -0,0 +1,48 @@
+//
+// SeparatorItemCodon.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.ComponentModel;
+using MonoDevelop.Components.Commands;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Commands.ExtensionNodes
+{
+ [ExtensionNode (Description="A separator menu or toolbar item")]
+ internal class SeparatorItemCodon : InstanceExtensionNode
+ {
+ public override object CreateInstance ()
+ {
+ return new CommandEntry (Command.Separator);
+ }
+ }
+}
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;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Diff.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Diff.cs
new file mode 100644
index 0000000000..11f0da8984
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Diff.cs
@@ -0,0 +1,639 @@
+/*
+ * Diff Algorithm in C#
+ * Based on Tye McQueen's Algorithm::Diff Perl module version 1.19_01
+ * Converted to C# by Joshua Tauberer <tauberer@for.net>
+ *
+ * The Perl module's copyright notice:
+ * Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved.
+ * Parts by Tye McQueen.
+ *
+ * The Perl module's readme has a ridiculously long list of
+ * thanks for all of the previous authors, who are:
+ * Mario Wolczko (author of SmallTalk code the module is based on)
+ * Ned Konz
+ * Mark-Jason Dominus
+ * Mike Schilli
+ * Amir Karger
+ * Christian Murphy
+ *
+ * The Perl module was released under the Perl Artistic License,
+ * and I leave my additions in the public domain, so I leave
+ * it up to you to figure out what you need to do if you want
+ * to distribute this file in some form.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonoDevelop.Components.Diff {
+ public interface IDiff : IEnumerable {
+ IList Left { get; }
+ IList Right { get; }
+ }
+
+ public abstract class Hunk {
+ internal Hunk() { }
+
+ public abstract int ChangedLists { get; }
+
+ public abstract bool Same { get; }
+ public abstract bool Conflict { get; }
+
+ public abstract bool IsSame(int index);
+
+ public abstract Range Original();
+ public abstract Range Changes(int index);
+
+ public int MaxLines() {
+ int m = Original().Count;
+ for (int i = 0; i < ChangedLists; i++)
+ if (Changes(i).Count > m)
+ m = Changes(i).Count;
+ return m;
+ }
+ }
+
+ public class Diff : IDiff {
+ internal IList left, right;
+ IComparer comparer;
+ IEqualityComparer hashcoder;
+
+ public IList Left { get { return left; } }
+ public IList Right { get { return right; } }
+
+ private class Trio {
+ public Trio a;
+ public int b, c;
+ public Trio(Trio a, int b, int c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+ }
+
+ public class Hunk : MonoDevelop.Components.Diff.Hunk {
+ IList left, right;
+ int s1start, s1end, s2start, s2end;
+ bool same;
+
+ internal Hunk(IList left, IList right, int s1start, int s1end, int s2start, int s2end, bool same) {
+ this.left = left;
+ this.right = right;
+ this.s1start = s1start;
+ this.s1end = s1end;
+ this.s2start = s2start;
+ this.s2end = s2end;
+ this.same = same;
+ }
+
+ internal void SetLists(IList left, IList right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public override int ChangedLists { get { return 1; } }
+
+ public override bool Same { get { return same; } }
+
+ public override bool Conflict { get { return false; } }
+
+ public override bool IsSame(int index) {
+ if (index != 0) throw new ArgumentException();
+ return Same;
+ }
+
+ private Range get(int seq) {
+ int start = (seq==1?s1start:s2start);
+ int end = (seq==1?s1end:s2end);
+ IList list = (seq==1?left:right);
+ if (end < start) return new Range(list, start, 0);
+ return new Range(list, start, end-start+1);
+ }
+
+ public Range Left { get { return get(1); } }
+ public Range Right { get { return get(2); } }
+
+ public override Range Original() { return Left; }
+ public override Range Changes(int index) {
+ if (index != 0) throw new ArgumentException();
+ return Right;
+ }
+
+ public override int GetHashCode() {
+ return unchecked(s1start + s1end + s2start + s2end);
+ }
+
+ public override bool Equals(object o) {
+ Hunk h = o as Hunk;
+ return
+ s1start == h.s1start &&
+ s1start == h.s1end &&
+ s1start == h.s2start &&
+ s1start == h.s2end &&
+ same == h.same;
+ }
+
+ public override string ToString() {
+ if (left == null || right == null)
+ return base.ToString();
+ return DiffString();
+ }
+
+ public string DiffString() {
+ if (left == null || right == null)
+ throw new InvalidOperationException("This hunk is based on a patch which does not have the compared data.");
+
+ StringBuilder ret = new StringBuilder();
+
+ if (Same) {
+ foreach (object item in Left) {
+ ret.Append(' ');
+ ret.Append(item.ToString());
+ ret.Append(Environment.NewLine);
+ }
+ } else {
+ foreach (object item in Left) {
+ ret.Append('<');
+ ret.Append(item.ToString());
+ ret.Append(Environment.NewLine);
+ }
+ foreach (object item in Right) {
+ ret.Append('>');
+ ret.Append(item.ToString());
+ ret.Append(Environment.NewLine);
+ }
+ }
+
+ return ret.ToString();
+ }
+
+ internal Hunk Crop(int shiftstart, int shiftend) {
+ return new Diff.Hunk(left, right, Left.Start+shiftstart, Left.End-shiftend, Right.Start+shiftstart, Right.End-shiftend, same);
+ }
+
+ internal Hunk Reverse() {
+ return new Diff.Hunk(right, left, Right.Start, Right.End, Left.Start, Left.End, same);
+ }
+ }
+
+ public Diff(IList left, IList right, IComparer comparer, IEqualityComparer hashcoder) {
+ this.left = left;
+ this.right = right;
+ this.comparer = comparer;
+ this.hashcoder = hashcoder;
+ init();
+ }
+
+ public Diff(string leftFile, string rightFile, bool caseSensitive, bool compareWhitespace)
+ : this(UnifiedDiff.LoadFileLines(leftFile), UnifiedDiff.LoadFileLines(rightFile), caseSensitive, compareWhitespace) {
+ }
+
+ public Diff(string[] left, string[] right, bool caseSensitive, bool compareWhitespace)
+ : this(
+ StripWhitespace(left, !compareWhitespace),
+ StripWhitespace(right, !compareWhitespace),
+ caseSensitive ? (IComparer)Comparer.Default : (IComparer)CaseInsensitiveComparer.Default,
+ caseSensitive ? null : StringComparer.InvariantCultureIgnoreCase
+ ) {
+ }
+
+ ////////////////////////////////////////////////////////////
+
+
+ private static string[] StripWhitespace(string[] lines, bool strip) {
+ if (!strip) return lines;
+ string[] ret = new string[lines.Length];
+ for (int i = 0; i < lines.Length; i++) {
+ StringBuilder sb = new StringBuilder();
+ foreach (char c in lines[i])
+ if (!char.IsWhiteSpace(c))
+ sb.Append(c);
+ ret[i] = sb.ToString();
+ }
+ return ret;
+ }
+
+ ////////////////////////////////////////////////////////////
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ if (cdif == null)
+ throw new InvalidOperationException("No comparison has been performed.");
+ return new Enumerator(this);
+ }
+
+ public override string ToString() {
+ System.IO.StringWriter w = new System.IO.StringWriter();
+ UnifiedDiff.WriteUnifiedDiff(this, w);
+ return w.ToString();
+ }
+
+ public Patch CreatePatch() {
+ int ctr = 0;
+ foreach (Hunk hunk in this)
+ if (!hunk.Same)
+ ctr += hunk.Right.Count;
+
+ object[] rightData = new object[ctr];
+
+ ArrayList hunks = new ArrayList();
+ ctr = 0;
+ foreach (Hunk hunk in this) {
+ if (hunk.Same) {
+ hunks.Add(new Patch.Hunk(rightData, hunk.Left.Start, hunk.Left.Count, 0, 0, true));
+ } else {
+ hunks.Add(new Patch.Hunk(rightData, hunk.Left.Start, hunk.Left.Count, ctr, hunk.Right.Count, false));
+ for (int i = 0; i < hunk.Right.Count; i++)
+ rightData[ctr++] = hunk.Right[i];
+ }
+ }
+
+
+ return new Patch((Patch.Hunk[])hunks.ToArray(typeof(Hunk)));
+ }
+
+ /*
+ # McIlroy-Hunt diff algorithm
+ # Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com>
+ # by Ned Konz, perl@bike-nomad.com
+ # Updates by Tye McQueen, http://perlmonks.org/?node=tye
+
+ # Create a hash that maps each element of $aCollection to the set of
+ # positions it occupies in $aCollection, restricted to the elements
+ # within the range of indexes specified by $start and $end.
+ # The fourth parameter is a subroutine reference that will be called to
+ # generate a string to use as a key.
+ # Additional parameters, if any, will be passed to this subroutine.
+ #
+ # my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
+ */
+
+ Hashtable _withPositionsOfInInterval(IList aCollection, int start, int end) {
+ Hashtable d = new Hashtable(hashcoder);
+ for (int index = start; index <= end; index++) {
+ object element = aCollection[index];
+ if (d.ContainsKey(element)) {
+ List<int> list = (List<int>)d[element];
+ list.Add(index);
+ } else {
+ List<int> list = new List<int>();
+ list.Add(index);
+ d[element] = list;
+ }
+ }
+ foreach (List<int> list in d.Values)
+ list.Reverse();
+ return d;
+ }
+
+ /*
+ # Find the place at which aValue would normally be inserted into the
+ # array. If that place is already occupied by aValue, do nothing, and
+ # return undef. If the place does not exist (i.e., it is off the end of
+ # the array), add it to the end, otherwise replace the element at that
+ # point with aValue. It is assumed that the array's values are numeric.
+ # This is where the bulk (75%) of the time is spent in this module, so
+ # try to make it fast!
+ */
+ // NOTE: Instead of returning undef, it returns -1.
+ int _replaceNextLargerWith(List<int> array, int value, int high) {
+ if (high <= 0)
+ high = array.Count-1;
+
+ // off the end?
+ if (high == -1 || value > (int)array[array.Count-1]) {
+ array.Add(value);
+ return array.Count-1;
+ }
+
+ // binary search for insertion point...
+ int low = 0;
+ int index, found;
+ while (low <= high) {
+ index = (high + low) / 2;
+
+ found = (int)array[index];
+
+ if (value == found)
+ return -1;
+ else if (value > found)
+ low = index + 1;
+ else
+ high = index - 1;
+ }
+
+ // # now insertion point is in $low.
+ array[low] = value; // overwrite next larger
+ return low;
+ }
+
+ /*
+ # This method computes the longest common subsequence in $a and $b.
+
+ # Result is array or ref, whose contents is such that
+ # $a->[ $i ] == $b->[ $result[ $i ] ]
+ # foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
+
+ # An additional argument may be passed; this is a hash or key generating
+ # function that should return a string that uniquely identifies the given
+ # element. It should be the case that if the key is the same, the elements
+ # will compare the same. If this parameter is undef or missing, the key
+ # will be the element as a string.
+
+ # By default, comparisons will use "eq" and elements will be turned into keys
+ # using the default stringizing operator '""'.
+
+ # Additional parameters, if any, will be passed to the key generation
+ # routine.
+ */
+
+ bool compare(object a, object b) {
+ if (comparer == null) return a.Equals(b);
+ return comparer.Compare(a, b) == 0;
+ }
+
+ bool IsPrepared(out Hashtable bMatches) {
+ bMatches = null;
+ return false;
+ }
+
+ List<int> _longestCommonSubsequence(IList a, IList b) {
+ int aStart = 0;
+ int aFinish = a.Count-1;
+ List<int> matchVector = new List<int>();
+ Hashtable bMatches;
+
+ // initialize matchVector to length of a
+ for (int i = 0; i < a.Count; i++)
+ matchVector.Add(-1);
+
+ if (!IsPrepared(out bMatches)) {
+ int bStart = 0;
+ int bFinish = b.Count-1;
+
+ // First we prune off any common elements at the beginning
+ while (aStart <= aFinish && bStart <= bFinish && compare(a[aStart], b[bStart]))
+ matchVector[aStart++] = bStart++;
+
+ // now the end
+ while (aStart <= aFinish && bStart <= bFinish && compare(a[aFinish], b[bFinish]))
+ matchVector[aFinish--] = bFinish--;
+
+ // Now compute the equivalence classes of positions of elements
+ bMatches =
+ _withPositionsOfInInterval(b, bStart, bFinish);
+ }
+
+ List<int> thresh = new List<int>();
+ List<Trio> links = new List<Trio>();
+
+ for (int i = aStart; i <= aFinish; i++) {
+ List<int> aimatches = (List<int>)bMatches[a[i]];
+ if (aimatches != null) {
+ int k = 0;
+ for (int ji = 0; ji < aimatches.Count; ji++) {
+ int j = aimatches[ji];
+ // # optimization: most of the time this will be true
+ if (k>0 && (int)thresh[k] > j && (int)thresh[k-1] < j)
+ thresh[k] = j;
+ else
+ k = _replaceNextLargerWith(thresh, j, k);
+
+ // oddly, it's faster to always test this (CPU cache?).
+ if (k != -1) {
+ Trio t = new Trio( (Trio)( k>0 ? links[k-1] : null ), i, j );
+ if (k == links.Count)
+ links.Add( t );
+ else
+ links[k] = t;
+ }
+ }
+ }
+ }
+
+ if (thresh.Count > 0) {
+ for (Trio link = (Trio)links[thresh.Count-1]; link != null; link = link.a)
+ matchVector[link.b] = link.c;
+ }
+
+ return matchVector;
+ }
+
+ /*void prepare(IList list) {
+ prepared = _withPositionsOfInInterval(list, 0, list.Count-1);
+ preparedlist = list;
+ }*/
+
+ void LCSidx(IList a, IList b, out List<int> am, out List<int> bm) {
+ List<int> match = _longestCommonSubsequence(a, b);
+ am = new List<int>();
+ for (int i = 0; i < match.Count; i++)
+ if ((int)match[i] != -1)
+ am.Add(i);
+ bm = new List<int>();
+ for (int vi = 0; vi < am.Count; vi++)
+ bm.Add(match[am[vi]]);
+ }
+
+ List<int> compact_diff(IList a, IList b) {
+ List<int> am, bm, cdiff;
+ LCSidx(a, b, out am, out bm);
+ cdiff = new List<int>();
+ int ai = 0, bi = 0;
+ cdiff.Add(ai);
+ cdiff.Add(bi);
+ while (true) {
+ while(am.Count > 0 && ai == (int)am[0] && bi == (int)bm[0]) {
+ am.RemoveAt(0);
+ bm.RemoveAt(0);
+ ++ai;
+ ++bi;
+ }
+
+ cdiff.Add(ai);
+ cdiff.Add(bi);
+ if (am.Count == 0) break;
+ ai = (int)am[0];
+ bi = (int)bm[0];
+ cdiff.Add(ai);
+ cdiff.Add(bi);
+ }
+
+ if (ai < a.Count || bi < b.Count) {
+ cdiff.Add(a.Count);
+ cdiff.Add(b.Count);
+ }
+
+ return cdiff;
+ }
+
+ int _End;
+ bool _Same;
+ List<int> cdif = null;
+
+ void init() {
+ cdif = compact_diff(left, right);
+ _Same = true;
+ if (0 == (int)cdif[2] && 0 == (int)cdif[3]) {
+ _Same = false;
+ cdif.RemoveAt(0);
+ cdif.RemoveAt(0);
+ }
+
+ _End = (1+cdif.Count)/2;
+ }
+
+ private class Enumerator : IEnumerator {
+ Diff diff;
+ int _Pos, _Off;
+
+ public Enumerator(Diff diff) {
+ this.diff = diff;
+ Reset();
+ }
+
+ public object Current { get { _ChkPos(); return gethunk(); } }
+
+ public bool MoveNext() { return next(); }
+
+ public void Reset() { reset(0); }
+
+ void _ChkPos() {
+ if (_Pos == 0) throw new InvalidOperationException("Position is reset.");
+ }
+
+ void reset(int pos) {
+ if (pos < 0 || diff._End <= pos) pos = -1;
+ _Pos = pos;
+ _Off = 2*pos - 1;
+ }
+
+ bool next() {
+ reset(_Pos+1);
+ return _Pos != -1;
+ }
+
+ Hunk gethunk() {
+ _ChkPos();
+
+ int a1, a2, b1, b2;
+
+ int off1 = 1 + _Off;
+ int off2 = 2 + _Off;
+
+ a1 = (int)diff.cdif[off1-2];
+ a2 = (int)diff.cdif[off1] - 1;
+ b1 = (int)diff.cdif[off2-2];
+ b2 = (int)diff.cdif[off2] - 1;
+
+ bool s = same();
+ return new Hunk(diff.left, diff.right, a1, a2, b1, b2, s);
+ }
+
+ bool same() {
+ _ChkPos();
+ if (diff._Same != ((1 & _Pos) != 0))
+ return false;
+ return true;
+ }
+ }
+ }
+
+ public class Range : IList {
+ IList list;
+ int start, count;
+
+ static ArrayList EmptyList = new ArrayList();
+
+ public Range(IList list, int start, int count) {
+ this.list = list;
+ this.start = start;
+ this.count = count;
+ }
+
+ public int Start { get { return start; } }
+
+ public int Count { get { return count; } }
+
+ public int End { get { return start+count-1; } }
+
+ private void Check() {
+ if (count > 0 && list == null)
+ throw new InvalidOperationException("This range does not refer to a list with data.");
+ }
+
+ public object this[int index] {
+ get {
+ Check();
+ if (index < 0 || index >= count)
+ throw new ArgumentException("index");
+ return list[index+start];
+ }
+ }
+
+ // IEnumerable Functions
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ if (count == 0 && list == null) return EmptyList.GetEnumerator();
+ Check();
+ return new Enumer(this);
+ }
+
+ private class Enumer : IEnumerator {
+ Range list;
+ int index = -1;
+ public Enumer(Range list) { this.list = list; }
+ public void Reset() { index = -1; }
+ public bool MoveNext() {
+ index++;
+ return index < list.Count;
+ }
+ public object Current { get { return list[index]; } }
+ }
+
+ // ICollection Functions
+
+ void ICollection.CopyTo(Array array, int index) {
+ Check();
+ for (int i = 0; i < Count; i++)
+ array.SetValue(this[i], i + index);
+ }
+ object ICollection.SyncRoot {
+ get { return null; }
+ }
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ // IList Functions
+
+ bool IList.IsFixedSize { get { return true; } }
+
+ bool IList.IsReadOnly { get { return true; } }
+
+ object IList.this[int index] { get { throw new InvalidOperationException(); } set { throw new InvalidOperationException(); } }
+
+ int IList.Add(object obj) { throw new InvalidOperationException(); }
+
+ void IList.Clear() { throw new InvalidOperationException(); }
+
+ void IList.Insert(int index, object obj) { throw new InvalidOperationException(); }
+
+ void IList.Remove(object obj) { throw new InvalidOperationException(); }
+
+ void IList.RemoveAt(int index) { throw new InvalidOperationException(); }
+
+ public bool Contains(object obj) {
+ return IndexOf(obj) != -1;
+ }
+
+ public int IndexOf(object obj) {
+ for (int i = 0; i < Count; i++)
+ if (obj.Equals(this[i]))
+ return i;
+ return -1;
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/DiffWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/DiffWidget.cs
new file mode 100644
index 0000000000..0717704871
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/DiffWidget.cs
@@ -0,0 +1,511 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+using Gtk;
+using Pango;
+
+namespace MonoDevelop.Components.Diff {
+
+ public class DiffWidget : VBox
+ {
+ Pango.FontDescription font = null;
+ ScrolledWindow scroller;
+ Table difftable;
+
+ public double Position {
+ get {
+ return scroller.Vadjustment.Value;
+ }
+ set {
+ scroller.Vadjustment.Value = value;
+ }
+ }
+
+ public class Options {
+ public string LeftName = null;
+ public string RightName = null;
+ public bool SideBySide = false;
+ public bool LineWrap = false;
+ public bool LineNumbers = true;
+ public string Font = "Mono 9";
+ }
+
+ Gdk.Color
+ ColorChanged = new Gdk.Color (0xFF, 0x99, 0x99),
+ ColorChangedHighlight = new Gdk.Color (0xFF, 0xBB, 0xBB),
+ ColorAdded = new Gdk.Color (0xFF, 0xAA, 0x33),
+ ColorAddedHighlight = new Gdk.Color (0xFF, 0xBB, 0x66),
+ ColorRemoved = new Gdk.Color (0x33, 0xAA, 0xFF),
+ ColorRemovedHighlight = new Gdk.Color (0x66, 0xBB, 0xFF),
+ ColorDefault = new Gdk.Color (0xFF, 0xFF, 0xFF),
+ ColorDefaultHighlight = new Gdk.Color (0xEE, 0xEE, 0xCC),
+ ColorGrey = new Gdk.Color (0x99, 0x99, 0x99),
+ ColorBlack = new Gdk.Color (0, 0, 0);
+
+ public DiffWidget(Diff diff, Options options) : this(Hunkify(diff), options)
+ {
+ }
+
+ public DiffWidget(Merge merge, Options options) : this(Hunkify(merge), options)
+ {
+ }
+ protected override void OnDestroyed ()
+ {
+ if (font != null) {
+ font.Dispose ();
+ font = null;
+ }
+ base.OnDestroyed ();
+ }
+
+ protected override void OnStyleSet (global::Gtk.Style previous_style)
+ {
+ ColorBlack = this.Style.Text (StateType.Normal);
+ ColorGrey = this.Style.Text (StateType.Insensitive);
+ ColorDefault = this.Style.Base (StateType.Normal);
+ ColorDefaultHighlight = this.Style.Base (StateType.Prelight);
+ base.OnStyleSet (previous_style);
+ }
+
+ private static Hunk[] Hunkify(IEnumerable e)
+ {
+ ArrayList a = new ArrayList();
+ foreach (Hunk x in e)
+ a.Add(x);
+ return (Hunk[])a.ToArray(typeof(Hunk));
+ }
+
+ private DiffWidget(Hunk[] hunks, Options options) : base(false, 0)
+ {
+ if (hunks == null || hunks.Length == 0 || options == null)
+ throw new ArgumentException();
+
+ if (options.SideBySide && options.LeftName != null && options.RightName != null) {
+ HBox filetitles = new HBox(true, 2);
+ PackStart(filetitles, false, false, 2);
+ Label leftlabel = new Label(options.LeftName);
+ Label rightlabel = new Label(options.RightName);
+ filetitles.PackStart(leftlabel);
+ filetitles.PackStart(rightlabel);
+ }
+
+ HBox centerpanel = new HBox(false, 0);
+ PackStart(centerpanel);
+
+ scroller = new ScrolledWindow();
+
+ centerpanel.PackStart (new OverviewRenderer (this, scroller, hunks, options.SideBySide), false, false, 0);
+
+ Viewport textviewport = new Viewport();
+
+ centerpanel.PackStart(scroller);
+ scroller.Add(textviewport);
+
+ int nRows = 0;
+ foreach (Hunk hunk in hunks) {
+ if (options.SideBySide) {
+ nRows += hunk.MaxLines();
+ } else {
+ if (hunk.Same) {
+ nRows += hunk.Original().Count;
+ } else {
+ for (int i = 0; i < hunk.ChangedLists; i++)
+ nRows += hunk.Changes(i).Count;
+ }
+ }
+ }
+
+ uint nCols = 1 + (uint)hunks[0].ChangedLists;
+ if (options.SideBySide) nCols += 2;
+ if (options.LineNumbers) nCols++;
+
+ VBox tablecontainer = new VBox(false, 0);
+ textviewport.Add(tablecontainer);
+
+ difftable = new Table((uint)nRows, (uint)nCols, false);
+
+ tablecontainer.PackStart(difftable, false, false, 0);
+
+ uint row = 0;
+
+ if (options.Font != null)
+ font = Pango.FontDescription.FromString(options.Font);
+
+ Hunk lastHunk = hunks.Length > 0 ? hunks[hunks.Length - 1] : null;
+
+ foreach (Hunk hunk in hunks) {
+ char leftmode = hunk.Same ? ' ' : (hunk.ChangedLists == 1 && hunk.Changes(0).Count == 0) ? '-' : 'C';
+ uint inc = 0;
+
+ if (options.SideBySide) {
+ ComposeLines(hunk.Original(), leftmode, -1, difftable, row, false, 0, options.LineWrap, font, options.LineNumbers, lastHunk.Original ());
+ inc = (uint)hunk.Original().Count;
+ } else {
+ if (leftmode == 'C') leftmode = '-';
+ int altlines = -1;
+ if (hunk.ChangedLists == 1 && hunk.Same)
+ altlines = hunk.Changes(0).Start;
+ ComposeLines(hunk.Original(), leftmode, altlines, difftable, row, true, 0, options.LineWrap, font, options.LineNumbers, lastHunk.Original ());
+ row += (uint)hunk.Original().Count;
+ }
+
+ for (int i = 0; i < hunk.ChangedLists; i++) {
+ char rightmode = hunk.Same ? ' ' : hunk.Original().Count == 0 ? '+' : 'C';
+
+ if (options.SideBySide) {
+ int colsper = 1 + (options.LineNumbers ? 1 : 0);
+ ComposeLines(hunk.Changes(i), rightmode, -1, difftable, row, false, (uint)((i+1)*colsper), options.LineWrap, font, options.LineNumbers, lastHunk.Original ());
+ if (hunk.Changes(i).Count > inc)
+ inc = (uint)hunk.Changes(i).Count;
+ } else {
+ if (rightmode == 'C') rightmode = '+';
+
+ if (!hunk.Same)
+ ComposeLines(hunk.Changes(i), rightmode, -1, difftable, row, true, 0, options.LineWrap, font, options.LineNumbers, lastHunk.Original ());
+
+ if (!hunk.Same) row += (uint)hunk.Changes(i).Count;
+ }
+ }
+
+ if (options.SideBySide)
+ row += inc;
+ }
+ }
+
+ void ComposeLines(MonoDevelop.Components.Diff.Range range, char style, int otherStart, Table table, uint startRow, bool axn, uint col, bool wrap, Pango.FontDescription font, bool lineNumbers, MonoDevelop.Components.Diff.Range lastRange)
+ {
+ if (range.Count == 0) return;
+
+ StringBuilder text = new StringBuilder();
+ for (uint i = 0; i < range.Count; i++) {
+ if (axn) {
+ text.Append(style);
+ text.Append(' ');
+ }
+
+ if (lineNumbers) {
+ string lineNo = (range.Start + i + 1).ToString();
+ if (otherStart != -1 && range.Start != otherStart)
+ lineNo += "/" + (otherStart + i + 1).ToString();
+ string max = lastRange.End + "/" + lastRange.End;
+
+ text.Append(lineNo);
+ for (int j = 0; j < max.Length - lineNo.Length + 1; j++) {
+ text.Append(' ');
+ }
+// text.Append('\t');
+ }
+
+ text.Append((string)range[(int)i]);
+ if (i < range.Count-1) text.Append('\n');
+ }
+ RangeEventBox rangebox = new RangeEventBox(new RangeRenderer(this, text.ToString(), style, wrap, font));
+ table.Attach(rangebox, 0,1, startRow,(uint)(startRow+range.Count));
+
+ /*
+ int off1 = lineNumbers ? 1 : 0;
+ int off2 = off1 + (axn ? 1 : 0);
+
+ for (uint i = 0; i < range.Count; i++) {
+ if (lineNumbers) {
+ string lineNo = (range.Start + i + 1).ToString();
+ if (otherStart != -1 && range.Start != otherStart)
+ lineNo += "/" + (otherStart + i + 1).ToString();
+
+ Label label = new Label(lineNo);
+ label.ModifyFont( font );
+ label.Yalign = 0;
+ table.Attach(label, col,col+1, startRow+i,startRow+i+1, AttachOptions.Shrink, AttachOptions.Shrink, 1, 1);
+ }
+
+ if (axn) {
+ Label actionlabel = new Label(style.ToString());
+ table.Attach(actionlabel, (uint)(col+off1),(uint)(col+off1+1), startRow+i,startRow+i+1, AttachOptions.Shrink, AttachOptions.Shrink, 1, 1);
+ }
+
+ RangeEventBox line = new RangeEventBox(new RangeRenderer((string)range[(int)i], style, wrap, font));
+ table.Attach(line, (uint)(col+off2),(uint)(col+off2+1), startRow+i,startRow+i+1);
+ }
+ */
+ }
+
+ /*string GetRangeText(Algorithm.Diff.Range range) {
+ string text = "";
+ foreach (string line in range)
+ text += line + "\n";
+ return text;
+ }*/
+
+ class RangeEventBox : EventBox
+ {
+ RangeRenderer renderer;
+
+ public RangeEventBox(RangeRenderer r)
+ {
+ renderer = r;
+ Add (r);
+ this.EnterNotifyEvent += EnterNotifyEventHandler;
+ this.LeaveNotifyEvent += LeaveNotifyEventHandler;
+ }
+
+ protected override void OnDestroyed ()
+ {
+ this.EnterNotifyEvent -= EnterNotifyEventHandler;
+ this.LeaveNotifyEvent -= LeaveNotifyEventHandler;
+ if (renderer != null)
+ renderer = null;
+ base.OnDestroyed ();
+ }
+
+ void EnterNotifyEventHandler (object o, EnterNotifyEventArgs args) {
+ renderer.Highlight();
+ }
+ void LeaveNotifyEventHandler (object o, LeaveNotifyEventArgs args) {
+ renderer.ClearHighlight();
+ }
+ }
+
+ class RangeRenderer : DrawingArea
+ {
+ Pango.Layout layout;
+ Pango.FontDescription font;
+ string text;
+ bool wrap;
+ char type;
+ DiffWidget widget;
+
+ public RangeRenderer(DiffWidget widget, string text, char type, bool wrap, Pango.FontDescription font)
+ {
+ this.widget = widget;
+ this.text = text;
+ this.wrap = wrap;
+ this.font = font;
+ this.type = type;
+ this.Realized += OnRealized;
+ this.SizeAllocated += SizeAllocatedHandler;
+ ClearHighlight();
+ }
+
+ protected override void OnDestroyed ()
+ {
+ this.Realized -= OnRealized;
+ this.SizeAllocated -= SizeAllocatedHandler;
+ if (layout != null) {
+ layout.Dispose ();
+ layout = null;
+ }
+ base.OnDestroyed ();
+ }
+
+ Gdk.Color bg_color {
+ get {
+ switch (type) {
+ case 'C':
+ return widget.ColorChanged;
+ case '+':
+ return widget.ColorAdded;
+ case '-':
+ return widget.ColorRemoved;
+ default:
+ return widget.ColorDefault;
+ }
+ }
+ }
+
+ Gdk.Color highlight_bg_color {
+ get {
+ switch (type) {
+ case 'C':
+ return widget.ColorChangedHighlight;
+ case '+':
+ return widget.ColorAddedHighlight;
+ case '-':
+ return widget.ColorRemovedHighlight;
+ default:
+ return widget.ColorDefaultHighlight;
+ }
+ }
+
+ }
+ bool inStyleSet = false;
+ protected override void OnStyleSet (global::Gtk.Style previous_style)
+ {
+ base.OnStyleSet (previous_style);
+ if (!inStyleSet) {
+ inStyleSet = true;
+ if (highlight) {
+ Highlight ();
+ } else {
+ ClearHighlight();
+ }
+ QueueDraw ();
+ inStyleSet = false;
+ }
+ }
+
+ bool highlight = false;
+ public void Highlight()
+ {
+ highlight = true;
+ ModifyBg (StateType.Normal, highlight_bg_color);
+ }
+
+ public void ClearHighlight()
+ {
+ highlight = false;
+ ModifyBg (StateType.Normal, bg_color);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ GdkWindow.DrawLayout (this.Style.TextGC (highlight ? StateType.Prelight : StateType.Normal), 1, 1, layout);
+ return true;
+ }
+
+ void OnRealized (object o, EventArgs args)
+ {
+ layout = new Pango.Layout(PangoContext);
+ layout.SingleParagraphMode = false;
+ layout.FontDescription = font;
+ layout.Indent = (int)(-Pango.Scale.PangoScale * 20);
+ layout.SetText(text);
+ Render();
+ }
+
+ void SizeAllocatedHandler (object o, SizeAllocatedArgs args) {
+ Render();
+ }
+
+ void Render()
+ {
+ if (layout == null) return;
+
+ if (wrap)
+ layout.Width = (int)(Allocation.Width * Pango.Scale.PangoScale);
+ else
+ layout.Width = int.MaxValue;
+
+ Rectangle ink, log;
+ layout.GetPixelExtents(out ink, out log);
+ HeightRequest = ink.Y + ink.Height + 3;
+ if (!wrap)
+ WidthRequest = ink.X + ink.Width + 3;
+ }
+ }
+
+ class OverviewRenderer : EventBox {
+ ScrolledWindow scroller;
+ public OverviewRenderer(DiffWidget widget, ScrolledWindow scroller, Hunk[] hunks, bool sidebyside)
+ {
+ this.scroller = scroller;
+ this.ButtonPressEvent += ButtonPressHandler;
+ Add (new OverviewRenderer2 (widget, scroller, hunks, sidebyside));
+ }
+
+ protected override void OnDestroyed ()
+ {
+ scroller = null;
+ this.ButtonPressEvent -= ButtonPressHandler;
+ base.OnDestroyed ();
+ }
+
+ void ButtonPressHandler(object o, ButtonPressEventArgs args)
+ {
+ double position = ((double)args.Event.Y / Allocation.Height - (double)scroller.Allocation.Height/scroller.Vadjustment.Upper/2) * scroller.Vadjustment.Upper;
+ if (position < 0) position = 0;
+ if (position + scroller.Allocation.Height > scroller.Vadjustment.Upper) position = scroller.Vadjustment.Upper - scroller.Allocation.Height;
+ scroller.Vadjustment.Value = position;
+ }
+ }
+
+ class OverviewRenderer2 : DrawingArea {
+ Hunk[] hunks;
+ ScrolledWindow scroller;
+ bool sidebyside;
+ DiffWidget widget;
+
+ public OverviewRenderer2(DiffWidget widget, ScrolledWindow scroller, Hunk[] hunks, bool sidebyside) {
+ this.widget = widget;
+ this.hunks = hunks;
+ this.scroller = scroller;
+ this.sidebyside = sidebyside;
+ scroller.ExposeEvent += OnScroll;
+ WidthRequest = 50;
+ }
+
+ protected override void OnDestroyed ()
+ {
+ if (scroller != null) {
+ scroller.ExposeEvent -= OnScroll;
+ scroller = null;
+ }
+ base.OnDestroyed ();
+ }
+
+ void OnScroll (object o, ExposeEventArgs args)
+ {
+ QueueDrawArea(0, 0, Allocation.Width, Allocation.Height);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose e)
+ {
+ Gdk.GC gc = new Gdk.GC (e.Window);
+
+ int count = 0;
+ foreach (Hunk h in hunks) {
+ IncPos(h, ref count);
+ }
+
+ int start = 0;
+ foreach (Hunk h in hunks) {
+ int size = 0;
+ IncPos(h, ref size);
+ if (h.Same)
+ gc.RgbFgColor = widget.ColorDefault;
+ else if (h.Original().Count == 0)
+ gc.RgbFgColor = widget.ColorAdded;
+ else if (h.ChangedLists == 1 && h.Changes(0).Count == 0)
+ gc.RgbFgColor = widget.ColorRemoved;
+ else
+ gc.RgbFgColor = widget.ColorChanged;
+
+ GdkWindow.DrawRectangle(gc, true, 0, Allocation.Height*start/count, Allocation.Width, Allocation.Height*size/count);
+
+ start += size;
+ }
+
+ gc.RgbFgColor = widget.ColorGrey;
+ e.Window.DrawRectangle(gc, false,
+ 1,
+ (int)(Allocation.Height*scroller.Vadjustment.Value/scroller.Vadjustment.Upper) + 1,
+ Allocation.Width-3,
+ (int)(Allocation.Height*((double)scroller.Allocation.Height/scroller.Vadjustment.Upper))-2);
+
+ gc.RgbFgColor = widget.ColorBlack;
+ e.Window.DrawRectangle(gc, false,
+ 0,
+ (int)(Allocation.Height*scroller.Vadjustment.Value/scroller.Vadjustment.Upper),
+ Allocation.Width-1,
+ (int)(Allocation.Height*((double)scroller.Allocation.Height/scroller.Vadjustment.Upper)));
+
+ gc.Dispose ();
+ return true;
+ }
+
+ private void IncPos(Hunk h, ref int pos) {
+ if (sidebyside)
+ pos += h.MaxLines();
+ else if (h.Same)
+ pos += h.Original().Count;
+ else {
+ pos += h.Original().Count;
+ for (int i = 0; i < h.ChangedLists; i++)
+ pos += h.Changes(i).Count;
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Merge.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Merge.cs
new file mode 100644
index 0000000000..4326cdd362
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Merge.cs
@@ -0,0 +1,308 @@
+/*
+ * Merges, a supporting class for Diffs
+ */
+
+using System;
+using System.Collections;
+using System.Text;
+
+namespace MonoDevelop.Components.Diff {
+ public class Merge : IEnumerable {
+ IDiff[] diffs;
+ ArrayList hunks = new ArrayList();
+
+ public Merge(IList original, IList[] changed, IComparer comparer, IEqualityComparer hashcoder)
+ : this(makediffs(original, changed, comparer, hashcoder))
+ {
+ }
+
+ public Merge(string original, string[] changed, IComparer comparer)
+ : this(makediffs(original, changed, comparer))
+ {
+ }
+
+ private static IDiff[] makediffs(IList original, IList[] changed, IComparer comparer, IEqualityComparer hashcoder) {
+ IDiff[] diffs = new IDiff[changed.Length];
+ for (int i = 0; i < changed.Length; i++)
+ diffs[i] = new Diff(original, changed[i], comparer, hashcoder);
+ return diffs;
+ }
+
+ private static IDiff[] makediffs(string original, string[] changed, IComparer comparer) {
+ IDiff[] diffs = new IDiff[changed.Length];
+ for (int i = 0; i < changed.Length; i++)
+ diffs[i] = new TextDiff(original, changed[i], comparer);
+ return diffs;
+ }
+
+ public Merge(IDiff[] diffs) {
+ this.diffs = diffs;
+
+ // initialize data structures
+
+ IEnumerator[] enumerators = new IEnumerator[diffs.Length];
+ ArrayList[] hunks = new ArrayList[diffs.Length];
+
+ for (int i = 0; i < hunks.Length; i++) {
+ enumerators[i] = ((IEnumerable)diffs[i]).GetEnumerator();
+ hunks[i] = new ArrayList();
+ }
+
+ int startline = 0;
+
+ while (true) {
+ int endline = -1;
+ bool hasmore = false;
+
+ // Get the next hunk for each diff, and find the longest
+ // hunk for which there are changes.
+
+ for (int i = 0; i < hunks.Length; i++) {
+ if (hunks[i].Count > 0) continue;
+ if (!enumerators[i].MoveNext()) return;
+ hasmore = true;
+ Diff.Hunk hunk = (Diff.Hunk)enumerators[i].Current;
+ hunks[i].Add(hunk);
+ if (!hunk.Same && hunk.Left.End > endline)
+ endline = hunk.Left.End;
+ }
+
+ if (!hasmore) return;
+
+ if (endline == -1) {
+ // All of the hunks represented no change. Find the shortest hunk,
+ // create a hunk from the current start line to the end of the
+ // shortest hunk, and retain all of the hunks that overlap into that
+ // hunk's next region. (Clear the rest.)
+ int start = int.MaxValue;
+ for (int i = 0; i < hunks.Length; i++) {
+ Diff.Hunk h = (Diff.Hunk)hunks[i][0];
+ if (h.Left.End < start) start = h.Left.End;
+ }
+
+ // Crop all of the hunks to the shortest region.
+ Diff.Hunk[][] h2 = new Diff.Hunk[hunks.Length][];
+ for (int i = 0; i < hunks.Length; i++) {
+ h2[i] = new Diff.Hunk[1];
+ h2[i][0] = (Diff.Hunk)hunks[i][0];
+ h2[i][0] = h2[i][0].Crop(startline - h2[i][0].Left.Start, h2[i][0].Left.End - start);
+ }
+ this.hunks.Add( new Hunk(this, h2, startline, start - startline + 1, true) );
+
+ for (int i = 0; i < hunks.Length; i++) {
+ Diff.Hunk h = (Diff.Hunk)hunks[i][0];
+ if (h.Left.End == start) hunks[i].Clear();
+ }
+ startline = start+1;
+ continue;
+ }
+
+ // For each diff, add in all of the non-same hunks that fall
+ // at least partially within the largest hunk region. If
+ // a hunk crosses the edge, push the edge further and then
+ // add more hunks again.
+ bool moreToAdd = true;
+ while (moreToAdd) {
+ moreToAdd = false;
+
+ for (int i = 0; i < hunks.Length; i++) {
+ Diff.Hunk last = (Diff.Hunk)hunks[i][hunks[i].Count-1];
+ while (last.Left.End < endline) {
+ if (!enumerators[i].MoveNext()) continue;
+ last = (Diff.Hunk)enumerators[i].Current;
+ hunks[i].Add(last);
+ if (last.Same) continue;
+ if (last.Left.End > endline) {
+ endline = last.Left.End;
+ moreToAdd = true;
+ }
+ }
+ }
+ }
+
+ Diff.Hunk[][] hunks2 = new Diff.Hunk[hunks.Length][];
+ for (int i = 0; i < hunks.Length; i++) {
+ // any same hunks that overlap the start or end need to be replaced
+ ArrayList hunks3 = new ArrayList();
+ foreach (Diff.Hunk h in hunks[i]) {
+ Diff.Hunk h2 = h;
+ int shiftstart = 0, shiftend = 0;
+ if (h2.Same && h2.Left.Start < startline)
+ shiftstart = startline - h2.Left.Start;
+ if (h2.Same && h2.Left.End > endline)
+ shiftend = h2.Left.End - endline;
+ if (shiftstart != 0 || shiftend != 0)
+ h2 = h2.Crop(shiftstart, shiftend);
+ hunks3.Add(h2);
+ }
+ hunks2[i] = (Diff.Hunk[])hunks3.ToArray(typeof(Diff.Hunk));
+ }
+ this.hunks.Add( new Hunk(this, hunks2, startline, endline - startline + 1, false) );
+
+ // In each hunk list, retain only the last hunk if it
+ // overlaps into the next region.
+ startline = endline+1;
+ for (int i = 0; i < hunks.Length; i++) {
+ if (hunks[i].Count == 0) continue;
+ Diff.Hunk h = (Diff.Hunk)hunks[i][hunks[i].Count-1];
+ hunks[i].Clear();
+ if (h.Left.End >= startline)
+ hunks[i].Add(h);
+ }
+
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return hunks.GetEnumerator();
+ }
+
+ public static IList MergeLists(IList original, IList[] changed, IComparer comparer, IEqualityComparer hashcoder) {
+ Merge m = new Merge(original, changed, comparer, hashcoder);
+ ArrayList ret = new ArrayList();
+ ArrayList newlines = new ArrayList();
+ foreach (Hunk h in m) {
+ newlines.Clear();
+
+ for (int i = 0; i < changed.Length; i++)
+ if (!h.IsSame(i))
+ newlines.Add(h.Changes(i));
+
+ // If there were no differences in this region, take the original.
+ if (newlines.Count == 0)
+ ret.AddRange(h.Original());
+
+ // If one list has changes, take them
+ else if (newlines.Count == 1)
+ ret.AddRange((Range)newlines[0]);
+
+ // Indicate conflict
+ else
+ ret.Add(new Conflict((Range[])newlines.ToArray(typeof(Range))));
+ }
+ return ret;
+ }
+
+ public class Conflict : IEnumerable {
+ Range[] ranges;
+
+ internal Conflict(Range[] ranges) {
+ this.ranges = ranges;
+ }
+
+ public Range[] Ranges { get { return ranges; } }
+
+ IEnumerator IEnumerable.GetEnumerator() { return ranges.GetEnumerator(); }
+
+ public override string ToString() {
+ StringBuilder b = new StringBuilder();
+ b.Append("<<<<<<<<<<");
+ b.Append(Environment.NewLine);
+ for (int i = 0; i < ranges.Length; i++) {
+ if (i > 0) {
+ b.Append("----------");
+ b.Append(Environment.NewLine);
+ }
+ foreach (object item in ranges[i]) {
+ b.Append(item);
+ b.Append(Environment.NewLine);
+ }
+ }
+ b.Append(">>>>>>>>>>");
+ b.Append(Environment.NewLine);
+ return b.ToString();
+ }
+ }
+
+ public class Hunk : MonoDevelop.Components.Diff.Hunk {
+ Merge merge;
+ Diff.Hunk[][] hunks;
+ int start, count;
+ bool same, conflict;
+
+ internal Hunk(Merge merge, Diff.Hunk[][] hunks, int start, int count, bool same) {
+ this.merge = merge;
+ this.hunks = hunks;
+ this.start = start;
+ this.count = count;
+ this.same = same;
+
+ int ct = 0;
+ foreach (Diff.Hunk[] hh in hunks) {
+ foreach (Diff.Hunk h in hh) {
+ if (!h.Same) {
+ ct++;
+ break;
+ }
+ }
+ }
+ conflict = (ct > 1);
+ }
+
+ public override int ChangedLists { get { return merge.diffs.Length; } }
+
+ // Returns the set of changes within this hunk's range for the
+ // diff of the given index.
+ public Diff.Hunk[] ChangesHunks(int index) {
+ return hunks[index];
+ }
+
+ public int ChangedIndex() {
+ if (Conflict) throw new InvalidOperationException("ChangedIndex cannot be called if there is a conflict.");
+ for (int i = 0; i < hunks.Length; i++) {
+ foreach (Diff.Hunk h in hunks[i])
+ if (!h.Same) return i;
+ }
+ return -1;
+ }
+
+ // Returns the range of elements corresponding to this hunk's range, in the original.
+ public override Range Original() {
+ return new Range(merge.diffs[0].Left, start, count);
+ }
+
+ // Returns the range of elements corresponding to this hunk's range, in the diff of the given index.
+ public override Range Changes(int index) {
+ return new Range(merge.diffs[index].Right, hunks[index][0].Right.Start, hunks[index][hunks[index].Length-1].Right.End - hunks[index][0].Right.Start + 1);
+ }
+
+ public override bool Same { get { return same; } }
+
+ public override bool Conflict { get { return conflict; } }
+
+ public override bool IsSame(int index) {
+ foreach (Diff.Hunk h in hunks[index])
+ if (!h.Same) return false;
+ return true;
+ }
+
+ public override string ToString() {
+ StringBuilder b = new StringBuilder();
+ if (Same) {
+ foreach (object item in Original()) {
+ b.Append(' ');
+ b.Append(item);
+ b.Append(Environment.NewLine);
+ }
+ return b.ToString();
+ }
+
+ b.Append("==========");
+ b.Append(Environment.NewLine);
+ for (int i = 0; i < hunks.Length; i++) {
+ if (i > 0) {
+ b.Append("----------");
+ b.Append(Environment.NewLine);
+ }
+ foreach (Diff.Hunk h in hunks[i])
+ b.Append(h);
+ }
+ b.Append("==========");
+ b.Append(Environment.NewLine);
+ return b.ToString();
+ }
+
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Patch.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Patch.cs
new file mode 100644
index 0000000000..d724de82e9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/Patch.cs
@@ -0,0 +1,59 @@
+/*
+ * Patches, a supporting class for Diffs
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+using System.Xml;
+
+namespace MonoDevelop.Components.Diff {
+
+ public class Patch : IEnumerable {
+ Hunk[] hunks;
+
+ internal Patch(Hunk[] hunks) {
+ this.hunks = hunks;
+ }
+
+ public class Hunk {
+ object[] rightData;
+ int leftstart, leftcount, rightstart, rightcount;
+ bool same;
+
+ internal Hunk(object[] rightData, int st, int c, int rs, int rc, bool s) {
+ this.rightData = rightData;
+ leftstart = st;
+ leftcount = c;
+ rightstart = rs;
+ rightcount = rc;
+ same = s;
+ }
+
+ public bool Same { get { return same; } }
+
+ public int Start { get { return leftstart; } }
+ public int Count { get { return leftcount; } }
+ public int End { get { return leftstart+leftcount-1; } }
+
+ public IList Right { get { if (same) return null; return new Range(rightData, rightstart, rightcount); } }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return hunks.GetEnumerator();
+ }
+
+ public IList Apply(IList original) {
+ ArrayList right = new ArrayList();
+ foreach (Hunk hunk in this) {
+ if (hunk.Same)
+ right.AddRange(new Range(original, hunk.Start, hunk.Count));
+ else
+ right.AddRange(hunk.Right);
+ }
+ return right;
+ }
+
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/StructuredDiff.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/StructuredDiff.cs
new file mode 100644
index 0000000000..657532ee4f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/StructuredDiff.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections;
+using System.Xml;
+
+namespace MonoDevelop.Components.Diff {
+ public abstract class StructuredDiff {
+ internal Hashtable nodeInterfaces = new Hashtable();
+ internal Hashtable comparisonCache = new Hashtable();
+
+ public virtual void AddInterface(Type type, NodeInterface nodeInterface) {
+ nodeInterfaces[type] = nodeInterface;
+ }
+
+ internal NodeInterface GetInterface(object obj) {
+ bool store = false;
+ Type type = obj.GetType();
+ while (type != null) {
+ NodeInterface ret = (NodeInterface)nodeInterfaces[type];
+ if (ret != null) {
+ if (store) nodeInterfaces[obj.GetType()] = ret;
+ return ret;
+ }
+ type = type.BaseType;
+ store = true;
+ }
+ throw new ArgumentException("Node type has no interface defined: " + obj.GetType());
+ }
+
+ public float CompareLists(IList left, IList right) {
+ return CompareLists(left, right, 0, false);
+ }
+
+ public void Compare(object left, object right) {
+ comparisonCache.Clear();
+ CompareLists(new object[] { left }, new object[] { right }, 0, true);
+ }
+
+ private float CompareLists(IList left, IList right, float threshold, bool output) {
+ // Given two lists, find the elements in the list that correspond.
+ // Two elements correspond if their 'difference metric' is less than
+ // or equal to threshold. For the hunks of correspondent items,
+ // recursively descend into items not truly equal. For hunks of
+ // irreconsiliable material, raise the threshold to the next useful
+ // level and rescan the items.
+
+ if (left.Count == 0 && right.Count == 0)
+ return 0;
+
+ NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this);
+
+ Diff diff = new Diff(left, right, comparer, comparer);
+
+ int nitems = 0, ndiffs = 0;
+
+ foreach (Diff.Hunk hunk in diff) {
+ if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) {
+ // This comprises a block of correspondent items who
+ // differ by no more than the threshold value.
+
+ nitems += hunk.Left.Count;
+
+ bool inSameRegion = false;
+
+ for (int i = 0; i < hunk.Left.Count; i++) {
+ object oleft = hunk.Left[i];
+ object oright = hunk.Right[i];
+
+ NodeInterface ileft = GetInterface(oleft);
+ NodeInterface iright = GetInterface(oright);
+
+ IList cleft = null, cright = null;
+ cleft = ileft.GetChildren(oleft);
+ cright = iright.GetChildren(oright);
+
+ float comp = 0;
+ if (ileft == iright)
+ comp = ileft.Compare(oleft, oright, this);
+
+ // If the nodes are equal, emit one node.
+ if (ileft == iright && comp == 0) {
+ if (output) {
+ if (!inSameRegion) { WritePushSame(); inSameRegion = true; }
+ WriteNodeSame(ileft, oleft, oright);
+ }
+
+ // Recurse into the lists of each node.
+ } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) {
+ if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
+ if (output) WritePushNode(ileft, oleft, oright);
+ float d = CompareLists(cleft, cright, 0, output);
+ d *= hunk.Left.Count;
+ if (d < 1) d = 1;
+ ndiffs += (int)d;
+ if (output) WritePopNode();
+
+ // The nodes are not equal, so emit removed and added nodes.
+ } else {
+ if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
+ if (output) WriteNodeChange(ileft, oleft, iright, oright);
+ ndiffs += hunk.Left.Count;
+ }
+ }
+
+ if (output && inSameRegion) WritePopSame();
+ } else {
+ int ct = hunk.Left.Count + hunk.Right.Count;
+ nitems += ct;
+ ndiffs += ct;
+
+ if (output) {
+ bool noRecurse = comparer.minimumDifference >= 1;
+ if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse))
+ WriteNodesRemoved(hunk.Left);
+ if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse))
+ WriteNodesAdded(hunk.Right);
+ if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse)
+ CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output);
+ }
+ }
+ }
+
+ return (float)ndiffs / (float)nitems;
+ }
+
+ protected abstract void WritePushNode(NodeInterface nodeInterface, object left, object right);
+
+ protected abstract void WritePushSame();
+
+ protected abstract void WriteNodeSame(NodeInterface nodeInterface, object left, object right);
+
+ protected abstract void WritePopSame();
+
+ protected abstract void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right);
+
+ protected abstract void WriteNodesRemoved(IList objects);
+
+ protected abstract void WriteNodesAdded(IList objects);
+
+ protected abstract void WritePopNode();
+ }
+
+ public class XmlOutputStructuredDiff : StructuredDiff {
+ bool deepOutput, allContext;
+ XmlWriter output;
+ Hashtable contextNodes;
+
+ public XmlOutputStructuredDiff(XmlWriter output, string context) {
+ this.output = output;
+ this.deepOutput = (context == null);
+ this.allContext = (context != null && context == "*");
+
+ if (!deepOutput && !allContext) {
+ contextNodes = new Hashtable();
+ foreach (string name in context.Split(','))
+ contextNodes[name.Trim()] = contextNodes;
+ }
+ }
+
+ public override void AddInterface(Type type, NodeInterface nodeInterface) {
+ if (!(nodeInterface is XmlOutputNodeInterface))
+ throw new ArgumentException("Node interfaces for the XmlOutputStructuredDiff must implement XmlOutputNodeInterface.");
+ base.AddInterface(type, nodeInterface);
+ }
+
+ protected override void WritePushSame() {
+ }
+
+ protected override void WritePopSame() {
+ }
+
+ protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
+ bool deep = deepOutput;
+
+ if (left is XmlNode && !deepOutput && !allContext) {
+ if (!contextNodes.ContainsKey(((XmlNode)left).Name)) {
+ return;
+ } else {
+ deep = true;
+ }
+ }
+
+ ((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, left, (XmlWriter)output);
+ output.WriteAttributeString("Status", "Same");
+ if (deep)
+ ((XmlOutputNodeInterface)nodeInterface).WriteNodeChildren(left, (XmlWriter)output);
+ output.WriteEndElement();
+ }
+
+ protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
+ ((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, right, (XmlWriter)output);
+ output.WriteAttributeString("Status", "Changed");
+ ((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, left, (XmlWriter)output);
+ ((XmlOutputNodeInterface)leftInterface).WriteNodeChildren(left, (XmlWriter)output);
+ output.WriteEndElement();
+ ((XmlOutputNodeInterface)rightInterface).WriteBeginNode(right, right, (XmlWriter)output);
+ ((XmlOutputNodeInterface)rightInterface).WriteNodeChildren(right, (XmlWriter)output);
+ output.WriteEndElement();
+ output.WriteEndElement();
+ }
+
+ protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
+ ((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, right, output);
+ }
+
+ protected override void WritePopNode() {
+ output.WriteEndElement();
+ }
+
+ void AddRemove(IList objects, string status) {
+ foreach (object obj in objects) {
+ XmlOutputNodeInterface i = (XmlOutputNodeInterface)GetInterface(obj);
+ ((XmlOutputNodeInterface)i).WriteBeginNode(obj, obj, output);
+ output.WriteAttributeString("Status", status);
+ i.WriteBeginNode(obj, obj, output);
+ i.WriteNodeChildren(obj, output);
+ output.WriteEndElement();
+ output.WriteEndElement();
+ }
+ }
+
+ protected override void WriteNodesRemoved(IList objects) {
+ AddRemove(objects, "Removed");
+ }
+ protected override void WriteNodesAdded(IList objects) {
+ AddRemove(objects, "Added");
+ }
+ }
+
+ public abstract class NodeInterface {
+ public abstract IList GetChildren(object node);
+ public abstract float Compare(object left, object right, StructuredDiff comparer);
+ public virtual int GetHashCode(object node) {
+ return node.GetHashCode();
+ }
+ }
+
+ public interface XmlOutputNodeInterface {
+ void WriteNodeChildren(object node, XmlWriter output);
+ void WriteBeginNode(object left, object right, XmlWriter output);
+ }
+
+ internal class NodeComparerWrapper : IComparer, IEqualityComparer {
+ float threshold;
+ StructuredDiff differ;
+
+ public float minimumDifference = 1;
+
+ bool useCache = true;
+
+ public NodeComparerWrapper(float threshold, StructuredDiff differ) {
+ this.threshold = threshold;
+ this.differ = differ;
+ }
+
+ private class Pair {
+ object left, right;
+ int code;
+ public Pair(object a, object b, StructuredDiff differ) {
+ left = a; right = b;
+ code = unchecked(differ.GetInterface(left).GetHashCode(left) + differ.GetInterface(right).GetHashCode(right));
+ }
+ public override bool Equals(object o) {
+ return ((Pair)o).left == left && ((Pair)o).right == right;
+ }
+ public override int GetHashCode() {
+ return code;
+ }
+ }
+
+ int IComparer.Compare(object left, object right) {
+ float ret;
+
+ Pair pair = new Pair(left, right, differ);
+
+ if (left.GetType() != right.GetType()) {
+ ret = 1;
+ } else if (useCache && differ.comparisonCache.ContainsKey(pair)) {
+ ret = (float)differ.comparisonCache[pair];
+ } else {
+ NodeInterface comparer = differ.GetInterface(left);
+ ret = comparer.Compare(left, right, differ);
+ }
+
+ if (useCache)
+ differ.comparisonCache[pair] = ret;
+
+ if (ret < minimumDifference && ret > threshold)
+ minimumDifference = ret;
+
+ if (ret <= threshold)
+ return 0;
+ else
+ return 1;
+ }
+
+ bool IEqualityComparer.Equals (object a, object b)
+ {
+ return ((IComparer)this).Compare (a, b) == 0;
+ }
+
+ int IEqualityComparer.GetHashCode (object obj) {
+ return differ.GetInterface(obj).GetHashCode(obj);
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/TextDiff.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/TextDiff.cs
new file mode 100644
index 0000000000..bbab4ab86d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/TextDiff.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Collections;
+
+namespace MonoDevelop.Components.Diff {
+
+ public class TextDiff : IDiff {
+ Node left, right;
+ ArrayList hunks = new ArrayList();
+
+ public TextDiff(string left, string right) : this (left, right, null) {
+ }
+
+ public TextDiff(string left, string right, IComparer comparer) {
+ this.left = new Node(left, 0, left.Length, 0);
+ this.right = new Node(right, 0, right.Length, 0);
+
+
+ TextDiffStructuredDiff diff = new TextDiffStructuredDiff(hunks, left.ToCharArray(), right.ToCharArray());
+ diff.AddInterface(typeof(Node), new TextDiffNodeInterface(comparer));
+ diff.Compare(this.left, this.right);
+ }
+
+ public IList Left { get { return left.ToString().ToCharArray(); } }
+ public IList Right { get { return right.ToString().ToCharArray(); } }
+
+ IEnumerator IEnumerable.GetEnumerator() {
+ return hunks.GetEnumerator();
+ }
+
+ private class TextDiffStructuredDiff : StructuredDiff {
+ ArrayList hunks;
+ IList leftlist, rightlist;
+
+ public TextDiffStructuredDiff(ArrayList hunks, IList leftlist, IList rightlist) {
+ this.hunks = hunks;
+ this.leftlist = leftlist;
+ this.rightlist = rightlist;
+ }
+
+ protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
+ }
+
+ protected override void WritePushSame() {
+ }
+
+ private void AddHunk(int lcount, int rcount, bool same) {
+ if (hunks.Count > 0 && same == ((Diff.Hunk)hunks[hunks.Count-1]).Same) {
+ Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
+ hunks[hunks.Count-1] = new Diff.Hunk(
+ leftlist,
+ rightlist,
+ prev.Left.Start,
+ prev.Left.End + lcount,
+ prev.Right.Start,
+ prev.Right.End + rcount,
+ same);
+ return;
+ }
+
+ int le = -1;
+ int re = -1;
+ if (hunks.Count > 0) {
+ Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
+ le = prev.Left.End;
+ re = prev.Right.End;
+ }
+
+ hunks.Add( new Diff.Hunk(
+ leftlist,
+ rightlist,
+ le + 1,
+ le + lcount,
+ re + 1,
+ re + rcount,
+ same) );
+ }
+
+ protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
+ AddHunk(((Node)left).count, ((Node)right).count, true);
+ }
+
+ protected override void WritePopSame() {
+ }
+
+ protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
+ AddHunk(((Node)left).count, ((Node)right).count, false);
+ }
+
+ protected override void WriteNodesRemoved(IList objects) {
+ int start = ((Node)objects[0]).start;
+ int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
+
+ AddHunk(end - start + 1, 0, false);
+ }
+
+ protected override void WriteNodesAdded(IList objects) {
+ int start = ((Node)objects[0]).start;
+ int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
+
+ AddHunk(0, end - start + 1, false);
+ }
+
+ protected override void WritePopNode() {
+ }
+ }
+
+ private class TextDiffNodeInterface : NodeInterface {
+ IComparer comparer;
+
+ public TextDiffNodeInterface(IComparer comparer) { this.comparer = comparer; }
+
+ public override IList GetChildren(object node) {
+ if (((Node)node).children.Count == 0) return null;
+ return ((Node)node).children;
+ }
+
+ private bool Equal(string a, string b) {
+ if (comparer == null)
+ return a == b;
+ return comparer.Compare(a, b) == 0;
+ }
+
+ public override float Compare(object left, object right, StructuredDiff comparer) {
+ string l = left.ToString(), r = right.ToString();
+ if (Equal(l, r)) return 0;
+ if (l.Length == 1 || r.Length == 1) return 1;
+ float d = comparer.CompareLists(GetChildren(left), GetChildren(right));
+ if (((Node)left).level == 2 && d >= .75) d = 1.1f;
+ return d;
+ }
+
+ public override int GetHashCode(object node) {
+ return node.ToString().GetHashCode();
+ }
+ }
+
+ private class Node {
+ public string source;
+ public int start, count;
+
+ public int level;
+ public ArrayList children = new ArrayList();
+
+ static char[][] delims = new char[][] {
+ new char[] { '\n', '\r' },
+ new char[] { ' ', '\t', '.', ',' }
+ };
+
+ public Node(string source, int start, int count, int level) {
+ this.source = source;
+ this.start = start;
+ this.count = count;
+ this.level = level;
+
+ if (level <= 1) {
+ int pos = start;
+ foreach (string child in ToString().Split(delims[level])) {
+ if (child.Length >= 1)
+ children.Add(new Node(source, pos, child.Length, level+1));
+ if (pos + child.Length < count)
+ children.Add(new Node(source, pos+child.Length, 1, level+1));
+ pos += child.Length + 1;
+ }
+ } else if (level == 2) {
+ for (int i = start; i < start + count; i++)
+ children.Add(new Node(source, i, 1, level+1));
+ }
+
+ }
+
+ public override string ToString() {
+ return source.Substring(start, count);
+ }
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/UnifiedDiff.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/UnifiedDiff.cs
new file mode 100644
index 0000000000..6f2a893bc2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Diff/UnifiedDiff.cs
@@ -0,0 +1,157 @@
+/*
+ * A utility class for writing unified diffs.
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+namespace MonoDevelop.Components.Diff {
+
+ public class UnifiedDiff {
+ private UnifiedDiff() {
+ }
+
+ public static void WriteUnifiedDiff(string[] leftLines, string leftName, string[] rightLines, string rightName, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) {
+ Diff diff = new Diff(leftLines, rightLines, caseSensitive, compareWhitespace);
+ WriteUnifiedDiff(diff, writer, leftName, rightName, context);
+ }
+
+ public static void WriteUnifiedDiff(string leftFile, string rightFile, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) {
+ WriteUnifiedDiff(LoadFileLines(leftFile), leftFile, LoadFileLines(rightFile), rightFile, writer, context, caseSensitive, compareWhitespace);
+ }
+
+ internal static string[] LoadFileLines(string file) {
+ ArrayList lines = new ArrayList();
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(file)) {
+ string s;
+ while ((s = reader.ReadLine()) != null)
+ lines.Add(s);
+ }
+ return (string[])lines.ToArray(typeof(string));
+ }
+
+ public static void WriteUnifiedDiff(Diff diff, TextWriter writer) {
+ WriteUnifiedDiff(diff, writer, "Left", "Right", 2);
+ }
+
+ public static void WriteUnifiedDiff(Diff diff, TextWriter writer, string fromfile, string tofile, int context) {
+ writer.Write("--- ");
+ writer.WriteLine(fromfile);
+ writer.Write("+++ ");
+ writer.WriteLine(tofile);
+
+ ArrayList hunkset = new ArrayList();
+
+ foreach (Diff.Hunk hunk in diff) {
+ Diff.Hunk lasthunk = null;
+ if (hunkset.Count > 0) lasthunk = (Diff.Hunk)hunkset[hunkset.Count-1];
+
+ if (hunk.Same) {
+ // At the start of a hunk set, keep only context lines of context.
+ if (lasthunk == null) {
+ if (hunk.Left.Count > context)
+ hunkset.Add( hunk.Crop(hunk.Left.Count-context, 0) );
+ else
+ hunkset.Add( hunk );
+ // Can't have two same hunks in a row, so the last one was a difference.
+ } else {
+ // Small enough context that this unified diff range will not stop.
+ if (hunk.Left.Count <= context*2) {
+ hunkset.Add( hunk );
+
+ // Too much of the same. Keep context lines and end this section.
+ // And then keep the last context lines as context for the next section.
+ } else {
+ hunkset.Add( hunk.Crop(0, hunk.Left.Count-context) );
+ WriteUnifiedDiffSection(writer, hunkset);
+ hunkset.Clear();
+
+ if (hunk.Left.Count > context)
+ hunkset.Add( hunk.Crop(hunk.Left.Count-context, 0) );
+ else
+ hunkset.Add( hunk );
+ }
+ }
+
+ } else {
+ hunkset.Add(hunk);
+ }
+ }
+
+ if (hunkset.Count > 0 && !(hunkset.Count == 1 && ((Diff.Hunk)hunkset[0]).Same))
+ WriteUnifiedDiffSection(writer, hunkset);
+ }
+
+ private static void WriteUnifiedDiffSection(TextWriter writer, ArrayList hunks) {
+ Diff.Hunk first = (Diff.Hunk)hunks[0];
+ Diff.Hunk last = (Diff.Hunk)hunks[hunks.Count-1];
+
+ writer.Write("@@ -");
+ writer.Write(first.Left.Start+1);
+ writer.Write(",");
+ writer.Write(last.Left.End-first.Left.Start+1);
+ writer.Write(" +");
+ writer.Write(first.Right.Start+1);
+ writer.Write(",");
+ writer.Write(last.Right.End-first.Right.Start+1);
+ writer.WriteLine(" @@");
+
+ foreach (Diff.Hunk hunk in hunks) {
+ if (hunk.Same) {
+ WriteBlock(writer, ' ', hunk.Left);
+ continue;
+ }
+
+ WriteBlock(writer, '-', hunk.Left);
+ WriteBlock(writer, '+', hunk.Right);
+ }
+ }
+
+ private static void WriteBlock(TextWriter writer, char prefix, Range items) {
+ if (items.Count > 0 && items[0] is char)
+ WriteCharBlock(writer, prefix, items);
+ else
+ WriteStringBlock(writer, prefix, items);
+ }
+
+ private static void WriteStringBlock(TextWriter writer, char prefix, Range items) {
+ foreach (object item in items) {
+ writer.Write(prefix);
+ writer.WriteLine(item.ToString());
+ }
+ }
+
+ private static void WriteCharBlock(TextWriter writer, char prefix, Range items) {
+ bool newline = true;
+ int counter = 0;
+ foreach (char c in items) {
+ if (c == '\n' && !newline) {
+ writer.WriteLine();
+ newline = true;
+ }
+
+ if (newline) {
+ writer.Write(prefix);
+ newline = false;
+ counter = 0;
+ }
+
+ if (c == '\n') {
+ writer.WriteLine("[newline]");
+ newline = true;
+ } else {
+ writer.Write(c);
+ counter++;
+ if (counter == 60) {
+ writer.WriteLine();
+ newline = true;
+ }
+ }
+ }
+ if (!newline) writer.WriteLine();
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/ArrowWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/ArrowWindow.cs
new file mode 100644
index 0000000000..8d02dc185d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/ArrowWindow.cs
@@ -0,0 +1,136 @@
+//
+// ArrowWindow.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 Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ internal class ArrowWindow: Gtk.Window
+ {
+ const int LineWidth = 3;
+ const int LineLength = 9;
+ const int PointerWidth = 13;
+ const int PointerLength = 6;
+
+ Direction direction;
+ Point[] arrow;
+ int width, height;
+
+ Gdk.GC redgc;
+
+ // Where does the arrow point to
+ public new enum Direction {
+ Up, Down, Left, Right
+ }
+
+ public ArrowWindow (DockToolbarFrame frame, Direction dir): base (Gtk.WindowType.Popup)
+ {
+ SkipTaskbarHint = true;
+ Decorated = false;
+ TransientFor = frame.TopWindow;
+
+ direction = dir;
+ arrow = CreateArrow ();
+ if (direction == Direction.Up || direction == Direction.Down) {
+ width = PointerWidth;
+ height = LineLength + PointerLength + 1;
+ } else {
+ height = PointerWidth;
+ width = LineLength + PointerLength + 1;
+ }
+
+ // Create the mask for the arrow
+
+ Gdk.Color black, white;
+ black = new Gdk.Color (0, 0, 0);
+ black.Pixel = 1;
+ white = new Gdk.Color (255, 255, 255);
+ white.Pixel = 0;
+
+ Gdk.Pixmap pm = new Pixmap (this.GdkWindow, width, height, 1);
+ Gdk.GC gc = new Gdk.GC (pm);
+ gc.Background = white;
+ gc.Foreground = white;
+ pm.DrawRectangle (gc, true, 0, 0, width, height);
+
+ gc.Foreground = black;
+ pm.DrawPolygon (gc, false, arrow);
+ pm.DrawPolygon (gc, true, arrow);
+
+ this.ShapeCombineMask (pm, 0, 0);
+
+ Realize ();
+
+ redgc = new Gdk.GC (GdkWindow);
+ redgc.RgbFgColor = new Gdk.Color (255, 0, 0);
+
+ Resize (width, height);
+ }
+
+ public int Width {
+ get { return width; }
+ }
+
+ public int Height {
+ get { return height; }
+ }
+
+ Point[] CreateArrow ()
+ {
+ Point[] ps = new Point [8];
+ ps [0] = GetPoint (0, (PointerWidth/2) - (LineWidth/2));
+ ps [1] = GetPoint (LineLength, (PointerWidth/2) - (LineWidth/2));
+ ps [2] = GetPoint (LineLength, 0);
+ ps [3] = GetPoint (PointerLength + LineLength, (PointerWidth/2));
+ ps [4] = GetPoint (LineLength, PointerWidth - 1);
+ ps [5] = GetPoint (LineLength, (PointerWidth/2) + (LineWidth/2));
+ ps [6] = GetPoint (0, (PointerWidth/2) + (LineWidth/2));
+ ps [7] = ps [0];
+ return ps;
+ }
+
+ Point GetPoint (int x, int y)
+ {
+ switch (direction) {
+ case Direction.Right: return new Point (x, y);
+ case Direction.Left: return new Point ((PointerLength + LineLength) - x, y);
+ case Direction.Down: return new Point (y, x);
+ default: return new Point (y, (PointerLength + LineLength) - x);
+ }
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose args)
+ {
+ GdkWindow.DrawPolygon (redgc, false, arrow);
+ GdkWindow.DrawPolygon (redgc, true, arrow);
+ return true;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockGrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockGrip.cs
new file mode 100644
index 0000000000..7f6e8b35ef
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockGrip.cs
@@ -0,0 +1,72 @@
+//
+// DockGrip.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 Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ internal class DockGrip: ToolItem
+ {
+ static int GripSize = MonoDevelop.Core.PropertyService.IsWindows? 4 : 6; //wimp theme engine looks ugly with width 6
+ const int MarginLeft = 1;
+ const int MarginRight = 3;
+
+ public DockGrip ()
+ {
+ }
+
+ protected override void OnSizeRequested (ref Requisition req)
+ {
+ if (Orientation == Orientation.Horizontal) {
+ req.Width = GripSize + MarginLeft + MarginRight;
+ req.Height = 0;
+ } else {
+ req.Width = 0;
+ req.Height = GripSize + MarginLeft + MarginRight;
+ }
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose args)
+ {
+ Rectangle rect = Allocation;
+ if (Orientation == Orientation.Horizontal) {
+ rect.Width = GripSize;
+ rect.X += MarginLeft;
+ } else {
+ rect.Height = GripSize;
+ rect.Y += MarginLeft;
+ }
+
+ Gtk.Orientation or = Orientation == Gtk.Orientation.Horizontal ? Gtk.Orientation.Vertical : Gtk.Orientation.Horizontal;
+ Gtk.Style.PaintHandle (this.Style, this.ParentWindow, this.State, Gtk.ShadowType.None, args.Area, this, "grip", rect.X, rect.Y, rect.Width, rect.Height, or);
+ return true;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs
new file mode 100644
index 0000000000..46dad94945
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs
@@ -0,0 +1,408 @@
+//
+// DockToolbar.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.Generic;
+using Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ public class DockToolbar: Toolbar, IDockToolbar
+ {
+ DockGrip grip;
+ DockToolbarFrame parentFrame;
+ bool dragging;
+ FloatingDock floatingDock;
+ string id;
+ string title;
+
+ int row;
+ int offset;
+ int shiftOffset = -1;
+
+ // Offset requested by the user. It is used to know where the
+ // toolbar is expected to move when the window is expanded.
+ int anchorOffset;
+
+ Animation animation;
+ DockToolbarPosition lastPosition;
+ DockToolbarStatus defaultStatus;
+
+ int defaultSize;
+ int defaultHeight;
+ bool gotSize = false;
+ bool gettingSize;
+ Dictionary<Gtk.Widget,int> calculatedSizes = new Dictionary<Gtk.Widget, int> ();
+
+ public DockToolbar (string id, string title)
+ {
+ grip = new DockGrip ();
+ Add (grip);
+ grip.Hide ();
+ this.id = id;
+ this.title = title;
+ ShowArrow = false;
+ }
+
+ internal void SetParentFrame (DockToolbarFrame frame)
+ {
+ parentFrame = frame;
+ grip.Show ();
+ }
+
+ public string Id {
+ get { return id; }
+ }
+
+ public string Title {
+ get { return title; }
+ }
+
+ public bool Floating {
+ get { return floatingDock != null; }
+ }
+
+ bool IDockToolbar.Visible {
+ get { return Parent != null; }
+ set {
+ if (value == ((IDockToolbar)this).Visible)
+ return;
+
+ if (value) {
+ if (lastPosition != null)
+ lastPosition.RestorePosition (parentFrame, this);
+ else
+ defaultStatus.Position.RestorePosition (parentFrame, this);
+ } else {
+ lastPosition = DockToolbarPosition.Create (this);
+ if (Floating) {
+ FloatingDock win = FloatingDock;
+ win.Detach ();
+ win.Destroy ();
+ }
+ else
+ DockPanel.RemoveBar (this);
+ }
+ }
+ }
+
+ internal DockToolbarPosition Position {
+ get {
+ if (((IDockToolbar)this).Visible) return DockToolbarPosition.Create (this);
+ else if (lastPosition != null) return lastPosition;
+ else return defaultStatus.Position;
+ }
+ }
+
+ internal DockToolbarStatus Status {
+ get {
+ return new DockToolbarStatus (id, ((IDockToolbar)this).Visible, Position);
+ }
+ set {
+ if (value.Visible) {
+ ((IDockToolbar)this).Visible = false;
+ lastPosition = value.Position;
+ ((IDockToolbar)this).Visible = true;
+ } else {
+ ((IDockToolbar)this).Visible = false;
+ lastPosition = value.Position;
+ }
+ }
+
+ }
+
+ internal DockToolbarStatus DefaultStatus {
+ get { return defaultStatus; }
+ set { defaultStatus = value; }
+ }
+
+ internal int DockRow {
+ get { return row; }
+ set { row = value; }
+ }
+
+ /// <summary>
+ /// The current offset of the toolbar
+ /// </summary>
+ internal int DockOffset {
+ get { return offset; }
+ set { offset = value; }
+ }
+
+ internal int DockShiftOffset {
+ get { return shiftOffset; }
+ set { shiftOffset = value; }
+ }
+
+ /// <summary>
+ /// The ideal offset of the toolbar. It may not be the real offset if the
+ /// toolbar has been forced to move to make room for other toolbars
+ /// </summary>
+ internal int AnchorOffset {
+ get { return anchorOffset; }
+ set { anchorOffset = value; }
+ }
+
+ internal int DefaultSize {
+ get {
+ if (!gotSize) CalcSizes ();
+ return defaultSize;
+ }
+ }
+
+ public int DefaultHeight {
+ get {
+ if (!gotSize) CalcSizes ();
+ return defaultHeight;
+ }
+ }
+
+ internal void CalcSizes ()
+ {
+ // Calculates the real size of the toolbar. ShowArrow=false is
+ // needed, since SizeRequest reports 0 size requested if not.
+
+ gettingSize = true;
+ bool olda = ShowArrow;
+ ShowArrow = false;
+ Requisition r = SizeRequest ();
+ if (Orientation == Orientation.Horizontal) {
+ defaultSize = r.Width;
+ defaultHeight = r.Height;
+ }
+ else {
+ defaultSize = r.Height;
+ defaultHeight = r.Width;
+ }
+
+ calculatedSizes.Clear ();
+ foreach (Widget w in Children) {
+ if (!w.Visible)
+ continue;
+ if (Orientation == Orientation.Horizontal)
+ calculatedSizes [w] = w.Allocation.Width;
+ else
+ calculatedSizes [w] = w.Allocation.Height;
+ }
+
+ ShowArrow = olda;
+ gotSize = true;
+ gettingSize = false;
+ }
+
+ internal bool CanDockTo (DockToolbarPanel panel)
+ {
+ return true;
+ }
+
+ internal FloatingDock FloatingDock {
+ get { return floatingDock; }
+ set { floatingDock = value; }
+ }
+
+ internal DockToolbarPanel DockPanel {
+ get { return Parent as DockToolbarPanel; }
+ }
+
+ internal Animation Animation {
+ get { return animation; }
+ set { animation = value; }
+ }
+
+ protected override bool OnButtonPressEvent (EventButton e)
+ {
+ if (parentFrame != null && e.Button == 1) {
+ if (Orientation == Orientation.Horizontal && e.X <= 10) {
+ dragging = true;
+ parentFrame.StartDragBar (this, (int)e.X, (int)e.Y, e.Time);
+ return true;
+ }
+ else if (Orientation == Orientation.Vertical && e.Y <= 10) {
+ dragging = true;
+ parentFrame.StartDragBar (this, (int)e.X, (int)e.Y, e.Time);
+ return true;
+ }
+ }
+ return base.OnButtonPressEvent (e);
+ }
+
+ protected override bool OnButtonReleaseEvent (EventButton e)
+ {
+ if (e.Button == 1 && dragging) {
+ dragging = false;
+ parentFrame.EndDragBar (this, e.Time);
+ }
+ return base.OnButtonReleaseEvent (e);
+ }
+
+ protected override bool OnExposeEvent (EventExpose evnt)
+ {
+ //use the base class to paint the background like a toolbar, which may be a gradient
+ // but only if horizontal, else the gradient usually looks really ugly
+ if (this.Orientation == Orientation.Horizontal){
+ //the WIMP theme engine's rendering is a bit off, need to force it to render wider
+ int widen = MonoDevelop.Core.PropertyService.IsWindows? 1 : 0;
+
+ var shadowType = (ShadowType)StyleGetProperty ("shadow-type");
+ Style.PaintBox (Style, evnt.Window, State, shadowType, evnt.Area, this, "toolbar",
+ Allocation.X - widen, Allocation.Y, Allocation.Width + widen + widen, Allocation.Height);
+ } else {
+ //else we paint a plain flat background to make everything even - see DockToolbarPanel.OnExposeEvent
+ GdkWindow.DrawRectangle (Style.BackgroundGC (State), true, Allocation);
+ }
+
+ foreach (Widget child in Children) {
+ PropagateExpose (child, evnt);
+ }
+
+ return true;
+ }
+
+ bool firstRealized;
+ protected override void OnRealized ()
+ {
+ base.OnRealized ();
+ if (!firstRealized) {
+ OnItemChange (null, null);
+ firstRealized = true;
+ }
+ }
+
+
+ internal void ResetSize ()
+ {
+ WidthRequest = -1;
+ HeightRequest = -1;
+ ShowArrow = false;
+ }
+
+ public int Size {
+ set {
+ if (Orientation == Orientation.Horizontal) {
+ if (value >= DefaultSize)
+ WidthRequest = -1;
+ else
+ WidthRequest = value;
+ }
+ else {
+ if (value >= DefaultSize)
+ HeightRequest = -1;
+ else
+ HeightRequest = value;
+ HeightRequest = value;
+ }
+ }
+ get {
+ if (Orientation == Orientation.Horizontal)
+ return SizeRequest ().Width;
+ else
+ return SizeRequest ().Height;
+ }
+ }
+
+ public new Orientation Orientation {
+ get {
+ return base.Orientation;
+ }
+ set {
+ if (value == base.Orientation)
+ return;
+
+ if (FloatingDock != null) {
+ // I create a new dock window because resizing the
+ // current one has lots of issues (mainly synchronization
+ // problems between the size change of the window and
+ // the toolbar).
+
+ int x,y;
+ FloatingDock w = FloatingDock;
+ w.GetPosition (out x, out y);
+ w.Detach ();
+
+ base.Orientation = value;
+
+ FloatingDock fdock = new FloatingDock (parentFrame);
+ fdock.Move (x, y);
+ fdock.Attach (this);
+ w.Destroy ();
+ } else
+ base.Orientation = value;
+ gotSize = false;
+ }
+ }
+
+ protected override void OnAdded (Widget w)
+ {
+ base.OnAdded (w);
+ gotSize = false;
+ if (DefaultSizeChanged != null)
+ DefaultSizeChanged (this, EventArgs.Empty);
+ w.Shown += OnItemChange;
+ w.Hidden += OnItemChange;
+ w.SizeAllocated += OnItemSizeChange;
+ }
+
+ protected override void OnRemoved (Widget w)
+ {
+ base.OnRemoved (w);
+ gotSize = false;
+ if (DefaultSizeChanged != null)
+ DefaultSizeChanged (this, EventArgs.Empty);
+ w.Shown -= OnItemChange;
+ w.Hidden -= OnItemChange;
+ w.SizeAllocated -= OnItemSizeChange;
+ }
+
+ void OnItemSizeChange (object o, SizeAllocatedArgs args)
+ {
+ if (gettingSize || !gotSize)
+ return;
+ int os;
+ if (calculatedSizes.TryGetValue ((Gtk.Widget) o, out os)) {
+ int ns = (Orientation == Orientation.Horizontal ? args.Allocation.Width : args.Allocation.Height);
+ if (os != ns) {
+ Gtk.Application.Invoke (delegate {
+ OnItemChange (null, null);
+ });
+ }
+ }
+ }
+
+ void OnItemChange (object o, EventArgs args)
+ {
+ // This notifies changes in the size of the toolbar
+ gotSize = false;
+ if (DefaultSizeChanged != null)
+ DefaultSizeChanged (this, EventArgs.Empty);
+ }
+
+ internal event EventHandler DefaultSizeChanged;
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs
new file mode 100644
index 0000000000..689288cfc9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs
@@ -0,0 +1,391 @@
+//
+// DockToolbarFrame.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 Gtk;
+using Gdk;
+using System.Collections;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ public class DockToolbarFrame: EventBox
+ {
+ DockToolbarPanel[] panels;
+ DockToolbarPanel targetPanel;
+ VBox vbox;
+ VBox contentBox;
+ DockToolbar dragBar;
+ int xDragDif, yDragDif;
+ ArrayList bars = new ArrayList ();
+
+ Hashtable layouts = new Hashtable ();
+ string currentLayout = "";
+
+ Cursor handCursor = new Cursor (CursorType.Fleur);
+
+ public DockToolbarFrame ()
+ {
+ vbox = new VBox ();
+ Add (vbox);
+
+ DockToolbarPanel topPanel = new DockToolbarPanel (this, Placement.Top);
+ DockToolbarPanel bottomPanel = new DockToolbarPanel (this, Placement.Bottom);
+ DockToolbarPanel leftPanel = new DockToolbarPanel (this, Placement.Left);
+ DockToolbarPanel rightPanel = new DockToolbarPanel (this, Placement.Right);
+
+ panels = new DockToolbarPanel [4];
+ panels [(int)Placement.Top] = topPanel;
+ panels [(int)Placement.Bottom] = bottomPanel;
+ panels [(int)Placement.Left] = leftPanel;
+ panels [(int)Placement.Right] = rightPanel;
+
+ vbox.PackStart (topPanel, false, false, 0);
+
+ HBox hbox = new HBox ();
+ contentBox = new VBox ();
+
+ hbox.PackStart (leftPanel, false, false, 0);
+ hbox.PackStart (contentBox, true, true, 0);
+ hbox.PackStart (rightPanel, false, false, 0);
+
+ vbox.PackStart (hbox, true, true, 0);
+ vbox.PackStart (bottomPanel, false, false, 0);
+
+ this.Events = EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask;
+ ShowAll ();
+ }
+
+ public void AddContent (Widget w)
+ {
+ contentBox.PackStart (w, true, true, 0);
+ }
+
+ public void RemoveContent (Widget w)
+ {
+ contentBox.Remove (w);
+ }
+
+ public string CurrentLayout {
+ get { return currentLayout; }
+ set {
+ if (value != currentLayout) {
+ SaveCurrentLayout ();
+ RestoreLayout (value);
+ }
+ }
+ }
+
+ public DockToolbarFrameStatus GetStatus ()
+ {
+ SaveCurrentLayout ();
+ DockToolbarFrameStatus col = new DockToolbarFrameStatus ();
+ col.Version = DockToolbarFrameStatus.CurrentVersion;
+
+ foreach (DictionaryEntry e in layouts) {
+ DockToolbarFrameLayout ctx = new DockToolbarFrameLayout ();
+ ctx.Id = (string)e.Key;
+ ctx.Bars = (DockToolbarStatus[]) e.Value;
+ col.Status.Add (ctx);
+ }
+ return col;
+ }
+
+ public void SetStatus (DockToolbarFrameStatus status)
+ {
+ layouts.Clear ();
+ if (status != null && status.Status != null) {
+ foreach (DockToolbarFrameLayout c in status.Status) {
+ if (status.Version < 2) {
+ // Convert from old to new toolbar id
+ foreach (DockToolbarStatus ts in c.Bars)
+ ts.BarId = ConvertToolbarId (ts.BarId);
+ }
+ layouts [c.Id] = c.Bars;
+ }
+ }
+ RestoreLayout ("");
+ }
+
+ public void SaveStatus (XmlWriter writer)
+ {
+ XmlSerializer ser = new XmlSerializer (typeof(DockToolbarFrameStatus));
+ ser.Serialize (writer, GetStatus ());
+ }
+
+ public void LoadStatus (XmlReader reader)
+ {
+ layouts.Clear ();
+ XmlSerializer ser = new XmlSerializer (typeof(DockToolbarFrameStatus));
+ DockToolbarFrameStatus col = (DockToolbarFrameStatus) ser.Deserialize (reader);
+ SetStatus (col);
+ }
+
+ string ConvertToolbarId (string id)
+ {
+ // Old MD versions include the display name of the toolbar in the id.
+ // New MD versions use a different id composition.
+ // This method translates the id from the old to the new format (when possible)
+
+ int i = id.LastIndexOf ('/');
+ if (i == -1)
+ return id;
+
+ string baseId = id.Substring (0, i + 1);
+
+ foreach (DockToolbar t in bars) {
+ if (t.Id == id)
+ return id;
+ if (baseId + t.Title == id)
+ return t.Id;
+ }
+ return id;
+ }
+
+ public IDockToolbar AddBar (DockToolbar bar)
+ {
+ return AddBar (bar, Placement.Top, true);
+ }
+
+ public IDockToolbar AddBar (DockToolbar bar, Placement defaultPanel, bool defaultVisible)
+ {
+ bar.SetParentFrame (this);
+ bars.Add (bar);
+
+ DockToolbarPosition pos = new DockedPosition (defaultPanel);
+ DockToolbarStatus s = new DockToolbarStatus (bar.Id, defaultVisible, pos);
+ bar.DefaultStatus = s;
+ bar.Status = s;
+
+ return bar;
+ }
+
+ public void RemoveToolbar(DockToolbar bar)
+ {
+ IDockToolbar db = (IDockToolbar)bar;
+ db.Visible = false;
+ bar.Destroy();
+ bars.Remove(bar);
+ }
+
+ public void ClearToolbars ()
+ {
+ foreach (DockToolbar bar in bars) {
+ IDockToolbar db = (IDockToolbar) bar;
+ db.Visible = false;
+ bar.Destroy ();
+ }
+ bars.Clear ();
+ }
+
+ public IDockToolbar GetBar (string id)
+ {
+ foreach (DockToolbar bar in bars)
+ if (bar.Id == id) return bar;
+ return null;
+ }
+
+ public ICollection Toolbars {
+ get { return bars; }
+ }
+
+ public void ResetToolbarPositions ()
+ {
+ foreach (DockToolbarPanel panel in panels)
+ panel.ResetBarPositions (false);
+ }
+
+ void SaveCurrentLayout ()
+ {
+ DockToolbarStatus[] status = SaveStatus ();
+ layouts [currentLayout] = status;
+ }
+
+ void RestoreLayout (string layout)
+ {
+ DockToolbarStatus[] status = (DockToolbarStatus[]) layouts [layout];
+ RestoreStatus (status);
+ currentLayout = layout;
+ }
+
+ DockToolbarStatus[] SaveStatus ()
+ {
+ DockToolbarStatus[] status = new DockToolbarStatus [bars.Count];
+ for (int n=0; n<bars.Count; n++) {
+ DockToolbar bar = (DockToolbar) bars [n];
+ status [n] = bar.Status;
+ }
+ return status;
+ }
+
+ void RestoreStatus (DockToolbarStatus[] status)
+ {
+ foreach (IDockToolbar b in bars)
+ b.Visible = false;
+
+ if (status == null) {
+ foreach (DockToolbar bar in bars)
+ bar.Status = bar.DefaultStatus;
+ } else {
+ foreach (DockToolbarStatus s in status) {
+ DockToolbar bar = (DockToolbar) GetBar (s.BarId);
+ if (bar != null)
+ bar.Status = s;
+ }
+ }
+ }
+
+ internal int DockMargin {
+ get { return 7; }
+ }
+
+ internal void DockToolbar (DockToolbar bar, Placement placement, int offset, int row)
+ {
+ DockToolbarPanel p = GetPanel (placement);
+ if (row != -1)
+ p.AddDockToolbar (bar, offset, row);
+ else
+ p.AddDockToolbar (bar);
+ }
+
+ internal void FloatBar (DockToolbar bar, Orientation orientation, int x, int y)
+ {
+ FloatingDock fdock = new FloatingDock (this);
+ fdock.Move (x, y);
+ bar.ResetSize ();
+ fdock.Attach (bar);
+ bar.Orientation = orientation;
+ }
+
+ internal Gtk.Window TopWindow {
+ get {
+ Widget w = Parent;
+ while (w != null && !(w is Gtk.Window))
+ w = w.Parent;
+ return (Gtk.Window) w;
+ }
+ }
+
+ DockToolbarPanel GetPanel (Placement o)
+ {
+ return panels [(int)o];
+ }
+
+ protected override bool OnMotionNotifyEvent (EventMotion e)
+ {
+ if (dragBar != null) {
+ int sx,sy;
+ this.GdkWindow.GetOrigin (out sx, out sy);
+ int rx = (int)e.XRoot - sx;
+ int ry = (int)e.YRoot - sy;
+
+ if (dragBar.Floating) {
+ bool foundPanel = false;
+ dragBar.FloatingDock.Move ((int)e.XRoot + xDragDif, (int)e.YRoot + yDragDif);
+ Rectangle barRect = new Rectangle (rx + xDragDif, ry + yDragDif, dragBar.Allocation.Width, dragBar.DefaultHeight);
+ foreach (DockToolbarPanel p in panels) {
+ if (p.Allocation.IntersectsWith (barRect)) {
+ if (targetPanel != null && targetPanel != p)
+ targetPanel.EndDragBar (dragBar);
+ p.Reposition (dragBar, rx, ry, xDragDif, yDragDif);
+ targetPanel = p;
+ foundPanel = true;
+ break;
+ }
+ }
+ if (!foundPanel && targetPanel != null)
+ targetPanel.EndDragBar (dragBar);
+ } else {
+ DockToolbarPanel panel = (DockToolbarPanel) dragBar.Parent;
+ panel.Reposition (dragBar, rx, ry, xDragDif, yDragDif);
+ }
+ }
+ return base.OnMotionNotifyEvent (e);
+ }
+
+ internal void StartDragBar (DockToolbar bar, int x, int y, uint time)
+ {
+ dragBar = bar;
+ xDragDif = -x;
+ yDragDif = -y;
+ Pointer.Grab (this.GdkWindow, false, EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask, null, handCursor, time);
+ if (!bar.Floating) {
+ DockToolbarPanel panel = (DockToolbarPanel) dragBar.Parent;
+ panel.StartDragBar (bar);
+ }
+ }
+
+ internal void EndDragBar (DockToolbar bar, uint time)
+ {
+ Pointer.Ungrab (time);
+ if (targetPanel != null) {
+ targetPanel.DropDragBar (bar);
+ targetPanel.EndDragBar (bar);
+ }
+ dragBar = null;
+ }
+
+ protected override bool OnButtonReleaseEvent (EventButton e)
+ {
+ if (dragBar != null)
+ EndDragBar (dragBar, e.Time);
+
+ return base.OnButtonReleaseEvent (e);
+ }
+
+ protected override bool OnButtonPressEvent (Gdk.EventButton e)
+ {
+ if (e.Button == 3) {
+ int sx,sy;
+ this.GdkWindow.GetOrigin (out sx, out sy);
+ int rx = (int)e.XRoot - sx;
+ int ry = (int)e.YRoot - sy;
+
+ foreach (DockToolbarPanel p in panels) {
+ if (p.Allocation.Contains (rx, ry))
+ OnPanelClick (e, p.Placement);
+ }
+ }
+ return base.OnButtonPressEvent (e);
+ }
+
+ protected virtual void OnPanelClick (Gdk.EventButton e, Placement placement)
+ {
+ }
+
+ protected override void OnDestroyed ()
+ {
+ if (handCursor != null) {
+ handCursor.Dispose ();
+ handCursor = null;
+ }
+ base.OnDestroyed ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameLayout.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameLayout.cs
new file mode 100644
index 0000000000..1330a6aa25
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameLayout.cs
@@ -0,0 +1,42 @@
+//
+// DockToolbarFrameLayout.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.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ public class DockToolbarFrameLayout
+ {
+ [XmlAttribute ("id")]
+ public string Id;
+
+ [XmlElement ("dockbar")]
+ public DockToolbarStatus[] Bars;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameStatus.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameStatus.cs
new file mode 100644
index 0000000000..95d34d70b0
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrameStatus.cs
@@ -0,0 +1,45 @@
+//
+// DockToolbarFrameStatus.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.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ public class DockToolbarFrameStatus
+ {
+ internal const int CurrentVersion = 2;
+
+ [XmlElement ("version")]
+ public int Version = 1;
+
+ [XmlElement ("layout", typeof(DockToolbarFrameLayout))]
+ public ArrayList Status = new ArrayList ();
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPanel.cs
new file mode 100644
index 0000000000..ab5ab134be
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPanel.cs
@@ -0,0 +1,965 @@
+//
+// DockToolbarPanel.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 Gtk;
+using Gdk;
+using System.Collections;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ internal class DockToolbarPanel: FixedPanel
+ {
+ DockToolbarFrame parentFrame;
+ ArrayList bars = new ArrayList ();
+ Orientation orientation;
+
+ ArrowWindow placeholderArrow1;
+ ArrowWindow placeholderArrow2;
+ PlaceholderWindow placeholder;
+ bool currentPlaceholderHorz;
+
+ int dropOffset;
+ int dropRow = -1;
+ bool dropNewRow;
+ bool enableAnimations = true;
+
+ public DockToolbarPanel (DockToolbarFrame parentFrame, Placement placement)
+ {
+ // ResizeMode = ResizeMode.Immediate;
+ Placement = placement;
+ switch (placement) {
+ case Placement.Top:
+ this.orientation = Orientation.Horizontal;
+ break;
+ case Placement.Bottom:
+ this.orientation = Orientation.Horizontal;
+ break;
+ case Placement.Left:
+ this.orientation = Orientation.Vertical;
+ break;
+ case Placement.Right:
+ this.orientation = Orientation.Vertical;
+ break;
+ }
+
+ this.parentFrame = parentFrame;
+ }
+
+ public Orientation Orientation {
+ get { return orientation; }
+ }
+
+ public void AddDockToolbar (DockToolbar bar)
+ {
+ bool ea = EnableAnimation (false);
+
+ Put (bar, 0, 0);
+ bar.Orientation = orientation;
+
+ if (bars.Count > 0 && IsRealized) {
+ DockToolbar last = (DockToolbar) bars [bars.Count - 1];
+ int width = bar.DefaultSize;
+ int lastx = last.DockOffset + last.DefaultSize;
+
+ if (lastx + width <= PanelWidth)
+ MoveBar (bar, lastx, last.DockRow, false);
+ else
+ MoveBar (bar, 0, last.DockRow + 1, false);
+ bar.AnchorOffset = bar.DockOffset;
+ InternalAdd (bar);
+ SortBars ();
+ } else {
+ MoveBar (bar, 0, 0);
+ bar.AnchorOffset = bar.DockOffset;
+ InternalAdd (bar);
+ }
+
+ EnableAnimation (ea);
+ }
+
+ public void AddDockToolbar (DockToolbar bar, int offset, int row)
+ {
+ bool ea = EnableAnimation (false);
+ InternalAdd (bar);
+ Put (bar, 0, 0);
+ bar.Orientation = orientation;
+ MoveBar (bar, offset, row, false);
+ bar.AnchorOffset = offset;
+ SortBars ();
+ UpdateRowSizes (bar.DockRow);
+ EnableAnimation (ea);
+ }
+
+ void InternalAdd (DockToolbar bar)
+ {
+ bars.Add (bar);
+ bar.DefaultSizeChanged += new EventHandler (OnBarSizeChanged);
+ }
+
+ public void RemoveBar (DockToolbar bar)
+ {
+ if (IsSingleBarRow (bar))
+ RemoveRow (bar.DockRow);
+
+ Remove (bar);
+ bars.Remove (bar);
+ bar.DefaultSizeChanged -= new EventHandler (OnBarSizeChanged);
+
+ UpdateRowHeight (bar.DockRow);
+ PackBars ();
+ }
+
+ protected override void OnRealized ()
+ {
+ base.OnRealized ();
+ ResetBarPositions (false);
+ }
+
+ public void ResetBarPositions (bool animate)
+ {
+ int x=0, row=0;
+ int width = PanelWidth;
+
+ bool ea = EnableAnimation (animate);
+
+ foreach (DockToolbar b in bars) {
+ int barw = GetChildWidth (b);
+ if (x + barw < width)
+ MoveBar (b, x, row);
+ else {
+ row++;
+ x = 0;
+ MoveBar (b, 0, row);
+ }
+ x += barw;
+ }
+ SortBars ();
+ EnableAnimation (ea);
+ }
+
+ void SetPlaceholder (DockToolbar bar, int offset, int row)
+ {
+ if (dropRow != row && dropRow != -1)
+ RestoreShiftedBars (dropRow);
+
+ ShowPlaceholder (bar, false, offset, GetRowTop (row), GetChildWidth (bar), GetRowSize (row));
+
+ dropOffset = offset;
+ dropRow = row;
+ dropNewRow = false;
+ }
+
+ void SetNewRowPlaceholder (DockToolbar bar, int offset, int toprow)
+ {
+ if (dropRow != -1)
+ RestoreShiftedBars (dropRow);
+
+ int y = GetRowTop (toprow) - parentFrame.DockMargin;
+ int h = parentFrame.DockMargin * 2;
+ ShowPlaceholder (bar, true, offset, y, GetChildWidth (bar), h);
+
+ dropOffset = offset;
+ dropRow = toprow;
+ dropNewRow = true;
+ }
+
+ void ShowPlaceholder (DockToolbar bar, bool horz, int x, int y, int w, int h)
+ {
+ if (orientation != Orientation.Horizontal)
+ horz = !horz;
+
+ PanelToWindow (x, y, w, h, out x, out y, out w, out h);
+
+ bool created = false;
+
+ if (placeholder == null || horz != currentPlaceholderHorz) {
+ HidePlaceholder ();
+ placeholder = new PlaceholderWindow (parentFrame);
+ placeholderArrow1 = new ArrowWindow (parentFrame, horz ? ArrowWindow.Direction.Right : ArrowWindow.Direction.Down);
+ placeholderArrow2 = new ArrowWindow (parentFrame, horz ? ArrowWindow.Direction.Left : ArrowWindow.Direction.Up);
+ currentPlaceholderHorz = horz;
+ created = true;
+ }
+
+ int sx, sy;
+ this.GdkWindow.GetOrigin (out sx, out sy);
+ sx += x;
+ sy += y;
+
+ int mg = -4;
+ placeholder.Move (sx - mg, sy - mg);
+ placeholder.Resize (w + mg*2, h + mg * 2);
+
+ if (horz) {
+ placeholderArrow1.Move (sx - placeholderArrow1.Width, sy + (h/2) - placeholderArrow1.Height/2);
+ placeholderArrow2.Move (sx + w, sy + (h/2) - placeholderArrow1.Height/2);
+ } else {
+ int px = sx + w/2 - placeholderArrow1.Width/2;
+ if (px < 0) px = 0;
+ placeholderArrow1.Move (px, sy - placeholderArrow1.Height);
+ placeholderArrow2.Move (px, sy + h);
+ }
+
+ if (created) {
+ placeholder.Show ();
+ placeholder.Present ();
+ if (bar.FloatingDock != null)
+ bar.FloatingDock.Present ();
+ placeholderArrow1.Present ();
+ placeholderArrow2.Present ();
+ }
+ }
+
+ void HidePlaceholder ()
+ {
+ if (placeholder == null) return;
+ placeholder.Destroy ();
+ placeholder = null;
+ placeholderArrow1.Destroy ();
+ placeholderArrow1 = null;
+ placeholderArrow2.Destroy ();
+ placeholderArrow2 = null;
+
+ if (dropRow != -1 && !dropNewRow) {
+ RestoreShiftedBars (dropRow);
+ dropRow = -1;
+ }
+ }
+
+ bool IsPlaceHolderVisible {
+ get { return placeholder != null; }
+ }
+
+ public void StartDragBar (DockToolbar bar)
+ {
+ }
+
+ public void DropDragBar (DockToolbar bar)
+ {
+ if (!IsPlaceHolderVisible) return;
+
+ foreach (DockToolbar b in bars) {
+ if (b.DockRow == dropRow && b.DockShiftOffset != -1) {
+ b.DockShiftOffset = -1;
+ b.AnchorOffset = b.DockRow;
+ }
+ }
+
+ if (dropRow != -1) {
+ if (dropNewRow)
+ InsertRow (bar, dropOffset, dropRow);
+ else {
+ MoveBar (bar, dropOffset, dropRow);
+ UpdateRowHeight (dropRow);
+ }
+ SortBars ();
+ dropRow = -1;
+ }
+ }
+
+ public void EndDragBar (DockToolbar bar)
+ {
+ if (IsPlaceHolderVisible) {
+ HidePlaceholder ();
+ }
+ }
+
+ void RestoreShiftedBars (int row)
+ {
+ foreach (DockToolbar b in bars) {
+ if (b.DockRow == row && b.DockShiftOffset != -1) {
+ MoveBar (b, b.DockShiftOffset, b.DockRow, false);
+ b.DockShiftOffset = -1;
+ }
+ }
+ }
+
+ public void Reposition (DockToolbar bar, int xcursor, int ycursor, int difx, int dify)
+ {
+ if (!bar.CanDockTo (this))
+ return;
+
+ bar.Orientation = orientation;
+
+ int x, y;
+ WindowToPanel (xcursor + difx, ycursor + dify, bar.Allocation.Width, bar.Allocation.Height, out x, out y);
+ WindowToPanel (xcursor, ycursor, 0, 0, out xcursor, out ycursor);
+
+ RepositionInternal (bar, x, y, xcursor, ycursor);
+ }
+
+ void RepositionInternal (DockToolbar bar, int x, int y, int xcursor, int ycursor)
+ {
+ int width = GetChildWidth (bar);
+
+ ycursor = y + bar.DefaultHeight / 2;
+
+ if (bars.Count == 0 && bar.Floating) {
+ SetNewRowPlaceholder (bar, x, 0);
+ return;
+ }
+
+ int dx = (x + width) - PanelWidth;
+ if (dx > parentFrame.DockMargin && !bar.Floating) {
+ HidePlaceholder ();
+ FloatBar (bar, x, y);
+ return;
+ }
+ else if (dx > 0)
+ x -= dx;
+ else if (x < -parentFrame.DockMargin && !bar.Floating) {
+ HidePlaceholder ();
+ FloatBar (bar, x, y);
+ return;
+ }
+ else if (x < 0)
+ x = 0;
+
+ int nx = x;
+ int row = -1;
+
+ // Get the old bar y position
+
+ int panelBottom = GetPanelBottom ();
+
+ if (ycursor < - parentFrame.DockMargin || ycursor > panelBottom + parentFrame.DockMargin) {
+ HidePlaceholder ();
+ FloatBar (bar, x, y);
+ return;
+ }
+
+ int rtop = 0;
+ int prevtop = 0;
+ row = 0;
+ while (ycursor >= rtop) {
+ prevtop = rtop;
+ row++;
+ if (rtop >= panelBottom) break;
+ rtop += GetRowSize (row - 1);
+ }
+
+ row--;
+ int ry = ycursor - prevtop;
+
+ if (ry <= parentFrame.DockMargin && ry >= 0) {
+ SetNewRowPlaceholder (bar, x, row);
+ FloatBar (bar, x, y);
+ return;
+ } else if (ry >= (GetRowSize(row) - parentFrame.DockMargin) || (ry < 0 && -ry < parentFrame.DockMargin)) {
+ SetNewRowPlaceholder (bar, x, row + 1);
+ FloatBar (bar, x, y);
+ return;
+ }
+
+ // Can't create a new row. Try to fit the bar in the current row
+ // Find the first bar in the row:
+
+ int ns = -1;
+ for (int n=0; n<bars.Count; n++) {
+ DockToolbar b = (DockToolbar)bars[n];
+
+ // Ignore the bar being moved
+ if (b == bar) continue;
+
+ if (b.DockRow == row) {
+ ns = n;
+ break;
+ }
+ }
+
+ if (ns == -1) {
+ // There are no other bars, no problem then
+ if (bar.Floating) {
+ SetPlaceholder (bar, nx, row);
+ return;
+ }
+
+ if ((nx == bar.DockOffset && row == bar.DockRow) || (row != bar.DockRow)) {
+ SetPlaceholder (bar, nx, row);
+ FloatBar (bar, x, y);
+ return;
+ }
+
+ HidePlaceholder ();
+ MoveBar (bar, nx, row);
+ return;
+ }
+
+ // Compute the available space, and find the bars at the
+ // left and the right of the bar being moved
+
+ int gapsTotal = 0;
+ int lastx = 0;
+ int leftIndex=-1, rightIndex = -1;
+ int gapsLeft = 0, gapsRight = 0;
+
+ for (int n=ns; n<bars.Count; n++) {
+ DockToolbar b = (DockToolbar)bars[n];
+
+ // Ignore the bar being moved
+ if (b == bar) continue;
+
+ if (b.DockRow != row) break;
+ int bx = b.DockOffset;
+
+ if (bx > x && (rightIndex == -1))
+ rightIndex = n;
+ else if (bx <= x)
+ leftIndex = n;
+
+ if (bx < x)
+ gapsLeft += bx - lastx;
+ else {
+ if (lastx < x) {
+ gapsLeft += x - lastx;
+ gapsRight += bx - x;
+ } else
+ gapsRight += bx - lastx;
+ }
+
+ gapsTotal += bx - lastx;
+ lastx = GetChildRightOffset (b);
+ }
+
+ if (lastx < x) {
+ gapsLeft += x - lastx;
+ gapsRight += PanelWidth - x;
+ } else {
+ gapsRight += PanelWidth - lastx;
+ }
+
+ gapsTotal += PanelWidth - lastx;
+
+ // Is there room for the bar?
+ if (gapsTotal < width) {
+ HidePlaceholder ();
+ FloatBar (bar, x, y);
+ return;
+ }
+
+ // Shift the bars at the left and the right
+
+ int oversizeLeft = 0;
+ int oversizeRight = 0;
+
+ if (leftIndex != -1) {
+ int r = GetChildRightOffset ((DockToolbar) bars [leftIndex]);
+ oversizeLeft = r - nx;
+ }
+
+ if (rightIndex != -1) {
+ int r = ((DockToolbar) bars [rightIndex]).DockOffset;
+ oversizeRight = (nx + width) - r;
+ }
+
+ if (oversizeLeft > gapsLeft)
+ oversizeRight += (oversizeLeft - gapsLeft);
+ else if (oversizeRight > gapsRight)
+ oversizeLeft += (oversizeRight - gapsRight);
+
+ if (leftIndex != -1 && oversizeLeft > 0) {
+ ShiftBar (leftIndex, -oversizeLeft);
+ nx = GetChildRightOffset ((DockToolbar) bars [leftIndex]);
+ }
+
+ if (rightIndex != -1 && oversizeRight > 0) {
+ ShiftBar (rightIndex, oversizeRight);
+ nx = ((DockToolbar) bars [rightIndex]).DockOffset - width;
+ }
+
+
+ if (bar.Floating) {
+ SetPlaceholder (bar, nx, row);
+ return;
+ }
+
+ if ((nx == bar.DockOffset && row == bar.DockRow) || (row != bar.DockRow)) {
+ if (bar.Floating) {
+ SetPlaceholder (bar, nx, row);
+ FloatBar (bar, x, y);
+ }
+ return;
+ }
+
+ HidePlaceholder ();
+ MoveBar (bar, nx, row);
+ }
+
+ void FloatBar (DockToolbar bar, int x, int y)
+ {
+ if (bar.Floating) return;
+
+ int wx,wy,w,h;
+ PanelToWindow (x, y, GetChildWidth (bar), bar.DefaultHeight, out x, out y, out w, out h);
+
+ this.GdkWindow.GetOrigin (out wx, out wy);
+ RemoveBar (bar);
+ parentFrame.FloatBar (bar, orientation, wx + x, wy + y);
+ }
+
+ void ShiftBar (int index, int size)
+ {
+ DockToolbar bar = (DockToolbar) bars [index];
+ if (bar.DockShiftOffset == -1)
+ bar.DockShiftOffset = bar.DockOffset;
+
+ if (size > 0) {
+ int rp = GetChildRightOffset (bar);
+ int gap = PanelWidth - rp;
+ if (index + 1 < bars.Count) {
+ DockToolbar obar = (DockToolbar) bars [index + 1];
+ if (bar.DockRow == obar.DockRow) {
+ gap = obar.DockOffset - rp;
+ if (gap < size) {
+ ShiftBar (index + 1, size - gap);
+ gap = obar.DockOffset - rp;
+ }
+ }
+ }
+ if (gap > size)
+ gap = size;
+ if (gap > 0)
+ MoveBar (bar, bar.DockOffset + gap, bar.DockRow, false);
+ } else {
+ size = -size;
+ int lp = bar.DockOffset;
+ int gap = lp;
+ if (index > 0) {
+ DockToolbar obar = (DockToolbar) bars [index - 1];
+ if (bar.DockRow == obar.DockRow) {
+ gap = lp - GetChildRightOffset (obar);
+ if (gap < size) {
+ ShiftBar (index - 1, gap - size);
+ gap = lp - GetChildRightOffset (obar);
+ }
+ }
+ }
+
+ if (gap > size)
+ gap = size;
+ if (gap > 0)
+ MoveBar (bar, bar.DockOffset - gap, bar.DockRow, false);
+ }
+ }
+
+ void MoveBar (DockToolbar bar, int x, int row)
+ {
+ MoveBar (bar, x, row, true);
+ }
+
+ void MoveBar (DockToolbar bar, int x, int row, bool setAnchorOffset)
+ {
+ int rt = GetRowTop (row);
+
+ bar.DockRow = row;
+ bar.DockOffset = x;
+
+ if (bar.Floating) {
+ FloatingDock win = bar.FloatingDock;
+ win.Detach ();
+ win.Destroy ();
+
+ InternalAdd (bar);
+ Put (bar, x, rt);
+ SortBars ();
+ ResetAnchorOffsets (row);
+
+ } else {
+ if (setAnchorOffset)
+ ResetAnchorOffsets (row);
+
+ InternalMove (bar, x, rt, true);
+ }
+ }
+
+ void ResetAnchorOffsets (int row)
+ {
+ for (int n=0; n<bars.Count; n++) {
+ DockToolbar b = (DockToolbar) bars [n];
+ if (b.DockRow < row) continue;
+ if (b.DockRow > row) return;
+ b.AnchorOffset = b.DockOffset;
+ }
+ }
+
+ void UpdateRowHeight (int row)
+ {
+ int nr = row + 1;
+ bool ea = EnableAnimation (false);
+ for (int n=0; n<bars.Count; n++) {
+ DockToolbar b = (DockToolbar) bars [n];
+ if (b.DockRow < nr) continue;
+ MoveBar (b, b.DockOffset, b.DockRow);
+ }
+ EnableAnimation (ea);
+ }
+
+ void OnBarSizeChanged (object s, EventArgs e)
+ {
+ if (IsRealized)
+ UpdateRowSizes (((DockToolbar)s).DockRow);
+ }
+
+ void UpdateRowSizes (int row)
+ {
+ int lastx = 0;
+ for (int n=0; n<bars.Count; n++) {
+ DockToolbar b = (DockToolbar) bars [n];
+ if (b.DockRow < row) continue;
+ if (b.DockRow > row) break;
+ if (b.AnchorOffset < lastx)
+ b.AnchorOffset = lastx;
+ lastx = b.AnchorOffset + b.DefaultSize;
+ }
+ PackBars ();
+ }
+
+ protected override void OnSizeAllocated (Rectangle rect)
+ {
+ Rectangle oldRect = Allocation;
+ base.OnSizeAllocated (rect);
+
+ if (!rect.Equals (oldRect))
+ PackBars ();
+ }
+
+ void PackBars ()
+ {
+ bool ea = EnableAnimation (false);
+ int n=0;
+ while (n < bars.Count)
+ n = PackRow (n);
+ EnableAnimation (ea);
+ }
+
+ int PackRow (int sn)
+ {
+ // The 'sn' parameter is the index if the first toolbar of the row.
+ // It returns the index of the first toolbar of the next row
+
+ int n = sn;
+ int row = ((DockToolbar)bars[n]).DockRow;
+ int lastx = 0;
+ int gaps = 0;
+
+ // Calculates the free space in the row
+
+ while (n < bars.Count) {
+ DockToolbar bar = (DockToolbar) bars [n];
+ if (bar.DockRow != row) break;
+
+ if (bar.AnchorOffset > lastx)
+ gaps += bar.AnchorOffset - lastx;
+
+ lastx = bar.AnchorOffset + bar.DefaultSize;
+ n++;
+ }
+
+ if (lastx <= PanelWidth) {
+ for (int i=sn; i<n; i++) {
+ DockToolbar b = (DockToolbar) bars[i];
+ if (b.AnchorOffset != b.DockOffset)
+ MoveBar (b, b.AnchorOffset, b.DockRow, false);
+ if (b.Size != b.DefaultSize) {
+ b.ShowArrow = false;
+ b.Size = b.DefaultSize;
+ }
+ }
+ return n;
+ }
+
+ int barsSize = lastx - gaps;
+ double barShrink = 1;
+ double gapShrink = 0;
+
+ if (barsSize > PanelWidth)
+ barShrink = (double)PanelWidth / (double)barsSize;
+ else
+ gapShrink = ((double)(PanelWidth - barsSize)) / (double)gaps;
+
+ lastx = 0;
+ int newlastx = 0;
+ for (int i=sn; i < n; i++) {
+ DockToolbar bar = (DockToolbar) bars [i];
+ int gap = bar.AnchorOffset - lastx;
+ lastx = bar.AnchorOffset + bar.DefaultSize;
+
+ int nx = (int)(newlastx + ((double)gap * gapShrink));
+ if (nx != bar.DockOffset)
+ MoveBar (bar, nx, bar.DockRow, false);
+
+ int nw = (int)((double)bar.DefaultSize * barShrink);
+ if (nw != bar.Size) {
+ bar.ShowArrow = nw != bar.DefaultSize;
+ bar.Size = nw;
+ }
+ newlastx = bar.DockOffset + nw;
+ }
+
+ return n;
+ }
+
+ int GetPanelBottom ()
+ {
+ if (bars.Count > 0) {
+ DockToolbar bar = (DockToolbar) bars [bars.Count - 1];
+ return GetRowTop (bar.DockRow + 1);
+ }
+ else
+ return 0;
+ }
+
+ bool IsSingleBarRow (DockToolbar bar)
+ {
+ int row = bar.DockRow;
+ foreach (DockToolbar b in bars) {
+ if (bar != b && b.DockRow == row)
+ return false;
+ }
+ return true;
+ }
+
+ void InsertRow (DockToolbar ibar, int offset, int row)
+ {
+ MoveBar (ibar, offset, row);
+ foreach (DockToolbar bar in bars) {
+ if (ibar != bar && bar.DockRow >= row)
+ bar.DockRow++;
+ }
+ SortBars ();
+ UpdateRowHeight (row);
+ }
+
+ void RemoveRow (int row)
+ {
+ foreach (DockToolbar bar in bars) {
+ if (bar.DockRow >= row)
+ MoveBar (bar, bar.DockOffset, bar.DockRow - 1, false);
+ }
+ }
+
+ int GetChildRightOffset (DockToolbar bar)
+ {
+ return bar.DockOffset + bar.Size;
+ }
+
+ int GetRowSize (int row)
+ {
+ int max = 0;
+ for (int n=0; n<bars.Count; n++) {
+ DockToolbar b = (DockToolbar) bars [n];
+ if (b.DockRow < row) continue;
+ if (b.DockRow > row) return max;
+ if (b.DefaultHeight > max)
+ max = b.DefaultHeight;
+ }
+ return max;
+ }
+
+ int GetRowTop (int row)
+ {
+ int t = 0;
+ for (int n=0; n < row; n++)
+ t += GetRowSize (n);
+ return t;
+ }
+
+ int MaxRow {
+ get {
+ var lastBar = (DockToolbar)bars[bars.Count -1];
+ return lastBar.DockRow;
+ }
+ }
+
+ void SortBars ()
+ {
+ bars.Sort (DocBarComparer.Instance);
+ }
+
+ void InternalMove (DockToolbar bar, int x, int y, bool animate)
+ {
+ if (bar.Animation != null) {
+ bar.Animation.Cancel ();
+ bar.Animation = null;
+ }
+
+ if (animate && enableAnimations) {
+ bar.Animation = new MoveAnimation (this, bar, x, y);
+ bar.Animation.Start ();
+ }
+ else
+ Move (bar, x, y);
+ }
+
+ bool EnableAnimation (bool enable)
+ {
+ bool r = enableAnimations;
+ enableAnimations = enable;
+ return r;
+ }
+
+ protected override bool OnExposeEvent (EventExpose evnt)
+ {
+ //leave this plain unless there are horizontal children, so that the panel matches the toolbars
+ //see DockToolbar.OnExposeEvent
+ if (bars.Count == 0 || this.Orientation != Orientation.Horizontal)
+ return base.OnExposeEvent (evnt);
+
+ // get a proper GTK+ toolbar style by using one of the children, i.e. a DockToolbar, a subclass of GtkToolbar
+ var styleProvider = (Widget)bars[0];
+ var shadowType = (ShadowType) styleProvider.StyleGetProperty ("shadow-type");
+
+ //render each row separately, so the theme treats each as a row, and they match the individual toolbars' themed painting
+ //FIXME: Mac only seems to display the last-painted row, even though we make paint calls for all of them
+ int row = MaxRow;
+ for (int i = bars.Count - 1; i >= 0; i--) {
+ var bar = (DockToolbar) bars[i];
+ if (bar.DockRow == row) {
+ row--;
+ Style.PaintBox (bar.Style, evnt.Window, State, shadowType, evnt.Area, bar, "toolbar",
+ Allocation.X, bar.Allocation.Y, Allocation.Width, bar.Allocation.Height);
+ }
+ }
+
+ foreach (DockToolbar bar in bars)
+ this.PropagateExpose (bar, evnt);
+ return true;
+ }
+ }
+
+
+ internal class DocBarComparer: IComparer
+ {
+ internal static DocBarComparer Instance = new DocBarComparer ();
+
+ public int Compare (object a, object b)
+ {
+ DockToolbar b1 = (DockToolbar) a;
+ DockToolbar b2 = (DockToolbar) b;
+
+ if (b1.DockRow < b2.DockRow) return -1;
+ else if (b1.DockRow > b2.DockRow) return 1;
+ else if (b1.DockOffset < b2.DockOffset) return -1;
+ else if (b1.DockOffset > b2.DockOffset) return 1;
+ else return 0;
+ }
+ }
+
+ internal abstract class AnimationManager
+ {
+ static ArrayList anims = new ArrayList ();
+ static int s = 0;
+
+ public static void Animate (Animation a)
+ {
+ if (anims.Count == 0)
+ GLib.Timeout.Add (10, new GLib.TimeoutHandler (Animate));
+ anims.Add (a);
+ }
+
+ public static void CancelAnimation (Animation a)
+ {
+ anims.Remove (a);
+ }
+
+ public static bool Animate ()
+ {
+ s++;
+ ArrayList toDelete = new ArrayList ();
+ foreach (Animation a in anims)
+ if (!a.Run ())
+ toDelete.Add (a);
+
+ foreach (object ob in toDelete)
+ anims.Remove (ob);
+
+ return anims.Count != 0;
+ }
+ }
+
+ internal abstract class Animation
+ {
+ protected Widget widget;
+
+ public Animation (Widget w)
+ {
+ widget = w;
+ }
+
+ public void Start ()
+ {
+ AnimationManager.Animate (this);
+ }
+
+ public void Cancel ()
+ {
+ AnimationManager.CancelAnimation (this);
+ }
+
+ internal protected abstract bool Run ();
+ }
+
+ internal class MoveAnimation: Animation
+ {
+ FixedPanel panel;
+ float destx, desty;
+ float curx, cury;
+
+ public MoveAnimation (FixedPanel f, Widget w, int destx, int desty): base (w)
+ {
+ panel = f;
+ int x, y;
+ f.GetPosition (w, out x, out y);
+ curx = (float)x;
+ cury = (float)y;
+ this.destx = (float) destx;
+ this.desty = (float) desty;
+ }
+
+ internal protected override bool Run ()
+ {
+ float dx = destx - curx;
+ float dy = desty - cury;
+
+ dx = dx / 4;
+ dy = dy / 4;
+
+ curx += dx;
+ cury += dy;
+
+ panel.Move (widget, (int)curx, (int)cury);
+
+ if(Math.Abs (dx) < 0.1 && Math.Abs (dy) < 0.1) {
+ panel.Move (widget, (int)destx, (int)desty);
+ return false;
+ } else
+ return true;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPosition.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPosition.cs
new file mode 100644
index 0000000000..dbf9121040
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarPosition.cs
@@ -0,0 +1,52 @@
+//
+// DockToolbarPosition.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 Gtk;
+using Gdk;
+using System.Collections;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ [XmlInclude (typeof(DockedPosition))]
+ [XmlInclude (typeof(FloatingPosition))]
+ public class DockToolbarPosition
+ {
+ internal virtual void RestorePosition (DockToolbarFrame frame, DockToolbar bar) {}
+
+ internal static DockToolbarPosition Create (DockToolbar bar)
+ {
+ if (bar.Floating)
+ return new FloatingPosition (bar);
+ else
+ return new DockedPosition (bar);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarStatus.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarStatus.cs
new file mode 100644
index 0000000000..6db8ebd857
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarStatus.cs
@@ -0,0 +1,71 @@
+//
+// DockToolbarStatus.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.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ [XmlType ("dockBar")]
+ public class DockToolbarStatus
+ {
+ bool visible;
+ DockToolbarPosition position;
+ string id;
+
+ public DockToolbarStatus ()
+ {
+ }
+
+ public DockToolbarStatus (string id, bool visible, DockToolbarPosition position)
+ {
+ this.visible = visible;
+ this.position = position;
+ this.id = id;
+ }
+
+ [XmlAttribute ("id")]
+ public string BarId {
+ get { return id; }
+ set { id = value; }
+ }
+
+ [XmlElement ("visible")]
+ public bool Visible {
+ get { return visible; }
+ set { visible = value; }
+ }
+
+ [XmlElement ("dockedPosition", typeof(DockedPosition))]
+ [XmlElement ("floatingPosition", typeof(FloatingPosition))]
+ public DockToolbarPosition Position {
+ get { return position; }
+ set { position = value; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockedPosition.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockedPosition.cs
new file mode 100644
index 0000000000..a3b3132c22
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockedPosition.cs
@@ -0,0 +1,81 @@
+//
+// DockedPosition.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.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ [XmlType ("dockedPosition")]
+ public class DockedPosition: DockToolbarPosition
+ {
+ Placement placement;
+ int dockOffset;
+ int dockRow;
+
+ public DockedPosition ()
+ {
+ }
+
+ internal DockedPosition (DockToolbar bar)
+ {
+ dockOffset = bar.AnchorOffset;
+ dockRow = bar.DockRow;
+ placement = ((DockToolbarPanel)bar.Parent).Placement;
+ }
+
+ internal DockedPosition (Placement placement)
+ {
+ this.placement = placement;
+ dockRow = -1;
+ }
+
+ [XmlAttribute ("offset")]
+ public int DockOffset {
+ get { return dockOffset; }
+ set { dockOffset = value; }
+ }
+
+ [XmlAttribute ("row")]
+ public int DockRow {
+ get { return dockRow; }
+ set { dockRow = value; }
+ }
+
+ [XmlAttribute ("placement")]
+ public Placement Placement {
+ get { return placement; }
+ set { placement = value; }
+ }
+
+ internal override void RestorePosition (DockToolbarFrame frame, DockToolbar bar)
+ {
+ frame.DockToolbar (bar, placement, dockOffset, dockRow);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs
new file mode 100644
index 0000000000..17fc214fe7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs
@@ -0,0 +1,263 @@
+//
+// FixedPanel.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 Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ public enum Placement
+ {
+ Top, Bottom, Left, Right
+ }
+
+ public class FixedPanel: Container
+ {
+ ArrayList widgets = new ArrayList ();
+ Placement placement = Placement.Top;
+
+ public FixedPanel ()
+ {
+ WidgetFlags |= WidgetFlags.NoWindow;
+ }
+
+ public Placement Placement {
+ get { return placement; }
+ set { placement = value; }
+ }
+
+ public override GLib.GType ChildType ()
+ {
+ return Widget.GType;
+ }
+
+ public void Put (Widget w, int x, int y)
+ {
+ WidgetPosition wpos = new WidgetPosition ();
+ wpos.X = x;
+ wpos.Y = y;
+ wpos.Widget = w;
+ widgets.Add (wpos);
+ w.Parent = this;
+ QueueResize ();
+ }
+
+ public void Move (Widget w, int x, int y)
+ {
+ int n = GetWidgetPosition (w);
+ if (n != -1) {
+ WidgetPosition wpos = (WidgetPosition) widgets [n];
+ if (wpos.X == x && wpos.Y == y) return;
+ wpos.X = x;
+ wpos.Y = y;
+ QueueResize ();
+ }
+ }
+
+ public bool GetPosition (Widget w, out int x, out int y)
+ {
+ int n = GetWidgetPosition (w);
+ if (n != -1) {
+ WidgetPosition wpos = (WidgetPosition) widgets [n];
+ x = wpos.X;
+ y = wpos.Y;
+ return true;
+ }
+ x = y = 0;
+ return false;
+ }
+
+ public int GetChildWidth (Widget w)
+ {
+ // ResizeChildren ();
+ if (placement == Placement.Top || placement == Placement.Bottom)
+ return w.Allocation.Width;
+ else
+ return w.Allocation.Height;
+ }
+
+ public int GetChildHeight (Widget w)
+ {
+ if (placement == Placement.Top || placement == Placement.Bottom)
+ return w.Allocation.Height;
+ else
+ return w.Allocation.Width;
+ }
+
+ public int PanelWidth {
+ get {
+ if (placement == Placement.Top || placement == Placement.Bottom)
+ return Allocation.Width;
+ else
+ return Allocation.Height;
+ }
+ }
+
+ public void WindowToPanel (int x, int y, int w, int h, out int rx, out int ry)
+ {
+ switch (placement) {
+ case Placement.Top:
+ rx = x - Allocation.X;
+ ry = y - Allocation.Y;
+ break;
+ case Placement.Bottom:
+ rx = x - Allocation.X;
+ ry = Allocation.Bottom - y - h - 1;
+ break;
+ case Placement.Left:
+ rx = y - Allocation.Y;
+ ry = x - Allocation.X;
+ break;
+ default:
+ rx = y - Allocation.Y;
+ ry = Allocation.Right - x - w - 1;
+ break;
+ }
+ }
+
+ public void PanelToWindow (int x, int y, int w, int h, out int rx, out int ry, out int rw, out int rh)
+ {
+ switch (placement) {
+ case Placement.Top:
+ rx = x + Allocation.X;
+ ry = y + Allocation.Y;
+ rw = w;
+ rh = h;
+ break;
+ case Placement.Bottom:
+ rx = x + Allocation.X;
+ ry = Allocation.Bottom - y - h - 1;
+ rw = w;
+ rh = h;
+ break;
+ case Placement.Left:
+ rx = y + Allocation.X;
+ ry = x + Allocation.Y;
+ rw = h;
+ rh = w;
+ break;
+ default:
+ rx = Allocation.Right - y - h - 1;
+ ry = x + Allocation.Y;
+ rw = h;
+ rh = w;
+ break;
+ }
+ }
+
+ protected override void OnAdded (Widget w)
+ {
+ Put (w, 0, 0);
+ }
+
+ protected override void OnRemoved (Widget w)
+ {
+ int i = GetWidgetPosition (w);
+ if (i != -1) {
+ widgets.RemoveAt (i);
+ w.Unparent ();
+ QueueResize ();
+ }
+ }
+
+ int GetWidgetPosition (Widget w)
+ {
+ for (int n=0; n<widgets.Count; n++)
+ if (((WidgetPosition)widgets[n]).Widget == w)
+ return n;
+ return -1;
+ }
+
+ protected override void OnSizeRequested (ref Requisition req)
+ {
+ req.Width = req.Height = 0;
+ foreach (WidgetPosition pos in widgets) {
+ Requisition wreq = pos.Widget.SizeRequest ();
+ if (placement == Placement.Top || placement == Placement.Bottom) {
+ if (pos.X + wreq.Width > req.Width)
+ req.Width = pos.X + wreq.Width;
+ if (pos.Y + wreq.Height > req.Height)
+ req.Height = pos.Y + wreq.Height;
+ } else {
+ if (pos.Y + wreq.Width > req.Width)
+ req.Width = pos.Y + wreq.Width;
+ if (pos.X + wreq.Height > req.Height)
+ req.Height = pos.X + wreq.Height;
+ }
+ }
+ if (placement == Placement.Top || placement == Placement.Bottom)
+ req.Width = 0;
+ else
+ req.Height = 0;
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle rect)
+ {
+ base.OnSizeAllocated (rect);
+ foreach (WidgetPosition pos in widgets) {
+ Requisition req = pos.Widget.ChildRequisition;
+ Rectangle crect = new Rectangle (pos.X, pos.Y, req.Width, req.Height);
+ switch (placement) {
+ case Placement.Top:
+ break;
+ case Placement.Bottom:
+ crect.Y = Allocation.Height - crect.Y - crect.Height;
+ break;
+ case Placement.Left: {
+ int t = crect.X; crect.X=crect.Y; crect.Y=t;
+ break;
+ }
+ case Placement.Right: {
+ int t = crect.X; crect.X=crect.Y; crect.Y=t;
+ crect.X = Allocation.Width - crect.X - crect.Width;
+ break;
+ }
+ }
+ crect.X += Allocation.X;
+ crect.Y += Allocation.Y;
+ pos.Widget.SizeAllocate (crect);
+ }
+ }
+
+ protected override void ForAll (bool include_internals, Gtk.Callback callback)
+ {
+ WidgetPosition[] positions = (WidgetPosition[]) widgets.ToArray (typeof (WidgetPosition));
+ foreach (WidgetPosition pos in positions)
+ callback (pos.Widget);
+ }
+ }
+
+ class WidgetPosition
+ {
+ public int X;
+ public int Y;
+ public Widget Widget;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingDock.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingDock.cs
new file mode 100644
index 0000000000..2cf64c9985
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingDock.cs
@@ -0,0 +1,65 @@
+//
+// FloatingDock.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 Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ internal class FloatingDock: Gtk.Window
+ {
+ DockToolbar bar;
+
+ public FloatingDock (DockToolbarFrame frame): base (Gtk.WindowType.Toplevel)
+ {
+ SkipTaskbarHint = true;
+ Decorated = false;
+ TransientFor = frame.TopWindow;
+ }
+
+ public void Attach (DockToolbar bar)
+ {
+ this.bar = bar;
+ bar.FloatingDock = this;
+ Frame f = new Frame ();
+ f.Shadow = ShadowType.Out;
+ f.Add (bar);
+ Add (f);
+ f.Show ();
+ bar.Show ();
+ Show ();
+ }
+
+ public void Detach ()
+ {
+ bar.FloatingDock = null;
+ ((Frame)bar.Parent).Remove (bar);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingPosition.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingPosition.cs
new file mode 100644
index 0000000000..56261d72e8
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FloatingPosition.cs
@@ -0,0 +1,75 @@
+//
+// FloatingPosition.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 Gtk;
+using System.Xml.Serialization;
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ [XmlType ("floatingPosition")]
+ public class FloatingPosition: DockToolbarPosition
+ {
+ Orientation orientation;
+ int x;
+ int y;
+
+ public FloatingPosition ()
+ {
+ }
+
+ internal FloatingPosition (DockToolbar bar)
+ {
+ orientation = bar.Orientation;
+ bar.FloatingDock.GetPosition (out x, out y);
+ }
+
+ [XmlAttribute ("x")]
+ public int X {
+ get { return x; }
+ set { x = value; }
+ }
+
+ [XmlAttribute ("y")]
+ public int Y {
+ get { return y; }
+ set { y = value; }
+ }
+
+ [XmlAttribute ("orientation")]
+ public Orientation Orientation {
+ get { return orientation; }
+ set { orientation = value; }
+ }
+
+ internal override void RestorePosition (DockToolbarFrame frame, DockToolbar bar)
+ {
+ frame.FloatBar (bar, orientation, x, y);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/IDockBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/IDockBar.cs
new file mode 100644
index 0000000000..121347720d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/IDockBar.cs
@@ -0,0 +1,37 @@
+//
+// IDockToolbar.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.
+//
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ public interface IDockToolbar
+ {
+ string Id { get; }
+ string Title { get; }
+ bool Visible { get; set; }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/PlaceholderWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/PlaceholderWindow.cs
new file mode 100644
index 0000000000..9ed4fd72de
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/PlaceholderWindow.cs
@@ -0,0 +1,55 @@
+//
+// PlaceholderWindow.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.
+//
+
+
+namespace MonoDevelop.Components.DockToolbars
+{
+ internal class PlaceholderWindow: Gtk.Window
+ {
+ Gdk.GC redgc;
+
+ public PlaceholderWindow (DockToolbarFrame frame): base (Gtk.WindowType.Toplevel)
+ {
+ SkipTaskbarHint = true;
+ Decorated = false;
+ TransientFor = frame.TopWindow;
+ Realize ();
+ redgc = new Gdk.GC (GdkWindow);
+ redgc.RgbFgColor = new Gdk.Color (255, 0, 0);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose args)
+ {
+ base.OnExposeEvent (args);
+ int w, h;
+ this.GetSize (out w, out h);
+ this.GdkWindow.DrawRectangle (redgc, false, 0, 0, w-1, h-1);
+ return true;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs
new file mode 100644
index 0000000000..2b181c0c96
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs
@@ -0,0 +1,403 @@
+//
+// AutoHideBox.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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;
+using Gdk;
+
+namespace MonoDevelop.Components.Docking
+{
+ class AutoHideBox: DockFrameTopLevel
+ {
+ static Gdk.Cursor resizeCursorW = new Gdk.Cursor (Gdk.CursorType.SbHDoubleArrow);
+ static Gdk.Cursor resizeCursorH = new Gdk.Cursor (Gdk.CursorType.SbVDoubleArrow);
+
+ bool resizing;
+ int resizePos;
+ int origSize;
+ int origPos;
+ bool horiz;
+ bool startPos;
+ DockFrame frame;
+ bool animating;
+ int targetSize;
+ int targetPos;
+ ScrollableContainer scrollable;
+ Gtk.PositionType position;
+ bool disposed;
+ bool insideGrip;
+
+ const int gripSize = 8;
+
+ public AutoHideBox (DockFrame frame, DockItem item, Gtk.PositionType pos, int size)
+ {
+ this.position = pos;
+ this.frame = frame;
+ this.targetSize = size;
+ horiz = pos == PositionType.Left || pos == PositionType.Right;
+ startPos = pos == PositionType.Top || pos == PositionType.Left;
+ Events = Events | Gdk.EventMask.EnterNotifyMask | Gdk.EventMask.LeaveNotifyMask;
+
+ Box fr;
+ CustomFrame cframe = new CustomFrame ();
+ switch (pos) {
+ case PositionType.Left: cframe.SetMargins (1, 1, 0, 1); break;
+ case PositionType.Right: cframe.SetMargins (1, 1, 1, 0); break;
+ case PositionType.Top: cframe.SetMargins (0, 1, 1, 1); break;
+ case PositionType.Bottom: cframe.SetMargins (1, 0, 1, 1); break;
+ }
+ EventBox sepBox = new EventBox ();
+ cframe.Add (sepBox);
+
+ if (horiz) {
+ fr = new HBox ();
+ sepBox.Realized += delegate { sepBox.GdkWindow.Cursor = resizeCursorW; };
+ sepBox.WidthRequest = gripSize;
+ } else {
+ fr = new VBox ();
+ sepBox.Realized += delegate { sepBox.GdkWindow.Cursor = resizeCursorH; };
+ sepBox.HeightRequest = gripSize;
+ }
+
+ sepBox.Events = EventMask.AllEventsMask;
+
+ if (pos == PositionType.Left || pos == PositionType.Top)
+ fr.PackEnd (cframe, false, false, 0);
+ else
+ fr.PackStart (cframe, false, false, 0);
+
+ Add (fr);
+ ShowAll ();
+ Hide ();
+
+ scrollable = new ScrollableContainer ();
+ scrollable.ScrollMode = false;
+ scrollable.Show ();
+
+ item.Widget.Show ();
+ scrollable.Add (item.Widget);
+ fr.PackStart (scrollable, true, true, 0);
+
+ sepBox.ButtonPressEvent += OnSizeButtonPress;
+ sepBox.ButtonReleaseEvent += OnSizeButtonRelease;
+ sepBox.MotionNotifyEvent += OnSizeMotion;
+ sepBox.ExposeEvent += OnGripExpose;
+ sepBox.EnterNotifyEvent += delegate { insideGrip = true; sepBox.QueueDraw (); };
+ sepBox.LeaveNotifyEvent += delegate { insideGrip = false; sepBox.QueueDraw (); };
+ }
+
+ public bool Disposed {
+ get { return disposed; }
+ set { disposed = value; }
+ }
+
+ public void AnimateShow ()
+ {
+ animating = true;
+ scrollable.ScrollMode = true;
+ scrollable.SetSize (position, targetSize);
+
+ switch (position) {
+ case PositionType.Left:
+ WidthRequest = 0;
+ break;
+ case PositionType.Right:
+ targetPos = X = X + WidthRequest;
+ WidthRequest = 0;
+ break;
+ case PositionType.Top:
+ HeightRequest = 0;
+ break;
+ case PositionType.Bottom:
+ targetPos = Y = Y + HeightRequest;
+ HeightRequest = 0;
+ break;
+ }
+ Show ();
+ GLib.Timeout.Add (10, RunAnimateShow);
+ }
+
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ }
+
+
+ public void AnimateHide ()
+ {
+ animating = true;
+ scrollable.ScrollMode = true;
+ scrollable.SetSize (position, targetSize);
+ GLib.Timeout.Add (10, RunAnimateHide);
+ }
+
+ bool RunAnimateShow ()
+ {
+ if (!animating)
+ return false;
+
+ switch (position) {
+ case PositionType.Left:
+ WidthRequest += 1 + (targetSize - WidthRequest) / 3;
+ if (WidthRequest < targetSize)
+ return true;
+ break;
+ case PositionType.Right:
+ WidthRequest += 1 + (targetSize - WidthRequest) / 3;
+ X = targetPos - WidthRequest;
+ if (WidthRequest < targetSize)
+ return true;
+ break;
+ case PositionType.Top:
+ HeightRequest += 1 + (targetSize - HeightRequest) / 3;
+ if (HeightRequest < targetSize)
+ return true;
+ break;
+ case PositionType.Bottom:
+ HeightRequest += 1 + (targetSize - HeightRequest) / 3;
+ Y = targetPos - HeightRequest;
+ if (HeightRequest < targetSize)
+ return true;
+ break;
+ }
+
+ scrollable.ScrollMode = false;
+ if (horiz)
+ WidthRequest = targetSize;
+ else
+ HeightRequest = targetSize;
+ animating = false;
+ return false;
+ }
+
+ bool RunAnimateHide ()
+ {
+ if (!animating)
+ return false;
+
+ switch (position) {
+ case PositionType.Left: {
+ int ns = WidthRequest - 1 - WidthRequest / 3;
+ if (ns > 0) {
+ WidthRequest = ns;
+ return true;
+ }
+ break;
+ }
+ case PositionType.Right: {
+ int ns = WidthRequest - 1 - WidthRequest / 3;
+ if (ns > 0) {
+ WidthRequest = ns;
+ X = targetPos - ns;
+ return true;
+ }
+ break;
+ }
+ case PositionType.Top: {
+ int ns = HeightRequest - 1 - HeightRequest / 3;
+ if (ns > 0) {
+ HeightRequest = ns;
+ return true;
+ }
+ break;
+ }
+ case PositionType.Bottom: {
+ int ns = HeightRequest - 1 - HeightRequest / 3;
+ if (ns > 0) {
+ HeightRequest = ns;
+ Y = targetPos - ns;
+ return true;
+ }
+ break;
+ }
+ }
+
+ Hide ();
+ animating = false;
+ return false;
+ }
+
+ protected override void OnHidden ()
+ {
+ base.OnHidden ();
+ animating = false;
+ }
+
+
+ public int Size {
+ get {
+ return horiz ? WidthRequest : HeightRequest;
+ }
+ }
+
+ void OnSizeButtonPress (object ob, Gtk.ButtonPressEventArgs args)
+ {
+ if (args.Event.Button == 1 && !animating) {
+ int n;
+ if (horiz) {
+ Toplevel.GetPointer (out resizePos, out n);
+ origSize = WidthRequest;
+ if (!startPos) {
+ origPos = X + origSize;
+ }
+ } else {
+ Toplevel.GetPointer (out n, out resizePos);
+ origSize = HeightRequest;
+ if (!startPos) {
+ origPos = Y + origSize;
+ }
+ }
+ resizing = true;
+ }
+ }
+
+ void OnSizeButtonRelease (object ob, Gtk.ButtonReleaseEventArgs args)
+ {
+ resizing = false;
+ }
+
+ void OnSizeMotion (object ob, Gtk.MotionNotifyEventArgs args)
+ {
+ if (resizing) {
+ int newPos, n;
+ if (horiz) {
+ Toplevel.GetPointer (out newPos, out n);
+ int diff = startPos ? (newPos - resizePos) : (resizePos - newPos);
+ int newSize = origSize + diff;
+ if (newSize < Child.SizeRequest ().Width)
+ newSize = Child.SizeRequest ().Width;
+ if (!startPos) {
+ X = origPos - newSize;
+ }
+ WidthRequest = newSize;
+ } else {
+ Toplevel.GetPointer (out n, out newPos);
+ int diff = startPos ? (newPos - resizePos) : (resizePos - newPos);
+ int newSize = origSize + diff;
+ if (newSize < Child.SizeRequest ().Height)
+ newSize = Child.SizeRequest ().Height;
+ if (!startPos) {
+ Y = origPos - newSize;
+ }
+ HeightRequest = newSize;
+ }
+ frame.QueueResize ();
+ }
+ }
+
+ void OnGripExpose (object ob, Gtk.ExposeEventArgs args)
+ {
+ EventBox w = (EventBox) ob;
+ Gdk.Rectangle handleRect = w.Allocation;
+// w.GdkWindow.DrawRectangle (w.Style.DarkGC (StateType.Normal), true, handleRect);
+ handleRect.X = handleRect.Y = 0;
+
+/* switch (position) {
+ case PositionType.Top:
+ handleRect.Height -= 4; handleRect.Y += 1;
+ Gtk.Style.PaintHline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Width, gripSize - 2);
+ break;
+ case PositionType.Bottom:
+ handleRect.Height -= 4; handleRect.Y += 3;
+ Gtk.Style.PaintHline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Width, 0);
+ break;
+ case PositionType.Left:
+ handleRect.Width -= 4; handleRect.X += 1;
+ Gtk.Style.PaintVline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Height, gripSize - 2);
+ break;
+ case PositionType.Right:
+ handleRect.Width -= 4; handleRect.X += 3;
+ Gtk.Style.PaintVline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Height, 0);
+ break;
+ }*/
+
+ Orientation or = horiz ? Orientation.Vertical : Orientation.Horizontal;
+ StateType s = insideGrip ? StateType.Prelight : StateType.Normal;
+ Gtk.Style.PaintHandle (w.Style, w.GdkWindow, s, ShadowType.None, args.Event.Area, w, "paned", handleRect.Left, handleRect.Top, handleRect.Width, handleRect.Height, or);
+ }
+ }
+
+ class ScrollableContainer: EventBox
+ {
+ PositionType expandPos;
+ bool scrollMode;
+ int targetSize;
+
+ public bool ScrollMode {
+ get {
+ return scrollMode;
+ }
+ set {
+ scrollMode = value;
+ QueueResize ();
+ }
+ }
+
+ public void SetSize (PositionType expandPosition, int targetSize)
+ {
+ this.expandPos = expandPosition;
+ this.targetSize = targetSize;
+ QueueResize ();
+ }
+
+ protected override void OnSizeRequested (ref Requisition req)
+ {
+ base.OnSizeRequested (ref req);
+ if (scrollMode || Child == null) {
+ req.Width = 0;
+ req.Height = 0;
+ }
+ else
+ req = Child.SizeRequest ();
+ }
+
+ protected override void OnSizeAllocated (Rectangle alloc)
+ {
+ if (scrollMode && Child != null) {
+ switch (expandPos) {
+ case PositionType.Bottom:
+ alloc = new Rectangle (alloc.X, alloc.Y, alloc.Width, targetSize);
+ break;
+ case PositionType.Top:
+ alloc = new Rectangle (alloc.X, alloc.Y - targetSize + alloc.Height, alloc.Width, targetSize);
+ break;
+ case PositionType.Right:
+ alloc = new Rectangle (alloc.X, alloc.Y, targetSize, alloc.Height);
+ break;
+ case PositionType.Left:
+ alloc = new Rectangle (alloc.X - targetSize + alloc.Width, alloc.Y, targetSize, alloc.Height);
+ break;
+ }
+ }
+ base.OnSizeAllocated (alloc);
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs
new file mode 100644
index 0000000000..d62ab5dd1c
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs
@@ -0,0 +1,176 @@
+//
+// DockBar.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Components.Docking
+{
+ public class DockBar: Gtk.EventBox
+ {
+ Gtk.PositionType position;
+ Box box;
+ DockFrame frame;
+ Label filler;
+ bool alwaysVisible;
+
+ internal DockBar (DockFrame frame, Gtk.PositionType position)
+ {
+ frame.ShadedContainer.Add (this);
+ VisibleWindow = false;
+ this.frame = frame;
+ this.position = position;
+ Gtk.Alignment al = new Alignment (0,0,0,0);
+ if (Orientation == Gtk.Orientation.Horizontal)
+ box = new HBox ();
+ else
+ box = new VBox ();
+
+ uint sizePadding = 1;
+ uint startPadding = 6;
+ switch (Frame.CompactGuiLevel) {
+ case 1: sizePadding = 2; break;
+ case 4: startPadding = 3; break;
+ case 5: startPadding = 0; sizePadding = 0; break;
+ }
+
+ switch (position) {
+ case PositionType.Top: al.BottomPadding = sizePadding; al.LeftPadding = al.RightPadding = startPadding; break;
+ case PositionType.Bottom: al.TopPadding = sizePadding; al.LeftPadding = al.RightPadding = startPadding; break;
+ case PositionType.Left: al.RightPadding = sizePadding; al.TopPadding = al.BottomPadding = startPadding; break;
+ case PositionType.Right: al.LeftPadding = sizePadding; al.TopPadding = al.BottomPadding = startPadding; break;
+ }
+
+ box.Spacing = 3;
+ al.Add (box);
+ Add (al);
+
+ filler = new Label ();
+ filler.WidthRequest = 4;
+ filler.HeightRequest = 4;
+ box.PackEnd (filler);
+
+ ShowAll ();
+ UpdateVisibility ();
+ }
+
+ public bool IsExtracted {
+ get { return OriginalBar != null; }
+ }
+
+ internal DockBar OriginalBar { get; set; }
+
+ public bool AlwaysVisible {
+ get { return this.alwaysVisible; }
+ set { this.alwaysVisible = value; UpdateVisibility (); }
+ }
+
+
+ internal Gtk.Orientation Orientation {
+ get {
+ return (position == PositionType.Left || position == PositionType.Right) ? Gtk.Orientation.Vertical : Gtk.Orientation.Horizontal;
+ }
+ }
+
+ internal Gtk.PositionType Position {
+ get {
+ return position;
+ }
+ }
+
+ internal DockFrame Frame {
+ get {
+ return frame;
+ }
+ }
+
+ internal DockBarItem AddItem (DockItem item, int size)
+ {
+ DockBarItem it = new DockBarItem (this, item, size);
+ box.PackStart (it, false, false, 0);
+ it.ShowAll ();
+ UpdateVisibility ();
+ it.Shown += OnItemVisibilityChanged;
+ it.Hidden += OnItemVisibilityChanged;
+ return it;
+ }
+
+ void OnItemVisibilityChanged (object o, EventArgs args)
+ {
+ UpdateVisibility ();
+ }
+
+ internal void OnCompactLevelChanged ()
+ {
+ UpdateVisibility ();
+ if (OriginalBar != null)
+ OriginalBar.UpdateVisibility ();
+ }
+
+ internal void UpdateVisibility ()
+ {
+ filler.Visible = (Frame.CompactGuiLevel < 3);
+ int visibleCount = 0;
+ foreach (Gtk.Widget w in box.Children) {
+ if (w.Visible)
+ visibleCount++;
+ }
+ Visible = alwaysVisible || filler.Visible || visibleCount > 0;
+ }
+
+ internal void RemoveItem (DockBarItem it)
+ {
+ box.Remove (it);
+ it.Shown -= OnItemVisibilityChanged;
+ it.Hidden -= OnItemVisibilityChanged;
+ UpdateVisibility ();
+ }
+
+ internal void UpdateTitle (DockItem item)
+ {
+ foreach (Widget w in box.Children) {
+ DockBarItem it = w as DockBarItem;
+ if (it != null && it.DockItem == item) {
+ it.UpdateTab ();
+ break;
+ }
+ }
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ frame.ShadedContainer.DrawBackground (this);
+ return base.OnExposeEvent (evnt);
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs
new file mode 100644
index 0000000000..b7354ad4b5
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs
@@ -0,0 +1,373 @@
+//
+// DockBarItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Docking
+{
+ class DockBarItem: EventBox
+ {
+ DockBar bar;
+ DockItem it;
+ Box box;
+ Label label;
+ Alignment mainBox;
+ AutoHideBox autoShowFrame;
+ AutoHideBox hiddenFrame;
+ uint autoShowTimeout = uint.MaxValue;
+ uint autoHideTimeout = uint.MaxValue;
+ int size;
+
+ public DockBarItem (DockBar bar, DockItem it, int size)
+ {
+ Events = Events | Gdk.EventMask.EnterNotifyMask | Gdk.EventMask.LeaveNotifyMask;
+ this.size = size;
+ this.bar = bar;
+ this.it = it;
+ VisibleWindow = false;
+ UpdateTab ();
+ }
+
+ public void Close ()
+ {
+ UnscheduleAutoShow ();
+ UnscheduleAutoHide ();
+ AutoHide (false);
+ bar.RemoveItem (this);
+ Destroy ();
+ }
+
+ public int Size {
+ get { return size; }
+ set { size = value; }
+ }
+
+ public void UpdateTab ()
+ {
+ if (Child != null) {
+ Widget w = Child;
+ Remove (w);
+ w.Destroy ();
+ }
+
+ mainBox = new Alignment (0,0,1,1);
+ if (bar.Orientation == Gtk.Orientation.Horizontal) {
+ box = new HBox ();
+ mainBox.LeftPadding = mainBox.RightPadding = 2;
+ }
+ else {
+ box = new VBox ();
+ mainBox.TopPadding = mainBox.BottomPadding = 2;
+ }
+
+ Gtk.Widget customLabel = null;
+ if (it.DockLabelProvider != null)
+ customLabel = it.DockLabelProvider.CreateLabel (bar.Orientation);
+
+ if (customLabel != null) {
+ customLabel.ShowAll ();
+ box.PackStart (customLabel, true, true, 0);
+ }
+ else {
+ if (it.Icon != null)
+ box.PackStart (new Gtk.Image (it.Icon), false, false, 0);
+
+ if (!string.IsNullOrEmpty (it.Label)) {
+ label = new Gtk.Label (it.Label);
+ label.UseMarkup = true;
+ if (bar.Orientation == Gtk.Orientation.Vertical)
+ label.Angle = 270;
+ box.PackStart (label, true, true, 0);
+ } else
+ label = null;
+ }
+
+ box.BorderWidth = 2;
+ box.Spacing = 2;
+ mainBox.Add (box);
+ mainBox.ShowAll ();
+ Add (mainBox);
+ SetNormalColor ();
+ }
+
+ public MonoDevelop.Components.Docking.DockItem DockItem {
+ get {
+ return it;
+ }
+ }
+
+ protected override void OnHidden ()
+ {
+ base.OnHidden ();
+ UnscheduleAutoShow ();
+ UnscheduleAutoHide ();
+ AutoHide (false);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ if (State == StateType.Prelight) {
+ int w = Allocation.Width, h = Allocation.Height;
+ double x=Allocation.Left, y=Allocation.Top, r=3;
+ x += 0.5; y += 0.5; h -=1; w -= 1;
+
+ using (Cairo.Context ctx = Gdk.CairoHelper.Create (GdkWindow)) {
+ HslColor c = new HslColor (Style.Background (Gtk.StateType.Normal));
+ HslColor c1 = c;
+ HslColor c2 = c;
+ if (State != StateType.Prelight) {
+ c1.L *= 0.8;
+ c2.L *= 0.95;
+ } else {
+ c1.L *= 1.1;
+ c2.L *= 1;
+ }
+ Cairo.Gradient pat;
+ switch (bar.Position) {
+ case PositionType.Top: pat = new Cairo.LinearGradient (x, y, x, y+h); break;
+ case PositionType.Bottom: pat = new Cairo.LinearGradient (x, y, x, y+h); break;
+ case PositionType.Left: pat = new Cairo.LinearGradient (x+w, y, x, y); break;
+ default: pat = new Cairo.LinearGradient (x, y, x+w, y); break;
+ }
+ pat.AddColorStop (0, c1);
+ pat.AddColorStop (1, c2);
+ ctx.NewPath ();
+ ctx.Arc (x+r, y+r, r, 180 * (Math.PI / 180), 270 * (Math.PI / 180));
+ ctx.LineTo (x+w-r, y);
+ ctx.Arc (x+w-r, y+r, r, 270 * (Math.PI / 180), 360 * (Math.PI / 180));
+ ctx.LineTo (x+w, y+h);
+ ctx.LineTo (x, y+h);
+ ctx.ClosePath ();
+ ctx.Pattern = pat;
+ ctx.FillPreserve ();
+ c1 = c;
+ c1.L *= 0.7;
+ ctx.LineWidth = 1;
+ ctx.Color = c1;
+ ctx.Stroke ();
+
+ // Inner line
+ ctx.NewPath ();
+ ctx.Arc (x+r+1, y+r+1, r, 180 * (Math.PI / 180), 270 * (Math.PI / 180));
+ ctx.LineTo (x+w-r-1, y+1);
+ ctx.Arc (x+w-r-1, y+r+1, r, 270 * (Math.PI / 180), 360 * (Math.PI / 180));
+ ctx.LineTo (x+w-1, y+h-1);
+ ctx.LineTo (x+1, y+h-1);
+ ctx.ClosePath ();
+ c1 = c;
+ //c1.L *= 0.9;
+ ctx.LineWidth = 1;
+ ctx.Color = c1;
+ ctx.Stroke ();
+ }
+ }
+
+ bool res = base.OnExposeEvent (evnt);
+ return res;
+ }
+
+ public void Present (bool giveFocus)
+ {
+ AutoShow ();
+ if (giveFocus) {
+ GLib.Timeout.Add (200, delegate {
+ // Using a small delay because AutoShow uses an animation and setting focus may
+ // not work until the item is visible
+ it.SetFocus ();
+ ScheduleAutoHide (false);
+ return false;
+ });
+ }
+ }
+
+ void AutoShow ()
+ {
+ UnscheduleAutoHide ();
+ if (autoShowFrame == null) {
+ if (hiddenFrame != null)
+ bar.Frame.AutoHide (it, hiddenFrame, false);
+ autoShowFrame = bar.Frame.AutoShow (it, bar, size);
+ autoShowFrame.EnterNotifyEvent += OnFrameEnter;
+ autoShowFrame.LeaveNotifyEvent += OnFrameLeave;
+ autoShowFrame.KeyPressEvent += OnFrameKeyPress;
+ SetPrelight ();
+ }
+ }
+
+ void AutoHide (bool animate)
+ {
+ UnscheduleAutoShow ();
+ if (autoShowFrame != null) {
+ size = autoShowFrame.Size;
+ hiddenFrame = autoShowFrame;
+ autoShowFrame.Hidden += delegate {
+ hiddenFrame = null;
+ };
+ bar.Frame.AutoHide (it, autoShowFrame, animate);
+ autoShowFrame.EnterNotifyEvent -= OnFrameEnter;
+ autoShowFrame.LeaveNotifyEvent -= OnFrameLeave;
+ autoShowFrame.KeyPressEvent -= OnFrameKeyPress;
+ autoShowFrame = null;
+ UnsetPrelight ();
+ }
+ }
+
+ void ScheduleAutoShow ()
+ {
+ UnscheduleAutoHide ();
+ if (autoShowTimeout == uint.MaxValue) {
+ autoShowTimeout = GLib.Timeout.Add (bar.Frame.AutoShowDelay, delegate {
+ autoShowTimeout = uint.MaxValue;
+ AutoShow ();
+ return false;
+ });
+ }
+ }
+
+ void ScheduleAutoHide (bool cancelAutoShow)
+ {
+ ScheduleAutoHide (cancelAutoShow, false);
+ }
+
+ void ScheduleAutoHide (bool cancelAutoShow, bool force)
+ {
+ if (cancelAutoShow)
+ UnscheduleAutoShow ();
+ if (force)
+ it.Widget.FocusChild = null;
+ if (autoHideTimeout == uint.MaxValue) {
+ autoHideTimeout = GLib.Timeout.Add (force ? 0 : bar.Frame.AutoHideDelay, delegate {
+ // Don't hide the item if it has the focus. Try again later.
+ if (it.Widget.FocusChild != null)
+ return true;
+ autoHideTimeout = uint.MaxValue;
+ AutoHide (true);
+ return false;
+ });
+ }
+ }
+
+ void UnscheduleAutoShow ()
+ {
+ if (autoShowTimeout != uint.MaxValue) {
+ GLib.Source.Remove (autoShowTimeout);
+ autoShowTimeout = uint.MaxValue;
+ }
+ }
+
+ void UnscheduleAutoHide ()
+ {
+ if (autoHideTimeout != uint.MaxValue) {
+ GLib.Source.Remove (autoHideTimeout);
+ autoHideTimeout = uint.MaxValue;
+ }
+ }
+
+ protected override bool OnEnterNotifyEvent (Gdk.EventCrossing evnt)
+ {
+ ScheduleAutoShow ();
+ SetPrelight ();
+ return base.OnEnterNotifyEvent (evnt);
+ }
+
+ protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing evnt)
+ {
+ ScheduleAutoHide (true);
+ if (autoShowFrame == null)
+ UnsetPrelight ();
+ return base.OnLeaveNotifyEvent (evnt);
+ }
+
+ void SetPrelight ()
+ {
+ if (State != StateType.Prelight) {
+ State = StateType.Prelight;
+ if (label != null)
+ label.ModifyFg (StateType.Normal, Style.Foreground (Gtk.StateType.Normal));
+ }
+ }
+
+ void UnsetPrelight ()
+ {
+ if (State == StateType.Prelight) {
+ State = StateType.Normal;
+ SetNormalColor ();
+ }
+ }
+
+ protected override void OnRealized ()
+ {
+ base.OnRealized();
+ SetNormalColor ();
+ }
+
+
+ void SetNormalColor ()
+ {
+ if (label != null) {
+ HslColor c = Style.Background (Gtk.StateType.Normal);
+ c.L *= 0.4;
+ label.ModifyFg (StateType.Normal, c);
+ }
+ }
+
+ void OnFrameEnter (object s, Gtk.EnterNotifyEventArgs args)
+ {
+ AutoShow ();
+ }
+
+ void OnFrameKeyPress (object s, Gtk.KeyPressEventArgs args)
+ {
+ if (args.Event.Key == Gdk.Key.Escape)
+ ScheduleAutoHide (true, true);
+ }
+
+ void OnFrameLeave (object s, Gtk.LeaveNotifyEventArgs args)
+ {
+ if (args.Event.Detail != Gdk.NotifyType.Inferior)
+ ScheduleAutoHide (true);
+ }
+
+ protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
+ {
+ if (evnt.Button == 1) {
+ if (evnt.Type == Gdk.EventType.TwoButtonPress)
+ it.Status = DockItemStatus.Dockable;
+ else
+ AutoShow ();
+ }
+ else if (evnt.Button == 3)
+ it.ShowDockPopupMenu (evnt.Time);
+ return base.OnButtonPressEvent (evnt);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs
new file mode 100644
index 0000000000..cba733da8e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs
@@ -0,0 +1,450 @@
+//
+// DockContainer.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Generic;
+using Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.Docking
+{
+ class DockContainer: Container, IShadedWidget
+ {
+ DockLayout layout;
+ DockFrame frame;
+
+ List<TabStrip> notebooks = new List<TabStrip> ();
+ List<DockItem> items = new List<DockItem> ();
+
+ bool needsRelayout = true;
+
+ DockGroup currentHandleGrp;
+ int currentHandleIndex;
+ bool dragging;
+ int dragPos;
+ int dragSize;
+
+ PlaceholderWindow placeholderWindow;
+
+ static Gdk.Cursor hresizeCursor = new Gdk.Cursor (CursorType.SbHDoubleArrow);
+ static Gdk.Cursor vresizeCursor = new Gdk.Cursor (CursorType.SbVDoubleArrow);
+
+ public DockContainer (DockFrame frame)
+ {
+ this.Events = EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask | EventMask.LeaveNotifyMask;
+ this.frame = frame;
+ frame.ShadedContainer.Add (this);
+ }
+
+ internal DockGroupItem FindDockGroupItem (string id)
+ {
+ if (layout == null)
+ return null;
+ else
+ return layout.FindDockGroupItem (id);
+ }
+
+ public List<DockItem> Items {
+ get { return items; }
+ }
+
+ public DockLayout Layout {
+ get { return layout; }
+ set { layout = value; }
+ }
+
+ public void Clear ()
+ {
+ layout = null;
+ }
+
+ public void LoadLayout (DockLayout dl)
+ {
+ // Sticky items currently selected in notebooks will remain
+ // selected after switching the layout
+ List<DockItem> sickyOnTop = new List<DockItem> ();
+ foreach (DockItem it in items) {
+ if ((it.Behavior & DockItemBehavior.Sticky) != 0) {
+ DockGroupItem gitem = FindDockGroupItem (it.Id);
+ if (gitem != null && gitem.ParentGroup.IsSelectedPage (it))
+ sickyOnTop.Add (it);
+ }
+ }
+
+ if (layout != null)
+ layout.StoreAllocation ();
+ layout = dl;
+ layout.RestoreAllocation ();
+
+ // Make sure items not present in this layout are hidden
+ foreach (DockItem it in items) {
+ if ((it.Behavior & DockItemBehavior.Sticky) != 0)
+ it.Visible = it.StickyVisible;
+ if (layout.FindDockGroupItem (it.Id) == null)
+ it.HideWidget ();
+ }
+
+ RelayoutWidgets ();
+
+ foreach (DockItem it in sickyOnTop)
+ it.Present (false);
+ }
+
+ public void StoreAllocation ()
+ {
+ if (layout != null)
+ layout.StoreAllocation ();
+ }
+
+ protected override void OnSizeRequested (ref Requisition req)
+ {
+ if (layout != null) {
+ LayoutWidgets ();
+ req = layout.SizeRequest ();
+ }
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle rect)
+ {
+ base.OnSizeAllocated (rect);
+ if (layout == null)
+ return;
+
+ // This container has its own window, so allocation of children
+ // is relative to 0,0
+ rect.X = rect.Y = 0;
+ LayoutWidgets ();
+ layout.Size = -1;
+ layout.SizeAllocate (rect);
+ }
+
+ protected override void ForAll (bool include_internals, Gtk.Callback callback)
+ {
+ List<Widget> widgets = new List<Widget> ();
+ foreach (Widget w in notebooks)
+ widgets.Add (w);
+ foreach (DockItem it in items) {
+ if (it.HasWidget && it.Widget.Parent == this)
+ widgets.Add (it.Widget);
+ }
+ foreach (Widget w in widgets)
+ callback (w);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ bool res = base.OnExposeEvent (evnt);
+
+ if (layout != null) {
+ layout.Draw (evnt.Area, currentHandleGrp, currentHandleIndex);
+ }
+ return res;
+ }
+
+
+ public void RelayoutWidgets ()
+ {
+ needsRelayout = true;
+ QueueResize ();
+ }
+
+ void LayoutWidgets ()
+ {
+ if (!needsRelayout)
+ return;
+ needsRelayout = false;
+
+ // Create the needed notebooks and place the widgets in there
+
+ List<DockGroup> tabbedGroups = new List<DockGroup> ();
+ GetTabbedGroups (layout, tabbedGroups);
+
+ for (int n=0; n<tabbedGroups.Count; n++) {
+ DockGroup grp = tabbedGroups [n];
+ TabStrip ts;
+ if (n < notebooks.Count) {
+ ts = notebooks [n];
+ }
+ else {
+ ts = new TabStrip (frame);
+ ts.Show ();
+ notebooks.Add (ts);
+ ts.Parent = this;
+ }
+ grp.UpdateNotebook (ts);
+ }
+
+ // Remove spare tab strips
+ for (int n = notebooks.Count - 1; n >= tabbedGroups.Count; n--) {
+ TabStrip ts = notebooks [n];
+ notebooks.RemoveAt (n);
+ ts.Clear ();
+ ts.Unparent ();
+ ts.Destroy ();
+ }
+
+ // Add widgets to the container
+
+ layout.LayoutWidgets ();
+ NotifySeparatorsChanged ();
+ }
+
+ void GetTabbedGroups (DockGroup grp, List<DockGroup> tabbedGroups)
+ {
+ if (grp.Type == DockGroupType.Tabbed) {
+ if (grp.VisibleObjects.Count > 1)
+ tabbedGroups.Add (grp);
+ else
+ grp.ResetNotebook ();
+ }
+ else {
+ // Make sure it doesn't have a notebook bound to it
+ grp.ResetNotebook ();
+ foreach (DockObject ob in grp.Objects) {
+ if (ob is DockGroup)
+ GetTabbedGroups ((DockGroup) ob, tabbedGroups);
+ }
+ }
+ }
+
+ protected override bool OnButtonPressEvent (Gdk.EventButton ev)
+ {
+ if (currentHandleGrp != null) {
+ dragging = true;
+ dragPos = (currentHandleGrp.Type == DockGroupType.Horizontal) ? (int)ev.X : (int)ev.Y;
+ DockObject obj = currentHandleGrp.VisibleObjects [currentHandleIndex];
+ dragSize = (currentHandleGrp.Type == DockGroupType.Horizontal) ? obj.Allocation.Width : obj.Allocation.Height;
+ }
+ return base.OnButtonPressEvent (ev);
+ }
+
+ protected override bool OnButtonReleaseEvent (Gdk.EventButton e)
+ {
+ dragging = false;
+ return base.OnButtonReleaseEvent (e);
+ }
+
+ protected override bool OnMotionNotifyEvent (Gdk.EventMotion e)
+ {
+ if (dragging) {
+ NotifySeparatorsChanged ();
+ int newpos = (currentHandleGrp.Type == DockGroupType.Horizontal) ? (int)e.X : (int)e.Y;
+ if (newpos != dragPos) {
+ int nsize = dragSize + (newpos - dragPos);
+ currentHandleGrp.ResizeItem (currentHandleIndex, nsize);
+ layout.DrawSeparators (Allocation, currentHandleGrp, currentHandleIndex, true, null);
+ }
+ }
+ else if (layout != null && placeholderWindow == null) {
+ int index;
+ DockGroup grp;
+ if (FindHandle (layout, (int)e.X, (int)e.Y, out grp, out index)) {
+ if (currentHandleGrp != grp || currentHandleIndex != index) {
+ if (grp.Type == DockGroupType.Horizontal)
+ this.GdkWindow.Cursor = hresizeCursor;
+ else
+ this.GdkWindow.Cursor = vresizeCursor;
+ currentHandleGrp = grp;
+ currentHandleIndex = index;
+ layout.DrawSeparators (Allocation, currentHandleGrp, currentHandleIndex, true, null);
+ }
+ }
+ else if (currentHandleGrp != null) {
+ ResetHandleHighlight ();
+ }
+ }
+ return base.OnMotionNotifyEvent (e);
+ }
+
+ void ResetHandleHighlight ()
+ {
+ this.GdkWindow.Cursor = null;
+ currentHandleGrp = null;
+ currentHandleIndex = -1;
+ if (layout != null)
+ layout.DrawSeparators (Allocation, null, -1, true, null);
+ }
+
+ protected override bool OnLeaveNotifyEvent (EventCrossing evnt)
+ {
+ if (!dragging && evnt.Mode != CrossingMode.Grab)
+ ResetHandleHighlight ();
+ return base.OnLeaveNotifyEvent (evnt);
+ }
+
+
+ bool FindHandle (DockGroup grp, int x, int y, out DockGroup foundGrp, out int objectIndex)
+ {
+ if (grp.Type != DockGroupType.Tabbed && grp.Allocation.Contains (x, y)) {
+ for (int n=0; n<grp.VisibleObjects.Count; n++) {
+ DockObject obj = grp.VisibleObjects [n];
+ if (n < grp.Objects.Count - 1) {
+ if ((grp.Type == DockGroupType.Horizontal && x > obj.Allocation.Right && x < obj.Allocation.Right + frame.TotalHandleSize) ||
+ (grp.Type == DockGroupType.Vertical && y > obj.Allocation.Bottom && y < obj.Allocation.Bottom + frame.TotalHandleSize))
+ {
+ foundGrp = grp;
+ objectIndex = n;
+ return true;
+ }
+ }
+ if (obj is DockGroup) {
+ if (FindHandle ((DockGroup) obj, x, y, out foundGrp, out objectIndex))
+ return true;
+ }
+ }
+ }
+
+ foundGrp = null;
+ objectIndex = 0;
+ return false;
+ }
+
+ protected override void OnRealized ()
+ {
+ WidgetFlags |= WidgetFlags.Realized;
+
+ Gdk.WindowAttr attributes = new Gdk.WindowAttr ();
+ attributes.X = Allocation.X;
+ attributes.Y = Allocation.Y;
+ attributes.Height = Allocation.Height;
+ attributes.Width = Allocation.Width;
+ attributes.WindowType = Gdk.WindowType.Child;
+ attributes.Wclass = Gdk.WindowClass.InputOutput;
+ attributes.Visual = Visual;
+ attributes.Colormap = Colormap;
+ attributes.EventMask = (int)(Events |
+ Gdk.EventMask.ExposureMask |
+ Gdk.EventMask.Button1MotionMask |
+ Gdk.EventMask.ButtonPressMask |
+ Gdk.EventMask.ButtonReleaseMask);
+
+ Gdk.WindowAttributesType attributes_mask =
+ Gdk.WindowAttributesType.X |
+ Gdk.WindowAttributesType.Y |
+ Gdk.WindowAttributesType.Colormap |
+ Gdk.WindowAttributesType.Visual;
+ GdkWindow = new Gdk.Window (ParentWindow, attributes, (int)attributes_mask);
+ GdkWindow.UserData = Handle;
+
+ Style = Style.Attach (GdkWindow);
+ Style.SetBackground (GdkWindow, State);
+
+ //GdkWindow.SetBackPixmap (null, true);
+ }
+
+ internal void ShowPlaceholder ()
+ {
+ placeholderWindow = new PlaceholderWindow (frame);
+ }
+
+ internal bool UpdatePlaceholder (DockItem item, Gdk.Size size, bool allowDocking)
+ {
+ if (placeholderWindow == null)
+ return false;
+
+ int px, py;
+ GetPointer (out px, out py);
+
+ placeholderWindow.AllowDocking = allowDocking;
+
+ DockDelegate dockDelegate;
+ Gdk.Rectangle rect;
+ if (allowDocking && layout.GetDockTarget (item, px, py, out dockDelegate, out rect)) {
+ int ox, oy;
+ GdkWindow.GetOrigin (out ox, out oy);
+
+ placeholderWindow.Relocate (ox + rect.X, oy + rect.Y, rect.Width, rect.Height, true);
+ placeholderWindow.Show ();
+ return true;
+ } else {
+ int ox, oy;
+ GdkWindow.GetOrigin (out ox, out oy);
+ placeholderWindow.Relocate (ox + px - size.Width / 2, oy + py - 18, size.Width, size.Height, false);
+ placeholderWindow.Show ();
+ }
+ return false;
+ }
+
+ internal void DockInPlaceholder (DockItem item)
+ {
+ if (placeholderWindow == null || !placeholderWindow.Visible)
+ return;
+
+ item.Status = DockItemStatus.Dockable;
+
+ int px, py;
+ GetPointer (out px, out py);
+
+ DockDelegate dockDelegate;
+ Gdk.Rectangle rect;
+ if (placeholderWindow.AllowDocking && layout.GetDockTarget (item, px, py, out dockDelegate, out rect)) {
+ DockGroupItem dummyItem = new DockGroupItem (frame, new DockItem (frame, "__dummy"));
+ DockGroupItem gitem = layout.FindDockGroupItem (item.Id);
+ gitem.ParentGroup.ReplaceItem (gitem, dummyItem);
+ dockDelegate (item);
+ dummyItem.ParentGroup.Remove (dummyItem);
+ RelayoutWidgets ();
+ } else {
+ DockGroupItem gi = FindDockGroupItem (item.Id);
+ int pw, ph;
+ placeholderWindow.GetPosition (out px, out py);
+ placeholderWindow.GetSize (out pw, out ph);
+ gi.FloatRect = new Rectangle (px, py, pw, ph);
+ item.Status = DockItemStatus.Floating;
+ }
+ }
+
+ internal void HidePlaceholder ()
+ {
+ if (placeholderWindow != null) {
+ placeholderWindow.Destroy ();
+ placeholderWindow = null;
+ }
+ }
+
+ public IEnumerable<Rectangle> GetShadedAreas ()
+ {
+ List<Gdk.Rectangle> rects = new List<Gdk.Rectangle> ();
+ layout.DrawSeparators (Allocation, currentHandleGrp, currentHandleIndex, true, rects);
+ return rects;
+ }
+
+ internal void NotifySeparatorsChanged ()
+ {
+ if (AreasChanged != null)
+ AreasChanged (this, EventArgs.Empty);
+ }
+
+ public event EventHandler AreasChanged;
+
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs
new file mode 100644
index 0000000000..f372dd4245
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs
@@ -0,0 +1,668 @@
+//
+// MonoDevelop.Components.Docking.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Xml;
+using System.Collections;
+using System.Collections.Generic;
+using Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.Docking
+{
+ public class DockFrame: HBox
+ {
+ internal const double ItemDockCenterArea = 0.4;
+ internal const int GroupDockSeparatorSize = 40;
+
+ internal bool ShadedSeparators = true;
+
+ DockContainer container;
+
+ int handleSize = IsWindows ? 4 : 6;
+ int handlePadding = 0;
+ int defaultItemWidth = 130;
+ int defaultItemHeight = 130;
+ uint autoShowDelay = 400;
+ uint autoHideDelay = 500;
+
+ SortedDictionary<string,DockLayout> layouts = new SortedDictionary<string,DockLayout> ();
+ List<DockFrameTopLevel> topLevels = new List<DockFrameTopLevel> ();
+ string currentLayout;
+ int compactGuiLevel = 3;
+
+ DockBar dockBarTop, dockBarBottom, dockBarLeft, dockBarRight;
+ VBox mainBox;
+ ShadedContainer shadedContainer;
+
+ public DockFrame ()
+ {
+ shadedContainer = new ShadedContainer ();
+
+ dockBarTop = new DockBar (this, Gtk.PositionType.Top);
+ dockBarBottom = new DockBar (this, Gtk.PositionType.Bottom);
+ dockBarLeft = new DockBar (this, Gtk.PositionType.Left);
+ dockBarRight = new DockBar (this, Gtk.PositionType.Right);
+
+ container = new DockContainer (this);
+ HBox hbox = new HBox ();
+ hbox.PackStart (dockBarLeft, false, false, 0);
+ hbox.PackStart (container, true, true, 0);
+ hbox.PackStart (dockBarRight, false, false, 0);
+ mainBox = new VBox ();
+ mainBox.PackStart (dockBarTop, false, false, 0);
+ mainBox.PackStart (hbox, true, true, 0);
+ mainBox.PackStart (dockBarBottom, false, false, 0);
+ Add (mainBox);
+ mainBox.ShowAll ();
+ mainBox.NoShowAll = true;
+ CompactGuiLevel = 2;
+ dockBarTop.UpdateVisibility ();
+ dockBarBottom.UpdateVisibility ();
+ dockBarLeft.UpdateVisibility ();
+ dockBarRight.UpdateVisibility ();
+ }
+
+ /// <summary>
+ /// Compactness level of the gui, from 1 (not compact) to 5 (very compact).
+ /// </summary>
+ public int CompactGuiLevel {
+ get { return compactGuiLevel; }
+ set {
+ compactGuiLevel = value;
+ switch (compactGuiLevel) {
+ case 1: handleSize = 6; break;
+ case 2:
+ case 3: handleSize = IsWindows ? 4 : 6; break;
+ case 4:
+ case 5: handleSize = 3; break;
+ }
+ handlePadding = 0;
+ dockBarTop.OnCompactLevelChanged ();
+ dockBarBottom.OnCompactLevelChanged ();
+ dockBarLeft.OnCompactLevelChanged ();
+ dockBarRight.OnCompactLevelChanged ();
+ container.RelayoutWidgets ();
+ }
+ }
+
+ public DockBar ExtractDockBar (PositionType pos)
+ {
+ DockBar db = new DockBar (this, pos);
+ switch (pos) {
+ case PositionType.Left: db.OriginalBar = dockBarLeft; dockBarLeft = db; break;
+ case PositionType.Top: db.OriginalBar = dockBarTop; dockBarTop = db; break;
+ case PositionType.Right: db.OriginalBar = dockBarRight; dockBarRight = db; break;
+ case PositionType.Bottom: db.OriginalBar = dockBarBottom; dockBarBottom = db; break;
+ }
+ return db;
+ }
+
+ internal DockBar GetDockBar (PositionType pos)
+ {
+ switch (pos) {
+ case Gtk.PositionType.Top: return dockBarTop;
+ case Gtk.PositionType.Bottom: return dockBarBottom;
+ case Gtk.PositionType.Left: return dockBarLeft;
+ case Gtk.PositionType.Right: return dockBarRight;
+ }
+ return null;
+ }
+
+ internal DockContainer Container {
+ get { return container; }
+ }
+
+ public ShadedContainer ShadedContainer {
+ get { return this.shadedContainer; }
+ }
+
+ public int HandleSize {
+ get {
+ return handleSize;
+ }
+ set {
+ handleSize = value;
+ }
+ }
+
+ public int HandlePadding {
+ get {
+ return handlePadding;
+ }
+ set {
+ handlePadding = value;
+ }
+ }
+
+ public int DefaultItemWidth {
+ get {
+ return defaultItemWidth;
+ }
+ set {
+ defaultItemWidth = value;
+ }
+ }
+
+ public int DefaultItemHeight {
+ get {
+ return defaultItemHeight;
+ }
+ set {
+ defaultItemHeight = value;
+ }
+ }
+
+ internal int TotalHandleSize {
+ get { return handleSize + handlePadding*2; }
+ }
+
+ public DockItem AddItem (string id)
+ {
+ foreach (DockItem dit in container.Items) {
+ if (dit.Id == id) {
+ if (dit.IsPositionMarker) {
+ dit.IsPositionMarker = false;
+ return dit;
+ }
+ throw new InvalidOperationException ("An item with id '" + id + "' already exists.");
+ }
+ }
+
+ DockItem it = new DockItem (this, id);
+ container.Items.Add (it);
+ return it;
+ }
+
+ public void RemoveItem (DockItem it)
+ {
+ if (container.Layout != null)
+ container.Layout.RemoveItemRec (it);
+ foreach (DockGroup grp in layouts.Values)
+ grp.RemoveItemRec (it);
+ container.Items.Remove (it);
+ }
+
+ public DockItem GetItem (string id)
+ {
+ foreach (DockItem it in container.Items) {
+ if (it.Id == id) {
+ if (!it.IsPositionMarker)
+ return it;
+ else
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public IEnumerable<DockItem> GetItems ()
+ {
+ return container.Items;
+ }
+
+ bool LoadLayout (string layoutName)
+ {
+ DockLayout dl;
+ if (!layouts.TryGetValue (layoutName, out dl))
+ return false;
+
+ container.LoadLayout (dl);
+ return true;
+ }
+
+ public void CreateLayout (string name)
+ {
+ CreateLayout (name, false);
+ }
+
+ public void DeleteLayout (string name)
+ {
+ layouts.Remove (name);
+ }
+
+ public void CreateLayout (string name, bool copyCurrent)
+ {
+ DockLayout dl;
+ if (container.Layout == null || !copyCurrent) {
+ dl = GetDefaultLayout ();
+ } else {
+ container.StoreAllocation ();
+ dl = (DockLayout) container.Layout.Clone ();
+ }
+ dl.Name = name;
+ layouts [name] = dl;
+ }
+
+ public string CurrentLayout {
+ get {
+ return currentLayout;
+ }
+ set {
+ if (currentLayout == value)
+ return;
+ if (LoadLayout (value)) {
+ currentLayout = value;
+ }
+ }
+ }
+
+ public bool HasLayout (string id)
+ {
+ return layouts.ContainsKey (id);
+ }
+
+ public string[] Layouts {
+ get {
+ if (layouts.Count == 0)
+ return new string [0];
+ string[] arr = new string [layouts.Count];
+ layouts.Keys.CopyTo (arr, 0);
+ return arr;
+ }
+ }
+
+ public uint AutoShowDelay {
+ get {
+ return autoShowDelay;
+ }
+ set {
+ autoShowDelay = value;
+ }
+ }
+
+ public uint AutoHideDelay {
+ get {
+ return autoHideDelay;
+ }
+ set {
+ autoHideDelay = value;
+ }
+ }
+
+ public void SaveLayouts (string file)
+ {
+ using (XmlTextWriter w = new XmlTextWriter (file, System.Text.Encoding.UTF8)) {
+ w.Formatting = Formatting.Indented;
+ SaveLayouts (w);
+ }
+ }
+
+ public void SaveLayouts (XmlWriter writer)
+ {
+ if (container.Layout != null)
+ container.Layout.StoreAllocation ();
+ writer.WriteStartElement ("layouts");
+ foreach (DockLayout la in layouts.Values)
+ la.Write (writer);
+ writer.WriteEndElement ();
+ }
+
+ public void LoadLayouts (string file)
+ {
+ using (XmlReader r = new XmlTextReader (new System.IO.StreamReader (file))) {
+ LoadLayouts (r);
+ }
+ }
+
+ public void LoadLayouts (XmlReader reader)
+ {
+ layouts.Clear ();
+ container.Clear ();
+ currentLayout = null;
+
+ reader.MoveToContent ();
+ if (reader.IsEmptyElement) {
+ reader.Skip ();
+ return;
+ }
+ reader.ReadStartElement ("layouts");
+ reader.MoveToContent ();
+ while (reader.NodeType != XmlNodeType.EndElement) {
+ if (reader.NodeType == XmlNodeType.Element) {
+ DockLayout layout = DockLayout.Read (this, reader);
+ layouts.Add (layout.Name, layout);
+ }
+ else
+ reader.Skip ();
+ reader.MoveToContent ();
+ }
+ reader.ReadEndElement ();
+ container.RelayoutWidgets ();
+ }
+
+ internal void UpdateTitle (DockItem item)
+ {
+ DockGroupItem gitem = container.FindDockGroupItem (item.Id);
+ if (gitem == null)
+ return;
+
+ gitem.ParentGroup.UpdateTitle (item);
+ dockBarTop.UpdateTitle (item);
+ dockBarBottom.UpdateTitle (item);
+ dockBarLeft.UpdateTitle (item);
+ dockBarRight.UpdateTitle (item);
+ }
+
+ internal void Present (DockItem item, bool giveFocus)
+ {
+ DockGroupItem gitem = container.FindDockGroupItem (item.Id);
+ if (gitem == null)
+ return;
+
+ gitem.ParentGroup.Present (item, giveFocus);
+ }
+
+ internal bool GetVisible (DockItem item)
+ {
+ DockGroupItem gitem = container.FindDockGroupItem (item.Id);
+ if (gitem == null)
+ return false;
+ return gitem.VisibleFlag;
+ }
+
+ internal bool GetVisible (DockItem item, string layoutName)
+ {
+ DockLayout dl;
+ if (!layouts.TryGetValue (layoutName, out dl))
+ return false;
+
+ DockGroupItem gitem = dl.FindDockGroupItem (item.Id);
+ if (gitem == null)
+ return false;
+ return gitem.VisibleFlag;
+ }
+
+ internal void SetVisible (DockItem item, bool visible)
+ {
+ if (container.Layout == null)
+ return;
+ DockGroupItem gitem = container.FindDockGroupItem (item.Id);
+
+ if (gitem == null) {
+ if (visible) {
+ // The item is not present in the layout. Add it now.
+ if (!string.IsNullOrEmpty (item.DefaultLocation))
+ gitem = AddDefaultItem (container.Layout, item);
+
+ if (gitem == null) {
+ // No default position
+ gitem = new DockGroupItem (this, item);
+ container.Layout.AddObject (gitem);
+ }
+ } else
+ return; // Already invisible
+ }
+ gitem.SetVisible (visible);
+ container.RelayoutWidgets ();
+ }
+
+ internal DockItemStatus GetStatus (DockItem item)
+ {
+ DockGroupItem gitem = container.FindDockGroupItem (item.Id);
+ if (gitem == null)
+ return DockItemStatus.Dockable;
+ return gitem.Status;
+ }
+
+ internal void SetStatus (DockItem item, DockItemStatus status)
+ {
+ DockGroupItem gitem = container.FindDockGroupItem (item.Id);
+ if (gitem == null) {
+ item.DefaultStatus = status;
+ return;
+ }
+ gitem.StoreAllocation ();
+ gitem.Status = status;
+ container.RelayoutWidgets ();
+ }
+
+ DockLayout GetDefaultLayout ()
+ {
+ DockLayout group = new DockLayout (this);
+
+ // Add items which don't have relative defaut positions
+
+ List<DockItem> todock = new List<DockItem> ();
+ foreach (DockItem item in container.Items) {
+ if (string.IsNullOrEmpty (item.DefaultLocation)) {
+ DockGroupItem dgt = new DockGroupItem (this, item);
+ dgt.SetVisible (item.DefaultVisible);
+ group.AddObject (dgt);
+ }
+ else
+ todock.Add (item);
+ }
+
+ // Add items with relative positions.
+ int lastCount = 0;
+ while (lastCount != todock.Count) {
+ lastCount = todock.Count;
+ for (int n=0; n<todock.Count; n++) {
+ DockItem it = todock [n];
+ if (AddDefaultItem (group, it) != null) {
+ todock.RemoveAt (n);
+ n--;
+ }
+ }
+ }
+
+ // Items which could not be docked because of an invalid default location
+ foreach (DockItem item in todock) {
+ DockGroupItem dgt = new DockGroupItem (this, item);
+ dgt.SetVisible (false);
+ group.AddObject (dgt);
+ }
+// group.Dump ();
+ return group;
+ }
+
+ DockGroupItem AddDefaultItem (DockGroup grp, DockItem it)
+ {
+ string[] positions = it.DefaultLocation.Split (';');
+ foreach (string pos in positions) {
+ int i = pos.IndexOf ('/');
+ if (i == -1) continue;
+ string id = pos.Substring (0,i).Trim ();
+ DockGroup g = grp.FindGroupContaining (id);
+ if (g != null) {
+ DockPosition dpos;
+ try {
+ dpos = (DockPosition) Enum.Parse (typeof(DockPosition), pos.Substring(i+1).Trim(), true);
+ }
+ catch {
+ continue;
+ }
+ DockGroupItem dgt = g.AddObject (it, dpos, id);
+ dgt.SetVisible (it.DefaultVisible);
+ dgt.Status = it.DefaultStatus;
+ return dgt;
+ }
+ }
+ return null;
+ }
+
+ internal void AddTopLevel (DockFrameTopLevel w, int x, int y)
+ {
+ w.Parent = this;
+ w.X = x;
+ w.Y = y;
+ Requisition r = w.SizeRequest ();
+ w.Allocation = new Gdk.Rectangle (Allocation.X + x, Allocation.Y + y, r.Width, r.Height);
+ topLevels.Add (w);
+ }
+
+ internal void RemoveTopLevel (DockFrameTopLevel w)
+ {
+ w.Unparent ();
+ topLevels.Remove (w);
+ QueueResize ();
+ }
+
+ public Gdk.Rectangle GetCoordinates (Gtk.Widget w)
+ {
+ int px, py;
+ if (!w.TranslateCoordinates (this, 0, 0, out px, out py))
+ return new Gdk.Rectangle (0,0,0,0);
+
+ Gdk.Rectangle rect = w.Allocation;
+ rect.X = px - Allocation.X;
+ rect.Y = py - Allocation.Y;
+ return rect;
+ }
+
+ internal void ShowPlaceholder ()
+ {
+ container.ShowPlaceholder ();
+ }
+
+ internal void DockInPlaceholder (DockItem item)
+ {
+ container.DockInPlaceholder (item);
+ }
+
+ internal void HidePlaceholder ()
+ {
+ container.HidePlaceholder ();
+ }
+
+ internal void UpdatePlaceholder (DockItem item, Gdk.Size size, bool allowDocking)
+ {
+ container.UpdatePlaceholder (item, size, allowDocking);
+ }
+
+ internal DockBarItem BarDock (Gtk.PositionType pos, DockItem item, int size)
+ {
+ return GetDockBar (pos).AddItem (item, size);
+ }
+
+ internal AutoHideBox AutoShow (DockItem item, DockBar bar, int size)
+ {
+ AutoHideBox aframe = new AutoHideBox (this, item, bar.Position, size);
+ Gdk.Size sTop = GetBarFrameSize (dockBarTop);
+ Gdk.Size sBot = GetBarFrameSize (dockBarBottom);
+ Gdk.Size sLeft = GetBarFrameSize (dockBarLeft);
+ Gdk.Size sRgt = GetBarFrameSize (dockBarRight);
+
+ int x,y;
+ if (bar == dockBarLeft || bar == dockBarRight) {
+ aframe.HeightRequest = Allocation.Height - sTop.Height - sBot.Height;
+ aframe.WidthRequest = size;
+ y = sTop.Height;
+ if (bar == dockBarLeft)
+ x = sLeft.Width;
+ else
+ x = Allocation.Width - size - sRgt.Width;
+ } else {
+ aframe.WidthRequest = Allocation.Width - sLeft.Width - sRgt.Width;
+ aframe.HeightRequest = size;
+ x = sLeft.Width;
+ if (bar == dockBarTop)
+ y = sTop.Height;
+ else
+ y = Allocation.Height - size - sBot.Height;
+ }
+ AddTopLevel (aframe, x, y);
+ aframe.AnimateShow ();
+ return aframe;
+ }
+
+ Gdk.Size GetBarFrameSize (DockBar bar)
+ {
+ if (bar.OriginalBar != null)
+ bar = bar.OriginalBar;
+ if (!bar.Visible)
+ return new Gdk.Size (0,0);
+ Gtk.Requisition req = bar.SizeRequest ();
+ return new Gdk.Size (req.Width, req.Height);
+ }
+
+ internal void AutoHide (DockItem item, AutoHideBox widget, bool animate)
+ {
+ if (animate) {
+ widget.Hidden += delegate {
+ if (!widget.Disposed)
+ AutoHide (item, widget, false);
+ };
+ widget.AnimateHide ();
+ }
+ else {
+ Gtk.Container parent = (Gtk.Container) item.Widget.Parent;
+ parent.Remove (item.Widget);
+ RemoveTopLevel (widget);
+ widget.Disposed = true;
+ widget.Destroy ();
+ }
+ }
+
+ protected override void OnSizeAllocated (Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation);
+
+ foreach (DockFrameTopLevel tl in topLevels) {
+ Requisition r = tl.SizeRequest ();
+ tl.SizeAllocate (new Gdk.Rectangle (allocation.X + tl.X, allocation.Y + tl.Y, r.Width, r.Height));
+ }
+ }
+
+ protected override void ForAll (bool include_internals, Callback callback)
+ {
+ base.ForAll (include_internals, callback);
+ List<DockFrameTopLevel> clone = new List<DockFrameTopLevel> (topLevels);
+ foreach (DockFrameTopLevel child in clone)
+ callback (child);
+ }
+
+ protected override void OnRealized ()
+ {
+ base.OnRealized ();
+ HslColor cLight = new HslColor (Style.Background (Gtk.StateType.Normal));
+ HslColor cDark = cLight;
+ cLight.L *= 0.9;
+ cDark.L *= 0.8;
+ shadedContainer.LightColor = cLight;
+ shadedContainer.DarkColor = cDark;
+ }
+
+
+ static internal bool IsWindows {
+ get { return System.IO.Path.DirectorySeparatorChar == '\\'; }
+ }
+
+ internal static Cairo.Color ToCairoColor (Gdk.Color color)
+ {
+ return new Cairo.Color (color.Red / (double) ushort.MaxValue, color.Green / (double) ushort.MaxValue, color.Blue / (double) ushort.MaxValue);
+ }
+ }
+
+
+ internal delegate void DockDelegate (DockItem item);
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs
new file mode 100644
index 0000000000..7cb00af05b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs
@@ -0,0 +1,61 @@
+//
+// DockFrameTopLevel.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Docking
+{
+ class DockFrameTopLevel: EventBox
+ {
+ int x, y;
+
+ public int X {
+ get { return x; }
+ set {
+ x = value;
+ if (Parent != null)
+ Parent.QueueResize ();
+ }
+ }
+
+ public int Y {
+ get { return y; }
+ set {
+ y = value;
+ if (Parent != null)
+ Parent.QueueResize ();
+ }
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs
new file mode 100644
index 0000000000..6fc5396660
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs
@@ -0,0 +1,1134 @@
+//
+// MonoDevelop.Components.Docking.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Xml;
+using System.Collections.Generic;
+using Gtk;
+
+namespace MonoDevelop.Components.Docking
+{
+ class DockGroup: DockObject
+ {
+ DockGroupType type;
+ List<DockObject> dockObjects = new List<DockObject> ();
+ List<DockObject> visibleObjects;
+ AllocStatus allocStatus = AllocStatus.NotSet;
+ TabStrip boundTabStrip;
+ DockGroupItem tabFocus;
+ int currentTabPage;
+
+ enum AllocStatus { NotSet, Invalid, RestorePending, NewSizeRequest, Valid };
+
+ public DockGroup (DockFrame frame, DockGroupType type): base (frame)
+ {
+ this.type = type;
+ }
+
+ internal DockGroup (DockFrame frame): base (frame)
+ {
+ }
+
+ public DockGroupType Type {
+ get {
+ return type;
+ }
+ set {
+ type = value;
+ }
+ }
+
+ public List<DockObject> Objects {
+ get { return dockObjects; }
+ }
+
+ void MarkForRelayout ()
+ {
+ if (allocStatus == AllocStatus.Valid)
+ allocStatus = AllocStatus.Invalid;
+ }
+
+ public void AddObject (DockObject obj)
+ {
+ obj.ParentGroup = this;
+ dockObjects.Add (obj);
+ ResetVisibleGroups ();
+ }
+
+ public DockGroupItem AddObject (DockItem obj, DockPosition pos, string relItemId)
+ {
+ int npos = -1;
+ if (relItemId != null) {
+ for (int n=0; n<dockObjects.Count; n++) {
+ DockGroupItem it = dockObjects [n] as DockGroupItem;
+ if (it != null && it.Id == relItemId)
+ npos = n;
+ }
+ }
+
+ if (npos == -1) {
+ if (pos == DockPosition.Left || pos == DockPosition.Top)
+ npos = 0;
+ else
+ npos = dockObjects.Count - 1;
+ }
+
+ DockGroupItem gitem = null;
+
+ if (pos == DockPosition.Left || pos == DockPosition.Right) {
+ if (type != DockGroupType.Horizontal)
+ gitem = Split (DockGroupType.Horizontal, pos == DockPosition.Left, obj, npos);
+ else
+ gitem = InsertObject (obj, npos, pos);
+ }
+ else if (pos == DockPosition.Top || pos == DockPosition.Bottom) {
+ if (type != DockGroupType.Vertical)
+ gitem = Split (DockGroupType.Vertical, pos == DockPosition.Top, obj, npos);
+ else
+ gitem = InsertObject (obj, npos, pos);
+ }
+ else if (pos == DockPosition.CenterBefore || pos == DockPosition.Center) {
+ if (type != DockGroupType.Tabbed)
+ gitem = Split (DockGroupType.Tabbed, pos == DockPosition.CenterBefore, obj, npos);
+ else {
+ if (pos == DockPosition.Center)
+ npos++;
+ gitem = new DockGroupItem (Frame, obj);
+ dockObjects.Insert (npos, gitem);
+ gitem.ParentGroup = this;
+ }
+ }
+ ResetVisibleGroups ();
+ return gitem;
+ }
+
+ DockGroupItem InsertObject (DockItem obj, int npos, DockPosition pos)
+ {
+ if (pos == DockPosition.Bottom || pos == DockPosition.Right)
+ npos++;
+
+ DockGroupItem gitem = new DockGroupItem (Frame, obj);
+ dockObjects.Insert (npos, gitem);
+ gitem.ParentGroup = this;
+ return gitem;
+ }
+
+ DockGroupItem Split (DockGroupType newType, bool addFirst, DockItem obj, int npos)
+ {
+ DockGroupItem item = new DockGroupItem (Frame, obj);
+
+ if (npos == -1 || type == DockGroupType.Tabbed) {
+ if (ParentGroup != null && ParentGroup.Type == newType) {
+ // No need to split. Just add the new item as a sibling of this one.
+ int i = ParentGroup.Objects.IndexOf (this);
+ if (addFirst)
+ ParentGroup.Objects.Insert (i, item);
+ else
+ ParentGroup.Objects.Insert (i+1, item);
+ item.ParentGroup = ParentGroup;
+ item.ResetDefaultSize ();
+ }
+ else {
+ DockGroup grp = Copy ();
+ dockObjects.Clear ();
+ if (addFirst) {
+ dockObjects.Add (item);
+ dockObjects.Add (grp);
+ } else {
+ dockObjects.Add (grp);
+ dockObjects.Add (item);
+ }
+ item.ParentGroup = this;
+ item.ResetDefaultSize ();
+ grp.ParentGroup = this;
+ grp.ResetDefaultSize ();
+ Type = newType;
+ }
+ }
+ else {
+ DockGroup grp = new DockGroup (Frame, newType);
+ DockObject replaced = dockObjects[npos];
+ if (addFirst) {
+ grp.AddObject (item);
+ grp.AddObject (replaced);
+ } else {
+ grp.AddObject (replaced);
+ grp.AddObject (item);
+ }
+ grp.CopySizeFrom (replaced);
+ dockObjects [npos] = grp;
+ grp.ParentGroup = this;
+ }
+ return item;
+ }
+
+ internal DockGroup FindGroupContaining (string id)
+ {
+ DockGroupItem it = FindDockGroupItem (id);
+ if (it != null)
+ return it.ParentGroup;
+ else
+ return null;
+ }
+
+ internal DockGroupItem FindDockGroupItem (string id)
+ {
+ foreach (DockObject ob in dockObjects) {
+ DockGroupItem it = ob as DockGroupItem;
+ if (it != null && it.Id == id)
+ return it;
+ DockGroup g = ob as DockGroup;
+ if (g != null) {
+ it = g.FindDockGroupItem (id);
+ if (it != null)
+ return it;
+ }
+ }
+ return null;
+ }
+
+ DockGroup Copy ()
+ {
+ DockGroup grp = new DockGroup (Frame, type);
+ grp.dockObjects = new List<MonoDevelop.Components.Docking.DockObject> (dockObjects);
+ foreach (DockObject obj in grp.dockObjects)
+ obj.ParentGroup = grp;
+
+ grp.CopySizeFrom (this);
+ return grp;
+ }
+
+ public int GetObjectIndex (DockObject obj)
+ {
+ for (int n=0; n<dockObjects.Count; n++) {
+ if (dockObjects [n] == obj)
+ return n;
+ }
+ return -1;
+ }
+
+ public bool RemoveItemRec (DockItem item)
+ {
+ foreach (DockObject ob in dockObjects) {
+ if (ob is DockGroup) {
+ if (((DockGroup)ob).RemoveItemRec (item))
+ return true;
+ } else {
+ DockGroupItem dit = ob as DockGroupItem;
+ if (dit != null && dit.Item == item) {
+ Remove (ob);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void Remove (DockObject obj)
+ {
+ dockObjects.Remove (obj);
+ Reduce ();
+ obj.ParentGroup = null;
+ visibleObjects = null;
+
+ if (VisibleObjects.Count > 0) {
+ CalcNewSizes ();
+ MarkForRelayout ();
+ } else
+ ParentGroup.UpdateVisible (this);
+ }
+
+ public void Reduce ()
+ {
+ if (ParentGroup != null && dockObjects.Count == 1) {
+ DockObject obj = dockObjects [0];
+ int n = ParentGroup.GetObjectIndex (this);
+ ParentGroup.dockObjects [n] = obj;
+ obj.ParentGroup = ParentGroup;
+ obj.CopySizeFrom (this);
+ dockObjects.Clear ();
+ ResetVisibleGroups ();
+ ParentGroup.ResetVisibleGroups ();
+ }
+ }
+
+ internal List<DockObject> VisibleObjects {
+ get {
+ if (visibleObjects == null) {
+ visibleObjects = new List<DockObject> ();
+ foreach (DockObject obj in dockObjects)
+ if (obj.Visible)
+ visibleObjects.Add (obj);
+ }
+ return visibleObjects;
+ }
+ }
+
+ void ResetVisibleGroups ()
+ {
+ visibleObjects = null;
+ MarkForRelayout ();
+ }
+
+ internal void UpdateVisible (DockObject child)
+ {
+ visibleObjects = null;
+ bool visChanged;
+ CalcNewSizes ();
+ MarkForRelayout ();
+
+ visChanged = child.Visible ? VisibleObjects.Count == 1 : VisibleObjects.Count == 0;
+
+ if (visChanged && ParentGroup != null)
+ ParentGroup.UpdateVisible (this);
+ }
+
+ internal override void RestoreAllocation ()
+ {
+ base.RestoreAllocation ();
+ allocStatus = Size >= 0 ? AllocStatus.RestorePending : AllocStatus.NotSet;
+ foreach (DockObject ob in dockObjects)
+ ob.RestoreAllocation ();
+ }
+
+ internal override void StoreAllocation ()
+ {
+ base.StoreAllocation ();
+ foreach (DockObject ob in dockObjects)
+ ob.StoreAllocation ();
+ if (Type == DockGroupType.Tabbed && boundTabStrip != null)
+ currentTabPage = boundTabStrip.CurrentTab;
+ }
+
+ public override bool Expand {
+ get {
+ foreach (DockObject ob in dockObjects)
+ if (ob.Expand)
+ return true;
+ return false;
+ }
+ }
+
+ public override void SizeAllocate (Gdk.Rectangle newAlloc)
+ {
+ Gdk.Rectangle oldAlloc = Allocation;
+ base.SizeAllocate (newAlloc);
+
+ if (type == DockGroupType.Tabbed) {
+ if (boundTabStrip != null) {
+ int tabsHeight = boundTabStrip.SizeRequest ().Height;
+ boundTabStrip.SizeAllocate (new Gdk.Rectangle (newAlloc.X, newAlloc.Bottom - tabsHeight, newAlloc.Width, tabsHeight));
+ }
+ if (allocStatus == AllocStatus.Valid && newAlloc == oldAlloc) {
+ // Even if allocation has not changed, SizeAllocation has to be called on all items to avoid redrawing issues.
+ foreach (DockObject ob in VisibleObjects)
+ ob.SizeAllocate (ob.Allocation);
+ return;
+ }
+ if (VisibleObjects.Count > 1 && boundTabStrip != null) {
+ int tabsHeight = boundTabStrip.SizeRequest ().Height;
+ newAlloc.Height -= tabsHeight;
+ boundTabStrip.QueueDraw ();
+ } else if (VisibleObjects.Count != 0) {
+ ((DockGroupItem)VisibleObjects [0]).Item.Widget.Show ();
+ }
+ allocStatus = AllocStatus.Valid;
+ foreach (DockObject ob in VisibleObjects) {
+ ob.Size = ob.PrefSize = -1;
+ ob.SizeAllocate (newAlloc);
+ }
+ return;
+ }
+
+ bool horiz = type == DockGroupType.Horizontal;
+ int pos = horiz ? Allocation.Left : Allocation.Top;
+
+ if (allocStatus == AllocStatus.Valid && newAlloc == oldAlloc) {
+ // The layout of this group (as a whole) has not changed, but the layout
+ // of child items may have changed. Assign the new sizes.
+
+ if (CheckMinSizes ())
+ allocStatus = AllocStatus.NewSizeRequest;
+ else {
+ foreach (DockObject ob in VisibleObjects) {
+ Gdk.Rectangle rect;
+ int ins = ob.AllocSize;
+ if (horiz)
+ rect = new Gdk.Rectangle (pos, Allocation.Y, ins, Allocation.Height);
+ else
+ rect = new Gdk.Rectangle (Allocation.X, pos, Allocation.Width, ins);
+ ob.SizeAllocate (rect);
+ pos += ins + Frame.TotalHandleSize;
+ }
+ return;
+ }
+ }
+
+ // This is the space available for the child items (excluding size
+ // required for the resize handles)
+ int realSize = GetRealSize (VisibleObjects);
+
+ if (allocStatus == AllocStatus.NotSet/* || allocStatus == AllocStatus.RestorePending*/) {
+ // It is the first size allocation. Calculate all sizes.
+ CalcNewSizes ();
+ }
+ else if (allocStatus != AllocStatus.NewSizeRequest) {
+ // Available space has changed, so the size of the items must be changed.
+ // First of all, get the change fraction
+ double change;
+ if (horiz)
+ change = (double) newAlloc.Width / (double) oldAlloc.Width;
+ else
+ change = (double) newAlloc.Height / (double) oldAlloc.Height;
+
+ // Get the old total size of the visible objects. Used to calculate the
+ // proportion of size of each item.
+ double tsize = 0;
+ double rsize = 0;
+ foreach (DockObject ob in VisibleObjects) {
+ tsize += ob.PrefSize;
+ rsize += ob.Size;
+ }
+
+ foreach (DockObject ob in dockObjects) {
+ if (ob.Visible) {
+ // Proportionally spread the new available space among all visible objects
+ ob.Size = ob.PrefSize = (ob.PrefSize / tsize) * (double) realSize;
+ } else {
+ // For non-visible objects, change the size by the same grow fraction. In this
+ // way, when the item is shown again, it size will have the correct proportions.
+ ob.Size = ob.Size * change;
+ ob.PrefSize = ob.PrefSize * change;
+ }
+ ob.DefaultSize = ob.DefaultSize * change;
+ }
+ CheckMinSizes ();
+ }
+
+ allocStatus = AllocStatus.Valid;
+
+ // Sizes for all items have been set.
+ // Sizes are real numbers to ensure that the values are not degradated when resizing
+ // pixel by pixel. Now those have to be converted to integers, that is, actual allocated sizes.
+
+ int ts = 0;
+ for (int n=0; n<VisibleObjects.Count; n++) {
+ DockObject ob = VisibleObjects [n];
+
+ int ins = (int) Math.Truncate (ob.Size);
+
+ if (n == VisibleObjects.Count - 1)
+ ins = realSize - ts;
+
+ ts += ins;
+
+ if (ins < 0)
+ ins = 0;
+
+ ob.AllocSize = ins;
+
+ if (horiz)
+ ob.SizeAllocate (new Gdk.Rectangle (pos, Allocation.Y, ins, Allocation.Height));
+ else
+ ob.SizeAllocate (new Gdk.Rectangle (Allocation.X, pos, Allocation.Width, ins));
+
+ pos += ins + Frame.TotalHandleSize;
+ }
+ }
+
+ int GetRealSize (List<DockObject> objects)
+ {
+ // Returns the space available for the child items (excluding size
+ // required for the resize handles)
+
+ int realSize;
+ if (type == DockGroupType.Horizontal)
+ realSize = Allocation.Width;
+ else
+ realSize = Allocation.Height;
+
+ // Ignore space required for the handles
+ if (objects.Count > 1)
+ realSize -= (Frame.TotalHandleSize * (objects.Count - 1));
+
+ return realSize;
+ }
+
+ internal void CalcNewSizes ()
+ {
+ // Calculates the size assigned by default to each child item.
+ // Size is proportionally assigned to each item, taking into account
+ // the available space, and the default size of each item.
+
+ // If there are items with the Expand flag set, those will proportionally
+ // take the space left after allocating the other (not exandable) items.
+
+ // This is the space available for the child items (excluding size
+ // required for the resize handles)
+ double realSize = (double) GetRealSize (VisibleObjects);
+
+ bool hasExpandItems = false;
+ double noexpandSize = 0;
+ double minExpandSize = 0;
+ double defaultExpandSize = 0;
+
+ for (int n=0; n<VisibleObjects.Count; n++) {
+ DockObject ob = VisibleObjects [n];
+ if (ob.Expand) {
+ minExpandSize += ob.MinSize;
+ defaultExpandSize += ob.DefaultSize;
+ hasExpandItems = true;
+ }
+ else {
+ ob.Size = ob.DefaultSize;
+ noexpandSize += ob.DefaultSize;
+ }
+ }
+
+ double expandSize = realSize - noexpandSize;
+ foreach (DockObject ob in VisibleObjects) {
+ if (!hasExpandItems)
+ ob.Size = (ob.DefaultSize / noexpandSize) * realSize;
+ else if (ob.Expand)
+ ob.Size = (ob.DefaultSize / defaultExpandSize) * expandSize;
+ ob.PrefSize = ob.Size;
+ }
+
+ CheckMinSizes ();
+ }
+
+ bool CheckMinSizes ()
+ {
+ // Checks if any of the items has a size smaller than permitted.
+ // In this case it tries to regain size by reducing other items.
+
+ // First of all calculate the size to be regained, and the size available
+ // from other items
+
+ bool sizesChanged = false;
+
+ double avSize = 0;
+ double regSize = 0;
+ foreach (DockObject ob in VisibleObjects) {
+ if (ob.Size < ob.MinSize) {
+ regSize += ob.MinSize - ob.Size;
+ ob.Size = ob.MinSize;
+ sizesChanged = true;
+ } else {
+ avSize += ob.Size - ob.MinSize;
+ }
+ }
+
+ if (!sizesChanged)
+ return false;
+
+ // Now spread the required size among the resizable items
+
+ if (regSize > avSize)
+ regSize = avSize;
+
+ double ratio = (avSize - regSize) / avSize;
+ foreach (DockObject ob in VisibleObjects) {
+ if (ob.Size <= ob.MinSize)
+ continue;
+ double avs = ob.Size - ob.MinSize;
+ ob.Size = ob.MinSize + avs * ratio;
+ }
+ return sizesChanged;
+ }
+
+ internal override Gtk.Requisition SizeRequest ()
+ {
+ bool getMaxW = true, getMaxH = true;
+ if (type == DockGroupType.Horizontal)
+ getMaxW = false;
+ else if (type == DockGroupType.Vertical)
+ getMaxH = false;
+
+ Requisition ret = new Requisition ();
+ ret.Height = VisibleObjects.Count * Frame.TotalHandleSize;
+ foreach (DockObject ob in VisibleObjects) {
+ Requisition req = ob.SizeRequest ();
+ if (getMaxH) {
+ if (req.Height > ret.Height)
+ ret.Height = req.Height;
+ } else
+ ret.Height += req.Height;
+
+ if (getMaxW) {
+ if (req.Width > ret.Width)
+ ret.Width = req.Width;
+ } else
+ ret.Width += req.Width;
+ }
+ if (type == DockGroupType.Tabbed && VisibleObjects.Count > 1 && boundTabStrip != null) {
+ Gtk.Requisition tabs = boundTabStrip.SizeRequest ();
+ ret.Height += tabs.Height;
+ if (ret.Width < tabs.Width)
+ ret.Width = tabs.Width;
+ }
+ return ret;
+ }
+
+ internal void UpdateNotebook (TabStrip ts)
+ {
+ Gtk.Widget oldpage = null;
+ int oldtab = -1;
+
+ if (tabFocus != null) {
+ oldpage = tabFocus.Item.Widget;
+ tabFocus = null;
+ } else if (boundTabStrip != null) {
+ oldpage = boundTabStrip.CurrentPage;
+ oldtab = boundTabStrip.CurrentTab;
+ }
+
+ ts.Clear ();
+
+ // Add missing pages
+ foreach (DockObject ob in VisibleObjects) {
+ DockGroupItem it = ob as DockGroupItem;
+ ts.AddTab (it.Item.Widget, it.Item.Icon, it.Item.Label);
+ }
+
+ boundTabStrip = ts;
+
+ if (currentTabPage != -1 && currentTabPage < boundTabStrip.TabCount) {
+ boundTabStrip.CurrentTab = currentTabPage;
+ // Discard the currentTabPage value. Current page is now tracked by the tab strip
+ currentTabPage = -1;
+ }
+ else if (oldpage != null)
+ boundTabStrip.CurrentPage = oldpage;
+
+ if (boundTabStrip.CurrentTab == -1) {
+ if (oldtab != -1) {
+ if (oldtab < boundTabStrip.TabCount)
+ boundTabStrip.CurrentTab = oldtab;
+ else
+ boundTabStrip.CurrentTab = boundTabStrip.TabCount - 1;
+ } else
+ boundTabStrip.CurrentTab = 0;
+ }
+ if (Frame.CompactGuiLevel == 3 && IsNextToMargin (PositionType.Bottom))
+ boundTabStrip.BottomPadding = 3;
+ else
+ boundTabStrip.BottomPadding = 0;
+ }
+
+ internal void Present (DockItem it, bool giveFocus)
+ {
+ if (type == DockGroupType.Tabbed) {
+ for (int n=0; n<VisibleObjects.Count; n++) {
+ DockGroupItem dit = VisibleObjects[n] as DockGroupItem;
+ if (dit.Item == it) {
+ currentTabPage = n;
+ if (boundTabStrip != null)
+ boundTabStrip.CurrentPage = it.Widget;
+ break;
+ }
+ }
+ }
+ if (giveFocus && it.Visible)
+ it.SetFocus ();
+ }
+
+ internal bool IsSelectedPage (DockItem it)
+ {
+ if (type != DockGroupType.Tabbed || boundTabStrip == null || boundTabStrip.CurrentTab == -1 || VisibleObjects == null || boundTabStrip.CurrentTab >= VisibleObjects.Count)
+ return false;
+ DockGroupItem dit = VisibleObjects[boundTabStrip.CurrentTab] as DockGroupItem;
+ return dit.Item == it;
+ }
+
+ internal void UpdateTitle (DockItem it)
+ {
+ if (it.Visible && type == DockGroupType.Tabbed && boundTabStrip != null)
+ boundTabStrip.SetTabLabel (it.Widget, it.Icon, it.Label);
+ }
+
+ internal void FocusItem (DockGroupItem it)
+ {
+ tabFocus = it;
+ }
+
+ internal void ResetNotebook ()
+ {
+ boundTabStrip = null;
+ }
+
+ public void LayoutWidgets ()
+ {
+ foreach (DockObject ob in VisibleObjects) {
+ DockGroupItem it = ob as DockGroupItem;
+ if (it != null) {
+ if (it.Item.Widget.Parent == null)
+ it.Item.Widget.Parent = Frame.Container;
+ if (!it.Item.Widget.Visible && type != DockGroupType.Tabbed)
+ it.Item.Widget.Show ();
+ }
+ else
+ ((DockGroup)ob).LayoutWidgets ();
+ }
+ }
+
+ internal override void GetDefaultSize (out int width, out int height)
+ {
+ if (type == DockGroupType.Tabbed) {
+ width = -1;
+ height = -1;
+ foreach (DockObject ob in VisibleObjects) {
+ int dh, dw;
+ ob.GetDefaultSize (out dw, out dh);
+ if (dw > width)
+ width = dw;
+ if (dh > height)
+ height = dh;
+ }
+ }
+ else if (type == DockGroupType.Vertical) {
+ height = VisibleObjects.Count > 0 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0;
+ width = -1;
+ foreach (DockObject ob in VisibleObjects) {
+ int dh, dw;
+ ob.GetDefaultSize (out dw, out dh);
+ if (dw > width)
+ width = dw;
+ height += dh;
+ }
+ }
+ else {
+ width = VisibleObjects.Count > 0 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0;
+ height = -1;
+ foreach (DockObject ob in VisibleObjects) {
+ int dh, dw;
+ ob.GetDefaultSize (out dw, out dh);
+ if (dh > height)
+ height = dh;
+ width += dw;
+ }
+ }
+ }
+
+ internal override void GetMinSize (out int width, out int height)
+ {
+ if (type == DockGroupType.Tabbed) {
+ width = -1;
+ height = -1;
+ foreach (DockObject ob in VisibleObjects) {
+ int dh, dw;
+ ob.GetMinSize (out dw, out dh);
+ if (dw > width)
+ width = dw;
+ if (dh > height)
+ height = dh;
+ }
+ }
+ else if (type == DockGroupType.Vertical) {
+ height = VisibleObjects.Count > 1 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0;
+ width = -1;
+ foreach (DockObject ob in VisibleObjects) {
+ int dh, dw;
+ ob.GetMinSize (out dw, out dh);
+ if (dw > width)
+ width = dw;
+ height += dh;
+ }
+ }
+ else {
+ width = VisibleObjects.Count > 0 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0;
+ height = -1;
+ foreach (DockObject ob in VisibleObjects) {
+ int dh, dw;
+ ob.GetMinSize (out dw, out dh);
+ if (dh > height)
+ height = dh;
+ width += dw;
+ }
+ }
+ }
+
+ public void Draw (Gdk.Rectangle exposedArea, DockGroup currentHandleGrp, int currentHandleIndex)
+ {
+ if (type != DockGroupType.Tabbed) {
+ DrawSeparators (exposedArea, currentHandleGrp, currentHandleIndex, false, false, null);
+ foreach (DockObject it in VisibleObjects) {
+ DockGroup grp = it as DockGroup;
+ if (grp != null)
+ grp.Draw (exposedArea, currentHandleGrp, currentHandleIndex);
+ }
+ }
+ }
+
+ public void DrawSeparators (Gdk.Rectangle exposedArea, DockGroup currentHandleGrp, int currentHandleIndex, bool invalidateOnly, List<Gdk.Rectangle> areasList)
+ {
+ DrawSeparators (exposedArea, currentHandleGrp, currentHandleIndex, invalidateOnly, true, areasList);
+ }
+
+ void DrawSeparators (Gdk.Rectangle exposedArea, DockGroup currentHandleGrp, int currentHandleIndex, bool invalidateOnly, bool drawChildrenSep, List<Gdk.Rectangle> areasList)
+ {
+ if (type == DockGroupType.Tabbed || VisibleObjects.Count == 0)
+ return;
+
+ DockObject last = VisibleObjects [VisibleObjects.Count - 1];
+
+ bool horiz = type == DockGroupType.Horizontal;
+ int x = Allocation.X;
+ int y = Allocation.Y;
+ int hw = horiz ? Frame.HandleSize : Allocation.Width;
+ int hh = horiz ? Allocation.Height : Frame.HandleSize;
+ Gtk.Orientation or = horiz ? Gtk.Orientation.Vertical : Gtk.Orientation.Horizontal;
+
+ for (int n=0; n<VisibleObjects.Count; n++) {
+ DockObject ob = VisibleObjects [n];
+ DockGroup grp = ob as DockGroup;
+ if (grp != null && drawChildrenSep)
+ grp.DrawSeparators (exposedArea, currentHandleGrp, currentHandleIndex, invalidateOnly, areasList);
+ if (ob != last) {
+ if (horiz)
+ x += ob.Allocation.Width + Frame.HandlePadding;
+ else
+ y += ob.Allocation.Height + Frame.HandlePadding;
+
+ if (areasList != null) {
+ if (Frame.ShadedSeparators)
+ areasList.Add (new Gdk.Rectangle (x, y, hw, hh));
+ } else if (invalidateOnly) {
+ Frame.Container.QueueDrawArea (x, y, hw, hh);
+ }
+ else {
+ if (Frame.ShadedSeparators) {
+ Frame.ShadedContainer.DrawBackground (Frame.Container, new Gdk.Rectangle (x, y, hw, hh));
+ } else {
+ StateType state = (currentHandleGrp == this && currentHandleIndex == n) ? StateType.Prelight : StateType.Normal;
+ if (!DockFrame.IsWindows)
+ Gtk.Style.PaintHandle (Frame.Style, Frame.Container.GdkWindow, state, ShadowType.None, exposedArea, Frame, "paned", x, y, hw, hh, or);
+ }
+ }
+
+ if (horiz)
+ x += Frame.HandleSize + Frame.HandlePadding;
+ else
+ y += Frame.HandleSize + Frame.HandlePadding;
+ }
+ }
+ }
+
+ public void ResizeItem (int index, int newSize)
+ {
+ DockObject o1 = VisibleObjects [index];
+ DockObject o2 = VisibleObjects [index+1];
+
+ int dsize;
+
+ dsize = newSize - o1.AllocSize;
+ if (dsize < 0 && o1.AllocSize + dsize < o1.MinSize)
+ dsize = o1.MinSize - o1.AllocSize;
+ else if (dsize > 0 && o2.AllocSize - dsize < o2.MinSize)
+ dsize = o2.AllocSize - o2.MinSize;
+
+ // Assign the new sizes, applying the current ratio
+ double sizeDif = (double)dsize;
+
+ o1.AllocSize += dsize;
+ o2.AllocSize -= dsize;
+
+ o1.DefaultSize += (o1.DefaultSize * sizeDif) / o1.Size;
+ o1.Size = o1.AllocSize;
+ o1.PrefSize = o1.Size;
+
+ o2.DefaultSize -= (o2.DefaultSize * sizeDif) / o2.Size;
+ o2.Size = o2.AllocSize;
+ o2.PrefSize = o2.Size;
+
+ o1.QueueResize ();
+ o2.QueueResize ();
+ }
+
+ internal override void QueueResize ()
+ {
+ foreach (DockObject obj in VisibleObjects)
+ obj.QueueResize ();
+ }
+
+ internal double GetObjectsSize ()
+ {
+ double total = 0;
+ foreach (DockObject obj in VisibleObjects)
+ total += obj.Size;
+ return total;
+ }
+
+ void DockTarget (DockItem item, int n)
+ {
+ DockGroupItem gitem = new DockGroupItem (Frame, item);
+ dockObjects.Insert (n, gitem);
+ gitem.ParentGroup = this;
+ gitem.SetVisible (true);
+ ResetVisibleGroups ();
+ CalcNewSizes ();
+ }
+
+ internal override bool GetDockTarget (DockItem item, int px, int py, out DockDelegate dockDelegate, out Gdk.Rectangle rect)
+ {
+ if (!Allocation.Contains (px, py) || VisibleObjects.Count == 0) {
+ dockDelegate = null;
+ rect = Gdk.Rectangle.Zero;
+ return false;
+ }
+
+ if (type == DockGroupType.Tabbed) {
+ // Tabs can only contain DockGroupItems
+ return ((DockGroupItem)VisibleObjects[0]).GetDockTarget (item, px, py, Allocation, out dockDelegate, out rect);
+ }
+ else if (type == DockGroupType.Horizontal) {
+ if (px >= Allocation.Right - DockFrame.GroupDockSeparatorSize) {
+ // Dock to the right of the group
+ dockDelegate = delegate (DockItem it) {
+ DockTarget (it, dockObjects.Count);
+ };
+ rect = new Gdk.Rectangle (Allocation.Right - DockFrame.GroupDockSeparatorSize, Allocation.Y, DockFrame.GroupDockSeparatorSize, Allocation.Height);
+ return true;
+ }
+ else if (px <= Allocation.Left + DockFrame.GroupDockSeparatorSize) {
+ // Dock to the left of the group
+ dockDelegate = delegate (DockItem it) {
+ DockTarget (it, 0);
+ };
+ rect = new Gdk.Rectangle (Allocation.Left, Allocation.Y, DockFrame.GroupDockSeparatorSize, Allocation.Height);
+ return true;
+ }
+ // Dock in a separator
+ for (int n=0; n<VisibleObjects.Count; n++) {
+ DockObject ob = VisibleObjects [n];
+ if (n < VisibleObjects.Count - 1 &&
+ px >= ob.Allocation.Right - DockFrame.GroupDockSeparatorSize/2 &&
+ px <= ob.Allocation.Right + DockFrame.GroupDockSeparatorSize/2)
+ {
+ int dn = dockObjects.IndexOf (ob);
+ dockDelegate = delegate (DockItem it) {
+ DockTarget (it, dn+1);
+ };
+ rect = new Gdk.Rectangle (ob.Allocation.Right - DockFrame.GroupDockSeparatorSize/2, Allocation.Y, DockFrame.GroupDockSeparatorSize, Allocation.Height);
+ return true;
+ }
+ else if (ob.GetDockTarget (item, px, py, out dockDelegate, out rect))
+ return true;
+ }
+ }
+ else if (type == DockGroupType.Vertical) {
+ if (py >= Allocation.Bottom - DockFrame.GroupDockSeparatorSize) {
+ // Dock to the bottom of the group
+ dockDelegate = delegate (DockItem it) {
+ DockTarget (it, dockObjects.Count);
+ };
+ rect = new Gdk.Rectangle (Allocation.X, Allocation.Bottom - DockFrame.GroupDockSeparatorSize, Allocation.Width, DockFrame.GroupDockSeparatorSize);
+ return true;
+ }
+ else if (py <= Allocation.Top + DockFrame.GroupDockSeparatorSize) {
+ // Dock to the top of the group
+ dockDelegate = delegate (DockItem it) {
+ DockTarget (it, 0);
+ };
+ rect = new Gdk.Rectangle (Allocation.X, Allocation.Top, Allocation.Width, DockFrame.GroupDockSeparatorSize);
+ return true;
+ }
+ // Dock in a separator
+ for (int n=0; n<VisibleObjects.Count; n++) {
+ DockObject ob = VisibleObjects [n];
+ if (n < VisibleObjects.Count - 1 &&
+ py >= ob.Allocation.Bottom - DockFrame.GroupDockSeparatorSize/2 &&
+ py <= ob.Allocation.Bottom + DockFrame.GroupDockSeparatorSize/2)
+ {
+ int dn = dockObjects.IndexOf (ob);
+ dockDelegate = delegate (DockItem it) {
+ DockTarget (it, dn+1);
+ };
+ rect = new Gdk.Rectangle (Allocation.X, ob.Allocation.Bottom - DockFrame.GroupDockSeparatorSize/2, Allocation.Width, DockFrame.GroupDockSeparatorSize);
+ return true;
+ }
+ else if (ob.GetDockTarget (item, px, py, out dockDelegate, out rect))
+ return true;
+ }
+ }
+ dockDelegate = null;
+ rect = Gdk.Rectangle.Zero;
+ return false;
+ }
+
+ public void ReplaceItem (DockObject ob1, DockObject ob2)
+ {
+ int i = dockObjects.IndexOf (ob1);
+ dockObjects [i] = ob2;
+ ob2.ParentGroup = this;
+ ob2.ResetDefaultSize ();
+ ob2.Size = ob1.Size;
+ ob2.DefaultSize = ob1.DefaultSize;
+ ob2.AllocSize = ob1.AllocSize;
+ ResetVisibleGroups ();
+ }
+
+ public override void CopyFrom (DockObject other)
+ {
+ base.CopyFrom (other);
+ DockGroup grp = (DockGroup) other;
+ dockObjects = new List<DockObject> ();
+ foreach (DockObject ob in grp.dockObjects) {
+ DockObject cob = ob.Clone ();
+ cob.ParentGroup = this;
+ dockObjects.Add (cob);
+ }
+ type = grp.type;
+ ResetVisibleGroups ();
+ boundTabStrip = null;
+ tabFocus = null;
+ }
+
+ internal override bool Visible {
+ get {
+ foreach (DockObject ob in dockObjects)
+ if (ob.Visible)
+ return true;
+ return false;
+ }
+ }
+
+ internal void Dump ()
+ {
+ Dump (0);
+ }
+
+ internal override void Dump (int ind)
+ {
+ Console.WriteLine (new string (' ', ind) + "Group (" + type + ") size:" + Size + " DefaultSize:" + DefaultSize + " alloc:" + Allocation);
+ foreach (DockObject ob in dockObjects) {
+ ob.Dump (ind + 2);
+ }
+ }
+
+ internal override void Write (XmlWriter writer)
+ {
+ base.Write (writer);
+ writer.WriteAttributeString ("type", type.ToString ());
+ if (type == DockGroupType.Tabbed && currentTabPage != -1)
+ writer.WriteAttributeString ("currentTabPage", currentTabPage.ToString ());
+
+ foreach (DockObject ob in dockObjects) {
+ if (ob is DockGroupItem)
+ writer.WriteStartElement ("item");
+ else
+ writer.WriteStartElement ("group");
+ ob.Write (writer);
+ writer.WriteEndElement ();
+ }
+ }
+
+ internal override void Read (XmlReader reader)
+ {
+ base.Read (reader);
+ type = (DockGroupType) Enum.Parse (typeof(DockGroupType), reader.GetAttribute ("type"));
+ if (type == DockGroupType.Tabbed) {
+ string s = reader.GetAttribute ("currentTabPage");
+ if (s != null)
+ currentTabPage = int.Parse (s);
+ }
+
+ reader.MoveToElement ();
+ if (reader.IsEmptyElement) {
+ reader.Skip ();
+ return;
+ }
+
+ reader.ReadStartElement ();
+ reader.MoveToContent ();
+ while (reader.NodeType != XmlNodeType.EndElement) {
+ if (reader.NodeType == XmlNodeType.Element) {
+ if (reader.LocalName == "item") {
+ string id = reader.GetAttribute ("id");
+ DockItem it = Frame.GetItem (id);
+ if (it == null) {
+ it = Frame.AddItem (id);
+ it.IsPositionMarker = true;
+ }
+ DockGroupItem gitem = new DockGroupItem (Frame, it);
+ gitem.Read (reader);
+ AddObject (gitem);
+
+ reader.MoveToElement ();
+ reader.Skip ();
+ }
+ else if (reader.LocalName == "group") {
+ DockGroup grp = new DockGroup (Frame);
+ grp.Read (reader);
+ AddObject (grp);
+ }
+ }
+ else
+ reader.Skip ();
+ reader.MoveToContent ();
+ }
+ reader.ReadEndElement ();
+ }
+
+ public bool IsChildNextToMargin (Gtk.PositionType margin, DockObject obj)
+ {
+ if (type == DockGroupType.Tabbed)
+ return true;
+ else if (type == DockGroupType.Horizontal) {
+ if (margin == PositionType.Top || margin == PositionType.Bottom)
+ return true;
+ int i = VisibleObjects.IndexOf (obj);
+ if (margin == PositionType.Left && i == 0)
+ return true;
+ if (margin == PositionType.Right && i == VisibleObjects.Count - 1)
+ return true;
+ }
+ else if (type == DockGroupType.Vertical) {
+ if (margin == PositionType.Left || margin == PositionType.Right)
+ return true;
+ int i = VisibleObjects.IndexOf (obj);
+ if (margin == PositionType.Top && i == 0)
+ return true;
+ if (margin == PositionType.Bottom && i == VisibleObjects.Count - 1)
+ return true;
+ }
+ return false;
+ }
+
+ internal TabStrip TabStrip {
+ get { return boundTabStrip; }
+ }
+
+ public override string ToString ()
+ {
+ return "[DockGroup " + type + "]";
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs
new file mode 100644
index 0000000000..89e01c2b90
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs
@@ -0,0 +1,385 @@
+//
+// DockGroupItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Xml;
+using Gtk;
+
+namespace MonoDevelop.Components.Docking
+{
+ internal class DockGroupItem: DockObject
+ {
+ DockItem item;
+ bool visibleFlag;
+ DockItemStatus status;
+ Gdk.Rectangle floatRect;
+ Gtk.PositionType barDocPosition;
+ int autoHideSize = -1;
+
+ public DockItem Item {
+ get {
+ return item;
+ }
+ set {
+ item = value;
+ }
+ }
+
+ public string Id {
+ get { return item.Id; }
+ }
+
+ public DockGroupItem (DockFrame frame, DockItem item): base (frame)
+ {
+ this.item = item;
+ visibleFlag = item.Visible;
+ }
+
+ internal override void GetDefaultSize (out int width, out int height)
+ {
+ width = item.DefaultWidth;
+ height = item.DefaultHeight;
+ }
+
+ internal override void GetMinSize (out int width, out int height)
+ {
+ Requisition req = SizeRequest ();
+ width = req.Width;
+ height = req.Height;
+ }
+
+ internal override Requisition SizeRequest ()
+ {
+ return item.Widget.SizeRequest ();
+ }
+
+ public override void SizeAllocate (Gdk.Rectangle newAlloc)
+ {
+ item.Widget.SizeAllocate (newAlloc);
+ base.SizeAllocate (newAlloc);
+ }
+
+ public override bool Expand {
+ get { return item.Expand; }
+ }
+
+ internal override void QueueResize ()
+ {
+ item.Widget.QueueResize ();
+ }
+
+ internal override bool GetDockTarget (DockItem item, int px, int py, out DockDelegate dockDelegate, out Gdk.Rectangle rect)
+ {
+ return GetDockTarget (item, px, py, Allocation, out dockDelegate, out rect);
+ }
+
+ public bool GetDockTarget (DockItem item, int px, int py, Gdk.Rectangle rect, out DockDelegate dockDelegate, out Gdk.Rectangle outrect)
+ {
+ dockDelegate = null;
+
+ if (item != this.item && this.item.Visible && rect.Contains (px, py)) {
+ int xdockMargin = (int) ((double)rect.Width * (1.0 - DockFrame.ItemDockCenterArea)) / 2;
+ int ydockMargin = (int) ((double)rect.Height * (1.0 - DockFrame.ItemDockCenterArea)) / 2;
+ DockPosition pos;
+
+/* if (ParentGroup.Type == DockGroupType.Tabbed) {
+ rect = new Gdk.Rectangle (rect.X + xdockMargin, rect.Y + ydockMargin, rect.Width - xdockMargin*2, rect.Height - ydockMargin*2);
+ pos = DockPosition.CenterAfter;
+ }
+*/ if (px <= rect.X + xdockMargin && ParentGroup.Type != DockGroupType.Horizontal) {
+ outrect = new Gdk.Rectangle (rect.X, rect.Y, xdockMargin, rect.Height);
+ pos = DockPosition.Left;
+ }
+ else if (px >= rect.Right - xdockMargin && ParentGroup.Type != DockGroupType.Horizontal) {
+ outrect = new Gdk.Rectangle (rect.Right - xdockMargin, rect.Y, xdockMargin, rect.Height);
+ pos = DockPosition.Right;
+ }
+ else if (py <= rect.Y + ydockMargin && ParentGroup.Type != DockGroupType.Vertical) {
+ outrect = new Gdk.Rectangle (rect.X, rect.Y, rect.Width, ydockMargin);
+ pos = DockPosition.Top;
+ }
+ else if (py >= rect.Bottom - ydockMargin && ParentGroup.Type != DockGroupType.Vertical) {
+ outrect = new Gdk.Rectangle (rect.X, rect.Bottom - ydockMargin, rect.Width, ydockMargin);
+ pos = DockPosition.Bottom;
+ }
+ else {
+ outrect = new Gdk.Rectangle (rect.X + xdockMargin, rect.Y + ydockMargin, rect.Width - xdockMargin*2, rect.Height - ydockMargin*2);
+ pos = DockPosition.Center;
+ }
+
+ dockDelegate = delegate (DockItem dit) {
+ DockGroupItem it = ParentGroup.AddObject (dit, pos, Id);
+ it.SetVisible (true);
+ ParentGroup.FocusItem (it);
+ };
+ return true;
+ }
+ outrect = Gdk.Rectangle.Zero;
+ return false;
+ }
+
+ internal override void Dump (int ind)
+ {
+ Console.WriteLine (new string (' ', ind) + item.Id + " size:" + Size + " alloc:" + Allocation);
+ }
+
+ internal override void Write (XmlWriter writer)
+ {
+ base.Write (writer);
+ writer.WriteAttributeString ("id", item.Id);
+ writer.WriteAttributeString ("visible", visibleFlag.ToString ());
+ writer.WriteAttributeString ("status", status.ToString ());
+
+ if (status == DockItemStatus.AutoHide)
+ writer.WriteAttributeString ("autoHidePosition", barDocPosition.ToString ());
+
+ if (autoHideSize != -1)
+ writer.WriteAttributeString ("autoHideSize", autoHideSize.ToString ());
+
+ if (!floatRect.Equals (Gdk.Rectangle.Zero)) {
+ writer.WriteAttributeString ("floatX", floatRect.X.ToString ());
+ writer.WriteAttributeString ("floatY", floatRect.Y.ToString ());
+ writer.WriteAttributeString ("floatWidth", floatRect.Width.ToString ());
+ writer.WriteAttributeString ("floatHeight", floatRect.Height.ToString ());
+ }
+ }
+
+ internal override void Read (XmlReader reader)
+ {
+ base.Read (reader);
+ visibleFlag = bool.Parse (reader.GetAttribute ("visible")) && !item.IsPositionMarker;
+ status = (DockItemStatus) Enum.Parse (typeof (DockItemStatus), reader.GetAttribute ("status"));
+ int fx=0, fy=0, fw=0, fh=0;
+ string s = reader.GetAttribute ("floatX");
+ if (s != null)
+ fx = int.Parse (s);
+ s = reader.GetAttribute ("floatY");
+ if (s != null)
+ fy = int.Parse (s);
+ s = reader.GetAttribute ("floatWidth");
+ if (s != null)
+ fw = int.Parse (s);
+ s = reader.GetAttribute ("floatHeight");
+ if (s != null)
+ fh = int.Parse (s);
+ s = reader.GetAttribute ("autoHidePosition");
+ if (s != null)
+ barDocPosition = (PositionType) Enum.Parse (typeof (PositionType), s);
+ s = reader.GetAttribute ("autoHideSize");
+ if (s != null)
+ autoHideSize = int.Parse (s);
+ floatRect = new Gdk.Rectangle (fx, fy, fw, fh);
+ }
+
+ public override void CopyFrom (DockObject ob)
+ {
+ base.CopyFrom (ob);
+ DockGroupItem it = (DockGroupItem)ob;
+ item = it.item;
+ visibleFlag = it.visibleFlag;
+ floatRect = it.floatRect;
+ }
+
+ internal override bool Visible {
+ get { return visibleFlag && status == DockItemStatus.Dockable; }
+ }
+
+ internal bool VisibleFlag {
+ get { return visibleFlag; }
+ }
+
+ public DockItemStatus Status {
+ get {
+ return status;
+ }
+ set {
+ if (status == value)
+ return;
+
+ DockItemStatus oldValue = status;
+ status = value;
+
+ if (status == DockItemStatus.Floating) {
+ if (floatRect.Equals (Gdk.Rectangle.Zero)) {
+ int x, y;
+ item.Widget.TranslateCoordinates (item.Widget.Toplevel, 0, 0, out x, out y);
+ Gtk.Window win = Frame.Toplevel as Window;
+ if (win != null) {
+ int wx, wy;
+ win.GetPosition (out wx, out wy);
+ floatRect = new Gdk.Rectangle (wx + x, wy + y, Allocation.Width, Allocation.Height);
+ }
+ }
+ item.SetFloatMode (floatRect);
+ }
+ else if (status == DockItemStatus.AutoHide) {
+ SetBarDocPosition ();
+ item.SetAutoHideMode (barDocPosition, GetAutoHideSize (barDocPosition));
+ }
+ else
+ item.ResetMode ();
+
+ if (oldValue == DockItemStatus.Dockable || status == DockItemStatus.Dockable) {
+ // Update visibility if changing from/to dockable mode
+ if (ParentGroup != null)
+ ParentGroup.UpdateVisible (this);
+ }
+ }
+ }
+
+ void SetBarDocPosition ()
+ {
+ // Determine the best position for docking the item
+
+ if (Allocation.IsEmpty) {
+ // If the item is in a group, use the dock location of other items
+ DockObject current = this;
+ do {
+ if (EstimateBarDocPosition (current.ParentGroup, current, out barDocPosition, out autoHideSize))
+ return;
+ current = current.ParentGroup;
+ } while (current.ParentGroup != null);
+
+ // Can't find a good location. Just guess.
+ barDocPosition = PositionType.Bottom;
+ autoHideSize = 200;
+ return;
+ }
+ barDocPosition = CalcBarDocPosition ();
+ }
+
+ bool EstimateBarDocPosition (DockGroup grp, DockObject ignoreChild, out PositionType pos, out int size)
+ {
+ foreach (DockObject ob in grp.Objects) {
+ if (ob == ignoreChild)
+ continue;
+ if (ob is DockGroup) {
+ if (EstimateBarDocPosition ((DockGroup)ob, null, out pos, out size))
+ return true;
+ } else if (ob is DockGroupItem) {
+ DockGroupItem it = (DockGroupItem) ob;
+ if (it.status == DockItemStatus.AutoHide) {
+ pos = it.barDocPosition;
+ size = it.autoHideSize;
+ return true;
+ }
+ if (!it.Allocation.IsEmpty) {
+ pos = it.CalcBarDocPosition ();
+ size = it.GetAutoHideSize (pos);
+ return true;
+ }
+ }
+ }
+ pos = PositionType.Bottom;
+ size = 0;
+ return false;
+ }
+
+ PositionType CalcBarDocPosition ()
+ {
+ if (Allocation.Width < Allocation.Height) {
+ int mid = Allocation.Left + Allocation.Width / 2;
+ if (mid > Frame.Allocation.Left + Frame.Allocation.Width / 2)
+ return PositionType.Right;
+ else
+ return PositionType.Left;
+ } else {
+ int mid = Allocation.Top + Allocation.Height / 2;
+ if (mid > Frame.Allocation.Top + Frame.Allocation.Height / 2)
+ return PositionType.Bottom;
+ else
+ return PositionType.Top;
+ }
+ }
+
+ internal void SetVisible (bool value)
+ {
+ if (visibleFlag != value) {
+ visibleFlag = value;
+ if (visibleFlag)
+ item.ShowWidget ();
+ else
+ item.HideWidget ();
+ if (ParentGroup != null)
+ ParentGroup.UpdateVisible (this);
+ }
+ }
+
+ internal override void StoreAllocation ()
+ {
+ base.StoreAllocation ();
+ if (Status == DockItemStatus.Floating)
+ floatRect = item.FloatingPosition;
+ else if (Status == DockItemStatus.AutoHide)
+ autoHideSize = item.AutoHideSize;
+ }
+
+ internal override void RestoreAllocation ()
+ {
+ base.RestoreAllocation ();
+ item.UpdateVisibleStatus ();
+
+ if (Status == DockItemStatus.Floating)
+ item.SetFloatMode (floatRect);
+ else if (Status == DockItemStatus.AutoHide)
+ item.SetAutoHideMode (barDocPosition, GetAutoHideSize (barDocPosition));
+ else
+ item.ResetMode ();
+
+ if (!visibleFlag)
+ item.HideWidget ();
+ }
+
+ int GetAutoHideSize (Gtk.PositionType pos)
+ {
+ if (autoHideSize != -1)
+ return autoHideSize;
+
+ if (pos == PositionType.Left || pos == PositionType.Right)
+ return Allocation.Width;
+ else
+ return Allocation.Height;
+ }
+
+ public Gdk.Rectangle FloatRect {
+ get {
+ return floatRect;
+ }
+ set {
+ floatRect = value;
+ }
+ }
+
+ public override string ToString ()
+ {
+ return "[DockItem " + Item.Id + "]";
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs
new file mode 100644
index 0000000000..f77a3e9a60
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs
@@ -0,0 +1,41 @@
+//
+// DockGroupType.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Docking
+{
+ enum DockGroupType
+ {
+ Horizontal,
+ Vertical,
+ Tabbed
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs
new file mode 100644
index 0000000000..dd47a99572
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs
@@ -0,0 +1,513 @@
+//
+// DockItem.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Xml;
+using Gtk;
+using Mono.Unix;
+
+namespace MonoDevelop.Components.Docking
+{
+ public class DockItem
+ {
+ Widget content;
+ DockItemContainer widget;
+ string defaultLocation;
+ bool defaultVisible = true;
+ DockItemStatus defaultStatus = DockItemStatus.Dockable;
+ string id;
+ DockFrame frame;
+ int defaultWidth = -1;
+ int defaultHeight = -1;
+ string label;
+ Gdk.Pixbuf icon;
+ bool expand;
+ bool drawFrame = true;
+ DockItemBehavior behavior;
+ Gtk.Window floatingWindow;
+ DockBarItem dockBarItem;
+ bool lastVisibleStatus;
+ bool lastContentVisibleStatus;
+ bool gettingContent;
+ bool isPositionMarker;
+ bool stickyVisible;
+ IDockItemLabelProvider dockLabelProvider;
+ DockItemToolbar toolbarTop;
+ DockItemToolbar toolbarBottom;
+ DockItemToolbar toolbarLeft;
+ DockItemToolbar toolbarRight;
+
+ public event EventHandler VisibleChanged;
+ public event EventHandler ContentVisibleChanged;
+ public event EventHandler ContentRequired;
+
+ internal DockItem (DockFrame frame, string id)
+ {
+ this.frame = frame;
+ this.id = id;
+ }
+
+ internal DockItem (DockFrame frame, Widget w, string id)
+ {
+ this.frame = frame;
+ this.id = id;
+ content = w;
+ }
+
+ public string Id {
+ get { return id; }
+ }
+
+ internal bool StickyVisible {
+ get { return stickyVisible; }
+ set { stickyVisible = value; }
+ }
+
+ public string Label {
+ get { return label ?? string.Empty; }
+ set {
+ label = value;
+ if (widget != null)
+ widget.Label = label;
+ frame.UpdateTitle (this);
+ if (floatingWindow != null)
+ floatingWindow.Title = GetWindowTitle ();
+ }
+ }
+
+ public bool Visible {
+ get {
+ return frame.GetVisible (this);
+ }
+ set {
+ stickyVisible = value;
+ frame.SetVisible (this, value);
+ UpdateVisibleStatus ();
+ }
+ }
+
+ public bool VisibleInLayout (string layout)
+ {
+ return frame.GetVisible (this, layout);
+ }
+
+ public DockItemStatus Status {
+ get {
+ return frame.GetStatus (this);
+ }
+ set {
+ frame.SetStatus (this, value);
+ }
+ }
+
+ public IDockItemLabelProvider DockLabelProvider {
+ get { return this.dockLabelProvider; }
+ set { this.dockLabelProvider = value; }
+ }
+
+ internal DockItemContainer Widget {
+ get {
+ if (widget == null) {
+ widget = new DockItemContainer (frame, this);
+ widget.Visible = false; // Required to ensure that the Shown event is fired
+ widget.Label = label;
+ widget.Shown += SetupContent;
+ }
+ return widget;
+ }
+ }
+
+ void SetupContent (object ob, EventArgs args)
+ {
+ widget.Shown -= SetupContent;
+
+ if (ContentRequired != null) {
+ gettingContent = true;
+ try {
+ ContentRequired (this, EventArgs.Empty);
+ } finally {
+ gettingContent = false;
+ }
+ }
+
+ widget.UpdateContent ();
+ widget.Shown += delegate {
+ UpdateContentVisibleStatus ();
+ };
+ widget.Hidden += delegate {
+ UpdateContentVisibleStatus ();
+ };
+ widget.ParentSet += delegate {
+ UpdateContentVisibleStatus ();
+ };
+ UpdateContentVisibleStatus ();
+ }
+
+ public Widget Content {
+ get {
+ return content;
+ }
+ set {
+ content = value;
+ if (!gettingContent && widget != null)
+ widget.UpdateContent ();
+ }
+ }
+
+ public DockItemToolbar GetToolbar (PositionType position)
+ {
+ switch (position) {
+ case PositionType.Top:
+ if (toolbarTop == null)
+ toolbarTop = new DockItemToolbar (this, PositionType.Top);
+ return toolbarTop;
+ case PositionType.Bottom:
+ if (toolbarBottom == null)
+ toolbarBottom = new DockItemToolbar (this, PositionType.Bottom);
+ return toolbarBottom;
+ case PositionType.Left:
+ if (toolbarLeft == null)
+ toolbarLeft = new DockItemToolbar (this, PositionType.Left);
+ return toolbarLeft;
+ case PositionType.Right:
+ if (toolbarRight == null)
+ toolbarRight = new DockItemToolbar (this, PositionType.Right);
+ return toolbarRight;
+ default: throw new ArgumentException ();
+ }
+ }
+
+ internal bool HasWidget {
+ get { return widget != null; }
+ }
+
+ public string DefaultLocation {
+ get { return defaultLocation; }
+ set { defaultLocation = value; }
+ }
+
+ public bool DefaultVisible {
+ get { return defaultVisible; }
+ set { defaultVisible = value; }
+ }
+
+ public DockItemStatus DefaultStatus {
+ get { return defaultStatus; }
+ set { defaultStatus = value; }
+ }
+
+ public int DefaultWidth {
+ get {
+ return defaultWidth;
+ }
+ set {
+ defaultWidth = value;
+ }
+ }
+
+ public int DefaultHeight {
+ get {
+ return defaultHeight;
+ }
+ set {
+ defaultHeight = value;
+ }
+ }
+
+ public Gdk.Pixbuf Icon {
+ get {
+ return icon;
+ }
+ set {
+ icon = value;
+ }
+ }
+
+ public DockItemBehavior Behavior {
+ get {
+ return behavior;
+ }
+ set {
+ behavior = value;
+ if (widget != null)
+ widget.UpdateBehavior ();
+ }
+ }
+
+ public bool Expand {
+ get {
+ return expand;
+ }
+ set {
+ expand = value;
+ }
+ }
+
+ public bool DrawFrame {
+ get {
+ return drawFrame;
+ }
+ set {
+ drawFrame = value;
+ }
+ }
+
+ public void Present (bool giveFocus)
+ {
+ if (dockBarItem != null)
+ dockBarItem.Present (Status == DockItemStatus.AutoHide || giveFocus);
+ else
+ frame.Present (this, Status == DockItemStatus.AutoHide || giveFocus);
+ }
+
+ public bool ContentVisible {
+ get {
+ if (widget == null)
+ return false;
+ return widget.Parent != null && widget.Visible;
+ }
+ }
+
+ internal void SetFocus ()
+ {
+ SetFocus (Widget);
+ }
+
+ internal static void SetFocus (Widget w)
+ {
+ w.ChildFocus (DirectionType.TabForward);
+
+ Window win = w.Toplevel as Gtk.Window;
+ if (win == null)
+ return;
+
+ // Make sure focus is not given to internal children
+ if (win.Focus != null) {
+ Container c = win.Focus.Parent as Container;
+ if (c.Children.Length == 0)
+ win.Focus = c;
+ }
+ }
+
+ internal void UpdateVisibleStatus ()
+ {
+ bool vis = frame.GetVisible (this);
+ if (vis != lastVisibleStatus) {
+ lastVisibleStatus = vis;
+ if (VisibleChanged != null)
+ VisibleChanged (this, EventArgs.Empty);
+ }
+ UpdateContentVisibleStatus ();
+ }
+
+ internal void UpdateContentVisibleStatus ()
+ {
+ bool vis = ContentVisible;
+ if (vis != lastContentVisibleStatus) {
+ lastContentVisibleStatus = vis;
+ if (ContentVisibleChanged != null)
+ ContentVisibleChanged (this, EventArgs.Empty);
+ }
+ }
+
+ internal void ShowWidget ()
+ {
+ if (floatingWindow != null)
+ floatingWindow.Show ();
+ if (dockBarItem != null)
+ dockBarItem.Show ();
+ Widget.Show ();
+ }
+
+ internal void HideWidget ()
+ {
+ if (floatingWindow != null)
+ floatingWindow.Hide ();
+ else if (dockBarItem != null)
+ dockBarItem.Hide ();
+ else if (widget != null)
+ widget.Hide ();
+ }
+
+ internal void SetFloatMode (Gdk.Rectangle rect)
+ {
+ ResetBarUndockMode ();
+ if (floatingWindow == null) {
+ if (Widget.Parent != null)
+ Widget.Unparent ();
+ floatingWindow = new Window (GetWindowTitle ());
+ floatingWindow.TransientFor = frame.Toplevel as Gtk.Window;
+ floatingWindow.TypeHint = Gdk.WindowTypeHint.Utility;
+ floatingWindow.Add (Widget);
+ floatingWindow.DeleteEvent += delegate (object o, DeleteEventArgs a) {
+ if (behavior == DockItemBehavior.CantClose)
+ Status = DockItemStatus.Dockable;
+ else
+ Visible = false;
+ a.RetVal = true;
+ };
+ }
+ floatingWindow.Move (rect.X, rect.Y);
+ floatingWindow.Resize (rect.Width, rect.Height);
+ floatingWindow.Show ();
+ Widget.UpdateBehavior ();
+ Widget.Show ();
+ }
+
+ internal void ResetFloatMode ()
+ {
+ if (floatingWindow != null) {
+ floatingWindow.Remove (Widget);
+ floatingWindow.Destroy ();
+ floatingWindow = null;
+ widget.UpdateBehavior ();
+ }
+ }
+
+ internal Gdk.Rectangle FloatingPosition {
+ get {
+ if (floatingWindow != null) {
+ int x,y,w,h;
+ floatingWindow.GetPosition (out x, out y);
+ floatingWindow.GetSize (out w, out h);
+ return new Gdk.Rectangle (x,y,w,h);
+ }
+ else
+ return Gdk.Rectangle.Zero;
+ }
+ }
+
+ internal void ResetMode ()
+ {
+ ResetFloatMode ();
+ ResetBarUndockMode ();
+ }
+
+ internal void SetAutoHideMode (Gtk.PositionType pos, int size)
+ {
+ ResetMode ();
+ if (widget != null) {
+ widget.Hide (); // Avoids size allocation warning
+ widget.Unparent ();
+ }
+ dockBarItem = frame.BarDock (pos, this, size);
+ if (widget != null)
+ widget.UpdateBehavior ();
+ }
+
+ void ResetBarUndockMode ()
+ {
+ if (dockBarItem != null) {
+ dockBarItem.Close ();
+ dockBarItem = null;
+ if (widget != null)
+ widget.UpdateBehavior ();
+ }
+ }
+
+ internal int AutoHideSize {
+ get {
+ if (dockBarItem != null)
+ return dockBarItem.Size;
+ else
+ return -1;
+ }
+ }
+
+ internal bool IsPositionMarker {
+ get {
+ return isPositionMarker;
+ }
+ set {
+ isPositionMarker = value;
+ }
+ }
+
+ string GetWindowTitle ()
+ {
+ if (Label.IndexOf ('<') == -1)
+ return Label;
+ try {
+ XmlDocument doc = new XmlDocument ();
+ doc.LoadXml ("<a>" + Label + "</a>");
+ return doc.InnerText;
+ } catch {
+ return label;
+ }
+ }
+
+ internal void ShowDockPopupMenu (uint time)
+ {
+ Menu menu = new Menu ();
+
+ // Hide menuitem
+ if ((Behavior & DockItemBehavior.CantClose) == 0) {
+ MenuItem mitem = new MenuItem (Catalog.GetString("Hide"));
+ mitem.Activated += delegate { Visible = false; };
+ menu.Append (mitem);
+ }
+
+ CheckMenuItem citem;
+
+ // Dockable menuitem
+ citem = new CheckMenuItem (Catalog.GetString("Dockable"));
+ citem.Active = Status == DockItemStatus.Dockable;
+ citem.DrawAsRadio = true;
+ citem.Toggled += delegate { Status = DockItemStatus.Dockable; };
+ menu.Append (citem);
+
+ // Floating menuitem
+ if ((Behavior & DockItemBehavior.NeverFloating) == 0) {
+ citem = new CheckMenuItem (Catalog.GetString("Floating"));
+ citem.Active = Status == DockItemStatus.Floating;
+ citem.DrawAsRadio = true;
+ citem.Toggled += delegate { Status = DockItemStatus.Floating; };
+ menu.Append (citem);
+ }
+
+ // Auto Hide menuitem
+ if ((Behavior & DockItemBehavior.CantAutoHide) == 0) {
+ citem = new CheckMenuItem (Catalog.GetString("Auto Hide"));
+ citem.Active = Status == DockItemStatus.AutoHide;
+ citem.DrawAsRadio = true;
+ citem.Toggled += delegate { Status = DockItemStatus.AutoHide; };
+ menu.Append (citem);
+ }
+
+ menu.ShowAll ();
+ menu.Popup (null, null, null, 3, time);
+ }
+ }
+
+ public interface IDockItemLabelProvider
+ {
+ Gtk.Widget CreateLabel (Orientation orientation);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs
new file mode 100644
index 0000000000..1c62d92188
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs
@@ -0,0 +1,48 @@
+//
+// DockItemBehavior.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Docking
+{
+ [Flags]
+ public enum DockItemBehavior
+ {
+ Normal,
+ NeverFloating = 1 << 0,
+ NeverVertical = 1 << 1,
+ NeverHorizontal = 1 << 2,
+ CantClose = 1 << 3,
+ CantAutoHide = 1 << 4,
+ NoGrip = 1 << 5,
+ Sticky = 1 << 6, // Visibility is the same for al layouts
+ Locked = NoGrip,
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs
new file mode 100644
index 0000000000..43af768044
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs
@@ -0,0 +1,425 @@
+//
+// DockItemContainer.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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;
+using Mono.Unix;
+
+namespace MonoDevelop.Components.Docking
+{
+ class DockItemContainer: VBox
+ {
+ static Gdk.Pixbuf pixClose;
+ static Gdk.Pixbuf pixAutoHide;
+ static Gdk.Pixbuf pixDock;
+
+ Gtk.Label title;
+ Gtk.Button btnClose;
+ Gtk.Button btnDock;
+ string txt;
+ Gtk.EventBox header;
+ Gtk.Alignment headerAlign;
+ DockFrame frame;
+ DockItem item;
+ Widget widget;
+ Container borderFrame;
+ bool allowPlaceholderDocking;
+ bool pointerHover;
+ Box contentBox;
+
+ static Gdk.Cursor fleurCursor = new Gdk.Cursor (Gdk.CursorType.Fleur);
+ static Gdk.Cursor handCursor = new Gdk.Cursor (Gdk.CursorType.Hand2);
+
+ static DockItemContainer ()
+ {
+ try {
+ pixClose = Gdk.Pixbuf.LoadFromResource ("stock-close-12.png");
+ pixAutoHide = Gdk.Pixbuf.LoadFromResource ("stock-auto-hide.png");
+ pixDock = Gdk.Pixbuf.LoadFromResource ("stock-dock.png");
+ } catch (Exception) {
+ }
+ }
+
+ public DockItemContainer (DockFrame frame, DockItem item)
+ {
+ this.frame = frame;
+ this.item = item;
+
+ ResizeMode = Gtk.ResizeMode.Queue;
+ Spacing = 0;
+
+ title = new Gtk.Label ();
+ title.Xalign = 0;
+ title.Xpad = 3;
+ title.UseMarkup = true;
+
+ btnDock = new Button (new Gtk.Image (pixAutoHide));
+ btnDock.Relief = ReliefStyle.None;
+ btnDock.CanFocus = false;
+ btnDock.WidthRequest = btnDock.HeightRequest = 17;
+ btnDock.Clicked += OnClickDock;
+
+ btnClose = new Button (new Gtk.Image (pixClose));
+ btnClose.TooltipText = Catalog.GetString ("Hide");
+ btnClose.Relief = ReliefStyle.None;
+ btnClose.CanFocus = false;
+ btnClose.WidthRequest = btnClose.HeightRequest = 17;
+ btnClose.Clicked += delegate {
+ item.Visible = false;
+ };
+
+ HBox box = new HBox (false, 0);
+ box.PackStart (title, true, true, 0);
+ box.PackEnd (btnClose, false, false, 0);
+ box.PackEnd (btnDock, false, false, 0);
+
+ headerAlign = new Alignment (0.0f, 0.0f, 1.0f, 1.0f);
+ headerAlign.TopPadding = headerAlign.BottomPadding = headerAlign.RightPadding = headerAlign.LeftPadding = 1;
+ headerAlign.Add (box);
+
+ header = new EventBox ();
+ header.Events |= Gdk.EventMask.KeyPressMask | Gdk.EventMask.KeyReleaseMask;
+ header.ButtonPressEvent += HeaderButtonPress;
+ header.ButtonReleaseEvent += HeaderButtonRelease;
+ header.MotionNotifyEvent += HeaderMotion;
+ header.KeyPressEvent += HeaderKeyPress;
+ header.KeyReleaseEvent += HeaderKeyRelease;
+ header.Add (headerAlign);
+ header.ExposeEvent += HeaderExpose;
+ header.Realized += delegate {
+ header.GdkWindow.Cursor = handCursor;
+ };
+
+ foreach (Widget w in new Widget [] { header, btnDock, btnClose }) {
+ w.EnterNotifyEvent += HeaderEnterNotify;
+ w.LeaveNotifyEvent += HeaderLeaveNotify;
+ }
+
+ PackStart (header, false, false, 0);
+ ShowAll ();
+
+ PackStart (item.GetToolbar (PositionType.Top).Container, false, false, 0);
+
+ HBox hbox = new HBox ();
+ hbox.Show ();
+ hbox.PackStart (item.GetToolbar (PositionType.Left).Container, false, false, 0);
+
+ contentBox = new HBox ();
+ contentBox.Show ();
+ hbox.PackStart (contentBox, true, true, 0);
+
+ hbox.PackStart (item.GetToolbar (PositionType.Right).Container, false, false, 0);
+
+ PackStart (hbox, true, true, 0);
+
+ PackStart (item.GetToolbar (PositionType.Bottom).Container, false, false, 0);
+
+ UpdateBehavior ();
+ }
+
+ void OnClickDock (object s, EventArgs a)
+ {
+ if (item.Status == DockItemStatus.AutoHide || item.Status == DockItemStatus.Floating)
+ item.Status = DockItemStatus.Dockable;
+ else
+ item.Status = DockItemStatus.AutoHide;
+ }
+
+ public void UpdateContent ()
+ {
+ if (widget != null)
+ ((Gtk.Container)widget.Parent).Remove (widget);
+ widget = item.Content;
+
+ if (item.DrawFrame) {
+ if (borderFrame == null) {
+ borderFrame = new CustomFrame (1, 1, 1, 1);
+ borderFrame.Show ();
+ contentBox.Add (borderFrame);
+ }
+ if (widget != null) {
+ borderFrame.Add (widget);
+ widget.Show ();
+ }
+ }
+ else if (widget != null) {
+ if (borderFrame != null) {
+ contentBox.Remove (borderFrame);
+ borderFrame = null;
+ }
+ contentBox.Add (widget);
+ widget.Show ();
+ }
+ }
+
+ public void UpdateBehavior ()
+ {
+ btnClose.Visible = (item.Behavior & DockItemBehavior.CantClose) == 0;
+ header.Visible = (item.Behavior & DockItemBehavior.Locked) == 0;
+ btnDock.Visible = (item.Behavior & DockItemBehavior.CantAutoHide) == 0;
+
+ if (item.Status == DockItemStatus.AutoHide || item.Status == DockItemStatus.Floating) {
+ btnDock.Image = new Gtk.Image (pixDock);
+ btnDock.TooltipText = Catalog.GetString ("Dock");
+ }
+ else {
+ btnDock.Image = new Gtk.Image (pixAutoHide);
+ btnDock.TooltipText = Catalog.GetString ("Auto Hide");
+ }
+ }
+
+ void HeaderButtonPress (object ob, Gtk.ButtonPressEventArgs args)
+ {
+ if (args.Event.Button == 1) {
+ frame.ShowPlaceholder ();
+ header.GdkWindow.Cursor = fleurCursor;
+ frame.Toplevel.KeyPressEvent += HeaderKeyPress;
+ frame.Toplevel.KeyReleaseEvent += HeaderKeyRelease;
+ allowPlaceholderDocking = true;
+ }
+ else if (args.Event.Button == 3) {
+ item.ShowDockPopupMenu (args.Event.Time);
+ }
+ }
+
+ void HeaderButtonRelease (object ob, Gtk.ButtonReleaseEventArgs args)
+ {
+ if (args.Event.Button == 1) {
+ frame.DockInPlaceholder (item);
+ frame.HidePlaceholder ();
+ if (header.GdkWindow != null)
+ header.GdkWindow.Cursor = handCursor;
+ frame.Toplevel.KeyPressEvent -= HeaderKeyPress;
+ frame.Toplevel.KeyReleaseEvent -= HeaderKeyRelease;
+ }
+ }
+
+ void HeaderMotion (object ob, Gtk.MotionNotifyEventArgs args)
+ {
+ frame.UpdatePlaceholder (item, Allocation.Size, allowPlaceholderDocking);
+ }
+
+ [GLib.ConnectBeforeAttribute]
+ void HeaderKeyPress (object ob, Gtk.KeyPressEventArgs a)
+ {
+ if (a.Event.Key == Gdk.Key.Control_L || a.Event.Key == Gdk.Key.Control_R) {
+ allowPlaceholderDocking = false;
+ frame.UpdatePlaceholder (item, Allocation.Size, false);
+ }
+ if (a.Event.Key == Gdk.Key.Escape) {
+ frame.HidePlaceholder ();
+ frame.Toplevel.KeyPressEvent -= HeaderKeyPress;
+ frame.Toplevel.KeyReleaseEvent -= HeaderKeyRelease;
+ Gdk.Pointer.Ungrab (0);
+ }
+ }
+
+ [GLib.ConnectBeforeAttribute]
+ void HeaderKeyRelease (object ob, Gtk.KeyReleaseEventArgs a)
+ {
+ if (a.Event.Key == Gdk.Key.Control_L || a.Event.Key == Gdk.Key.Control_R) {
+ allowPlaceholderDocking = true;
+ frame.UpdatePlaceholder (item, Allocation.Size, true);
+ }
+ }
+
+ private void HeaderExpose (object ob, Gtk.ExposeEventArgs a)
+ {
+ Gdk.Rectangle rect = new Gdk.Rectangle (0, 0, header.Allocation.Width - 1, header.Allocation.Height);
+ HslColor gcol = frame.Style.Background (Gtk.StateType.Normal);
+
+ if (pointerHover)
+ gcol.L *= 1.1;
+ else
+ gcol.L *= 1;
+
+ if (gcol.L > 1)
+ gcol.L = 1;
+
+ using (Cairo.Context cr = Gdk.CairoHelper.Create (a.Event.Window)) {
+ cr.NewPath ();
+ cr.MoveTo (0, 0);
+ cr.RelLineTo (rect.Width, 0);
+ cr.RelLineTo (0, rect.Height);
+ cr.RelLineTo (-rect.Width, 0);
+ cr.RelLineTo (0, -rect.Height);
+ cr.ClosePath ();
+ Cairo.Gradient pat = new Cairo.LinearGradient (0, 0, rect.Width, rect.Height);
+ Cairo.Color color1 = gcol;
+ pat.AddColorStop (0, color1);
+ color1.A = 0.3;
+ pat.AddColorStop (1, color1);
+ cr.Pattern = pat;
+ cr.FillPreserve ();
+ }
+
+// header.GdkWindow.DrawRectangle (gc, true, rect);
+ header.GdkWindow.DrawRectangle (frame.Style.DarkGC (Gtk.StateType.Normal), false, rect);
+
+ foreach (Widget child in header.Children)
+ header.PropagateExpose (child, a.Event);
+ }
+
+ private void HeaderLeaveNotify (object ob, EventArgs a)
+ {
+ pointerHover = false;
+ header.QueueDraw ();
+ }
+
+ private void HeaderEnterNotify (object ob, EventArgs a)
+ {
+ pointerHover = true;
+ header.QueueDraw ();
+ }
+
+ public string Label {
+ get { return txt; }
+ set {
+ title.Markup = "<small>" + value + "</small>";
+ txt = value;
+ }
+ }
+ }
+
+ class CustomFrame: Bin
+ {
+ Gtk.Widget child;
+ int topMargin;
+ int bottomMargin;
+ int leftMargin;
+ int rightMargin;
+
+ int topPadding;
+ int bottomPadding;
+ int leftPadding;
+ int rightPadding;
+
+ public CustomFrame ()
+ {
+ }
+
+ public CustomFrame (int topMargin, int bottomMargin, int leftMargin, int rightMargin)
+ {
+ SetMargins (topMargin, bottomMargin, leftMargin, rightMargin);
+ }
+
+ public void SetMargins (int topMargin, int bottomMargin, int leftMargin, int rightMargin)
+ {
+ this.topMargin = topMargin;
+ this.bottomMargin = bottomMargin;
+ this.leftMargin = leftMargin;
+ this.rightMargin = rightMargin;
+ }
+
+ public void SetPadding (int topPadding, int bottomPadding, int leftPadding, int rightPadding)
+ {
+ this.topPadding = topPadding;
+ this.bottomPadding = bottomPadding;
+ this.leftPadding = leftPadding;
+ this.rightPadding = rightPadding;
+ }
+
+ public bool GradientBackround { get; set; }
+
+ protected override void OnAdded (Widget widget)
+ {
+ base.OnAdded (widget);
+ child = widget;
+ }
+
+ protected override void OnSizeRequested (ref Requisition requisition)
+ {
+ requisition = child.SizeRequest ();
+ requisition.Width += leftMargin + rightMargin + leftPadding + rightPadding;
+ requisition.Height += topMargin + bottomMargin + topPadding + bottomPadding;
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation);
+ if (allocation.Width > leftMargin + rightMargin + leftPadding + rightPadding) {
+ allocation.X += leftMargin + leftPadding;
+ allocation.Width -= leftMargin + rightMargin + leftPadding + rightPadding;
+ }
+ if (allocation.Height > topMargin + bottomMargin + topPadding + bottomPadding) {
+ allocation.Y += topMargin + topPadding;
+ allocation.Height -= topMargin + bottomMargin + topPadding + bottomPadding;
+ }
+ child.SizeAllocate (allocation);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ Gdk.Rectangle rect;
+
+ if (GradientBackround) {
+ rect = new Gdk.Rectangle (Allocation.X, Allocation.Y, Allocation.Width, Allocation.Height);
+ HslColor gcol = Style.Background (Gtk.StateType.Normal);
+
+ using (Cairo.Context cr = Gdk.CairoHelper.Create (GdkWindow)) {
+ cr.NewPath ();
+ cr.MoveTo (rect.X, rect.Y);
+ cr.RelLineTo (rect.Width, 0);
+ cr.RelLineTo (0, rect.Height);
+ cr.RelLineTo (-rect.Width, 0);
+ cr.RelLineTo (0, -rect.Height);
+ cr.ClosePath ();
+ Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Bottom);
+ Cairo.Color color1 = gcol;
+ pat.AddColorStop (0, color1);
+ gcol.L -= 0.1;
+ if (gcol.L < 0) gcol.L = 0;
+ pat.AddColorStop (1, gcol);
+ cr.Pattern = pat;
+ cr.FillPreserve ();
+ }
+ }
+
+ bool res = base.OnExposeEvent (evnt);
+
+ Gdk.GC borderColor = Style.DarkGC (Gtk.StateType.Normal);
+
+ rect = Allocation;
+ for (int n=0; n<topMargin; n++)
+ GdkWindow.DrawLine (borderColor, rect.X, rect.Y + n, rect.Right - 1, rect.Y + n);
+
+ for (int n=0; n<bottomMargin; n++)
+ GdkWindow.DrawLine (borderColor, rect.X, rect.Bottom - n - 1, rect.Right - 1, rect.Bottom - n - 1);
+
+ for (int n=0; n<leftMargin; n++)
+ GdkWindow.DrawLine (borderColor, rect.X + n, rect.Y, rect.X + n, rect.Bottom - 1);
+
+ for (int n=0; n<rightMargin; n++)
+ GdkWindow.DrawLine (borderColor, rect.Right - n - 1, rect.Y, rect.Right - n - 1, rect.Bottom - 1);
+
+ return res;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs
new file mode 100644
index 0000000000..e6e56d2fa4
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs
@@ -0,0 +1,42 @@
+//
+// DockItemStatus.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Docking
+{
+ public enum DockItemStatus
+ {
+ Dockable,
+ Floating,
+ AutoHide
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs
new file mode 100644
index 0000000000..d85c8c8f89
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs
@@ -0,0 +1,181 @@
+//
+// DockItemToolbar.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;
+using Gtk;
+
+namespace MonoDevelop.Components.Docking
+{
+ public class DockItemToolbar
+ {
+ DockItem parentItem;
+ CustomFrame frame;
+ Box box;
+ PositionType position;
+ bool empty = true;
+
+ internal DockItemToolbar (DockItem parentItem, PositionType position)
+ {
+ this.parentItem = parentItem;
+ frame = new CustomFrame ();
+ switch (position) {
+ case PositionType.Top:
+ frame.SetMargins (0, 0, 1, 1);
+ frame.SetPadding (0, 2, 2, 0);
+ break;
+ case PositionType.Bottom:
+ frame.SetMargins (0, 1, 1, 1);
+ frame.SetPadding (2, 2, 2, 0);
+ break;
+ case PositionType.Left:
+ frame.SetMargins (0, 1, 1, 0);
+ frame.SetPadding (0, 0, 2, 2);
+ break;
+ case PositionType.Right:
+ frame.SetMargins (0, 1, 0, 1);
+ frame.SetPadding (0, 0, 2, 2);
+ break;
+ }
+ this.position = position;
+ if (position == PositionType.Top || position == PositionType.Bottom)
+ box = new HBox (false, 3);
+ else
+ box = new VBox (false, 3);
+ box.Show ();
+ frame.Add (box);
+ frame.GradientBackround = true;
+ }
+
+ public DockItem DockItem {
+ get { return parentItem; }
+ }
+
+ internal Widget Container {
+ get { return frame; }
+ }
+
+ public PositionType Position {
+ get { return this.position; }
+ }
+
+ public void Add (Widget widget)
+ {
+ Add (widget, false);
+ }
+
+ public void Add (Widget widget, bool fill)
+ {
+ Add (widget, fill, -1);
+ }
+
+ public void Add (Widget widget, bool fill, int padding)
+ {
+ Add (widget, fill, padding, -1);
+ }
+
+ void Add (Widget widget, bool fill, int padding, int index)
+ {
+ int defaultPadding = 3;
+
+ if (widget is Button) {
+ ((Button)widget).Relief = ReliefStyle.None;
+ ((Button)widget).FocusOnClick = false;
+ defaultPadding = 0;
+ }
+ else if (widget is Entry) {
+ ((Entry)widget).HasFrame = false;
+ }
+ else if (widget is ComboBox) {
+ ((ComboBox)widget).HasFrame = false;
+ }
+ else if (widget is VSeparator)
+ ((VSeparator)widget).HeightRequest = 10;
+
+ if (padding == -1)
+ padding = defaultPadding;
+
+ box.PackStart (widget, fill, fill, (uint)padding);
+ if (empty) {
+ empty = false;
+ frame.Show ();
+ }
+ if (index != -1) {
+ Box.BoxChild bc = (Box.BoxChild) box [widget];
+ bc.Position = index;
+ }
+ }
+
+ public void Insert (Widget w, int index)
+ {
+ Add (w, false, 0, index);
+ }
+
+ public void Remove (Widget widget)
+ {
+ box.Remove (widget);
+ }
+
+ public bool Visible {
+ get {
+ return empty || frame.Visible;
+ }
+ set {
+ frame.Visible = value;
+ }
+ }
+
+ public bool Sensitive {
+ get { return frame.Sensitive; }
+ set { frame.Sensitive = value; }
+ }
+
+ public void ShowAll ()
+ {
+ frame.ShowAll ();
+ }
+
+ public Widget[] Children {
+ get { return box.Children; }
+ }
+ }
+
+ public class DockToolButton: Gtk.Button
+ {
+ public DockToolButton (string stockId)
+ {
+ Image = new Gtk.Image (stockId, IconSize.Menu);
+ Image.Show ();
+ }
+
+ public DockToolButton (string stockId, string label)
+ {
+ Label = label;
+ Image = new Gtk.Image (stockId, IconSize.Menu);
+ Image.Show ();
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs
new file mode 100644
index 0000000000..e215378f3f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs
@@ -0,0 +1,107 @@
+//
+// MonoDevelop.Components.Docking.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Xml;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Components.Docking
+{
+ class DockLayout: DockGroup
+ {
+ string name;
+ int layoutWidth = 1024;
+ int layoutHeight = 768;
+
+ public DockLayout (DockFrame frame): base (frame, DockGroupType.Horizontal)
+ {
+ }
+
+ public string Name {
+ get {
+ return name;
+ }
+ set {
+ name = value;
+ }
+ }
+
+ internal override void Write (XmlWriter writer)
+ {
+ writer.WriteStartElement ("layout");
+ writer.WriteAttributeString ("name", name);
+ writer.WriteAttributeString ("width", layoutWidth.ToString ());
+ writer.WriteAttributeString ("height", layoutHeight.ToString ());
+ base.Write (writer);
+ writer.WriteEndElement ();
+ }
+
+ internal override void Read (XmlReader reader)
+ {
+ name = reader.GetAttribute ("name");
+ string s = reader.GetAttribute ("width");
+ if (s != null)
+ layoutWidth = int.Parse (s);
+ s = reader.GetAttribute ("height");
+ if (s != null)
+ layoutHeight = int.Parse (s);
+ base.Read (reader);
+ }
+
+ public static DockLayout Read (DockFrame frame, XmlReader reader)
+ {
+ DockLayout layout = new DockLayout (frame);
+ layout.Read (reader);
+ return layout;
+ }
+
+ public override void SizeAllocate (Gdk.Rectangle rect)
+ {
+ Size = rect.Width;
+ base.SizeAllocate (rect);
+ }
+
+ internal override void StoreAllocation ()
+ {
+ base.StoreAllocation ();
+ layoutWidth = Allocation.Width;
+ layoutHeight = Allocation.Height;
+ }
+
+ internal override void RestoreAllocation ()
+ {
+ Allocation = new Gdk.Rectangle (0, 0, layoutWidth, layoutHeight);
+ base.RestoreAllocation ();
+ }
+
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs
new file mode 100644
index 0000000000..f909bf3542
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs
@@ -0,0 +1,284 @@
+//
+// DockObject.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Xml;
+using Gtk;
+using System.Globalization;
+
+namespace MonoDevelop.Components.Docking
+{
+ internal abstract class DockObject
+ {
+ DockGroup parentGroup;
+ DockFrame frame;
+ Gdk.Rectangle rect;
+
+ // The current size in pixels of this item
+ double size = -1;
+
+ // The current size in pixels of this item, but as an integer.
+ // In general it is the same value as size, but it may change a bit due to rounding.
+ int allocSize = -1;
+
+ double defaultHorSize = -1;
+ double defaultVerSize = -1;
+ double prefSize = 0;
+
+ // Those are the last known coordinates of the item. They are stored in StoreAllocation
+ // and restored to rect in RestoreAllocation. This is needed for example when a layout
+ // is cloned. It is convenient to have allocation information in the cloned layout, even
+ // if the layout has never been displayed (e.g., to decide the autohide dock location)
+ int ax=-1, ay=-1;
+
+ public DockObject (DockFrame frame)
+ {
+ this.frame = frame;
+ }
+
+ internal DockGroup ParentGroup {
+ get {
+ return parentGroup;
+ }
+ set {
+ parentGroup = value;
+ if (size < 0)
+ size = prefSize = DefaultSize;
+ }
+ }
+
+ public double Size {
+ get {
+ return size;
+ }
+ set {
+ size = value;
+ }
+ }
+
+ public bool HasAllocatedSize {
+ get { return allocSize != -1; }
+ }
+
+ public double DefaultSize {
+ get {
+ if (defaultHorSize < 0)
+ InitDefaultSizes ();
+ if (parentGroup != null) {
+ if (parentGroup.Type == DockGroupType.Horizontal)
+ return defaultHorSize;
+ else if (parentGroup.Type == DockGroupType.Vertical)
+ return defaultVerSize;
+ }
+ return 0;
+ }
+ set {
+ if (parentGroup != null) {
+ if (parentGroup.Type == DockGroupType.Horizontal)
+ defaultHorSize = value;
+ else if (parentGroup.Type == DockGroupType.Vertical)
+ defaultVerSize = value;
+ }
+ }
+ }
+
+ internal void ResetDefaultSize ()
+ {
+ defaultHorSize = -1;
+ defaultVerSize = -1;
+ }
+
+ public int MinSize {
+ get {
+ int w,h;
+ GetMinSize (out w, out h);
+ if (parentGroup != null) {
+ if (parentGroup.Type == DockGroupType.Horizontal)
+ return w;
+ else if (parentGroup.Type == DockGroupType.Vertical)
+ return h;
+ }
+ return w;
+ }
+ }
+
+ public abstract bool Expand { get; }
+
+ public virtual void SizeAllocate (Gdk.Rectangle rect)
+ {
+ this.rect = rect;
+ }
+
+ internal Gdk.Rectangle Allocation {
+ get {
+ return rect;
+ }
+ set {
+ rect = value;
+ }
+ }
+
+ public int AllocSize {
+ get {
+ return allocSize;
+ }
+ set {
+ allocSize = value;
+ }
+ }
+
+ public MonoDevelop.Components.Docking.DockFrame Frame {
+ get {
+ return frame;
+ }
+ }
+
+ public double PrefSize {
+ get {
+ return prefSize;
+ }
+ set {
+ prefSize = value;
+ }
+ }
+
+ void InitDefaultSizes ()
+ {
+ int width, height;
+ GetDefaultSize (out width, out height);
+ if (width == -1)
+ width = frame.DefaultItemWidth;
+ if (height == -1)
+ height = frame.DefaultItemHeight;
+ defaultHorSize = (double) width;
+ defaultVerSize = (double) height;
+ }
+
+ internal virtual void GetDefaultSize (out int width, out int height)
+ {
+ width = -1;
+ height = -1;
+ }
+
+ internal virtual void GetMinSize (out int width, out int height)
+ {
+ width = 0;
+ height = 0;
+ }
+
+ internal abstract void QueueResize ();
+
+ internal abstract bool GetDockTarget (DockItem item, int px, int py, out DockDelegate dockDelegate, out Gdk.Rectangle rect);
+
+ internal abstract Gtk.Requisition SizeRequest ();
+
+ internal abstract bool Visible { get; }
+
+ internal abstract void Dump (int ind);
+
+ internal virtual void RestoreAllocation ()
+ {
+ if (parentGroup != null) {
+ int x = ax != -1 ? ax : 0;
+ int y = ay != -1 ? ay : 0;
+ if (parentGroup.Type == DockGroupType.Horizontal)
+ rect = new Gdk.Rectangle (x, y, (int)size, parentGroup.Allocation.Height);
+ else if (parentGroup.Type == DockGroupType.Vertical)
+ rect = new Gdk.Rectangle (x, y, parentGroup.Allocation.Width, (int)size);
+ }
+ }
+
+ internal virtual void StoreAllocation ()
+ {
+ if (Visible) {
+ if (parentGroup == null || parentGroup.Type == DockGroupType.Horizontal)
+ size = prefSize = (int) rect.Width;
+ else if (parentGroup.Type == DockGroupType.Vertical)
+ size = prefSize = (int) rect.Height;
+ ax = Allocation.X;
+ ay = Allocation.Y;
+ }
+ }
+
+ internal virtual void Write (XmlWriter writer)
+ {
+ writer.WriteAttributeString ("size", size.ToString (CultureInfo.InvariantCulture));
+ writer.WriteAttributeString ("prefSize", prefSize.ToString (CultureInfo.InvariantCulture));
+ writer.WriteAttributeString ("defaultHorSize", defaultHorSize.ToString (CultureInfo.InvariantCulture));
+ writer.WriteAttributeString ("defaultVerSize", defaultVerSize.ToString (CultureInfo.InvariantCulture));
+ }
+
+ internal virtual void Read (XmlReader reader)
+ {
+ size = double.Parse (reader.GetAttribute ("size"), CultureInfo.InvariantCulture);
+ prefSize = double.Parse (reader.GetAttribute ("prefSize"), CultureInfo.InvariantCulture);
+ defaultHorSize = double.Parse (reader.GetAttribute ("defaultHorSize"), CultureInfo.InvariantCulture);
+ defaultVerSize = double.Parse (reader.GetAttribute ("defaultVerSize"), CultureInfo.InvariantCulture);
+ }
+
+ public virtual void CopyFrom (DockObject ob)
+ {
+ parentGroup = null;
+ frame = ob.frame;
+ rect = ob.rect;
+ size = ob.size;
+ allocSize = ob.allocSize;
+ defaultHorSize = ob.defaultHorSize;
+ defaultVerSize = ob.defaultVerSize;
+ prefSize = ob.prefSize;
+ }
+
+ public DockObject Clone ()
+ {
+ DockObject ob = (DockObject) this.MemberwiseClone ();
+ ob.CopyFrom (this);
+ return ob;
+ }
+
+ public virtual void CopySizeFrom (DockObject obj)
+ {
+ size = obj.size;
+ allocSize = obj.allocSize;
+ defaultHorSize = obj.defaultHorSize;
+ defaultVerSize = obj.defaultVerSize;
+ prefSize = obj.prefSize;
+ }
+
+ public virtual bool IsNextToMargin (Gtk.PositionType margin)
+ {
+ if (ParentGroup == null)
+ return true;
+ if (!ParentGroup.IsNextToMargin (margin))
+ return false;
+ return ParentGroup.IsChildNextToMargin (margin, this);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs
new file mode 100644
index 0000000000..39404ad97e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs
@@ -0,0 +1,45 @@
+//
+// DockPosition.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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.Docking
+{
+ enum DockPosition
+ {
+ Left,
+ Right,
+ Top,
+ Bottom,
+ Center,
+ CenterBefore
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs
new file mode 100644
index 0000000000..a2f1f4977a
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs
@@ -0,0 +1,143 @@
+//
+// PlaceholderWindow.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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 Gdk;
+using Gtk;
+
+namespace MonoDevelop.Components.Docking
+{
+ internal class PlaceholderWindow: Gtk.Window
+ {
+ Gdk.GC redgc;
+ uint anim;
+ int rx, ry, rw, rh;
+ bool allowDocking;
+
+ public bool AllowDocking {
+ get {
+ return allowDocking;
+ }
+ set {
+ allowDocking = value;
+ }
+ }
+
+ public PlaceholderWindow (DockFrame frame): base (Gtk.WindowType.Popup)
+ {
+ SkipTaskbarHint = true;
+ Decorated = false;
+ TransientFor = (Gtk.Window) frame.Toplevel;
+ TypeHint = WindowTypeHint.Utility;
+
+ // Create the mask for the arrow
+
+ Realize ();
+ redgc = new Gdk.GC (GdkWindow);
+ redgc.RgbFgColor = frame.Style.Background (StateType.Selected);
+ }
+
+ void CreateShape (int width, int height)
+ {
+ Gdk.Color black, white;
+ black = new Gdk.Color (0, 0, 0);
+ black.Pixel = 1;
+ white = new Gdk.Color (255, 255, 255);
+ white.Pixel = 0;
+
+ Gdk.Pixmap pm = new Pixmap (this.GdkWindow, width, height, 1);
+ Gdk.GC gc = new Gdk.GC (pm);
+ gc.Background = white;
+ gc.Foreground = white;
+ pm.DrawRectangle (gc, true, 0, 0, width, height);
+
+ gc.Foreground = black;
+ pm.DrawRectangle (gc, false, 0, 0, width - 1, height - 1);
+ pm.DrawRectangle (gc, false, 1, 1, width - 3, height - 3);
+
+ this.ShapeCombineMask (pm, 0, 0);
+ }
+
+ protected override void OnSizeAllocated (Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation);
+ CreateShape (allocation.Width, allocation.Height);
+ }
+
+
+ protected override bool OnExposeEvent (Gdk.EventExpose args)
+ {
+ //base.OnExposeEvent (args);
+ int w, h;
+ this.GetSize (out w, out h);
+ this.GdkWindow.DrawRectangle (redgc, false, 0, 0, w-1, h-1);
+ this.GdkWindow.DrawRectangle (redgc, false, 1, 1, w-3, h-3);
+ return true;
+ }
+
+ public void Relocate (int x, int y, int w, int h, bool animate)
+ {
+ if (x != rx || y != ry || w != rw || h != rh) {
+ Move (x, y);
+ Resize (w, h);
+
+ rx = x; ry = y; rw = w; rh = h;
+
+ if (anim != 0) {
+ GLib.Source.Remove (anim);
+ anim = 0;
+ }
+ if (animate && w < 150 && h < 150) {
+ int sa = 7;
+ Move (rx-sa, ry-sa);
+ Resize (rw+sa*2, rh+sa*2);
+ anim = GLib.Timeout.Add (10, RunAnimation);
+ }
+ }
+ }
+
+ bool RunAnimation ()
+ {
+ int cx, cy, ch, cw;
+ GetSize (out cw, out ch);
+ GetPosition (out cx, out cy);
+
+ if (cx != rx) {
+ cx++; cy++;
+ ch-=2; cw-=2;
+ Move (cx, cy);
+ Resize (cw, ch);
+ return true;
+ }
+ anim = 0;
+ return false;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs
new file mode 100644
index 0000000000..f8d20a906e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs
@@ -0,0 +1,314 @@
+//
+// ShadedContainer.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;
+using System.Collections.Generic;
+using Gtk;
+using MonoDevelop.Components;
+
+namespace MonoDevelop.Components.Docking
+{
+ public class ShadedContainer
+ {
+ struct Section {
+ public int Offset;
+ public int Size;
+ }
+
+ Gdk.Color lightColor;
+ Gdk.Color darkColor;
+ int shadowSize = 2;
+
+ List<Widget> widgets = new List<Widget> ();
+ Dictionary<Widget, Gdk.Rectangle[]> allocations = new Dictionary<Widget, Gdk.Rectangle[]> ();
+
+ public ShadedContainer ()
+ {
+ }
+
+ public int ShadowSize {
+ get { return this.shadowSize; }
+ set { this.shadowSize = value; RedrawAll (); }
+ }
+
+ public Gdk.Color LightColor {
+ get { return this.lightColor; }
+ set { this.lightColor = value; RedrawAll (); }
+ }
+
+ public Gdk.Color DarkColor {
+ get { return this.darkColor; }
+ set { this.darkColor = value; RedrawAll (); }
+ }
+
+ public void Add (Gtk.Widget w)
+ {
+ widgets.Add (w);
+ UpdateAllocation (w);
+ w.Destroyed += HandleWDestroyed;
+ w.Shown += HandleWShown;
+ w.Hidden += HandleWHidden;
+ w.SizeAllocated += HandleWSizeAllocated;
+ IShadedWidget sw = w as IShadedWidget;
+ if (sw != null)
+ sw.AreasChanged += HandleSwAreasChanged;
+ RedrawAll ();
+ }
+
+ public void Remove (Widget w)
+ {
+ widgets.Remove (w);
+ allocations.Remove (w);
+ w.Destroyed -= HandleWDestroyed;
+ w.Shown -= HandleWShown;
+ w.Hidden -= HandleWHidden;
+ IShadedWidget sw = w as IShadedWidget;
+ if (sw != null)
+ sw.AreasChanged -= HandleSwAreasChanged;
+ RedrawAll ();
+ }
+
+ void UpdateAllocation (Widget w)
+ {
+ if (w.IsRealized) {
+ int x, y;
+ w.GdkWindow.GetOrigin (out x, out y);
+ IShadedWidget sw = w as IShadedWidget;
+ if (sw != null) {
+ List<Gdk.Rectangle> rects = new List<Gdk.Rectangle> ();
+ foreach (Gdk.Rectangle ar in sw.GetShadedAreas ()) {
+ Gdk.Rectangle r = new Gdk.Rectangle (x + ar.X, y + ar.Y, ar.Width, ar.Height);
+ rects.Add (r);
+ }
+ allocations [w] = rects.ToArray ();
+ } else {
+ Gdk.Rectangle r = new Gdk.Rectangle (x + w.Allocation.X, y + w.Allocation.Y, w.Allocation.Width, w.Allocation.Height);
+ allocations [w] = new Gdk.Rectangle [] { r };
+ }
+ }
+ else {
+ allocations.Remove (w);
+ }
+ }
+
+ void HandleSwAreasChanged (object sender, EventArgs e)
+ {
+ UpdateAllocation ((Gtk.Widget)sender);
+ RedrawAll ();
+ }
+
+ void HandleWSizeAllocated (object o, SizeAllocatedArgs args)
+ {
+ UpdateAllocation ((Widget) o);
+ RedrawAll ();
+ }
+
+ void HandleWHidden (object sender, EventArgs e)
+ {
+ RedrawAll ();
+ }
+
+ void HandleWShown (object sender, EventArgs e)
+ {
+ RedrawAll ();
+ }
+
+ void HandleWDestroyed (object sender, EventArgs e)
+ {
+ Remove ((Widget)sender);
+ }
+
+ void RedrawAll ()
+ {
+ foreach (Widget w in widgets)
+ w.QueueDraw ();
+ }
+
+ public void DrawBackground (Gtk.Widget w)
+ {
+ DrawBackground (w, w.Allocation);
+ }
+
+ public void DrawBackground (Gtk.Widget w, Gdk.Rectangle allocation)
+ {
+ if (shadowSize == 0) {
+ Gdk.Rectangle wr = new Gdk.Rectangle (allocation.X, allocation.Y, allocation.Width, allocation.Height);
+ using (Cairo.Context ctx = Gdk.CairoHelper.Create (w.GdkWindow)) {
+ ctx.Rectangle (wr.X, wr.Y, wr.Width, wr.Height);
+ ctx.Color = GtkUtil.ToCairoColor (lightColor);
+ ctx.Fill ();
+ }
+ return;
+ }
+
+ List<Section> secsT = new List<Section> ();
+ List<Section> secsB = new List<Section> ();
+ List<Section> secsR = new List<Section> ();
+ List<Section> secsL = new List<Section> ();
+
+ int x, y;
+ w.GdkWindow.GetOrigin (out x, out y);
+ Gdk.Rectangle rect = new Gdk.Rectangle (x + allocation.X, y + allocation.Y, allocation.Width, allocation.Height);
+
+ Section s = new Section ();
+ s.Size = rect.Width;
+ secsT.Add (s);
+ secsB.Add (s);
+ s.Size = rect.Height;
+ secsL.Add (s);
+ secsR.Add (s);
+
+ foreach (var rects in allocations.Values) {
+ foreach (Gdk.Rectangle sr in rects) {
+ if (sr == rect)
+ continue;
+
+ if (sr.Right == rect.X)
+ RemoveSection (secsL, sr.Y - rect.Y, sr.Height);
+ if (sr.Bottom == rect.Y)
+ RemoveSection (secsT, sr.X - rect.X, sr.Width);
+ if (sr.X == rect.Right)
+ RemoveSection (secsR, sr.Y - rect.Y, sr.Height);
+ if (sr.Y == rect.Bottom)
+ RemoveSection (secsB, sr.X - rect.X, sr.Width);
+ }
+ }
+
+ Gdk.Rectangle r = new Gdk.Rectangle (allocation.X, allocation.Y, allocation.Width, allocation.Height);
+ using (Cairo.Context ctx = Gdk.CairoHelper.Create (w.GdkWindow)) {
+ ctx.Rectangle (r.X, r.Y, r.Width, r.Height);
+ ctx.Color = GtkUtil.ToCairoColor (lightColor);
+ ctx.Fill ();
+
+ DrawShadow (ctx, r, PositionType.Left, secsL);
+ DrawShadow (ctx, r, PositionType.Top, secsT);
+ DrawShadow (ctx, r, PositionType.Right, secsR);
+ DrawShadow (ctx, r, PositionType.Bottom, secsB);
+ }
+ }
+
+ void DrawShadow (Cairo.Context ctx, Gdk.Rectangle ar, PositionType pos, List<Section> secs)
+ {
+ foreach (Section s in secs) {
+ Cairo.Gradient pat = null;
+ Gdk.Rectangle r = ar;
+ switch (pos) {
+ case PositionType.Top:
+ r.Height = shadowSize > r.Height ? r.Height / 2 : shadowSize;
+ r.X += s.Offset;
+ r.Width = s.Size;
+ pat = new Cairo.LinearGradient (r.X, r.Y, r.X, r.Bottom);
+ break;
+ case PositionType.Bottom:
+ r.Y = r.Bottom - shadowSize;
+ r.Height = shadowSize > r.Height ? r.Height / 2 : shadowSize;
+ r.X = r.X + s.Offset;
+ r.Width = s.Size;
+ pat = new Cairo.LinearGradient (r.X, r.Bottom, r.X, r.Y);
+ break;
+ case PositionType.Left:
+ r.Width = shadowSize > r.Width ? r.Width / 2 : shadowSize;
+ r.Y += s.Offset;
+ r.Height = s.Size;
+ pat = new Cairo.LinearGradient (r.X, r.Y, r.Right, r.Y);
+ break;
+ case PositionType.Right:
+ r.X = r.Right - shadowSize;
+ r.Width = shadowSize > r.Width ? r.Width / 2 : shadowSize;
+ r.Y += s.Offset;
+ r.Height = s.Size;
+ pat = new Cairo.LinearGradient (r.Right, r.Y, r.X, r.Y);
+ break;
+ }
+ Cairo.Color c = GtkUtil.ToCairoColor (darkColor);
+ pat.AddColorStop (0, c);
+ c.A = 0;
+ pat.AddColorStop (1, c);
+ ctx.NewPath ();
+ ctx.Rectangle (r.X, r.Y, r.Width, r.Height);
+ ctx.Pattern = pat;
+ ctx.Fill ();
+ }
+ }
+
+ void RemoveSection (List<Section> secs, int offset, int size)
+ {
+ if (offset < 0) {
+ size += offset;
+ offset = 0;
+ }
+ if (size <= 0 || secs.Count == 0)
+ return;
+ Section last = secs [secs.Count - 1];
+ int rem = (last.Offset + last.Size) - (offset + size);
+ if (rem < 0) {
+ size += rem;
+ if (size <= 0)
+ return;
+ }
+ for (int n=0; n<secs.Count; n++) {
+ Section s = secs [n];
+ if (s.Offset >= offset + size)
+ continue;
+ if (offset >= s.Offset + s.Size)
+ continue;
+ if (offset <= s.Offset && offset + size >= s.Offset + s.Size) {
+ // Remove the whole section
+ secs.RemoveAt (n);
+ n--;
+ continue;
+ }
+ if (offset <= s.Offset) {
+ int newOfs = offset + size;
+ s.Size = s.Size - (newOfs - s.Offset);
+ s.Offset = newOfs;
+ secs [n] = s;
+ // Nothing else to remove
+ return;
+ }
+ if (offset + size >= s.Offset + s.Size) {
+ s.Size = offset - s.Offset;
+ secs [n] = s;
+ continue;
+ }
+ // Split section
+ Section s2 = new Section ();
+ s2.Offset = offset + size;
+ s2.Size = (s.Offset + s.Size) - (offset + size);
+ secs.Insert (n + 1, s2);
+ s.Size = offset - s.Offset;
+ secs [n] = s;
+ }
+ }
+ }
+
+ public interface IShadedWidget
+ {
+ IEnumerable<Gdk.Rectangle> GetShadedAreas ();
+ event EventHandler AreasChanged;
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs
new file mode 100644
index 0000000000..67976a6ee5
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs
@@ -0,0 +1,407 @@
+//
+// TabStrip.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+
+//
+// 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 Gtk;
+
+using System;
+
+namespace MonoDevelop.Components.Docking
+{
+ class TabStrip: Notebook
+ {
+ int currentTab = -1;
+ bool ellipsized = true;
+ HBox box = new HBox ();
+ DockFrame frame;
+ Label bottomFiller = new Label ();
+
+ public TabStrip (DockFrame frame)
+ {
+ this.frame = frame;
+ frame.ShadedContainer.Add (this);
+ VBox vbox = new VBox ();
+ box = new HBox ();
+ vbox.PackStart (box, false, false, 0);
+ vbox.PackStart (bottomFiller, false, false, 0);
+ AppendPage (vbox, null);
+ ShowBorder = false;
+ ShowTabs = false;
+ ShowAll ();
+ bottomFiller.Hide ();
+ BottomPadding = 3;
+ }
+
+ public int BottomPadding {
+ get { return bottomFiller.HeightRequest; }
+ set {
+ bottomFiller.HeightRequest = value;
+ bottomFiller.Visible = value > 0;
+ }
+ }
+
+ public void AddTab (Gtk.Widget page, Gdk.Pixbuf icon, string label)
+ {
+ Tab tab = new Tab ();
+ tab.SetLabel (page, icon, label);
+ tab.ShowAll ();
+ box.PackStart (tab, true, true, 0);
+ if (currentTab == -1)
+ CurrentTab = box.Children.Length - 1;
+ else {
+ tab.Active = false;
+ page.Hide ();
+ }
+
+ tab.ButtonPressEvent += OnTabPress;
+ }
+
+ public void SetTabLabel (Gtk.Widget page, Gdk.Pixbuf icon, string label)
+ {
+ foreach (Tab tab in box.Children) {
+ if (tab.Page == page) {
+ tab.SetLabel (page, icon, label);
+ UpdateEllipsize (Allocation);
+ break;
+ }
+ }
+ }
+
+ public int TabCount {
+ get { return box.Children.Length; }
+ }
+
+ public int CurrentTab {
+ get { return currentTab; }
+ set {
+ if (currentTab == value)
+ return;
+ if (currentTab != -1) {
+ Tab t = (Tab) box.Children [currentTab];
+ t.Page.Hide ();
+ t.Active = false;
+ }
+ currentTab = value;
+ if (currentTab != -1) {
+ Tab t = (Tab) box.Children [currentTab];
+ t.Active = true;
+ t.Page.Show ();
+ }
+ }
+ }
+
+ new public Gtk.Widget CurrentPage {
+ get {
+ if (currentTab != -1) {
+ Tab t = (Tab) box.Children [currentTab];
+ return t.Page;
+ } else
+ return null;
+ }
+ set {
+ if (value != null) {
+ Gtk.Widget[] tabs = box.Children;
+ for (int n = 0; n < tabs.Length; n++) {
+ Tab tab = (Tab) tabs [n];
+ if (tab.Page == value) {
+ CurrentTab = n;
+ return;
+ }
+ }
+ }
+ CurrentTab = -1;
+ }
+ }
+
+ public void Clear ()
+ {
+ ellipsized = true;
+ currentTab = -1;
+ foreach (Widget w in box.Children) {
+ box.Remove (w);
+ w.Destroy ();
+ }
+ }
+
+ void OnTabPress (object s, Gtk.ButtonPressEventArgs args)
+ {
+ CurrentTab = Array.IndexOf (box.Children, s);
+ Tab t = (Tab) s;
+ DockItem.SetFocus (t.Page);
+ QueueDraw ();
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ {
+ UpdateEllipsize (allocation);
+ base.OnSizeAllocated (allocation);
+ }
+
+ void UpdateEllipsize (Gdk.Rectangle allocation)
+ {
+ int tsize = 0;
+ foreach (Tab tab in box.Children)
+ tsize += tab.LabelWidth;
+
+ bool ellipsize = tsize > allocation.Width;
+ if (ellipsize != ellipsized) {
+ foreach (Tab tab in box.Children) {
+ tab.SetEllipsize (ellipsize);
+ Gtk.Box.BoxChild bc = (Gtk.Box.BoxChild) box [tab];
+ bc.Expand = bc.Fill = ellipsize;
+ }
+ ellipsized = ellipsize;
+ }
+ }
+
+ public Gdk.Rectangle GetTabArea (int ntab)
+ {
+ Gtk.Widget[] tabs = box.Children;
+ Tab tab = (Tab) tabs[ntab];
+ Gdk.Rectangle rect = GetTabArea (tab, ntab);
+ int x, y;
+ tab.GdkWindow.GetRootOrigin (out x, out y);
+ rect.X += x;
+ rect.Y += y;
+ return rect;
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ frame.ShadedContainer.DrawBackground (this);
+
+ Gtk.Widget[] tabs = box.Children;
+ for (int n=tabs.Length - 1; n>=0; n--) {
+ Tab tab = (Tab) tabs [n];
+ if (n != currentTab)
+ DrawTab (evnt, tab, n);
+ }
+ if (currentTab != -1) {
+ Tab ctab = (Tab) tabs [currentTab];
+// GdkWindow.DrawLine (Style.DarkGC (Gtk.StateType.Normal), Allocation.X, Allocation.Y, Allocation.Right, Allocation.Y);
+ DrawTab (evnt, ctab, currentTab);
+ }
+ return base.OnExposeEvent (evnt);
+ }
+
+ public Gdk.Rectangle GetTabArea (Tab tab, int pos)
+ {
+ Gdk.Rectangle rect = tab.Allocation;
+
+ int xdif = 0;
+ if (pos > 0)
+ xdif = 2;
+
+ int reqh;
+// StateType st;
+
+ if (tab.Active) {
+// st = StateType.Normal;
+ reqh = tab.Allocation.Height;
+ }
+ else {
+ reqh = tab.Allocation.Height - 3;
+// st = StateType.Active;
+ }
+
+ if (DockFrame.IsWindows) {
+ rect.Height = reqh - 1;
+ rect.Width--;
+ if (pos > 0) {
+ rect.X--;
+ rect.Width++;
+ }
+ return rect;
+ }
+ else {
+ rect.X -= xdif;
+ rect.Width += xdif;
+ rect.Height = reqh;
+ return rect;
+ }
+ }
+
+ void DrawTab (Gdk.EventExpose evnt, Tab tab, int pos)
+ {
+ Gdk.Rectangle rect = GetTabArea (tab, pos);
+ StateType st;
+ if (tab.Active)
+ st = StateType.Normal;
+ else
+ st = StateType.Active;
+
+ if (DockFrame.IsWindows) {
+ GdkWindow.DrawRectangle (Style.DarkGC (Gtk.StateType.Normal), false, rect);
+ rect.X++;
+ rect.Width--;
+ if (tab.Active) {
+ GdkWindow.DrawRectangle (Style.LightGC (Gtk.StateType.Normal), true, rect);
+ }
+ else {
+ using (Cairo.Context cr = Gdk.CairoHelper.Create (evnt.Window)) {
+ cr.NewPath ();
+ cr.MoveTo (rect.X, rect.Y);
+ cr.RelLineTo (rect.Width, 0);
+ cr.RelLineTo (0, rect.Height);
+ cr.RelLineTo (-rect.Width, 0);
+ cr.RelLineTo (0, -rect.Height);
+ cr.ClosePath ();
+ Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Y + rect.Height);
+ Cairo.Color color1 = DockFrame.ToCairoColor (Style.Mid (Gtk.StateType.Normal));
+ pat.AddColorStop (0, color1);
+ color1.R *= 1.2;
+ color1.G *= 1.2;
+ color1.B *= 1.2;
+ pat.AddColorStop (1, color1);
+ cr.Pattern = pat;
+ cr.FillPreserve ();
+ }
+ }
+ }
+ else
+ Gtk.Style.PaintExtension (Style, GdkWindow, st, ShadowType.Out, evnt.Area, this, "tab", rect.X, rect.Y, rect.Width, rect.Height, Gtk.PositionType.Top);
+ }
+ }
+
+ class Tab: Gtk.EventBox
+ {
+ bool active;
+ Gtk.Widget page;
+ Gtk.Label labelWidget;
+ int labelWidth;
+
+ const int TopPadding = 2;
+ const int BottomPadding = 4;
+ const int TopPaddingActive = 3;
+ const int BottomPaddingActive = 5;
+ const int HorzPadding = 5;
+
+ public Tab ()
+ {
+ this.VisibleWindow = false;
+ }
+
+ public void SetLabel (Gtk.Widget page, Gdk.Pixbuf icon, string label)
+ {
+ Pango.EllipsizeMode oldMode = Pango.EllipsizeMode.End;
+
+ this.page = page;
+ if (Child != null) {
+ if (labelWidget != null)
+ oldMode = labelWidget.Ellipsize;
+ Gtk.Widget oc = Child;
+ Remove (oc);
+ oc.Destroy ();
+ }
+
+ Gtk.HBox box = new HBox ();
+ box.Spacing = 2;
+
+ if (icon != null)
+ box.PackStart (new Gtk.Image (icon), false, false, 0);
+
+ if (!string.IsNullOrEmpty (label)) {
+ labelWidget = new Gtk.Label (label);
+ labelWidget.UseMarkup = true;
+ box.PackStart (labelWidget, true, true, 0);
+ } else {
+ labelWidget = null;
+ }
+
+ Add (box);
+
+ // Get the required size before setting the ellipsize property, since ellipsized labels
+ // have a width request of 0
+ ShowAll ();
+ labelWidth = SizeRequest ().Width;
+
+ if (labelWidget != null)
+ labelWidget.Ellipsize = oldMode;
+ }
+
+ public void SetEllipsize (bool elipsize)
+ {
+ if (labelWidget != null) {
+ if (elipsize)
+ labelWidget.Ellipsize = Pango.EllipsizeMode.End;
+ else
+ labelWidget.Ellipsize = Pango.EllipsizeMode.None;
+ }
+ }
+
+ public int LabelWidth {
+ get { return labelWidth; }
+ }
+
+ public bool Active {
+ get {
+ return active;
+ }
+ set {
+ active = value;
+ this.QueueResize ();
+ QueueDraw ();
+ }
+ }
+
+ public Widget Page {
+ get {
+ return page;
+ }
+ }
+
+ protected override void OnSizeRequested (ref Gtk.Requisition req)
+ {
+ req = Child.SizeRequest ();
+ req.Width += HorzPadding * 2;
+ if (active)
+ req.Height += TopPaddingActive + BottomPaddingActive;
+ else
+ req.Height += TopPadding + BottomPadding;
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle rect)
+ {
+ base.OnSizeAllocated (rect);
+
+ rect.X += HorzPadding;
+ rect.Width -= HorzPadding * 2;
+
+ if (active) {
+ rect.Y += TopPaddingActive;
+ rect.Height = Child.SizeRequest ().Height;
+ }
+ else {
+ rect.Y += TopPadding;
+ rect.Height = Child.SizeRequest ().Height;
+ }
+ Child.SizeAllocate (rect);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.png
new file mode 100644
index 0000000000..e31a471569
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.png
new file mode 100644
index 0000000000..71016e2f06
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.png
new file mode 100644
index 0000000000..225c2abfc7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.png
new file mode 100644
index 0000000000..c1d3befddc
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.png
new file mode 100644
index 0000000000..df945cd85f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/ISelectFileDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/ISelectFileDialog.cs
new file mode 100644
index 0000000000..0018fc84c1
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/ISelectFileDialog.cs
@@ -0,0 +1,127 @@
+//
+// ISelectFileDialog.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.Extensions
+{
+ /// <summary>
+ /// This interface can be implemented to provide a custom implementation
+ /// for the SelectFileDialog dialog.
+ /// </summary>
+ public interface ISelectFileDialogHandler
+ {
+ bool Run (SelectFileDialogData data);
+ }
+
+ /// <summary>
+ /// Data for the ISelectFileDialogHandler implementation
+ /// </summary>
+ public class SelectFileDialogData: PlatformDialogData
+ {
+ public Gtk.FileChooserAction Action { get; set; }
+ public string CurrentFolder { get; set; }
+ public bool SelectMultiple { get; set; }
+ public string[] SelectedFiles { get; set; }
+ public string InitialFileName { get; set; }
+ }
+
+ /// <summary>
+ /// Generic class to be used to implement file selectors.
+ /// The T type argument is the type of the handler.
+ /// The U type is the type of the data parameter (must subclass SelectFileDialogData)
+ /// </summary>
+ public class SelectFileDialog<T,U>: PlatformDialog<T,U> where U:SelectFileDialogData, new()
+ {
+ /// <summary>
+ /// Action to perform with the file dialog.
+ /// </summary>
+ public Gtk.FileChooserAction Action {
+ get { return data.Action; }
+ set { data.Action = value; }
+ }
+
+ /// <summary>
+ /// Folder to show by default.
+ /// </summary>
+ public string CurrentFolder {
+ get { return data.CurrentFolder; }
+ set { data.CurrentFolder = value; }
+ }
+
+ /// <summary>
+ /// Set to True to allow multiple selection.
+ /// </summary>
+ public bool SelectMultiple {
+ get { return data.SelectMultiple; }
+ set { data.SelectMultiple = value; }
+ }
+
+ /// <summary>
+ /// List of selected files (or folders).
+ /// </summary>
+ public string[] SelectedFiles {
+ get { return data.SelectedFiles; }
+ }
+
+ /// <summary>
+ /// Selected file (or folder) when using single selection mode.
+ /// </summary>
+ public string SelectedFile {
+ get { return data.SelectedFiles.Length > 0 ? data.SelectedFiles [0] : null; }
+ }
+
+ /// <summary>
+ /// File name to show by default.
+ /// </summary>
+ public string InitialFileName {
+ get { return data.InitialFileName; }
+ set { data.InitialFileName = value; }
+ }
+
+ /// <summary>
+ /// Runs the default implementation of the dialog.
+ /// </summary>
+ protected bool RunDefault ()
+ {
+ FileSelector fdiag = new FileSelector (data.Title, data.Action);
+ fdiag.SelectMultiple = data.SelectMultiple;
+ fdiag.TransientFor = data.TransientFor;
+ if (!string.IsNullOrEmpty(data.CurrentFolder))
+ fdiag.SetCurrentFolder(data.CurrentFolder);
+ if (!string.IsNullOrEmpty(data.InitialFileName))
+ fdiag.SetFilename (data.InitialFileName);
+
+ try {
+ int result = fdiag.Run ();
+ data.SelectedFiles = fdiag.Filenames;
+ return result == (int) Gtk.ResponseType.Ok;
+ } finally {
+ fdiag.Destroy ();
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeNode.cs
new file mode 100644
index 0000000000..0bb81a42f0
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeNode.cs
@@ -0,0 +1,133 @@
+// MimeTypeNode.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.Text;
+using System.IO;
+using System.Text.RegularExpressions;
+using Mono.Addins;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Ide.Extensions
+{
+ [ExtensionNodeChild (typeof(MimeTypeFileNode), "File")]
+ class MimeTypeNode: ExtensionNode
+ {
+ [NodeAttribute]
+ string icon;
+
+ [NodeAttribute ("_description", Localizable=true)]
+ string description;
+
+ [NodeAttribute (Required=false)]
+ string baseType;
+
+ [NodeAttribute (Required=false)]
+ protected bool isText;
+
+ Regex regex;
+
+ public IconId Icon {
+ get {
+ return icon;
+ }
+ set {
+ icon = value;
+ }
+ }
+
+ public string Description {
+ get {
+ return description;
+ }
+ set {
+ description = value;
+ }
+ }
+
+ public string BaseType {
+ get {
+ if (string.IsNullOrEmpty (baseType))
+ return isText ? "text/plain" : null;
+ else
+ return baseType;
+ }
+ }
+
+ Regex CreateRegex ()
+ {
+ StringBuilder globalPattern = new StringBuilder ();
+
+ foreach (MimeTypeFileNode file in ChildNodes) {
+ string pattern = Regex.Escape (file.Pattern);
+ pattern = pattern.Replace ("\\*",".*");
+ pattern = pattern.Replace ("\\?",".");
+ pattern = pattern.Replace ("\\|","$|^");
+ pattern = "^" + pattern + "$";
+ if (globalPattern.Length > 0)
+ globalPattern.Append ('|');
+ globalPattern.Append (pattern);
+ }
+ return new Regex (globalPattern.ToString ());
+ }
+
+
+ public bool SupportsFile (string fileName)
+ {
+ if (regex == null)
+ regex = CreateRegex ();
+ return regex.IsMatch (fileName);
+ }
+
+ protected override void OnChildNodeAdded (ExtensionNode node)
+ {
+ base.OnChildNodeAdded (node);
+ regex = null;
+ }
+
+ protected override void OnChildNodeRemoved (ExtensionNode node)
+ {
+ base.OnChildNodeRemoved (node);
+ regex = null;
+ }
+ }
+
+ class MimeTypeFileNode: ExtensionNode
+ {
+ [NodeAttribute]
+ string pattern;
+
+ public string Pattern {
+ get {
+ return pattern;
+ }
+ set {
+ pattern = value;
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeOptionsPanelNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeOptionsPanelNode.cs
new file mode 100644
index 0000000000..25669768dc
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/MimeTypeOptionsPanelNode.cs
@@ -0,0 +1,48 @@
+//
+// MimeTypeOptionsPanelNode.cs
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2009 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;
+
+namespace MonoDevelop.Projects.Gui.Extensions
+{
+ public class MimeTypeOptionsPanelNode: TypeExtensionNode
+ {
+ [NodeAttribute]
+ protected string mimeType;
+
+ [NodeAttribute ("_label")]
+ protected string label;
+
+ public string MimeType {
+ get { return mimeType; }
+ }
+
+ public string Label {
+ get { return label; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsDialogSection.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsDialogSection.cs
new file mode 100644
index 0000000000..c1637c862b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsDialogSection.cs
@@ -0,0 +1,64 @@
+// OptionsDialogSection.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 Mono.Addins;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Ide.Extensions
+{
+ [ExtensionNodeChild (typeof(OptionsDialogSection))]
+ [ExtensionNodeChild (typeof(OptionsPanelNode))]
+ [ExtensionNode ("Section")]
+ public class OptionsDialogSection: OptionsPanelNode, ICloneable
+ {
+ [NodeAttribute ()]
+ string icon;
+
+ public OptionsDialogSection ()
+ {
+ }
+
+ public OptionsDialogSection (Type panelType): base (panelType)
+ {
+ }
+
+ public IconId Icon {
+ get {
+ return icon;
+ }
+ set {
+ icon = value;
+ }
+ }
+
+ public object Clone ()
+ {
+ return MemberwiseClone ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsPanelNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsPanelNode.cs
new file mode 100644
index 0000000000..2d8dc1e493
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/OptionsPanelNode.cs
@@ -0,0 +1,114 @@
+// OptionsPanelExtensionNode.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 Mono.Addins;
+using MonoDevelop.Ide.Gui.Dialogs;
+
+namespace MonoDevelop.Ide.Extensions
+{
+ [ExtensionNode ("Panel")]
+ public class OptionsPanelNode: TypeExtensionNode
+ {
+ [NodeAttribute ("class")]
+ protected string typeName;
+
+ [NodeAttribute ("_label", Localizable=true)]
+ protected string label;
+
+ [NodeAttribute]
+ protected PanelGrouping grouping = PanelGrouping.Auto;
+
+ [NodeAttribute]
+ protected bool fill = false;
+
+ Type panelType;
+
+ public OptionsPanelNode ()
+ {
+ }
+
+ public OptionsPanelNode (Type panelType)
+ {
+ this.panelType = panelType;
+ }
+
+ public string Label {
+ get {
+ return label;
+ }
+ set {
+ label = value;
+ }
+ }
+
+ public PanelGrouping Grouping {
+ get {
+ return grouping;
+ }
+ set {
+ grouping = value;
+ }
+ }
+
+ public bool Fill {
+ get {
+ return fill;
+ }
+ set {
+ fill = value;
+ }
+ }
+
+ public string TypeName {
+ get {
+ return typeName;
+ }
+ }
+
+ internal bool CustomNode {
+ get { return panelType != null; }
+ }
+
+ public virtual IOptionsPanel CreatePanel ()
+ {
+ if (panelType != null)
+ return (IOptionsPanel) Activator.CreateInstance (panelType);
+ IOptionsPanel p = Addin.CreateInstance (typeName, true) as IOptionsPanel;
+ if (p == null)
+ throw new System.InvalidOperationException ("Type '" + typeName + "' does not implement IOptionsPanel");
+ return p;
+ }
+ }
+
+ public enum PanelGrouping
+ {
+ Auto,
+ Tab,
+ Box
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/PlatformDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/PlatformDialog.cs
new file mode 100644
index 0000000000..f759ec6101
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/PlatformDialog.cs
@@ -0,0 +1,87 @@
+//
+// PlatformDialog.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;
+using Mono.Addins;
+
+namespace MonoDevelop.Components.Extensions
+{
+ /// <summary>
+ /// Base class for platform dialog data
+ /// </summary>
+ public class PlatformDialogData
+ {
+ public string Title { get; set; }
+ public Gtk.Window TransientFor { get; set; }
+ }
+
+ /// <summary>
+ /// Base class to be used to implement platform-specific dialogs.
+ /// T is the handler type.
+ /// U is the data type where data will be hold.
+ /// </summary>
+ public class PlatformDialog<T,U> where U: PlatformDialogData, new()
+ {
+ T handler;
+ bool gotHandler;
+
+ /// <summary>
+ /// Dialog data
+ /// </summary>
+ protected U data = new U ();
+
+ protected T Handler {
+ get {
+ if (!gotHandler) {
+ gotHandler = true;
+ foreach (object h in AddinManager.GetExtensionObjects ("/MonoDevelop/Components/DialogHandlers", true)) {
+ if (h is T) {
+ handler = (T) h;
+ break;
+ }
+ }
+ }
+ return handler;
+ }
+ }
+
+ /// <summary>
+ /// Title of the dialog.
+ /// </summary>
+ public string Title {
+ get { return data.Title; }
+ set { data.Title = value; }
+ }
+
+ /// <summary>
+ /// Parent window.
+ /// </summary>
+ public Gtk.Window TransientFor {
+ get { return data.TransientFor; }
+ set { data.TransientFor = value; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/StockIconCodon.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/StockIconCodon.cs
new file mode 100644
index 0000000000..5b07d1747e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/StockIconCodon.cs
@@ -0,0 +1,77 @@
+//
+// StockIconAssembly.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.ComponentModel;
+using Mono.Addins;
+
+namespace MonoDevelop.Ide.Extensions
+{
+ [ExtensionNode (Description="A stock icon. It is possible to register several icons with the same 'id' and different sizes.")]
+ internal class StockIconCodon : ExtensionNode
+ {
+ [NodeAttribute ("stockid", true, "Id of the stock icon.")]
+ string stockid;
+
+ [NodeAttribute ("size", "Size of the icon.")]
+ Gtk.IconSize size = Gtk.IconSize.Invalid;
+
+ [NodeAttribute ("resource", "Name of the resource where the icon is stored.")]
+ string resource;
+
+ [NodeAttribute ("file", "Name of the file where the icon is stored.")]
+ string file;
+
+ [NodeAttribute ("icon", "Id of another icon or combination of icons to assign to this stock id.")]
+ string iconid;
+
+ public string StockId {
+ get { return stockid; }
+ }
+
+ public Gtk.IconSize IconSize {
+ get { return size; }
+ }
+
+ public string Resource {
+ get { return resource; }
+ }
+
+ public string File {
+ get { return file; }
+ }
+
+ public string IconId {
+ get { return iconid; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/BooleanEditorCell.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/BooleanEditorCell.cs
new file mode 100644
index 0000000000..747735037d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/BooleanEditorCell.cs
@@ -0,0 +1,92 @@
+//
+// BooleanEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (bool))]
+ public class BooleanEditorCell : PropertyEditorCell
+ {
+ static int indicatorSize;
+ static int indicatorSpacing;
+
+ static BooleanEditorCell ()
+ {
+ Gtk.CheckButton cb = new Gtk.CheckButton ();
+ indicatorSize = (int) cb.StyleGetProperty ("indicator-size");
+ indicatorSpacing = (int) cb.StyleGetProperty ("indicator-spacing");
+ }
+
+ public override void GetSize (int availableWidth, out int width, out int height)
+ {
+ width = 20;
+ height = 20;
+ }
+
+ public override void Render (Gdk.Drawable window, Gdk.Rectangle bounds, Gtk.StateType state)
+ {
+ Gtk.ShadowType sh = (bool) Value ? Gtk.ShadowType.In : Gtk.ShadowType.Out;
+ int s = indicatorSize - 1;
+ if (s > bounds.Height)
+ s = bounds.Height;
+ if (s > bounds.Width)
+ s = bounds.Width;
+ Gtk.Style.PaintCheck (Container.Style, window, state, sh, bounds, Container, "checkbutton", bounds.X + indicatorSpacing - 1, bounds.Y + (bounds.Height - s)/2, s, s);
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ return new BooleanEditor ();
+ }
+ }
+
+ public class BooleanEditor : Gtk.CheckButton, IPropertyEditor
+ {
+ public void Initialize (EditSession session)
+ {
+ if (session.Property.PropertyType != typeof(bool))
+ throw new ApplicationException ("Boolean editor does not support editing values of type " + session.Property.PropertyType);
+ }
+
+ public object Value {
+ get { return Active; }
+ set { Active = (bool) value; }
+ }
+
+ protected override void OnToggled ()
+ {
+ base.OnToggled ();
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+
+ public event EventHandler ValueChanged;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CharPropertyEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CharPropertyEditor.cs
new file mode 100644
index 0000000000..69019a5214
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CharPropertyEditor.cs
@@ -0,0 +1,73 @@
+//
+// CharPropertyEditor.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+
+ [PropertyEditorType (typeof (char))]
+ public class CharPropertyEditor : Gtk.Entry, IPropertyEditor
+ {
+ public CharPropertyEditor ()
+ {
+ MaxLength = 1;
+ HasFrame = false;
+ }
+
+ public void Initialize (EditSession session)
+ {
+ if (session.Property.PropertyType != typeof(char))
+ throw new ApplicationException ("Char editor does not support editing values of type " + session.Property.PropertyType);
+ }
+
+ char last;
+
+ public object Value {
+ get {
+ if (Text.Length == 0)
+ return last;
+ else
+ return Text[0];
+ }
+ set {
+ Text = value.ToString ();
+ last = (char) value;
+ }
+ }
+
+ protected override void OnChanged ()
+ {
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+
+ public event EventHandler ValueChanged;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CollectionEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CollectionEditor.cs
new file mode 100644
index 0000000000..067e6676e7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/CollectionEditor.cs
@@ -0,0 +1,359 @@
+/*
+ * CollectionEditor.cs - The base class for the visual type editors
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Collections;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ class CollectionEditor : PropertyEditorCell
+ {
+ //TODO: Support for multiple object types
+ private Type[] types;
+
+ protected override void Initialize ()
+ {
+ base.Initialize ();
+ this.types = new Type[] { EditorManager.GetCollectionItemType (Property.PropertyType) };
+ }
+
+ protected virtual Type[] NewItemTypes ()
+ {
+ return types;
+ }
+
+ public override bool DialogueEdit
+ {
+ get { return true; }
+ }
+
+ public override bool EditsReadOnlyObject {
+ get { return true; }
+ }
+
+ protected override string GetValueText ()
+ {
+ return MonoDevelop.Core.GettextCatalog.GetString ("(Collection)");
+ }
+
+ public override void LaunchDialogue ()
+ {
+ //the Type in the collection
+ IList collection = (IList) Value;
+ string displayName = Property.DisplayName;
+
+ //populate list with existing items
+ ListStore itemStore = new ListStore (typeof (object), typeof (int), typeof (string));
+ for (int i=0; i<collection.Count; i++)
+ itemStore.AppendValues(collection [i], i, collection [i].ToString ());
+
+ #region Building Dialogue
+
+ TreeView itemTree;
+ PropertyGrid grid;
+ TreeIter previousIter = TreeIter.Zero;
+
+ //dialogue and buttons
+ Dialog dialog = new Dialog () {
+ Title = displayName + " Editor",
+ Modal = true,
+ AllowGrow = true,
+ AllowShrink = true,
+ };
+ var toplevel = this.Container.Toplevel as Window;
+ if (toplevel != null)
+ dialog.TransientFor = toplevel;
+
+ dialog.AddActionWidget (new Button (Stock.Cancel), ResponseType.Cancel);
+ dialog.AddActionWidget (new Button (Stock.Ok), ResponseType.Ok);
+
+ //three columns for items, sorting, PropGrid
+ HBox hBox = new HBox ();
+ dialog.VBox.PackStart (hBox, true, true, 5);
+
+ //propGrid at end
+ grid = new PropertyGrid (base.EditorManager) {
+ CurrentObject = null,
+ WidthRequest = 200,
+ ShowHelp = false
+ };
+ hBox.PackEnd (grid, true, true, 5);
+
+ //followed by a ButtonBox
+ VBox buttonBox = new VBox ();
+ buttonBox.Spacing = 6;
+ hBox.PackEnd (buttonBox, false, false, 5);
+
+ //add/remove buttons
+ Button addButton = new Button (new Image (Stock.Add, IconSize.Button));
+ buttonBox.PackStart (addButton, false, false, 0);
+ if (types [0].IsAbstract)
+ addButton.Sensitive = false;
+ Button removeButton = new Button (new Gtk.Image (Stock.Remove, IconSize.Button));
+ buttonBox.PackStart (removeButton, false, false, 0);
+
+ //sorting buttons
+ Button upButton = new Button (new Image (Stock.GoUp, IconSize.Button));
+ buttonBox.PackStart (upButton, false, false, 0);
+ Button downButton = new Button (new Image (Stock.GoDown, IconSize.Button));
+ buttonBox.PackStart (downButton, false, false, 0);
+
+ //Third column has list (TreeView) in a ScrolledWindow
+ ScrolledWindow listScroll = new ScrolledWindow ();
+ listScroll.WidthRequest = 200;
+ listScroll.HeightRequest = 320;
+ hBox.PackStart (listScroll, false, false, 5);
+
+ itemTree = new TreeView (itemStore);
+ itemTree.Selection.Mode = SelectionMode.Single;
+ itemTree.HeadersVisible = false;
+ listScroll.AddWithViewport (itemTree);
+
+ //renderers and attribs for TreeView
+ CellRenderer rdr = new CellRendererText ();
+ itemTree.AppendColumn (new TreeViewColumn ("Index", rdr, "text", 1));
+ rdr = new CellRendererText ();
+ itemTree.AppendColumn (new TreeViewColumn ("Object", rdr, "text", 2));
+
+ #endregion
+
+ #region Events
+
+ addButton.Clicked += delegate {
+ //create the object
+ object instance = System.Activator.CreateInstance (types[0]);
+
+ //get existing selection and insert after it
+ TreeIter oldIter, newIter;
+ if (itemTree.Selection.GetSelected (out oldIter))
+ newIter = itemStore.InsertAfter (oldIter);
+ //or append if no previous selection
+ else
+ newIter = itemStore.Append ();
+ itemStore.SetValue (newIter, 0, instance);
+
+ //select, set name and update all the indices
+ itemTree.Selection.SelectIter (newIter);
+ UpdateName (itemStore, newIter);
+ UpdateIndices (itemStore);
+ };
+
+ removeButton.Clicked += delegate {
+ //get selected iter and the replacement selection
+ TreeIter iter, newSelection;
+ if (!itemTree.Selection.GetSelected (out iter))
+ return;
+
+ newSelection = iter;
+ if (!IterPrev (itemStore, ref newSelection)) {
+ newSelection = iter;
+ if (!itemStore.IterNext (ref newSelection))
+ newSelection = TreeIter.Zero;
+ }
+
+ //new selection. Zeroing previousIter prevents trying to update name of deleted iter.
+ previousIter = TreeIter.Zero;
+ if (itemStore.IterIsValid (newSelection))
+ itemTree.Selection.SelectIter (newSelection);
+
+ //and the removal and index update
+ itemStore.Remove (ref iter);
+ UpdateIndices (itemStore);
+ };
+
+ upButton.Clicked += delegate {
+ TreeIter iter, prev;
+ if (!itemTree.Selection.GetSelected (out iter))
+ return;
+
+ //get previous iter
+ prev = iter;
+ if (!IterPrev (itemStore, ref prev))
+ return;
+
+ //swap the two
+ itemStore.Swap (iter, prev);
+
+ //swap indices too
+ object prevVal = itemStore.GetValue (prev, 1);
+ object iterVal = itemStore.GetValue (iter, 1);
+ itemStore.SetValue (prev, 1, iterVal);
+ itemStore.SetValue (iter, 1, prevVal);
+ };
+
+ downButton.Clicked += delegate {
+ TreeIter iter, next;
+ if (!itemTree.Selection.GetSelected (out iter))
+ return;
+
+ //get next iter
+ next = iter;
+ if (!itemStore.IterNext (ref next))
+ return;
+
+ //swap the two
+ itemStore.Swap (iter, next);
+
+ //swap indices too
+ object nextVal = itemStore.GetValue (next, 1);
+ object iterVal = itemStore.GetValue (iter, 1);
+ itemStore.SetValue (next, 1, iterVal);
+ itemStore.SetValue (iter, 1, nextVal);
+ };
+
+ itemTree.Selection.Changed += delegate {
+ TreeIter iter;
+ if (!itemTree.Selection.GetSelected (out iter)) {
+ removeButton.Sensitive = false;
+ return;
+ }
+ removeButton.Sensitive = true;
+
+ //update grid
+ object obj = itemStore.GetValue (iter, 0);
+ grid.CurrentObject = obj;
+
+ //update previously selected iter's name
+ UpdateName (itemStore, previousIter);
+
+ //update current selection so we can update
+ //name next selection change
+ previousIter = iter;
+ };
+
+ grid.Changed += delegate {
+ TreeIter iter;
+ if (itemTree.Selection.GetSelected (out iter))
+ UpdateName (itemStore, iter);
+ };
+
+ TreeIter selectionIter;
+ removeButton.Sensitive = itemTree.Selection.GetSelected (out selectionIter);
+
+ dialog.ShowAll ();
+ grid.ShowToolbar = false;
+
+ #endregion
+
+ //show and get response
+ ResponseType response = (ResponseType) dialog.Run();
+ dialog.Destroy ();
+
+ //if 'OK' put items back in collection
+ if (response == ResponseType.Ok)
+ {
+ DesignerTransaction tran = CreateTransaction (Instance);
+ object old = collection;
+
+ try {
+ collection.Clear();
+ foreach (object[] o in itemStore)
+ collection.Add (o[0]);
+ EndTransaction (Instance, tran, old, collection, true);
+ }
+ catch {
+ EndTransaction (Instance, tran, old, collection, false);
+ throw;
+ }
+ }
+ }
+
+ //This and EndTransaction are from Mono internal class System.ComponentModel.ReflectionPropertyDescriptor
+ // Lluis Sanchez Gual (lluis@ximian.com), (C) Novell, Inc, MIT X11 license
+ DesignerTransaction CreateTransaction (object obj)
+ {
+ IComponent com = obj as IComponent;
+ if (com == null || com.Site == null) return null;
+
+ IDesignerHost dh = (IDesignerHost) com.Site.GetService (typeof(IDesignerHost));
+ if (dh == null) return null;
+
+ DesignerTransaction tran = dh.CreateTransaction ();
+ IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
+ if (ccs != null)
+ ccs.OnComponentChanging (com, Property);
+ return tran;
+ }
+
+ void EndTransaction (object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit)
+ {
+ if (tran == null) return;
+
+ if (commit) {
+ IComponent com = obj as IComponent;
+ IComponentChangeService ccs = (IComponentChangeService) com.Site.GetService (typeof(IComponentChangeService));
+ if (ccs != null)
+ ccs.OnComponentChanged (com, Property, oldValue, newValue);
+ tran.Commit ();
+ }
+ else
+ tran.Cancel ();
+ }
+
+ static void UpdateIndices (ListStore itemStore)
+ {
+ TreeIter iter;
+ int i = 0;
+ if (!itemStore.GetIterFirst (out iter))
+ return;
+
+ do {
+ itemStore.SetValue (iter, 1, i);
+ i++;
+ } while (itemStore.IterNext (ref iter));
+ }
+
+ static void UpdateName (ListStore itemStore, TreeIter iter)
+ {
+ if (iter.Equals (TreeIter.Zero))
+ return;
+
+ object item = itemStore.GetValue (iter, 0);
+ string name = item.ToString ();
+ if (string.IsNullOrEmpty (name))
+ name = MonoDevelop.Core.GettextCatalog.GetString ("(Empty)");;
+
+ itemStore.SetValue(iter, 2, name);
+ }
+
+ //generally useful function... why not in model already?
+ static bool IterPrev (TreeModel model, ref TreeIter iter)
+ {
+ TreePath tp = model.GetPath (iter);
+ return tp.Prev() && model.GetIter (out iter, tp);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ColorEditorCell.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ColorEditorCell.cs
new file mode 100644
index 0000000000..af94facd33
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ColorEditorCell.cs
@@ -0,0 +1,115 @@
+//
+// BooleanEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+// Michael Hutchinson <m.j.hutchinson@gmail.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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (System.Drawing.Color))]
+ public class ColorEditorCell: PropertyEditorCell
+ {
+ const int ColorBoxSize = 16;
+ const int ColorBoxSpacing = 3;
+
+ public override void GetSize (int availableWidth, out int width, out int height)
+ {
+ base.GetSize (availableWidth - ColorBoxSize - ColorBoxSpacing, out width, out height);
+ width += ColorBoxSize + ColorBoxSpacing;
+ if (height < ColorBoxSize) height = ColorBoxSize;
+ }
+
+ protected override string GetValueText ()
+ {
+ System.Drawing.Color color = (System.Drawing.Color) Value;
+ //TODO: dropdown known color selector so this does something
+ if (color.IsKnownColor)
+ return color.Name;
+ else if (color.IsEmpty)
+ return "";
+ else
+ return String.Format("#{0:x2}{1:x2}{2:x2}", color.R, color.G, color.B);
+ }
+
+ public override void Render (Gdk.Drawable window, Gdk.Rectangle bounds, Gtk.StateType state)
+ {
+ Gdk.GC gc = new Gdk.GC (window);
+ gc.RgbFgColor = GetColor ();
+ int yd = (bounds.Height - ColorBoxSize) / 2;
+ window.DrawRectangle (gc, true, bounds.X, bounds.Y + yd, ColorBoxSize - 1, ColorBoxSize - 1);
+ window.DrawRectangle (Container.Style.BlackGC, false, bounds.X, bounds.Y + yd, ColorBoxSize - 1, ColorBoxSize - 1);
+ bounds.X += ColorBoxSize + ColorBoxSpacing;
+ bounds.Width -= ColorBoxSize + ColorBoxSpacing;
+ base.Render (window, bounds, state);
+ }
+
+ private Gdk.Color GetColor ()
+ {
+ System.Drawing.Color color = (System.Drawing.Color) Value;
+ //TODO: Property.Converter.ConvertTo() fails: why?
+ return new Gdk.Color (color.R, color.G, color.B);
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ return new ColorEditor ();
+ }
+ }
+
+ public class ColorEditor : Gtk.ColorButton, IPropertyEditor
+ {
+ public void Initialize (EditSession session)
+ {
+ if (session.Property.PropertyType != typeof(System.Drawing.Color))
+ throw new ApplicationException ("Color editor does not support editing values of type " + session.Property.PropertyType);
+ }
+
+ public object Value {
+ get {
+ int red = (int) (255 * (float) Color.Red / ushort.MaxValue);
+ int green = (int) (255 * (float) Color.Green / ushort.MaxValue);
+ int blue = (int) (255 * (float) Color.Blue / ushort.MaxValue);
+ return System.Drawing.Color.FromArgb (red, green, blue);
+ }
+ set {
+ System.Drawing.Color color = (System.Drawing.Color) value;
+ Color = new Gdk.Color (color.R, color.G, color.B);
+ }
+ }
+
+ protected override void OnColorSet ()
+ {
+ base.OnColorSet ();
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+
+ public event EventHandler ValueChanged;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DateTimeEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DateTimeEditor.cs
new file mode 100644
index 0000000000..0e2f6e1be7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DateTimeEditor.cs
@@ -0,0 +1,98 @@
+//
+// DateTimeEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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;
+using Gdk;
+using System.Text;
+using System.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (DateTime))]
+ public class DateTimeEditorCell: PropertyEditorCell
+ {
+ protected override string GetValueText ()
+ {
+ return ((DateTime)Value).ToLongDateString ();
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ return new DateTimeEditor ();
+ }
+ }
+
+ public class DateTimeEditor: Gtk.HBox, IPropertyEditor
+ {
+ Gtk.Entry entry;
+ DateTime time;
+
+ public DateTimeEditor()
+ {
+ entry = new Gtk.Entry ();
+ entry.Changed += OnChanged;
+ entry.HasFrame = false;
+ PackStart (entry, true, true, 0);
+ ShowAll ();
+ }
+
+ public void Initialize (EditSession session)
+ {
+ }
+
+ public object Value {
+ get { return time; }
+ set {
+ time = (DateTime) value;
+ entry.Changed -= OnChanged;
+ entry.Text = time.ToString ("G");
+ entry.Changed += OnChanged;
+ }
+ }
+
+ void OnChanged (object o, EventArgs a)
+ {
+ string s = entry.Text;
+
+ foreach (string form in formats) {
+ try {
+ time = DateTime.ParseExact (s, form, null);
+ if (ValueChanged != null)
+ ValueChanged (this, a);
+ break;
+ } catch {
+ }
+ }
+ }
+
+ public event EventHandler ValueChanged;
+
+ static string[] formats = {"u", "G", "g", "d", "T", "t"};
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DefaultEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DefaultEditor.cs
new file mode 100644
index 0000000000..f05387164f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/DefaultEditor.cs
@@ -0,0 +1,49 @@
+/*
+ * DefaultEditor.cs - The default visual 'editor' for types we don't have
+ * an editor for.
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ * Lluis Sanchez Gual
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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;
+using System.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ public class DefaultEditor : PropertyEditorCell
+ {
+ protected override string GetValueMarkup ()
+ {
+ return "<span foreground=\"grey\">&lt;" + Property.PropertyType.ToString() + "&gt;</span>";
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EnumerationEditorCell.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EnumerationEditorCell.cs
new file mode 100644
index 0000000000..f924f87716
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EnumerationEditorCell.cs
@@ -0,0 +1,144 @@
+//
+// EnumerationEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+using System.Reflection;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType(typeof(System.Enum))]
+ public class EnumerationEditorCell: PropertyEditorCell
+ {
+ protected override string GetValueText ()
+ {
+ if (Value == null)
+ return "";
+
+ string val = Value.ToString ();
+
+ // If the enum value has a Description attribute, return the description
+ foreach (FieldInfo f in Property.PropertyType.GetFields ()) {
+ if (f.Name == val) {
+ DescriptionAttribute att = (DescriptionAttribute) Attribute.GetCustomAttribute (f, typeof(DescriptionAttribute));
+ if (att != null)
+ return att.Description;
+ else
+ return val;
+ }
+ }
+ return val;
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ return new EnumerationEditor ();
+ }
+ }
+
+ public class EnumerationEditor : Gtk.HBox, IPropertyEditor {
+
+ Gtk.EventBox ebox;
+ Gtk.ComboBoxEntry combo;
+ Array values;
+
+ public EnumerationEditor () : base (false, 0)
+ {
+ }
+
+ public void Initialize (EditSession session)
+ {
+ PropertyDescriptor prop = session.Property;
+
+ if (!prop.PropertyType.IsEnum)
+ throw new ApplicationException ("Enumeration editor does not support editing values of type " + prop.PropertyType);
+
+ values = System.Enum.GetValues (prop.PropertyType);
+ Hashtable names = new Hashtable ();
+ foreach (FieldInfo f in prop.PropertyType.GetFields ()) {
+ DescriptionAttribute att = (DescriptionAttribute) Attribute.GetCustomAttribute (f, typeof(DescriptionAttribute));
+ if (att != null)
+ names [f.Name] = att.Description;
+ else
+ names [f.Name] = f.Name;
+ }
+
+
+ ebox = new Gtk.EventBox ();
+ ebox.Show ();
+ PackStart (ebox, true, true, 0);
+
+ combo = Gtk.ComboBoxEntry.NewText ();
+ combo.Changed += combo_Changed;
+ combo.Entry.IsEditable = false;
+ combo.Entry.CanFocus = false;
+ combo.Entry.HasFrame = false;
+ combo.Entry.HeightRequest = combo.SizeRequest ().Height;
+ combo.Show ();
+ ebox.Add (combo);
+
+ foreach (object value in values) {
+ string str = prop.Converter.ConvertToString (value);
+ if (names.Contains (str))
+ str = (string) names [str];
+ combo.AppendText (str);
+ }
+ }
+
+ protected override void OnDestroyed ()
+ {
+ base.OnDestroyed ();
+ ((IDisposable)this).Dispose ();
+ }
+
+ void IDisposable.Dispose ()
+ {
+ }
+
+ public object Value {
+ get {
+ return values.GetValue (combo.Active);
+ }
+ set {
+ int i = Array.IndexOf (values, value);
+ if (i != -1)
+ combo.Active = i;
+ }
+ }
+
+ public event EventHandler ValueChanged;
+
+ void combo_Changed (object o, EventArgs args)
+ {
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ ebox.TooltipText = Value != null ? Value.ToString () : null;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs
new file mode 100644
index 0000000000..a07c47b6ca
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs
@@ -0,0 +1,135 @@
+/*
+ * EventEditor.cs - Visual editor for Events
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ * Lluis Sanchez Gual
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.ComponentModel;
+using System.ComponentModel.Design;
+using System.Collections;
+using Gtk;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (Delegate), true)]
+ public class EventEditorCell : PropertyEditorCell
+ {
+ IEventBindingService evtBind;
+
+ protected override void Initialize ()
+ {
+ IComponent comp = Instance as IComponent;
+ evtBind = (IEventBindingService) comp.Site.GetService (typeof (IEventBindingService));
+ base.Initialize ();
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ //get existing method names
+ ICollection IColl = evtBind.GetCompatibleMethods (evtBind.GetEvent (Property)) ;
+ string[] methods = new string [IColl.Count + 1];
+ IColl.CopyTo (methods, 1);
+
+ //add a suggestion
+ methods [0] = evtBind.CreateUniqueMethodName ((IComponent) Instance, evtBind.GetEvent (Property));
+
+ EventEditor combo = new EventEditor (evtBind, methods);
+
+ if (Value != null)
+ combo.Entry.Text = (string) Value;
+
+ combo.WidthRequest = 30; //Don't artificially inflate the width. It expands anyway.
+
+ return combo;
+ }
+
+ }
+
+ class EventEditor: ComboBoxEntry, IPropertyEditor
+ {
+ bool isNull;
+ PropertyDescriptor prop;
+ IEventBindingService evtBind;
+ object component;
+
+ public EventEditor (IEventBindingService evtBind, string[] ops): base (ops)
+ {
+ this.evtBind = evtBind;
+ }
+
+ public void Initialize (EditSession session)
+ {
+ this.prop = session.Property;
+ component = session.Instance;
+ Entry.Destroyed += new EventHandler (entry_Changed);
+ Entry.Activated += new EventHandler (entry_Activated);
+ }
+
+ public object Value {
+ get {
+ //if value was null and new value is empty, leave as null
+ if (Entry.Text.Length == 0 && isNull)
+ return null;
+ else
+ return Entry.Text;
+ }
+ set {
+ isNull = value == null;
+ if (isNull)
+ Entry.Text = "";
+ else
+ Entry.Text = (string) value;
+ }
+ }
+
+ protected override void OnChanged ()
+ {
+ if (component == null)
+ return;
+ entry_Changed (this, null);
+ evtBind.ShowCode ((IComponent) component, evtBind.GetEvent (prop));
+ }
+
+ void entry_Activated (object sender, EventArgs e)
+ {
+ entry_Changed (sender, e);
+ evtBind.ShowCode ((IComponent) component, evtBind.GetEvent (prop));
+ }
+
+ void entry_Changed (object sender, EventArgs e)
+ {
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+
+ public event EventHandler ValueChanged;
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs
new file mode 100644
index 0000000000..83b471600a
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs
@@ -0,0 +1,65 @@
+/*
+ * ExpandableObjectEditor.cs - Temporary editor until we get expandable object support in main grid
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ * Lluis Sanchez Gual
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Collections;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ class ExpandableObjectEditor : PropertyEditorCell
+ {
+ protected override string GetValueMarkup ()
+ {
+ string val;
+ if (Property.Converter.CanConvertTo (typeof(string)))
+ val = Property.Converter.ConvertToString (Value);
+ else
+ val = Value != null ? Value.ToString () : "";
+
+ return "<b>" + GLib.Markup.EscapeText (val) + "</b>";
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, StateType state)
+ {
+ if (Property.Converter.CanConvertTo (typeof(string)) && Property.Converter.CanConvertFrom (typeof(string)))
+ return new TextEditor ();
+ else
+ return null;
+ }
+
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsEditorCell.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsEditorCell.cs
new file mode 100644
index 0000000000..3f51ea4903
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsEditorCell.cs
@@ -0,0 +1,205 @@
+//
+// FlagsEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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 Gtk;
+using System;
+using System.Collections;
+using System.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors {
+
+ public class FlagsEditorCell: PropertyEditorCell
+ {
+ protected override string GetValueText ()
+ {
+ if (Value == null)
+ return "";
+
+ ulong value = Convert.ToUInt64 (Value);
+ Array values = System.Enum.GetValues (base.Property.PropertyType);
+ string txt = "";
+ foreach (object val in values) {
+ if ((value & Convert.ToUInt64 (value)) != 0) {
+ if (txt.Length > 0) txt += ", ";
+ txt += val.ToString ();
+ }
+ }
+ return txt;
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ return new FlagsEditor ();
+ }
+ }
+
+ public class FlagsEditor : Gtk.HBox, IPropertyEditor
+ {
+ Hashtable flags;
+ Gtk.Entry flagsLabel;
+ string property;
+ Type propType;
+ Array values;
+
+ public FlagsEditor ()
+ {
+ }
+
+ public void Initialize (EditSession session)
+ {
+ PropertyDescriptor prop = session.Property;
+
+ if (!prop.PropertyType.IsEnum)
+ throw new ApplicationException ("Flags editor does not support editing values of type " + prop.PropertyType);
+
+ Spacing = 3;
+ propType = prop.PropertyType;
+
+ property = prop.Description;
+ if (property == null || property.Length == 0)
+ property = prop.Name;
+
+ // For small enums, the editor is a list of checkboxes inside a frame
+ // For large enums (>5), use a selector dialog.
+
+ values = System.Enum.GetValues (prop.PropertyType);
+
+ if (values.Length < 6)
+ {
+ Gtk.VBox vbox = new Gtk.VBox (true, 3);
+
+ flags = new Hashtable ();
+
+ foreach (object value in values) {
+ Gtk.CheckButton check = new Gtk.CheckButton (value.ToString ());
+ check.TooltipText = value.ToString ();
+ ulong uintVal = Convert.ToUInt64 (value);
+ flags[check] = uintVal;
+ flags[uintVal] = check;
+
+ check.Toggled += FlagToggled;
+ vbox.PackStart (check, false, false, 0);
+ }
+
+ Gtk.Frame frame = new Gtk.Frame ();
+ frame.Add (vbox);
+ frame.ShowAll ();
+ PackStart (frame, true, true, 0);
+ }
+ else
+ {
+ flagsLabel = new Gtk.Entry ();
+ flagsLabel.IsEditable = false;
+ flagsLabel.HasFrame = false;
+ flagsLabel.ShowAll ();
+ PackStart (flagsLabel, true, true, 0);
+
+ Gtk.Button but = new Gtk.Button ("...");
+ but.Clicked += OnSelectFlags;
+ but.ShowAll ();
+ PackStart (but, false, false, 0);
+ }
+ }
+
+ protected override void OnDestroyed ()
+ {
+ base.OnDestroyed ();
+ ((IDisposable)this).Dispose ();
+ }
+
+ void IDisposable.Dispose ()
+ {
+ }
+
+ public object Value {
+ get {
+ return Enum.ToObject (propType, UIntValue);
+ }
+ set {
+ ulong newVal = Convert.ToUInt64 (value);
+ if (flagsLabel != null) {
+ string txt = "";
+ foreach (object val in values) {
+ if ((newVal & Convert.ToUInt64(val)) != 0) {
+ if (txt.Length > 0) txt += ", ";
+ txt += val.ToString ();
+ }
+ }
+ flagsLabel.Text = txt;
+ UIntValue = newVal;
+ }
+ else {
+ for (ulong i = 1; i <= uintValue || i <= newVal; i = i << 1) {
+ if ((uintValue & i) != (newVal & i)) {
+ Gtk.CheckButton check = (Gtk.CheckButton)flags[i];
+ if (check != null)
+ check.Active = !check.Active;
+ }
+ }
+ }
+ }
+ }
+
+ public event EventHandler ValueChanged;
+
+ ulong uintValue;
+
+ ulong UIntValue {
+ get {
+ return uintValue;
+ }
+ set {
+ if (uintValue != value) {
+ uintValue = value;
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+ }
+ }
+
+ void FlagToggled (object o, EventArgs args)
+ {
+ Gtk.CheckButton check = (Gtk.CheckButton)o;
+ ulong val = (ulong)flags[o];
+
+ if (check.Active)
+ UIntValue |= val;
+ else
+ UIntValue &= ~val;
+ }
+
+ void OnSelectFlags (object o, EventArgs args)
+ {
+ using (FlagsSelectorDialog dialog = new FlagsSelectorDialog (null, propType, UIntValue, property)) {
+ if (dialog.Run () == (int) ResponseType.Ok) {
+ Value = Enum.ToObject (propType, dialog.Value);
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsSelectorDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsSelectorDialog.cs
new file mode 100644
index 0000000000..287073b5f9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FlagsSelectorDialog.cs
@@ -0,0 +1,117 @@
+//
+// FlagsSelectorDialog.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.PropertyGrid.PropertyEditors
+{
+ public class FlagsSelectorDialog: IDisposable
+ {
+ Gtk.TreeView treeView;
+ Gtk.Dialog dialog;
+ Gtk.ListStore store;
+ Gtk.Window parent;
+ ulong flags;
+
+ public FlagsSelectorDialog (Gtk.Window parent, Type enumDesc, ulong flags, string title)
+ {
+ this.flags = flags;
+ this.parent = parent;
+
+ Gtk.ScrolledWindow sc = new Gtk.ScrolledWindow ();
+ sc.HscrollbarPolicy = Gtk.PolicyType.Automatic;
+ sc.VscrollbarPolicy = Gtk.PolicyType.Automatic;
+ sc.ShadowType = Gtk.ShadowType.In;
+ sc.BorderWidth = 6;
+
+ treeView = new Gtk.TreeView ();
+ sc.Add (treeView);
+
+ dialog = new Gtk.Dialog ();
+ dialog.VBox.Add (sc);
+ dialog.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
+ dialog.AddButton (Gtk.Stock.Ok, Gtk.ResponseType.Ok);
+
+ store = new Gtk.ListStore (typeof(bool), typeof(string), typeof(ulong));
+ treeView.Model = store;
+ treeView.HeadersVisible = false;
+
+ Gtk.TreeViewColumn col = new Gtk.TreeViewColumn ();
+
+ Gtk.CellRendererToggle tog = new Gtk.CellRendererToggle ();
+ tog.Toggled += new Gtk.ToggledHandler (OnToggled);
+ col.PackStart (tog, false);
+ col.AddAttribute (tog, "active", 0);
+
+ Gtk.CellRendererText crt = new Gtk.CellRendererText ();
+ col.PackStart (crt, true);
+ col.AddAttribute (crt, "text", 1);
+
+ treeView.AppendColumn (col);
+
+ foreach (object value in System.Enum.GetValues (enumDesc)) {
+ ulong val = Convert.ToUInt64 (value);
+ store.AppendValues (((flags & val) != 0), value.ToString (), val);
+ }
+ }
+
+ public int Run ()
+ {
+ dialog.DefaultWidth = 500;
+ dialog.DefaultHeight = 400;
+ dialog.ShowAll ();
+ dialog.TransientFor = parent;
+ return dialog.Run ();
+ }
+
+ public void Dispose ()
+ {
+ dialog.Destroy ();
+ }
+
+ void OnToggled (object s, Gtk.ToggledArgs args)
+ {
+ Gtk.TreeIter iter;
+ if (!store.GetIterFromString (out iter, args.Path))
+ return;
+
+ bool oldValue = (bool) store.GetValue (iter, 0);
+ ulong flag = (ulong) store.GetValue (iter, 2);
+ store.SetValue (iter, 0, !oldValue);
+
+ if (oldValue)
+ flags &= ~flag;
+ else
+ flags |= flag;
+ }
+
+ public ulong Value {
+ get { return flags; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FloatRange.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FloatRange.cs
new file mode 100644
index 0000000000..8a3e7134dc
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/FloatRange.cs
@@ -0,0 +1,69 @@
+//
+// FloatRange.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (Double))]
+ [PropertyEditorType (typeof (Single))]
+ public class FloatRange : Gtk.SpinButton, IPropertyEditor
+ {
+ Type propType;
+
+ public FloatRange (): base (0, 0, 0.01)
+ {
+ }
+
+ public void Initialize (EditSession session)
+ {
+ propType = session.Property.PropertyType;
+
+ double min, max;
+
+ if (propType == typeof(double)) {
+ min = Double.MinValue;
+ max = Double.MaxValue;
+ } else if (propType == typeof(float)) {
+ min = float.MinValue;
+ max = float.MaxValue;
+ } else
+ throw new ApplicationException ("FloatRange editor does not support editing values of type " + propType);
+
+ SetRange (min, max);
+
+ Digits = 2;
+ }
+
+ object IPropertyEditor.Value {
+ get { return Convert.ChangeType (base.Value, propType); }
+ set { base.Value = (double) Convert.ChangeType (value, typeof(double)); }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/IntRange.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/IntRange.cs
new file mode 100644
index 0000000000..ed7bc4fe74
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/IntRange.cs
@@ -0,0 +1,103 @@
+//
+// FlagsSelectorDialog.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (byte))]
+ [PropertyEditorType (typeof (sbyte))]
+ [PropertyEditorType (typeof (Int16))]
+ [PropertyEditorType (typeof (UInt16))]
+ [PropertyEditorType (typeof (Int32))]
+ [PropertyEditorType (typeof (UInt32))]
+ [PropertyEditorType (typeof (Int64))]
+ [PropertyEditorType (typeof (UInt64))]
+ [PropertyEditorType (typeof (Decimal))]
+ public class IntRangeEditor : Gtk.SpinButton, IPropertyEditor
+ {
+ Type propType;
+
+ public IntRangeEditor () : base (0, 0, 1.0)
+ {
+ this.HasFrame = false;
+ }
+
+ public void Initialize (EditSession session)
+ {
+ propType = session.Property.PropertyType;
+
+ double min, max;
+
+ switch (Type.GetTypeCode (propType)) {
+ case TypeCode.Int16:
+ min = (double) Int16.MinValue;
+ max = (double) Int16.MaxValue;
+ break;
+ case TypeCode.UInt16:
+ min = (double) UInt16.MinValue;
+ max = (double) UInt16.MaxValue;
+ break;
+ case TypeCode.Int32:
+ min = (double) Int32.MinValue;
+ max = (double) Int32.MaxValue;
+ break;
+ case TypeCode.UInt32:
+ min = (double) UInt32.MinValue;
+ max = (double) UInt32.MaxValue;
+ break;
+ case TypeCode.Int64:
+ min = (double) Int64.MinValue;
+ max = (double) Int64.MaxValue;
+ break;
+ case TypeCode.UInt64:
+ min = (double) UInt64.MinValue;
+ max = (double) UInt64.MaxValue;
+ break;
+ case TypeCode.Byte:
+ min = (double) Byte.MinValue;
+ max = (double) Byte.MaxValue;
+ break;
+ case TypeCode.SByte:
+ min = (double) SByte.MinValue;
+ max = (double) SByte.MaxValue;
+ break;
+ default:
+ throw new ApplicationException ("IntRange editor does not support editing values of type " + session.Property.PropertyType);
+ }
+
+ SetRange (min, max);
+ }
+
+ object IPropertyEditor.Value {
+ get { return Convert.ChangeType (base.Value, propType); }
+ set { base.Value = (double) Convert.ChangeType (value, typeof(double)); }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditor.cs
new file mode 100644
index 0000000000..de866f675d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditor.cs
@@ -0,0 +1,193 @@
+//
+// TextEditor.cs
+//
+// Author:
+// Lluis Sanchez Gual
+// Michael Hutchinson
+//
+// 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.ComponentModel;
+
+using Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (string))]
+ public class TextEditor: Gtk.HBox, IPropertyEditor
+ {
+ EditSession session;
+ bool disposed;
+ string initialText;
+ Entry entry;
+
+ public TextEditor()
+ {
+ }
+
+ public void Initialize (EditSession session)
+ {
+ this.session = session;
+
+ //if standard values are supported by the converter, then
+ //we list them in a combo
+ if (session.Property.Converter.GetStandardValuesSupported (session))
+ {
+ ListStore store = new ListStore (typeof (string));
+ ComboBoxEntry combo = new ComboBoxEntry (store, 0);
+ PackStart (combo, true, true, 0);
+ combo.Changed += TextChanged;
+ entry = combo.Entry;
+ entry.HeightRequest = combo.SizeRequest ().Height;
+
+ //but if the converter doesn't allow nonstandard values,
+ // then we make the entry uneditable
+ if (session.Property.Converter.GetStandardValuesExclusive (session)) {
+ entry.IsEditable = false;
+ entry.CanFocus = false;
+ }
+
+ //fill the list
+ foreach (object stdValue in session.Property.Converter.GetStandardValues (session)) {
+ store.AppendValues (session.Property.Converter.ConvertToString (session, stdValue));
+ }
+
+ //a value of "--" gets rendered as a --, if typeconverter marked with UsesDashesForSeparator
+ object[] atts = session.Property.Converter.GetType ()
+ .GetCustomAttributes (typeof (StandardValuesSeparatorAttribute), true);
+ if (atts.Length > 0) {
+ string separator = ((StandardValuesSeparatorAttribute)atts[0]).Separator;
+ combo.RowSeparatorFunc = delegate (TreeModel model, TreeIter iter) {
+ return separator == ((string) model.GetValue (iter, 0));
+ };
+ }
+ }
+ // no standard values, so just use an entry
+ else {
+ entry = new Entry ();
+ PackStart (entry, true, true, 0);
+ }
+
+ //either way we have an entry to play with
+ entry.HasFrame = false;
+ entry.Activated += TextChanged;
+
+ if (ShouldShowDialogButton ()) {
+ Button button = new Button ("...");
+ PackStart (button, false, false, 0);
+ button.Clicked += ButtonClicked;
+ }
+
+ Spacing = 3;
+ ShowAll ();
+ }
+
+ protected virtual bool ShouldShowDialogButton ()
+ {
+ //if the object's Localizable, show a dialog, since the text's likely to be more substantial
+ LocalizableAttribute at = (LocalizableAttribute) session.Property.Attributes [typeof(LocalizableAttribute)];
+ return (at != null && at.IsLocalizable);
+ }
+
+ void ButtonClicked (object s, EventArgs a)
+ {
+ using (TextEditorDialog dlg = new TextEditorDialog ()) {
+ dlg.TransientFor = this.Toplevel as Gtk.Window;
+ dlg.Text = entry.Text;
+ if (dlg.Run () == (int) ResponseType.Ok) {
+ entry.Text = dlg.Text;
+ TextChanged (null, null);
+ }
+ }
+ }
+
+ void TextChanged (object s, EventArgs a)
+ {
+ if (initialText == entry.Text)
+ return;
+
+ bool valid = false;
+ if (session.Property.Converter.IsValid (session, entry.Text)) {
+ try {
+ session.Property.Converter.ConvertFromString (session, entry.Text);
+ initialText = entry.Text;
+ if (ValueChanged != null)
+ ValueChanged (this, a);
+ valid = true;
+ } catch {
+ // Invalid format
+ }
+ }
+
+ if (valid)
+ entry.ModifyFg (Gtk.StateType.Normal);
+ else
+ entry.ModifyFg (Gtk.StateType.Normal, new Gdk.Color (255, 0, 0));
+ }
+
+ // Gets/Sets the value of the editor. If the editor supports
+ // several value types, it is the responsibility of the editor
+ // to return values with the expected type.
+ public object Value {
+ get {
+ return session.Property.Converter.ConvertFromString (session, entry.Text);
+ }
+ set {
+ string val = session.Property.Converter.ConvertToString (session, value);
+ initialText = entry.Text;
+ entry.Text = val ?? string.Empty;
+ }
+ }
+
+ protected override void OnDestroyed ()
+ {
+ base.OnDestroyed ();
+ ((IDisposable)this).Dispose ();
+ }
+
+ void IDisposable.Dispose ()
+ {
+ if (!disposed && initialText != entry.Text) {
+ TextChanged (null, null);
+ }
+ disposed = true;
+ }
+
+ // To be fired when the edited value changes.
+ public event EventHandler ValueChanged;
+ }
+
+ public class StandardValuesSeparatorAttribute : Attribute
+ {
+ string separator;
+
+ public string Separator { get { return separator; } }
+
+ public StandardValuesSeparatorAttribute (string separator)
+ {
+ this.separator = separator;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditorDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditorDialog.cs
new file mode 100644
index 0000000000..a43d26e6a1
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditorDialog.cs
@@ -0,0 +1,78 @@
+//
+// TextEditorDialog.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ public class TextEditorDialog: IDisposable
+ {
+ Gtk.TextView textview;
+ Gtk.Dialog dialog;
+
+ public TextEditorDialog ()
+ {
+ Gtk.ScrolledWindow sc = new Gtk.ScrolledWindow ();
+ sc.HscrollbarPolicy = Gtk.PolicyType.Automatic;
+ sc.VscrollbarPolicy = Gtk.PolicyType.Automatic;
+ sc.ShadowType = Gtk.ShadowType.In;
+ sc.BorderWidth = 6;
+
+ textview = new Gtk.TextView ();
+ sc.Add (textview);
+
+ dialog = new Gtk.Dialog ();
+ dialog.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel);
+ dialog.AddButton (Gtk.Stock.Ok, Gtk.ResponseType.Ok);
+ dialog.VBox.Add (sc);
+ }
+
+ public Gtk.Window TransientFor {
+ set { dialog.TransientFor = value; }
+ }
+
+ public string Text {
+ get { return textview.Buffer.Text; }
+ set { textview.Buffer.Text = value; }
+ }
+
+ public int Run ()
+ {
+ dialog.DefaultWidth = 500;
+ dialog.DefaultHeight = 400;
+ dialog.ShowAll ();
+ return dialog.Run ();
+ }
+
+ public void Dispose ()
+ {
+ dialog.Destroy ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TimeSpanEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TimeSpanEditor.cs
new file mode 100644
index 0000000000..eaeb855ab3
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TimeSpanEditor.cs
@@ -0,0 +1,93 @@
+//
+// TimeSpanEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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;
+using Gdk;
+using System.Text;
+using System.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ [PropertyEditorType (typeof (TimeSpan))]
+ public class TimeSpanEditorCell: PropertyEditorCell
+ {
+ protected override string GetValueText ()
+ {
+ return ((TimeSpan)Value).ToString ();
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
+ {
+ return new TimeSpanEditor ();
+ }
+ }
+
+ public class TimeSpanEditor: Gtk.HBox, IPropertyEditor
+ {
+ Gtk.Entry entry;
+ TimeSpan time;
+
+ public TimeSpanEditor()
+ {
+ entry = new Gtk.Entry ();
+ entry.Changed += OnChanged;
+ entry.HasFrame = false;
+ PackStart (entry, true, true, 0);
+ ShowAll ();
+ }
+
+ public void Initialize (EditSession session)
+ {
+ }
+
+ public object Value {
+ get { return time; }
+ set {
+ time = (TimeSpan) value;
+ entry.Changed -= OnChanged;
+ entry.Text = time.ToString ();
+ entry.Changed += OnChanged;
+ }
+ }
+
+ void OnChanged (object o, EventArgs a)
+ {
+ string s = entry.Text;
+
+ try {
+ time = TimeSpan.Parse (s);
+ if (ValueChanged != null)
+ ValueChanged (this, a);
+ } catch {
+ }
+ }
+
+ public event EventHandler ValueChanged;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/DefaultPropertyTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/DefaultPropertyTab.cs
new file mode 100644
index 0000000000..55737bfa78
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/DefaultPropertyTab.cs
@@ -0,0 +1,98 @@
+ /*
+ * DefaultPropertyTab.cs - The default PropertyTab
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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 Gtk;
+using System;
+using System.ComponentModel;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ public class DefaultPropertyTab : PropertyTab
+ {
+ public DefaultPropertyTab ()
+ : base ()
+ {
+ }
+
+ public override string TabName {
+ get {return GettextCatalog.GetString ("Properties"); }
+ }
+
+ public override bool CanExtend (object extendee)
+ {
+ return true;
+ }
+
+ public override PropertyDescriptor GetDefaultProperty (object component)
+ {
+ if (component == null)
+ return null;
+ return TypeDescriptor.GetDefaultProperty (component);
+ }
+
+ public override PropertyDescriptorCollection GetProperties (object component, Attribute[] attributes)
+ {
+ if (component == null)
+ return new PropertyDescriptorCollection (new PropertyDescriptor[] {});
+ return TypeDescriptor.GetProperties (component);
+ }
+ }
+
+ public abstract class PropertyTab
+ {
+ public abstract string TabName { get; }
+ public abstract bool CanExtend (object extendee);
+ public abstract PropertyDescriptor GetDefaultProperty (object component);
+ public abstract PropertyDescriptorCollection GetProperties (object component, Attribute[] attributes);
+
+ public PropertyDescriptorCollection GetProperties (object component)
+ {
+ return GetProperties (component, null);
+ }
+
+ public Gdk.Pixbuf GetIcon ()
+ {
+ using (var stream = GetType ().Assembly.GetManifestResourceStream (GetType ().FullName + ".bmp")) {
+ if (stream != null) {
+ try {
+ return new Gdk.Pixbuf (stream);
+ } catch (Exception e) {
+ LoggingService.LogError ("Can't create pixbuf from resource:" + GetType ().FullName + ".bmp", e);
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs
new file mode 100644
index 0000000000..5ec06c6c97
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs
@@ -0,0 +1,182 @@
+/*
+ * EditorManager.cs - Used to register, lookup and select visual editors.
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ * Lluis Sanchez Gual
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.Reflection;
+using System.ComponentModel;
+using MonoDevelop.Components.PropertyGrid.PropertyEditors;
+using System.Drawing.Design;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ internal class EditorManager
+ {
+ private Hashtable editors = new Hashtable ();
+ private Hashtable inheritingEditors = new Hashtable ();
+ private Hashtable surrogates = new Hashtable ();
+ static PropertyEditorCell Default = new PropertyEditorCell ();
+ static Hashtable cellCache = new Hashtable ();
+
+ internal EditorManager ()
+ {
+ LoadEditor (Assembly.GetAssembly (typeof (EditorManager)));
+ }
+
+ public void LoadEditor (Assembly editorAssembly)
+ {
+ foreach (Type t in editorAssembly.GetTypes ()) {
+ foreach (Attribute currentAttribute in Attribute.GetCustomAttributes (t)) {
+ if (currentAttribute.GetType() == typeof (PropertyEditorTypeAttribute)) {
+ PropertyEditorTypeAttribute peta = (PropertyEditorTypeAttribute)currentAttribute;
+ Type editsType = peta.Type;
+ if (t.IsSubclassOf (typeof (PropertyEditorCell)))
+ if (peta.Inherits)
+ inheritingEditors.Add (editsType, t);
+ else
+ editors.Add (editsType, t);
+ }
+ else if (currentAttribute.GetType () == typeof (SurrogateUITypeEditorAttribute)) {
+ Type editsType = (currentAttribute as SurrogateUITypeEditorAttribute).Type;
+ surrogates.Add (editsType, t);
+ }
+ }
+ }
+ }
+
+ public PropertyEditorCell GetEditor (PropertyDescriptor pd)
+ {
+ PropertyEditorCell cell = pd.GetEditor (typeof(PropertyEditorCell)) as PropertyEditorCell;
+ if (cell != null)
+ return cell;
+
+ Type editorType = GetEditorType (pd);
+ if (editorType == null)
+ return Default;
+
+ if (typeof(IPropertyEditor).IsAssignableFrom (editorType)) {
+ if (!typeof(Gtk.Widget).IsAssignableFrom (editorType))
+ throw new Exception ("The property editor '" + editorType + "' must be a Gtk Widget");
+ return Default;
+ }
+
+ cell = cellCache [editorType] as PropertyEditorCell;
+ if (cell != null)
+ return cell;
+
+ if (!typeof(PropertyEditorCell).IsAssignableFrom (editorType))
+ throw new Exception ("The property editor '" + editorType + "' must be a subclass of Stetic.PropertyEditorCell or implement Stetic.IPropertyEditor");
+
+ cell = (PropertyEditorCell) Activator.CreateInstance (editorType);
+ cellCache [editorType] = cell;
+ return cell;
+ }
+
+ public Type GetEditorType (PropertyDescriptor pd)
+ {
+ //try to find a custom editor
+ //TODO: Find a way to provide a IWindowsFormsEditorService so this can work directly
+ //for now, substitute GTK#-based editors
+ /*
+ UITypeEditor UITypeEd = (UITypeEditor) pd.GetEditor(typeof (System.Drawing.Design.UITypeEditor));//first, does it have custom editors?
+ if (UITypeEd != null)
+ if (surrogates.Contains(UITypeEd.GetType ()))
+ return instantiateEditor((Type) surrogates[UITypeEd.GetType()], parentRow);
+ */
+
+ //does a registered GTK# editor support this natively?
+ Type editType = pd.PropertyType;
+ if (editors.Contains (editType))
+ return (Type) editors [editType];
+
+ //editors that edit derived types
+ foreach (DictionaryEntry de in inheritingEditors)
+ if (editType.IsSubclassOf((Type) de.Key))
+ return (Type) de.Value;
+
+ if (pd.PropertyType.IsEnum) {
+ if (pd.PropertyType.IsDefined (typeof (FlagsAttribute), true))
+ return typeof (PropertyEditors.FlagsEditorCell);
+ else
+ return typeof (PropertyEditors.EnumerationEditorCell);
+ }
+
+ //collections with items of single type that aren't just objects
+ if (typeof(IList).IsAssignableFrom (editType)) {
+ // Iterate through all properties since there may be more than one indexer.
+ if (GetCollectionItemType (editType) != null)
+ return typeof (CollectionEditor);
+ }
+
+ //TODO: support simple SWF collection editor derivatives that just override Types available
+ // and reflect protected Type[] NewItemTypes {get;} to get types
+ //if (UITypeEd is System.ComponentModel.Design.CollectionEditor)
+ // ((System.ComponentModel.Design.CollectionEditor)UITypeEd).
+
+ //can we use a type converter with a built-in editor?
+ TypeConverter tc = pd.Converter;
+
+ if (typeof (ExpandableObjectConverter).IsAssignableFrom (tc.GetType ()))
+ return typeof(ExpandableObjectEditor);
+
+ //This is a temporary workaround *and* and optimisation
+ //First, most unknown types will be most likely to convert to/from strings
+ //Second, System.Web.UI.WebControls/UnitConverter.cs dies on non-strings
+ if (tc.CanConvertFrom (typeof (string)) && tc.CanConvertTo (typeof(string)))
+ return typeof(TextEditor);
+
+ foreach (DictionaryEntry editor in editors)
+ if (tc.CanConvertFrom((Type) editor.Key) && tc.CanConvertTo((Type) editor.Key))
+ return (Type) editor.Value;
+
+ foreach (DictionaryEntry de in inheritingEditors)
+ if (tc.CanConvertFrom((Type) de.Key) && tc.CanConvertTo((Type) de.Key))
+ return (Type) de.Value;
+
+ //nothing found - just display type
+ return null;
+ }
+
+ public static Type GetCollectionItemType (Type colType)
+ {
+ foreach (PropertyInfo member in colType.GetProperties ()) {
+ if (member.Name == "Item") {
+ if (member.PropertyType != typeof (object))
+ return member.PropertyType;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EventPropertyTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EventPropertyTab.cs
new file mode 100644
index 0000000000..32d03e4e03
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EventPropertyTab.cs
@@ -0,0 +1,86 @@
+ /*
+ * EventPropertyTab.cs - A PropertyTab that displays events
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.ComponentModel;
+using System.ComponentModel.Design;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ public class EventPropertyTab : PropertyTab
+ {
+ public EventPropertyTab ()
+ {
+ }
+
+ public override string TabName {
+ get {return GettextCatalog.GetString ("Events"); }
+ }
+
+ public override bool CanExtend (object extendee)
+ {
+ IComponent comp = extendee as IComponent;
+ if (comp == null || comp.Site == null)
+ return false;
+
+ IEventBindingService evtBind = (IEventBindingService) comp.Site.GetService (typeof (IEventBindingService));
+ return !(evtBind == null);
+ }
+
+ public override PropertyDescriptor GetDefaultProperty (object component)
+ {
+ IEventBindingService evtBind = GetEventService (component);
+ EventDescriptor e = TypeDescriptor.GetDefaultEvent (component);
+
+ return (e == null)? null : evtBind.GetEventProperty (e);
+ }
+
+ public override PropertyDescriptorCollection GetProperties (object component, Attribute[] attributes)
+ {
+ IEventBindingService evtBind = GetEventService (component);
+ return evtBind.GetEventProperties (TypeDescriptor.GetEvents (component));
+ }
+
+ private IEventBindingService GetEventService (object component)
+ {
+ IComponent comp = component as IComponent;
+ if (comp == null || comp.Site == null)
+ throw new Exception ("Check whether a tab can display a component before displaying it");
+ IEventBindingService evtBind = (IEventBindingService) comp.Site.GetService (typeof (IEventBindingService));
+ if (evtBind == null)
+ throw new Exception ("Check whether a tab can display a component before displaying it");
+ return evtBind;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs
new file mode 100644
index 0000000000..f95fc7cc35
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs
@@ -0,0 +1,406 @@
+//
+// PropertyEditorCell.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.Reflection;
+using System.Collections;
+using System.ComponentModel;
+using MonoDevelop.Components.PropertyGrid.PropertyEditors;
+using Gtk;
+using Gdk;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ public class PropertyEditorCell
+ {
+ Pango.Layout layout;
+ PropertyDescriptor property;
+ object obj;
+ Gtk.Widget container;
+ EditorManager editorManager;
+
+ public object Instance {
+ get { return obj; }
+ }
+
+ public PropertyDescriptor Property {
+ get { return property; }
+ }
+
+ public Gtk.Widget Container {
+ get { return container; }
+ }
+
+ internal EditorManager EditorManager {
+ get { return editorManager; }
+ }
+
+ internal void Initialize (Widget container, EditorManager editorManager, PropertyDescriptor property, object obj)
+ {
+ this.container = container;
+ this.editorManager = editorManager;
+
+ layout = new Pango.Layout (container.PangoContext);
+ layout.Width = -1;
+
+ Pango.FontDescription des = container.Style.FontDescription.Copy();
+ layout.FontDescription = des;
+
+ this.property = property;
+ this.obj = obj;
+ Initialize ();
+ }
+
+ public EditSession StartEditing (Gdk.Rectangle cell_area, StateType state)
+ {
+ IPropertyEditor ed = CreateEditor (cell_area, state);
+ if (ed == null)
+ return null;
+ return new EditSession (container, obj, property, ed);
+ }
+
+ protected virtual string GetValueText ()
+ {
+ if (obj == null) return "";
+ object val = property.GetValue (obj);
+ if (val == null) return "";
+ else return property.Converter.ConvertToString (val);
+ }
+
+ protected virtual string GetValueMarkup ()
+ {
+ return null;
+ }
+
+ string GetNormalizedText (string s)
+ {
+ if (s == null)
+ return "";
+
+ int i = s.IndexOf ('\n');
+ if (i == -1)
+ return s;
+
+ s = s.TrimStart ('\n',' ','\t');
+ i = s.IndexOf ('\n');
+ if (i != -1)
+ return s.Substring (0, i) + "...";
+ else
+ return s;
+ }
+
+ public object Value {
+ get { return obj != null ? property.GetValue (obj) : null; }
+ }
+
+ protected virtual void Initialize ()
+ {
+ string s = GetValueMarkup ();
+ if (s != null)
+ layout.SetMarkup (GetNormalizedText (s));
+ else
+ layout.SetText (GetNormalizedText (GetValueText ()));
+ }
+
+ public virtual void GetSize (int availableWidth, out int width, out int height)
+ {
+ layout.GetPixelSize (out width, out height);
+ }
+
+ public virtual void Render (Drawable window, Gdk.Rectangle bounds, StateType state)
+ {
+ int w, h;
+ layout.GetPixelSize (out w, out h);
+ int dy = (bounds.Height - h) / 2;
+ window.DrawLayout (container.Style.TextGC (state), bounds.X, dy + bounds.Y, layout);
+ }
+
+ protected virtual IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, StateType state)
+ {
+ if (DialogueEdit && (!property.IsReadOnly || EditsReadOnlyObject)) {
+ return new PropertyDialogueEditor (this);
+ }
+ else {
+ Type editorType = editorManager.GetEditorType (property);
+ if (editorType == null)
+ return null;
+
+ IPropertyEditor editor = Activator.CreateInstance (editorType) as IPropertyEditor;
+ if (editor == null)
+ throw new Exception ("The property editor '" + editorType + "' must implement the interface IPropertyEditor");
+ return editor;
+ }
+ }
+
+ /// <summary>
+ /// Whether the editor should show a button.
+ /// </summary>
+ public virtual bool DialogueEdit {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// If the property is read-only, is is usually not edited. If the editor
+ /// can edit sub-properties of a read-only complex object, this must return true.
+ /// <remarks>The default value is false.</remarks>
+ /// </summary>
+ /// <returns>True if the editor can edit read-only properties</returns>
+ public virtual bool EditsReadOnlyObject {
+ get { return false; }
+ }
+
+ public virtual void LaunchDialogue ()
+ {
+ if (DialogueEdit)
+ throw new NotImplementedException();
+ }
+ }
+
+
+ class DefaultPropertyEditor: Gtk.Entry, IPropertyEditor
+ {
+ PropertyDescriptor property;
+
+ public void Initialize (EditSession session)
+ {
+ this.property = session.Property;
+ }
+
+ public object Value {
+ get {
+ return Convert.ChangeType (Text, property.PropertyType);
+ }
+ set {
+ if (value == null)
+ Text = "";
+ else
+ Text = Convert.ToString (value);
+ }
+ }
+
+ protected override void OnChanged ()
+ {
+ base.OnChanged ();
+ if (ValueChanged != null)
+ ValueChanged (this, EventArgs.Empty);
+ }
+
+ public event EventHandler ValueChanged;
+ }
+
+ public class EditSession : ITypeDescriptorContext
+ {
+ PropertyDescriptor property;
+ object obj;
+ Gtk.Widget container;
+ IPropertyEditor currentEditor;
+ bool syncing;
+
+ public event EventHandler Changed;
+
+ public EditSession (Gtk.Widget container, object instance, PropertyDescriptor property, IPropertyEditor currentEditor)
+ {
+ this.property = property;
+ this.obj = instance;
+ this.container = container;
+ this.currentEditor = currentEditor;
+
+ currentEditor.Initialize (this);
+ if (instance != null)
+ currentEditor.Value = property.GetValue (instance);
+
+ currentEditor.ValueChanged += OnValueChanged;
+ }
+
+ public void Dispose ()
+ {
+ currentEditor.Dispose ();
+ }
+
+ public object Instance {
+ get { return obj; }
+ }
+
+ public PropertyDescriptor Property {
+ get { return property; }
+ }
+
+ PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor {
+ get { return property; }
+ }
+
+ public Gtk.Widget Container {
+ get { return container; }
+ }
+
+ public IPropertyEditor Editor {
+ get { return currentEditor; }
+ }
+
+ void OnValueChanged (object s, EventArgs a)
+ {
+ if (!syncing) {
+ syncing = true;
+ if (!property.IsReadOnly) {
+ property.SetValue (obj, currentEditor.Value);
+ if (Changed != null)
+ Changed (s, a);
+ }
+ syncing = false;
+ }
+ }
+
+/* public void AttachObject (object ob)
+ {
+ if (ob == null)
+ throw new ArgumentNullException ("ob");
+
+ syncing = true;
+ this.obj = ob;
+ currentEditor.AttachObject (obj);
+
+ // It is the responsibility of the editor to convert value types
+ object initial = property.GetValue (obj);
+ currentEditor.Value = initial;
+
+ syncing = false;
+ }
+*/
+ public void UpdateEditor ()
+ {
+ if (!syncing) {
+ syncing = true;
+ currentEditor.Value = property.GetValue (obj);
+ syncing = false;
+ }
+ }
+
+ #region FIXME Unimplemented ITypeDescriptorContext and IServiceProvider members
+
+ object IServiceProvider.GetService (Type serviceType)
+ {
+ return null;
+ }
+
+ void ITypeDescriptorContext.OnComponentChanged ()
+ {
+ }
+
+ bool ITypeDescriptorContext.OnComponentChanging ()
+ {
+ return true;
+ }
+
+ IContainer ITypeDescriptorContext.Container { get { return null; } }
+
+ #endregion
+ }
+
+ class CellRendererWidget: Gtk.DrawingArea
+ {
+ PropertyEditorCell cell;
+ object obj;
+ PropertyDescriptor property;
+ EditorManager em;
+
+ public CellRendererWidget (PropertyEditorCell cell)
+ {
+ this.cell = cell;
+ this.obj = cell.Instance;
+ this.property = cell.Property;
+ em = cell.EditorManager;
+ this.ModifyBg (Gtk.StateType.Normal, this.Style.White);
+ }
+
+ protected override bool OnExposeEvent (EventExpose evnt)
+ {
+ bool res = base.OnExposeEvent (evnt);
+ cell.Initialize (this, em, property, obj);
+
+ Gdk.Rectangle rect = Allocation;
+ rect.Inflate (-3, 0);// Add some margin
+
+ cell.Render (this.GdkWindow, rect, StateType.Normal);
+ return res;
+ }
+ }
+
+ class PropertyDialogueEditor: HBox, IPropertyEditor
+ {
+ PropertyEditorCell cell;
+
+ public PropertyDialogueEditor (PropertyEditorCell cell)
+ {
+ this.cell = cell;
+ Spacing = 3;
+ PackStart (new CellRendererWidget (cell), true, true, 0);
+ Label buttonLabel = new Label ();
+ buttonLabel.UseMarkup = true;
+ buttonLabel.Xpad = 0; buttonLabel.Ypad = 0;
+ buttonLabel.Markup = "<span size=\"small\">...</span>";
+ Button dialogueButton = new Button (buttonLabel);
+ dialogueButton.Clicked += new EventHandler (DialogueButtonClicked);
+ PackStart (dialogueButton, false, false, 0);
+ this.ModifyBg (Gtk.StateType.Normal, this.Style.White);
+ ShowAll ();
+ }
+
+ void DialogueButtonClicked (object s, EventArgs args)
+ {
+ cell.LaunchDialogue ();
+ if (ValueChanged != null)
+ ValueChanged (this, args);
+ }
+
+ public void Initialize (EditSession session)
+ {
+ }
+
+ public object Value {
+ get { return cell.Value; }
+ set { }
+ }
+
+ public event EventHandler ValueChanged;
+
+ }
+
+ public interface IPropertyEditor: IDisposable
+ {
+ // Called once to initialize the editor.
+ void Initialize (EditSession session);
+
+ // Gets/Sets the value of the editor. If the editor supports
+ // several value types, it is the responsibility of the editor
+ // to return values with the expected type.
+ object Value { get; set; }
+
+ // To be fired when the edited value changes.
+ event EventHandler ValueChanged;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorTypeAttribute.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorTypeAttribute.cs
new file mode 100644
index 0000000000..2b7d2fa623
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorTypeAttribute.cs
@@ -0,0 +1,63 @@
+/*
+ * PropertyTypeAttribute.cs - Shows which types a visual type editor can edit
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Eric Butler <eric@extremeboredom.net>
+ *
+ * Copyright (C) 2005 Eric Butler
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.PropertyGrid
+{
+ [AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
+ public class PropertyEditorTypeAttribute : Attribute
+ {
+ private Type type;
+ private bool inherits = false;
+
+ public PropertyEditorTypeAttribute (Type type)
+ {
+ this.type = type;
+ }
+
+ public PropertyEditorTypeAttribute (Type myType, bool inherits)
+ {
+ this.type = myType;
+ this.inherits = inherits;
+ }
+
+ public bool Inherits {
+ get { return inherits; }
+ }
+
+ public Type Type {
+ get {return type; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs
new file mode 100644
index 0000000000..56dffeba1b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs
@@ -0,0 +1,467 @@
+/*
+ * PropertyGrid.cs - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.comk>
+ * Eric Butler <eric@extremeboredom.net>
+ * Lluis Sanchez Gual <lluis@novell.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ * Copyright (C) 2005 Eric Butler
+ * Copyright (C) 2007 Novell, Inc (http://www.novell.com)
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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;
+using System.ComponentModel;
+
+using Gtk;
+using Gdk;
+using MonoDevelop.Core;
+using MonoDevelop.Components.PropertyGrid.PropertyEditors;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ [System.ComponentModel.Category("MonoDevelop.Components")]
+ [System.ComponentModel.ToolboxItem(true)]
+ public class PropertyGrid: Gtk.VBox
+ {
+ object currentObject;
+ object[] propertyProviders;
+
+ PropertyGridTree tree;
+ HSeparator helpSeparator;
+ HSeparator toolbarSeparator;
+ VPaned vpaned;
+
+ IToolbarProvider toolbar;
+ RadioButton catButton;
+ RadioButton alphButton;
+ ToggleButton helpButton;
+
+ string descTitle, descText;
+ Label descTitleLabel;
+ TextView descTextView;
+ Gtk.Widget descFrame;
+
+ EditorManager editorManager;
+
+ PropertySort propertySort = PropertySort.Categorized;
+
+ const string PROP_HELP_KEY = "MonoDevelop.PropertyPad.ShowHelp";
+
+ public PropertyGrid (): this (new EditorManager ())
+ {
+ }
+
+ internal PropertyGrid (EditorManager editorManager)
+ {
+ this.editorManager = editorManager;
+
+ #region Toolbar
+
+ PropertyGridToolbar tb = new PropertyGridToolbar ();
+ base.PackStart (tb, false, false, 0);
+ toolbar = tb;
+
+ catButton = new RadioButton ((Gtk.RadioButton)null);
+ catButton.DrawIndicator = false;
+ catButton.Relief = ReliefStyle.None;
+ Gdk.Pixbuf pixbuf = null;
+ try {
+ pixbuf = new Gdk.Pixbuf (typeof (PropertyGrid).Assembly, "MonoDevelop.Components.PropertyGrid.SortByCat.png");
+ } catch (Exception e) {
+ LoggingService.LogError ("Can't create pixbuf from resource: MonoDevelop.Components.PropertyGrid.SortByCat.png", e);
+ }
+ if (pixbuf != null) {
+ catButton.Image = new Gtk.Image (pixbuf);
+ catButton.Image.Show ();
+ }
+ catButton.TooltipText = GettextCatalog.GetString ("Sort in categories");
+ catButton.Toggled += new EventHandler (toolbarClick);
+ toolbar.Insert (catButton, 0);
+
+ alphButton = new RadioButton (catButton);
+ alphButton.DrawIndicator = false;
+ alphButton.Relief = ReliefStyle.None;
+ alphButton.Image = new Gtk.Image (Stock.SortAscending, IconSize.Menu);
+ alphButton.Image.Show ();
+ alphButton.TooltipText = GettextCatalog.GetString ("Sort alphabetically");
+ alphButton.Clicked += new EventHandler (toolbarClick);
+ toolbar.Insert (alphButton, 1);
+
+ catButton.Active = true;
+
+ toolbar.Insert (new SeparatorToolItem (), 2);
+ helpButton = new ToggleButton ();
+ helpButton.Relief = ReliefStyle.None;
+ helpButton.Image = new Gtk.Image (Gtk.Stock.Help, IconSize.Menu);
+ helpButton.TooltipText = GettextCatalog.GetString ("Show help panel");
+ helpButton.Clicked += delegate {
+ ShowHelp = helpButton.Active;
+ MonoDevelop.Core.PropertyService.Set (PROP_HELP_KEY, helpButton.Active);
+ };
+ toolbar.Insert (helpButton, 3);
+
+ #endregion
+
+ vpaned = new VPaned ();
+
+ tree = new PropertyGridTree (editorManager, this);
+ tree.Changed += delegate {
+ Update ();
+ };
+
+ VBox tbox = new VBox ();
+ toolbarSeparator = new HSeparator ();
+ toolbarSeparator.Visible = true;
+ tbox.PackStart (toolbarSeparator, false, false, 0);
+ tbox.PackStart (tree, true, true, 0);
+ helpSeparator = new HSeparator ();
+ tbox.PackStart (helpSeparator, false, false, 0);
+ helpSeparator.NoShowAll = true;
+ vpaned.Pack1 (tbox, true, true);
+
+ AddPropertyTab (new DefaultPropertyTab ());
+ AddPropertyTab (new EventPropertyTab ());
+
+ base.PackEnd (vpaned);
+ base.FocusChain = new Gtk.Widget [] { vpaned };
+
+ helpButton.Active = ShowHelp = MonoDevelop.Core.PropertyService.Get<bool> (PROP_HELP_KEY, true);
+
+ Populate ();
+ UpdateTabs ();
+ }
+
+ public void SetToolbarProvider (IToolbarProvider toolbarProvider)
+ {
+ PropertyGridToolbar t = toolbar as PropertyGridToolbar;
+ if (t == null)
+ throw new InvalidOperationException ("Custom toolbar provider already set");
+ Remove (t);
+ foreach (Widget w in t.Children) {
+ t.Remove (w);
+ toolbarProvider.Insert (w, -1);
+ }
+ t.Destroy ();
+ toolbarSeparator.Hide ();
+ toolbar = toolbarProvider;
+ UpdateTabs ();
+ }
+
+ public event EventHandler Changed {
+ add { tree.Changed += value; }
+ remove { tree.Changed -= value; }
+ }
+
+ internal EditorManager EditorManager {
+ get { return editorManager; }
+ }
+
+ #region Toolbar state and handlers
+
+ private const int FirstTabIndex = 3;
+
+ void toolbarClick (object sender, EventArgs e)
+ {
+ if (sender == alphButton)
+ PropertySort = PropertySort.Alphabetical;
+ else if (sender == catButton)
+ PropertySort = PropertySort.Categorized;
+ else {
+ TabRadioToolButton button = (TabRadioToolButton) sender;
+ if (selectedTab == button.Tab) return;
+ selectedTab = button.Tab;
+ Populate ();
+ }
+ }
+
+ public PropertySort PropertySort {
+ get { return propertySort; }
+ set {
+ if (value != propertySort) {
+ propertySort = value;
+ Populate ();
+ }
+ }
+ }
+
+ ArrayList propertyTabs = new ArrayList ();
+ PropertyTab selectedTab;
+
+ public PropertyTab SelectedTab {
+ get { return selectedTab; }
+ }
+
+ TabRadioToolButton firstTab;
+
+ private void AddPropertyTab (PropertyTab tab)
+ {
+ TabRadioToolButton rtb;
+ if (propertyTabs.Count == 0) {
+ selectedTab = tab;
+ rtb = new TabRadioToolButton (null);
+ rtb.Active = true;
+ firstTab = rtb;
+ toolbar.Insert (new SeparatorToolItem (), FirstTabIndex - 1);
+ }
+ else
+ rtb = new TabRadioToolButton (firstTab);
+
+ //load image from PropertyTab's bitmap
+ var icon = tab.GetIcon ();
+ if (icon != null)
+ rtb.Image = new Gtk.Image (icon);
+ else
+ rtb.Image = new Gtk.Image (Stock.MissingImage, IconSize.Menu);
+
+ rtb.Tab = tab;
+ rtb.TooltipText = tab.TabName;
+ rtb.Toggled += new EventHandler (toolbarClick);
+
+ toolbar.Insert (rtb, propertyTabs.Count + FirstTabIndex);
+
+ propertyTabs.Add(tab);
+ }
+
+ #endregion
+
+ public object CurrentObject {
+ get { return currentObject; }
+ set { SetCurrentObject (value, new object[] {value}); }
+ }
+
+ public void SetCurrentObject (object obj, object[] propertyProviders)
+ {
+ if (this.currentObject == obj)
+ return;
+ this.currentObject = obj;
+ this.propertyProviders = propertyProviders;
+ UpdateTabs ();
+ Populate();
+ }
+
+ public void CommitPendingChanges ()
+ {
+ tree.CommitChanges ();
+ }
+
+ void UpdateTabs ()
+ {
+ foreach (Gtk.Widget w in toolbar.Children) {
+ TabRadioToolButton but = w as TabRadioToolButton;
+ if (but != null)
+ but.Visible = currentObject != null && but.Tab.CanExtend (currentObject);
+ }
+ }
+
+ //TODO: add more intelligence for editing state etc. Maybe need to know which property has changed, then
+ //just update that
+ public void Refresh ()
+ {
+ QueueDraw ();
+ }
+
+ void Populate ()
+ {
+ PropertyDescriptorCollection properties;
+
+ tree.SaveStatus ();
+ tree.Clear ();
+ tree.PropertySort = propertySort;
+
+ if (currentObject == null) {
+ properties = new PropertyDescriptorCollection (new PropertyDescriptor[0] {});
+ tree.Populate (properties, currentObject);
+ }
+ else {
+ foreach (object prov in propertyProviders) {
+ properties = selectedTab.GetProperties (prov);
+ tree.Populate (properties, prov);
+ }
+ }
+ tree.RestoreStatus ();
+ }
+
+ void Update ()
+ {
+ PropertyDescriptorCollection properties;
+
+ if (currentObject == null) {
+ properties = new PropertyDescriptorCollection (new PropertyDescriptor[0] {});
+ tree.Update (properties, currentObject);
+ }
+ else {
+ foreach (object prov in propertyProviders) {
+ properties = selectedTab.GetProperties (prov);
+ tree.Update (properties, prov);
+ }
+ }
+ }
+
+ public bool ShowToolbar {
+ get { return toolbar.Visible; }
+ set { toolbar.Visible = toolbarSeparator.Visible = value; }
+ }
+
+ public ShadowType ShadowType {
+ get { return tree.ShadowType; }
+ set { tree.ShadowType = value; }
+ }
+
+ #region Hel Pane
+
+ public bool ShowHelp
+ {
+ get { return descFrame != null; }
+ set {
+ if (value == ShowHelp)
+ return;
+ if (value) {
+ AddHelpPane ();
+ helpSeparator.Show ();
+ } else {
+ vpaned.Remove (descFrame);
+ descFrame.Destroy ();
+ descFrame = null;
+ descTextView = null;
+ descTitleLabel = null;
+ helpSeparator.Hide ();
+ }
+ }
+ }
+
+ void AddHelpPane ()
+ {
+ VBox desc = new VBox (false, 0);
+
+ descTitleLabel = new Label ();
+ descTitleLabel.SetAlignment(0, 0);
+ descTitleLabel.SetPadding (5, 2);
+ descTitleLabel.UseMarkup = true;
+ desc.PackStart (descTitleLabel, false, false, 0);
+
+ ScrolledWindow textScroll = new ScrolledWindow ();
+ textScroll.HscrollbarPolicy = PolicyType.Never;
+ textScroll.VscrollbarPolicy = PolicyType.Automatic;
+
+ desc.PackEnd (textScroll, true, true, 0);
+
+ //TODO: Use label, but wrapping seems dodgy.
+ descTextView = new TextView ();
+ descTextView.WrapMode = WrapMode.Word;
+ descTextView.WidthRequest = 1;
+ descTextView.HeightRequest = 70;
+ descTextView.Editable = false;
+ descTextView.LeftMargin = 5;
+ descTextView.RightMargin = 5;
+
+ Pango.FontDescription font = Style.FontDescription.Copy ();
+ font.Size = (font.Size * 8) / 10;
+ descTextView.ModifyFont (font);
+
+ textScroll.Add (descTextView);
+
+ descFrame = desc;
+ vpaned.Pack2 (descFrame, false, true);
+ descFrame.ShowAll ();
+ UpdateHelp ();
+ }
+
+ public void SetHelp (string title, string description)
+ {
+ descTitle = title;
+ descText = description;
+ UpdateHelp ();
+ }
+
+ void UpdateHelp ()
+ {
+ if (!ShowHelp)
+ return;
+ descTextView.Buffer.Clear ();
+ if (descText != null)
+ descTextView.Buffer.InsertAtCursor (descText);
+ descTitleLabel.Markup = descTitle != null?
+ "<b>" + descTitle + "</b>" : string.Empty;
+ }
+
+ public void ClearHelp()
+ {
+ descTitle = descText = null;
+ UpdateHelp ();
+ }
+
+ public interface IToolbarProvider
+ {
+ void Insert (Widget w, int pos);
+ Widget[] Children { get; }
+ void ShowAll ();
+ bool Visible { get; set; }
+ }
+
+ class PropertyGridToolbar: HBox, IToolbarProvider
+ {
+ public PropertyGridToolbar ()
+ {
+ Spacing = 3;
+ }
+
+ public void Insert (Widget w, int pos)
+ {
+ PackStart (w, false, false, 0);
+ if (pos != -1) {
+ Box.BoxChild bc = (Box.BoxChild) this [w];
+ bc.Position = pos;
+ }
+ }
+ }
+
+ #endregion
+ }
+
+ class TabRadioToolButton: RadioButton
+ {
+ public TabRadioToolButton (RadioButton group): base (group)
+ {
+ DrawIndicator = false;
+ Relief = ReliefStyle.None;
+ NoShowAll = true;
+ }
+
+ public PropertyTab Tab;
+ }
+
+ public enum PropertySort
+ {
+ NoSort = 0,
+ Alphabetical,
+ Categorized,
+ CategorizedAlphabetical
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTree.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTree.cs
new file mode 100644
index 0000000000..bb38ce46dc
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTree.cs
@@ -0,0 +1,711 @@
+//
+// PropertyGridTree.cs
+//
+// Author:
+// Lluis Sanchez Gual
+//
+// 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.ComponentModel;
+
+using Gtk;
+using Gdk;
+using MonoDevelop.Core;
+using MonoDevelop.Components.PropertyGrid.PropertyEditors;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ internal class PropertyGridTree: Gtk.ScrolledWindow
+ {
+ Gtk.TreeStore store;
+ InternalTree tree;
+ TreeViewColumn editorColumn;
+ Hashtable propertyRows;
+ ArrayList collapseStatus = new ArrayList ();
+ EditorManager editorManager;
+ PropertyGrid parentGrid;
+
+ public event EventHandler Changed;
+
+ private PropertySort propertySort = PropertySort.Categorized;
+
+
+ public PropertyGridTree (EditorManager editorManager, PropertyGrid parentGrid)
+ {
+ this.editorManager = editorManager;
+ this.parentGrid = parentGrid;
+
+ propertyRows = new Hashtable ();
+
+ store = new TreeStore (typeof (string), typeof(object), typeof(bool), typeof(object));
+
+ tree = new InternalTree (this, store);
+
+ CellRendererText crt;
+
+ TreeViewColumn col;
+
+ col = new TreeViewColumn ();
+ col.Title = GettextCatalog.GetString ("Property");
+ crt = new CellRendererPropertyGroup (tree);
+ crt.Xpad = 0;
+ col.PackStart (crt, true);
+ col.SetCellDataFunc (crt, new TreeCellDataFunc (GroupData));
+ col.Resizable = true;
+ col.Expand = false;
+ col.Sizing = TreeViewColumnSizing.Fixed;
+ col.FixedWidth = 180;
+ tree.AppendColumn (col);
+
+ editorColumn = new TreeViewColumn ();
+ editorColumn.Title = GettextCatalog.GetString ("Value");
+
+ CellRendererProperty crp = new CellRendererProperty (tree);
+
+ editorColumn.PackStart (crp, true);
+ editorColumn.SetCellDataFunc (crp, new TreeCellDataFunc (PropertyData));
+ editorColumn.Sizing = TreeViewColumnSizing.Fixed;
+ editorColumn.Resizable = false;
+ editorColumn.Expand = true;
+ tree.AppendColumn (editorColumn);
+
+ tree.HeadersVisible = false;
+ this.ShadowType = Gtk.ShadowType.None;
+
+ this.HscrollbarPolicy = Gtk.PolicyType.Never;
+
+ Add (tree);
+
+ ShowAll ();
+
+ tree.Selection.Changed += OnSelectionChanged;
+ }
+
+ public void CommitChanges ()
+ {
+ tree.Selection.UnselectAll ();
+ }
+
+ public void SaveStatus ()
+ {
+ collapseStatus.Clear ();
+
+ TreeIter iter;
+ if (!tree.Model.GetIterFirst (out iter))
+ return;
+
+ do {
+ if (!tree.GetRowExpanded (tree.Model.GetPath (iter))) {
+ collapseStatus.Add (tree.Model.GetValue (iter, 0));
+ }
+ } while (tree.Model.IterNext (ref iter));
+ }
+
+ public void RestoreStatus ()
+ {
+ TreeIter iter;
+ if (!tree.Model.GetIterFirst (out iter))
+ return;
+
+ // If the tree only has one group, show it always expanded
+ TreeIter iter2 = iter;
+ if (!tree.Model.IterNext (ref iter2)) {
+ tree.ExpandRow (tree.Model.GetPath (iter), true);
+ return;
+ }
+
+ do {
+ object grp = tree.Model.GetValue (iter, 0);
+ if (!collapseStatus.Contains (grp))
+ tree.ExpandRow (tree.Model.GetPath (iter), false);
+ } while (tree.Model.IterNext (ref iter));
+ }
+
+ public virtual void Clear ()
+ {
+ store.Clear ();
+ propertyRows.Clear ();
+ }
+
+ public virtual void Update ()
+ {
+ // Just repaint the cells
+ QueueDraw ();
+ }
+
+ public PropertySort PropertySort {
+ get { return propertySort; }
+ set { propertySort = value; }
+ }
+
+ internal void Populate (PropertyDescriptorCollection properties, object instance)
+ {
+ bool categorised = PropertySort == PropertySort.Categorized;
+
+ //transcribe browsable properties
+ ArrayList sorted = new ArrayList();
+
+ foreach (PropertyDescriptor descriptor in properties)
+ if (descriptor.IsBrowsable)
+ sorted.Add (descriptor);
+
+ if (sorted.Count == 0)
+ return;
+
+ InstanceData idata = new InstanceData (instance);
+
+ if (!categorised) {
+ if (PropertySort != PropertySort.NoSort)
+ sorted.Sort(new SortByName ());
+ foreach (PropertyDescriptor pd in sorted)
+ AppendProperty (TreeIter.Zero, pd, idata);
+ }
+ else {
+ sorted.Sort (new SortByCat ());
+ string oldCat = null;
+ TreeIter catIter = TreeIter.Zero;
+
+ foreach (PropertyDescriptor pd in sorted) {
+ if (pd.Category != oldCat) {
+ catIter = store.AppendValues (pd.Category, null, true, idata);
+ oldCat = pd.Category;
+ }
+ AppendProperty (catIter, pd, idata);
+ }
+ }
+ }
+
+ internal void Update (PropertyDescriptorCollection properties, object instance)
+ {
+ foreach (PropertyDescriptor pd in properties) {
+ TreeIter it;
+ if (!store.GetIterFirst (out it))
+ continue;
+
+ UpdateProperty (pd, it, instance);
+ }
+ }
+
+ bool UpdateProperty (PropertyDescriptor pd, TreeIter it, object instance)
+ {
+ do {
+ PropertyDescriptor prop = (PropertyDescriptor) store.GetValue (it, 1);
+ InstanceData idata = (InstanceData) store.GetValue (it, 3);
+ if (prop != null && idata != null && prop.Name == pd.Name && idata.Instance == instance) {
+ // Don't update the current editing node, since it may cause tree update problems
+ if (!tree.Editing || !store.GetPath (tree.EditingIter).Equals (store.GetPath (it)))
+ store.SetValue (it, 1, pd);
+ return true;
+ }
+ TreeIter ci;
+ if (store.IterChildren (out ci, it)) {
+ if (UpdateProperty (pd, ci, instance))
+ return true;
+ }
+ }
+ while (store.IterNext (ref it));
+ return false;
+ }
+
+ protected void AppendProperty (PropertyDescriptor prop, object instance)
+ {
+ AppendProperty (TreeIter.Zero, prop, new InstanceData (instance));
+ }
+
+ protected void AppendProperty (TreeIter piter, PropertyDescriptor prop, object instance)
+ {
+ AppendProperty (piter, prop, new InstanceData (instance));
+ }
+
+ void AppendProperty (TreeIter piter, PropertyDescriptor prop, InstanceData idata)
+ {
+ TreeIter iter;
+
+ if (piter.Equals (TreeIter.Zero))
+ iter = store.AppendValues (prop.DisplayName, prop, false, idata);
+ else
+ iter = store.AppendValues (piter, prop.DisplayName, prop, false, idata);
+ propertyRows [prop] = store.GetStringFromIter (iter);
+
+ TypeConverter tc = prop.Converter;
+ if (typeof (ExpandableObjectConverter).IsAssignableFrom (tc.GetType ())) {
+ object cob = prop.GetValue (idata.Instance);
+ foreach (PropertyDescriptor cprop in TypeDescriptor.GetProperties (cob))
+ AppendProperty (iter, cprop, cob);
+ }
+ }
+
+ protected virtual void OnObjectChanged ()
+ {
+ // Delay the notification of the change. There may be problems if the
+ // handler of this event starts its own gui loop.
+ GLib.Timeout.Add (0, delegate {
+ if (Changed != null)
+ Changed (this, EventArgs.Empty);
+ return false;
+ });
+ }
+
+ void OnSelectionChanged (object s, EventArgs a)
+ {
+ TreePath[] rows = tree.Selection.GetSelectedRows ();
+ if (!tree.dragging && rows != null && rows.Length > 0) {
+ tree.SetCursor (rows[0], editorColumn, true);
+ }
+ TreeIter iter;
+ if (tree.Selection.GetSelected (out iter)) {
+ PropertyDescriptor prop = (PropertyDescriptor) store.GetValue (iter, 1);
+ if (prop != null)
+ parentGrid.SetHelp (prop.DisplayName, prop.Description);
+ else
+ parentGrid.SetHelp (string.Empty, string.Empty);
+ } else {
+ parentGrid.SetHelp (string.Empty, string.Empty);
+ }
+ }
+
+ internal void NotifyChanged ()
+ {
+ OnObjectChanged ();
+ }
+
+ void PropertyData (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
+ {
+ CellRendererProperty rc = (CellRendererProperty) cell;
+ bool group = (bool) model.GetValue (iter, 2);
+ if (group) {
+ rc.SetData (null, null, null);
+ } else {
+ PropertyDescriptor prop = (PropertyDescriptor) model.GetValue (iter, 1);
+ PropertyEditorCell propCell = editorManager.GetEditor (prop);
+ InstanceData idata = (InstanceData) model.GetValue (iter, 3);
+ propCell.Initialize (tree, editorManager, prop, idata.Instance);
+ rc.SetData (idata.Instance, prop, propCell);
+ }
+ }
+
+ void GroupData (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
+ {
+ CellRendererPropertyGroup rc = (CellRendererPropertyGroup) cell;
+ rc.IsGroup = (bool) model.GetValue (iter, 2);
+ rc.Text = (string) model.GetValue (iter, 0);
+
+ PropertyDescriptor prop = (PropertyDescriptor) model.GetValue (iter, 1);
+ if (prop != null)
+ rc.SensitiveProperty = !prop.IsReadOnly;
+ else
+ rc.SensitiveProperty = true;
+ }
+
+ private class SortByCat : IComparer
+ {
+ public int Compare (object x, object y)
+ {
+ int catcomp = ((PropertyDescriptor)x).Category.CompareTo (((PropertyDescriptor)y).Category);
+
+ if (catcomp == 0)
+ return ((PropertyDescriptor)x).DisplayName.CompareTo (((PropertyDescriptor)y).DisplayName);
+ else
+ return catcomp;
+ }
+ }
+
+ private class SortByName : IComparer
+ {
+ public int Compare(object x, object y)
+ {
+ return ((PropertyDescriptor)x).DisplayName.CompareTo (((PropertyDescriptor)y).DisplayName);
+ }
+ }
+ }
+
+ class InternalTree: TreeView
+ {
+ internal ArrayList Groups = new ArrayList ();
+ Pango.Layout layout;
+ bool editing;
+ TreeIter editingIter;
+ PropertyGridTree tree;
+ internal bool dragging;
+ int dragPos;
+ Gdk.Cursor resizeCursor = new Gdk.Cursor (CursorType.SbHDoubleArrow);
+
+ public InternalTree (PropertyGridTree tree, TreeModel model): base (model)
+ {
+ this.tree = tree;
+ layout = new Pango.Layout (this.PangoContext);
+ layout.Wrap = Pango.WrapMode.Char;
+ }
+
+ public bool Editing {
+ get { return editing; }
+ set { editing = value; }
+ }
+
+ public TreeIter EditingIter {
+ get { return editingIter; }
+ set { editingIter = value; }
+ }
+
+ public PropertyGridTree PropertyTree {
+ get { return tree; }
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose e)
+ {
+ Groups.Clear ();
+
+ bool res = base.OnExposeEvent (e);
+
+ foreach (TreeGroup grp in Groups) {
+ layout.SetMarkup ("<b>" + grp.Group + "</b>");
+ e.Window.DrawLayout (this.Style.TextGC (grp.State), grp.X, grp.Y, layout);
+ }
+
+ return res;
+ }
+
+ protected override void OnDestroyed ()
+ {
+ base.OnDestroyed ();
+ if (resizeCursor != null) {
+ resizeCursor.Dispose ();
+ resizeCursor = null;
+ }
+ }
+
+ protected override bool OnMotionNotifyEvent (EventMotion evnt)
+ {
+ if (dragging) {
+ int nw = (int)(evnt.X) + dragPos;
+ if (nw <= 40) nw = 40;
+ GLib.Idle.Add (delegate {
+ Columns[0].FixedWidth = nw;
+ return false;
+ });
+ } else {
+ int w = Columns[0].Width;
+ if (Math.Abs (w - evnt.X) < 5)
+ this.GdkWindow.Cursor = resizeCursor;
+ else
+ this.GdkWindow.Cursor = null;
+ }
+ return base.OnMotionNotifyEvent (evnt);
+ }
+
+ protected override bool OnButtonPressEvent (EventButton evnt)
+ {
+ int w = Columns[0].Width;
+ if (Math.Abs (w - evnt.X) < 5) {
+ TreePath[] rows = Selection.GetSelectedRows ();
+ if (rows != null && rows.Length > 0)
+ SetCursor (rows[0], Columns[0], false);
+ dragging = true;
+ dragPos = w - (int) evnt.X;
+ this.GdkWindow.Cursor = resizeCursor;
+ }
+ return base.OnButtonPressEvent (evnt);
+ }
+
+ protected override bool OnButtonReleaseEvent (EventButton evnt)
+ {
+ if (dragging) {
+ this.GdkWindow.Cursor = null;
+ dragging = false;
+ }
+ return base.OnButtonReleaseEvent (evnt);
+ }
+
+ public virtual void Update ()
+ {
+ }
+ }
+
+ class TreeGroup
+ {
+ public string Group;
+ public int X;
+ public int Y;
+ public StateType State;
+ }
+
+ class CellRendererProperty: CellRenderer
+ {
+ TreeView tree;
+ PropertyDescriptor property;
+ object instance;
+ int rowHeight;
+ PropertyEditorCell editorCell;
+ bool sensitive = true;
+ bool visible = true;
+
+ public CellRendererProperty (TreeView tree)
+ {
+ this.tree = tree;
+ Xalign = 0;
+ Xpad = 3;
+
+ Mode |= Gtk.CellRendererMode.Editable;
+ Entry dummyEntry = new Gtk.Entry ();
+ dummyEntry.HasFrame = false;
+ rowHeight = dummyEntry.SizeRequest ().Height;
+ }
+
+ public void SetData (object instance, PropertyDescriptor property, PropertyEditorCell editor)
+ {
+ this.instance = instance;
+ this.property = property;
+ if (property == null) {
+ this.CellBackgroundGdk = tree.Style.MidColors [(int) Gtk.StateType.Normal];
+ sensitive = true;
+ }
+ else {
+ this.CellBackground = null;
+ sensitive = !property.IsReadOnly || (editor != null && editor.EditsReadOnlyObject);
+ }
+
+ editorCell = editor;
+ }
+
+ public override void GetSize (Widget widget, ref Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+ {
+ if (editorCell != null)
+ editorCell.GetSize ((int)(cell_area.Width - this.Xpad * 2), out width, out height);
+ else {
+ width = height = 0;
+ }
+
+ width += (int) this.Xpad * 2;
+ height += (int) this.Ypad * 2;
+
+ x_offset = 0;
+ y_offset = 0;
+
+ if (height < rowHeight)
+ height = rowHeight;
+ }
+
+ protected override void Render (Drawable window, Widget widget, Rectangle background_area, Rectangle cell_area, Rectangle expose_area, CellRendererState flags)
+ {
+ if (instance == null || !visible)
+ return;
+ int width = 0, height = 0;
+ int iwidth = cell_area.Width - (int) this.Xpad * 2;
+
+ if (editorCell != null)
+ editorCell.GetSize ((int)(cell_area.Width - this.Xpad * 2), out width, out height);
+
+ Rectangle bounds = new Rectangle ();
+ bounds.Width = width > iwidth ? iwidth : width;
+ bounds.Height = height;
+ bounds.X = (int) (cell_area.X + this.Xpad);
+ bounds.Y = cell_area.Y + (cell_area.Height - height) / 2;
+
+ StateType state = GetState (flags);
+
+ if (editorCell != null)
+ editorCell.Render (window, bounds, state);
+ }
+
+ public override CellEditable StartEditing (Gdk.Event ev, Widget widget, string path, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, CellRendererState flags)
+ {
+ if (property == null || editorCell == null || !sensitive)
+ return null;
+
+ StateType state = GetState (flags);
+ EditSession session = editorCell.StartEditing (cell_area, state);
+ if (session == null)
+ return null;
+ Gtk.Widget propEditor = (Gtk.Widget) session.Editor;
+ propEditor.Show ();
+ HackEntry e = new HackEntry (session, propEditor);
+ e.Show ();
+ session.Changed += delegate {
+ ((InternalTree)widget).PropertyTree.NotifyChanged ();
+ };
+ TreeIter it;
+ ((InternalTree)widget).Model.GetIterFromString (out it, path);
+ ((InternalTree)widget).EditingIter = it;
+ return e;
+ }
+
+ StateType GetState (CellRendererState flags)
+ {
+ if (!sensitive)
+ return StateType.Insensitive;
+ else if ((flags & CellRendererState.Selected) != 0)
+ return StateType.Selected;
+ else
+ return StateType.Normal;
+ }
+ }
+
+ class CellRendererPropertyGroup: CellRendererText
+ {
+ TreeView tree;
+ Pango.Layout layout;
+ bool isGroup;
+ bool sensitive = true;
+
+ public bool IsGroup {
+ get { return isGroup; }
+ set {
+ isGroup = value;
+ if (value)
+ this.CellBackgroundGdk = tree.Style.MidColors [(int) Gtk.StateType.Normal];
+ else
+ this.CellBackground = null;
+ }
+ }
+
+ public bool SensitiveProperty {
+ get { return sensitive; }
+ set { sensitive = value; }
+ }
+
+ public CellRendererPropertyGroup (TreeView tree)
+ {
+ this.tree = tree;
+ layout = new Pango.Layout (tree.PangoContext);
+ layout.Wrap = Pango.WrapMode.Char;
+ }
+
+ protected void GetCellSize (Widget widget, int availableWidth, out int width, out int height)
+ {
+ layout.SetMarkup (Text);
+ layout.Width = -1;
+ layout.GetPixelSize (out width, out height);
+ }
+
+ public override void GetSize (Widget widget, ref Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
+ {
+ GetCellSize (widget, (int)(cell_area.Width - this.Xpad * 2), out width, out height);
+ width += (int) this.Xpad * 2;
+ height += (int) this.Ypad * 2;
+
+ x_offset = y_offset = 0;
+
+ if (IsGroup)
+ width = 0;
+ }
+
+ protected override void Render (Drawable window, Widget widget, Rectangle background_area, Rectangle cell_area, Rectangle expose_area, CellRendererState flags)
+ {
+ int width, height;
+ GetCellSize (widget, (int)(cell_area.Width - this.Xpad * 2), out width, out height);
+
+ int x = (int) (cell_area.X + this.Xpad);
+ int y = cell_area.Y + (cell_area.Height - height) / 2;
+
+ StateType state;
+ if (!sensitive)
+ state = StateType.Insensitive;
+ else if ((flags & CellRendererState.Selected) != 0)
+ state = StateType.Selected;
+ else
+ state = StateType.Normal;
+
+ if (IsGroup) {
+ TreeGroup grp = new TreeGroup ();
+ grp.X = x;
+ grp.Y = y;
+ grp.Group = Text;
+ grp.State = state;
+ InternalTree tree = (InternalTree) widget;
+ tree.Groups.Add (grp);
+ } else {
+ window.DrawLayout (widget.Style.TextGC (state), x, y, layout);
+ int bx = background_area.X + background_area.Width - 1;
+ Gdk.GC gc = new Gdk.GC (window);
+ gc.RgbFgColor = tree.Style.MidColors [(int) Gtk.StateType.Normal];
+ window.DrawLine (gc, bx, background_area.Y, bx, background_area.Y + background_area.Height);
+ }
+ }
+ }
+
+ class HackEntry: Entry
+ {
+ EventBox box;
+ EditSession session;
+
+ public HackEntry (EditSession session, Gtk.Widget child)
+ {
+ this.session = session;
+ box = new EventBox ();
+ box.ButtonPressEvent += new ButtonPressEventHandler (OnClickBox);
+ box.ModifyBg (StateType.Normal, Style.White);
+ box.Add (child);
+ }
+
+ [GLib.ConnectBefore]
+ void OnClickBox (object s, ButtonPressEventArgs args)
+ {
+ // Avoid forwarding the button press event to the
+ // tree, since it would hide the cell editor.
+ args.RetVal = true;
+ }
+
+ protected override void OnParentSet (Gtk.Widget parent)
+ {
+ base.OnParentSet (parent);
+
+ if (Parent != null) {
+ if (this.ParentWindow != null)
+ box.ParentWindow = this.ParentWindow;
+ box.Parent = Parent;
+ box.Show ();
+ ((InternalTree)Parent).Editing = true;
+ }
+ else {
+ session.Dispose ();
+ ((InternalTree)parent).Editing = false;
+ if (box.Parent != null && box.Parent.IsRealized)
+ box.Unparent ();
+ }
+ }
+
+ protected override void OnShown ()
+ {
+ // Do nothing.
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation);
+ box.SizeRequest ();
+ box.Allocation = allocation;
+ }
+ }
+
+ class InstanceData
+ {
+ public InstanceData (object instance)
+ {
+ Instance = instance;
+ }
+
+ public object Instance;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventArgs.cs
new file mode 100644
index 0000000000..789ac9fc79
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventArgs.cs
@@ -0,0 +1,65 @@
+/*
+ * PropertyValueChangedEventArgs.cs - The arguments for the PropertyGrid's PropertyValueChanged event
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.ComponentModel;
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ public class PropertyValueChangedEventArgs
+ {
+ private PropertyDescriptor changedItem;
+ private object oldValue;
+ private object newValue;
+
+ public PropertyValueChangedEventArgs (PropertyDescriptor changedItem, object oldValue, object newValue)
+ {
+ this.changedItem = changedItem;
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ }
+
+ public object OldValue {
+ get { return oldValue; }
+ }
+
+ public object NewValue
+ {
+ get { return newValue; }
+ }
+
+ public PropertyDescriptor ChangedItem {
+ get { return changedItem; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventHandler.cs
new file mode 100644
index 0000000000..d681b44fb1
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyValueChangedEventHandler.cs
@@ -0,0 +1,37 @@
+/*
+ * PropertyValueChangedEventHandler.cs - The delegate for the PropertyGrid's PropertyValueChanged event
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.
+ */
+
+namespace MonoDevelop.Components.PropertyGrid
+{
+ public delegate void PropertyValueChangedEventHandler (object sender, PropertyValueChangedEventArgs e);
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/SurrogateUITypeEditorAttribute.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/SurrogateUITypeEditorAttribute.cs
new file mode 100644
index 0000000000..a79751fdfd
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/SurrogateUITypeEditorAttribute.cs
@@ -0,0 +1,61 @@
+/*
+ * SurrogateUITypeEditorAttribute.cs - Marks a GTK# Visual Editor as a substitute
+ * for a particular System.Drawing.Design.UITypeEditor-derived SWF editor.
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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.PropertyGrid
+{
+
+ [AttributeUsage (AttributeTargets.Class, AllowMultiple = true)]
+ public class SurrogateUITypeEditorAttribute : Attribute
+ {
+ public Type Type;
+ public SurrogateUITypeEditorAttribute (Type myType)
+ {
+ this.Type = myType;
+ }
+ }
+
+ /* TODO: Surrogates for...
+ *
+ * System.Drawing.Design.FontEditor
+ * System.Drawing.Design.ImageEditor
+ * System.Web.UI.Design.DataBindingCollectionEditor
+ * System.Web.UI.Design.UrlEditor
+ * System.Web.UI.Design.WebControls.DataGridColumnCollectionEditor
+ * System.Web.UI.Design.WebControls.RegexTypeEditor
+ * System.Web.UI.Design.XmlFileEditor
+ * System.Web.UI.Design.TreeNodeCollectionEditor *STUPID: isn't based on CollectionEditor*
+ */
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkColors.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkColors.cs
new file mode 100644
index 0000000000..8cc8de1800
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkColors.cs
@@ -0,0 +1,162 @@
+//
+// GtkColors.cs
+//
+// Author:
+// Aaron Bockover <abockover@novell.com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// 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 Cairo;
+using Gtk;
+
+namespace MonoDevelop.Components.Theming
+{
+ public enum GtkColorClass
+ {
+ Light,
+ Mid,
+ Dark,
+ Base,
+ Text,
+ Background,
+ Foreground
+ }
+
+ public class GtkColors
+ {
+ private Cairo.Color[] gtk_colors;
+ private Widget widget;
+ private bool refreshing = false;
+
+ public event EventHandler Refreshed;
+
+ public Widget Widget {
+ get { return widget; }
+ set {
+ if (widget == value) {
+ return;
+ } else if (widget != null) {
+ widget.Realized -= OnWidgetRealized;
+ widget.StyleSet -= OnWidgetStyleSet;
+ }
+
+ widget = value;
+
+ if (widget.IsRealized) {
+ RefreshColors ();
+ }
+
+ widget.Realized += OnWidgetRealized;
+ widget.StyleSet += OnWidgetStyleSet;
+ }
+ }
+
+ public GtkColors ()
+ {
+ }
+
+ private void OnWidgetRealized (object o, EventArgs args)
+ {
+ RefreshColors ();
+ }
+
+ private void OnWidgetStyleSet (object o, StyleSetArgs args)
+ {
+ RefreshColors ();
+ }
+
+ public Cairo.Color GetWidgetColor (GtkColorClass @class, StateType state)
+ {
+ if (gtk_colors == null) {
+ RefreshColors ();
+ }
+
+ return gtk_colors[(int)@class * ((int)StateType.Insensitive + 1) + (int)state];
+ }
+
+ public void RefreshColors ()
+ {
+ if (refreshing) {
+ return;
+ }
+
+ refreshing = true;
+
+ int sn = (int)StateType.Insensitive + 1;
+ int cn = (int)GtkColorClass.Foreground + 1;
+
+ if (gtk_colors == null) {
+ gtk_colors = new Cairo.Color[sn * cn];
+ }
+
+ for (int c = 0, i = 0; c < cn; c++) {
+ for (int s = 0; s < sn; s++,i++) {
+ Gdk.Color color = Gdk.Color.Zero;
+
+ if (widget != null && widget.IsRealized) {
+ switch ((GtkColorClass)c) {
+ case GtkColorClass.Light:
+ color = widget.Style.LightColors[s];
+ break;
+ case GtkColorClass.Mid:
+ color = widget.Style.MidColors[s];
+ break;
+ case GtkColorClass.Dark:
+ color = widget.Style.DarkColors[s];
+ break;
+ case GtkColorClass.Base:
+ color = widget.Style.BaseColors[s];
+ break;
+ case GtkColorClass.Text:
+ color = widget.Style.TextColors[s];
+ break;
+ case GtkColorClass.Background:
+ color = widget.Style.Backgrounds[s];
+ break;
+ case GtkColorClass.Foreground:
+ color = widget.Style.Foregrounds[s];
+ break;
+ }
+ } else {
+ color = new Gdk.Color (0, 0, 0);
+ }
+
+ gtk_colors[c * sn + s] = CairoExtensions.GdkColorToCairoColor (color);
+ }
+ }
+
+ OnRefreshed ();
+
+ refreshing = false;
+ }
+
+ protected virtual void OnRefreshed ()
+ {
+ EventHandler handler = Refreshed;
+ if (handler != null) {
+ handler (this, EventArgs.Empty);
+ }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs
new file mode 100644
index 0000000000..286ad263dc
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs
@@ -0,0 +1,310 @@
+//
+// GtkTheme.cs
+//
+// Author:
+// Aaron Bockover <abockover@novell.com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// 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 Cairo;
+using Gtk;
+
+namespace MonoDevelop.Components.Theming
+{
+ public class GtkTheme : Theme
+ {
+ private Cairo.Color rule_color;
+ private Cairo.Color border_color;
+
+ public GtkTheme (Widget widget) : base(widget)
+ {
+ }
+
+ public static Cairo.Color GetCairoTextMidColor (Widget widget)
+ {
+ Cairo.Color text_color = CairoExtensions.GdkColorToCairoColor (widget.Style.Foreground (StateType.Normal));
+ Cairo.Color background_color = CairoExtensions.GdkColorToCairoColor (widget.Style.Background (StateType.Normal));
+ return CairoExtensions.AlphaBlend (text_color, background_color, 0.5);
+ }
+
+ public static Gdk.Color GetGdkTextMidColor (Widget widget)
+ {
+ Cairo.Color color = GetCairoTextMidColor (widget);
+ Gdk.Color gdk_color = new Gdk.Color ((byte)(color.R * 255), (byte)(color.G * 255), (byte)(color.B * 255));
+ Gdk.Colormap.System.AllocColor (ref gdk_color, true, true);
+ return gdk_color;
+ }
+
+ protected override void OnColorsRefreshed ()
+ {
+ base.OnColorsRefreshed ();
+
+ rule_color = CairoExtensions.ColorShade (ViewFill, 0.95);
+ border_color = Colors.GetWidgetColor (GtkColorClass.Dark, StateType.Active);
+ }
+
+ public override void DrawPie (double fraction)
+ {
+ // Calculate the pie path
+ fraction = Theme.Clamp (0.0, 1.0, fraction);
+ double a1 = 3.0 * Math.PI / 2.0;
+ double a2 = a1 + 2.0 * Math.PI * fraction;
+
+ if (fraction == 0.0) {
+ return;
+ }
+
+ Context.Cairo.MoveTo (Context.X, Context.Y);
+ Context.Cairo.Arc (Context.X, Context.Y, Context.Radius, a1, a2);
+ Context.Cairo.LineTo (Context.X, Context.Y);
+
+ // Fill the pie
+ Color color_a = Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected);
+ Color color_b = CairoExtensions.ColorShade (color_a, 1.4);
+
+ RadialGradient fill = new RadialGradient (Context.X, Context.Y, 0, Context.X, Context.Y, 2.0 * Context.Radius);
+ fill.AddColorStop (0, color_a);
+ fill.AddColorStop (1, color_b);
+ Context.Cairo.Pattern = fill;
+
+ Context.Cairo.FillPreserve ();
+ fill.Destroy ();
+
+ // Stroke the pie
+ Context.Cairo.Color = CairoExtensions.ColorShade (color_a, 0.8);
+ Context.Cairo.LineWidth = Context.LineWidth;
+ Context.Cairo.Stroke ();
+ }
+
+ public override void DrawArrow (Context cr, Gdk.Rectangle alloc, SortType type)
+ {
+ cr.LineWidth = 1;
+ cr.Translate (0.5, 0.5);
+ double x1 = alloc.X;
+ double x3 = alloc.X + alloc.Width / 2.0;
+ double x2 = x3 + (x3 - x1);
+ double y1 = alloc.Y;
+ double y2 = alloc.Bottom;
+
+ if (type == SortType.Ascending) {
+ cr.MoveTo (x1, y1);
+ cr.LineTo (x2, y1);
+ cr.LineTo (x3, y2);
+ cr.LineTo (x1, y1);
+ } else {
+ cr.MoveTo (x3, y1);
+ cr.LineTo (x2, y2);
+ cr.LineTo (x1, y2);
+ cr.LineTo (x3, y1);
+ }
+
+ cr.Color = Colors.GetWidgetColor (GtkColorClass.Base, StateType.Normal);
+ cr.FillPreserve ();
+ cr.Color = Colors.GetWidgetColor (GtkColorClass.Text, StateType.Normal);
+ cr.Stroke ();
+ cr.Translate (-0.5, -0.5);
+ }
+
+ public override void DrawFrameBackground (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color, Cairo.Pattern pattern)
+ {
+ color.A = Context.FillAlpha;
+ if (pattern != null) {
+ cr.Pattern = pattern;
+ } else {
+ cr.Color = color;
+ }
+ CairoExtensions.RoundedRectangle (cr, alloc.X, alloc.Y, alloc.Width, alloc.Height, Context.Radius, CairoCorners.All);
+ cr.Fill ();
+ }
+
+ public override void DrawFrameBorder (Cairo.Context cr, Gdk.Rectangle alloc)
+ {
+ cr.LineWidth = BorderWidth;
+ cr.Color = border_color;
+ double offset = (double)cr.LineWidth / 2.0;
+ CairoExtensions.RoundedRectangle (cr, alloc.X + offset, alloc.Y + offset, alloc.Width - cr.LineWidth, alloc.Height - cr.LineWidth, Context.Radius, CairoCorners.All);
+ cr.Stroke ();
+ }
+
+ public override void DrawFrameBorderFocused (Cairo.Context cr, Gdk.Rectangle alloc)
+ {
+ cr.LineWidth = BorderWidth * 1.5;
+ cr.Color = CairoExtensions.ColorShade (border_color, 0.8);
+ double offset = (double)cr.LineWidth / 2.0;
+ CairoExtensions.RoundedRectangle (cr, alloc.X + offset, alloc.Y + offset, alloc.Width - cr.LineWidth, alloc.Height - cr.LineWidth, Context.Radius, CairoCorners.All);
+ cr.Stroke ();
+ }
+
+ public override void DrawColumnHighlight (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color)
+ {
+ Cairo.Color light_color = CairoExtensions.ColorShade (color, 1.6);
+ Cairo.Color dark_color = CairoExtensions.ColorShade (color, 1.3);
+
+ LinearGradient grad = new LinearGradient (alloc.X, alloc.Y, alloc.X, alloc.Bottom - 1);
+ grad.AddColorStop (0, light_color);
+ grad.AddColorStop (1, dark_color);
+
+ cr.Pattern = grad;
+ cr.Rectangle (alloc.X + 1.5, alloc.Y + 1.5, alloc.Width - 3, alloc.Height - 2);
+ cr.Fill ();
+ grad.Destroy ();
+ }
+
+ public override void DrawHeaderBackground (Cairo.Context cr, Gdk.Rectangle alloc)
+ {
+ Cairo.Color gtk_background_color = Colors.GetWidgetColor (GtkColorClass.Background, StateType.Normal);
+ Cairo.Color light_color = CairoExtensions.ColorShade (gtk_background_color, 1.1);
+ Cairo.Color dark_color = CairoExtensions.ColorShade (gtk_background_color, 0.95);
+
+ CairoCorners corners = CairoCorners.TopLeft | CairoCorners.TopRight;
+
+ LinearGradient grad = new LinearGradient (alloc.X, alloc.Y, alloc.X, alloc.Bottom);
+ grad.AddColorStop (0, light_color);
+ grad.AddColorStop (0.75, dark_color);
+ grad.AddColorStop (0, light_color);
+
+ cr.Pattern = grad;
+ CairoExtensions.RoundedRectangle (cr, alloc.X, alloc.Y, alloc.Width, alloc.Height, Context.Radius, corners);
+ cr.Fill ();
+
+ cr.Color = border_color;
+ cr.Rectangle (alloc.X, alloc.Bottom, alloc.Width, BorderWidth);
+ cr.Fill ();
+ grad.Destroy ();
+ }
+
+ public override void DrawColumnHeaderFocus (Cairo.Context cr, Gdk.Rectangle alloc)
+ {
+ double top_offset = 2.0;
+ double right_offset = 2.0;
+
+ double margin = 0.5;
+ double line_width = 0.7;
+
+ Cairo.Color stroke_color = CairoExtensions.ColorShade (Colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected), 0.8);
+
+ stroke_color.A = 0.1;
+ cr.Color = stroke_color;
+
+ CairoExtensions.RoundedRectangle (cr, alloc.X + margin + line_width + right_offset, alloc.Y + margin + line_width + top_offset, alloc.Width - (margin + line_width) * 2.0 - right_offset, alloc.Height - (margin + line_width) * 2.0 - top_offset, Context.Radius / 2.0, CairoCorners.None);
+
+ cr.Fill ();
+
+ stroke_color.A = 1.0;
+ cr.LineWidth = line_width;
+ cr.Color = stroke_color;
+ CairoExtensions.RoundedRectangle (cr, alloc.X + margin + line_width + right_offset, alloc.Y + margin + line_width + top_offset, alloc.Width - (line_width + margin) * 2.0 - right_offset, alloc.Height - (line_width + margin) * 2.0 - right_offset, Context.Radius / 2.0, CairoCorners.All);
+ cr.Stroke ();
+ }
+
+ public override void DrawHeaderSeparator (Cairo.Context cr, Gdk.Rectangle alloc, int x)
+ {
+ Cairo.Color gtk_background_color = Colors.GetWidgetColor (GtkColorClass.Background, StateType.Normal);
+ Cairo.Color dark_color = CairoExtensions.ColorShade (gtk_background_color, 0.8);
+ Cairo.Color light_color = CairoExtensions.ColorShade (gtk_background_color, 1.1);
+
+ int y_1 = alloc.Top + 4;
+ int y_2 = alloc.Bottom - 3;
+
+ cr.LineWidth = 1;
+ cr.Antialias = Cairo.Antialias.None;
+
+ cr.Color = dark_color;
+ cr.MoveTo (x, y_1);
+ cr.LineTo (x, y_2);
+ cr.Stroke ();
+
+ cr.Color = light_color;
+ cr.MoveTo (x + 1, y_1);
+ cr.LineTo (x + 1, y_2);
+ cr.Stroke ();
+
+ cr.Antialias = Cairo.Antialias.Default;
+ }
+
+ public override void DrawListBackground (Context cr, Gdk.Rectangle alloc, Color color)
+ {
+ color.A = Context.FillAlpha;
+ cr.Color = color;
+ cr.Rectangle (alloc.X, alloc.Y, alloc.Width, alloc.Height);
+ cr.Fill ();
+ }
+
+ public override void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height, Cairo.Color color, CairoCorners corners)
+ {
+ cr.LineWidth = 1.25;
+ cr.Color = color;
+ CairoExtensions.RoundedRectangle (cr, x + cr.LineWidth / 2.0, y + cr.LineWidth / 2.0, width - cr.LineWidth, height - cr.LineWidth, Context.Radius, corners, true);
+ cr.Stroke ();
+ }
+
+ public override void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height, bool filled, bool stroked, Cairo.Color color, CairoCorners corners)
+ {
+ Cairo.Color selection_color = color;
+ Cairo.Color selection_highlight = CairoExtensions.ColorShade (selection_color, 1.24);
+ Cairo.Color selection_stroke = CairoExtensions.ColorShade (selection_color, 0.85);
+ selection_highlight.A = 0.5;
+ selection_stroke.A = color.A;
+
+ if (filled) {
+ Cairo.Color selection_fill_light = CairoExtensions.ColorShade (selection_color, 1.12);
+ Cairo.Color selection_fill_dark = selection_color;
+
+ selection_fill_light.A = color.A;
+ selection_fill_dark.A = color.A;
+
+ LinearGradient grad = new LinearGradient (x, y, x, y + height);
+ grad.AddColorStop (0, selection_fill_light);
+ grad.AddColorStop (0.4, selection_fill_dark);
+ grad.AddColorStop (1, selection_fill_light);
+
+ cr.Pattern = grad;
+ CairoExtensions.RoundedRectangle (cr, x, y, width, height, Context.Radius, corners, true);
+ cr.Fill ();
+ grad.Destroy ();
+ }
+
+ if (filled && stroked) {
+ cr.LineWidth = 1.0;
+ cr.Color = selection_highlight;
+ CairoExtensions.RoundedRectangle (cr, x + 1.5, y + 1.5, width - 3, height - 3, Context.Radius - 1, corners, true);
+ cr.Stroke ();
+ }
+
+ if (stroked) {
+ cr.LineWidth = 1.0;
+ cr.Color = selection_stroke;
+ CairoExtensions.RoundedRectangle (cr, x + 0.5, y + 0.5, width - 1, height - 1, Context.Radius, corners, true);
+ cr.Stroke ();
+ }
+ }
+
+ public override void DrawRowRule (Cairo.Context cr, int x, int y, int width, int height)
+ {
+ cr.Color = new Cairo.Color (rule_color.R, rule_color.G, rule_color.B, Context.FillAlpha);
+ cr.Rectangle (x, y, width, height);
+ cr.Fill ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/Theme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/Theme.cs
new file mode 100644
index 0000000000..9c9e23af08
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/Theme.cs
@@ -0,0 +1,252 @@
+//
+// Theme.cs
+//
+// Author:
+// Aaron Bockover <abockover@novell.com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// 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.Generic;
+using Gtk;
+using Gdk;
+using Cairo;
+
+namespace MonoDevelop.Components.Theming
+{
+ public enum SortType
+ {
+ None,
+ Ascending,
+ Descending
+ }
+
+ public abstract class Theme
+ {
+ private static Cairo.Color black = new Cairo.Color (0, 0, 0);
+ private Stack<ThemeContext> contexts = new Stack<ThemeContext> ();
+ private GtkColors colors;
+
+ private Cairo.Color selection_fill;
+ private Cairo.Color selection_stroke;
+
+ private Cairo.Color view_fill;
+ private Cairo.Color view_fill_transparent;
+
+ public GtkColors Colors {
+ get { return colors; }
+ }
+
+ public Theme (Widget widget) : this(widget, new GtkColors ())
+ {
+ }
+
+ public Theme (Widget widget, GtkColors colors)
+ {
+ this.colors = colors;
+ this.colors.Refreshed += delegate { OnColorsRefreshed (); };
+ this.colors.Widget = widget;
+
+ PushContext ();
+ }
+
+ protected virtual void OnColorsRefreshed ()
+ {
+ selection_fill = colors.GetWidgetColor (GtkColorClass.Dark, StateType.Active);
+ selection_stroke = colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected);
+
+ view_fill = colors.GetWidgetColor (GtkColorClass.Base, StateType.Normal);
+ view_fill_transparent = view_fill;
+ view_fill_transparent.A = 0;
+ }
+
+ #region Drawing
+
+ public abstract void DrawPie (double fraction);
+
+ public abstract void DrawArrow (Cairo.Context cr, Gdk.Rectangle alloc, SortType type);
+
+ public void DrawFrame (Cairo.Context cr, Gdk.Rectangle alloc, bool baseColor)
+ {
+ DrawFrameBackground (cr, alloc, baseColor);
+ DrawFrameBorder (cr, alloc);
+ }
+
+ public void DrawFrame (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color)
+ {
+ DrawFrameBackground (cr, alloc, color);
+ DrawFrameBorder (cr, alloc);
+ }
+
+ public void DrawFrameBackground (Cairo.Context cr, Gdk.Rectangle alloc, bool baseColor)
+ {
+ DrawFrameBackground (cr, alloc, baseColor ? colors.GetWidgetColor (GtkColorClass.Base, StateType.Normal) : colors.GetWidgetColor (GtkColorClass.Background, StateType.Normal));
+ }
+
+ public void DrawFrameBackground (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color)
+ {
+ DrawFrameBackground (cr, alloc, color, null);
+ }
+
+ public void DrawFrameBackground (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Pattern pattern)
+ {
+ DrawFrameBackground (cr, alloc, black, pattern);
+ }
+
+ public abstract void DrawFrameBackground (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color, Cairo.Pattern pattern);
+
+ public abstract void DrawFrameBorder (Cairo.Context cr, Gdk.Rectangle alloc);
+
+ public abstract void DrawFrameBorderFocused (Cairo.Context cr, Gdk.Rectangle alloc);
+
+ public abstract void DrawHeaderBackground (Cairo.Context cr, Gdk.Rectangle alloc);
+
+ public abstract void DrawColumnHeaderFocus (Cairo.Context cr, Gdk.Rectangle alloc);
+
+ public abstract void DrawHeaderSeparator (Cairo.Context cr, Gdk.Rectangle alloc, int x);
+
+ public void DrawListBackground (Cairo.Context cr, Gdk.Rectangle alloc, bool baseColor)
+ {
+ DrawListBackground (cr, alloc, baseColor ? colors.GetWidgetColor (GtkColorClass.Base, StateType.Normal) : colors.GetWidgetColor (GtkColorClass.Background, StateType.Normal));
+ }
+
+ public abstract void DrawListBackground (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color);
+
+ public void DrawColumnHighlight (Cairo.Context cr, double cellWidth, double cellHeight)
+ {
+ Gdk.Rectangle alloc = new Gdk.Rectangle ();
+ alloc.Width = (int)cellWidth;
+ alloc.Height = (int)cellHeight;
+ DrawColumnHighlight (cr, alloc);
+ }
+
+ public void DrawColumnHighlight (Cairo.Context cr, Gdk.Rectangle alloc)
+ {
+ DrawColumnHighlight (cr, alloc, colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected));
+ }
+
+ public abstract void DrawColumnHighlight (Cairo.Context cr, Gdk.Rectangle alloc, Cairo.Color color);
+
+ public void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height)
+ {
+ DrawRowSelection (cr, x, y, width, height, true);
+ }
+
+ public void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height, bool filled)
+ {
+ DrawRowSelection (cr, x, y, width, height, filled, true, colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected), CairoCorners.All);
+ }
+
+ public void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height, bool filled, bool stroked, Cairo.Color color)
+ {
+ DrawRowSelection (cr, x, y, width, height, filled, stroked, color, CairoCorners.All);
+ }
+
+ public void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height)
+ {
+ DrawRowCursor (cr, x, y, width, height, colors.GetWidgetColor (GtkColorClass.Background, StateType.Selected));
+ }
+
+ public void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height, Cairo.Color color)
+ {
+ DrawRowCursor (cr, x, y, width, height, color, CairoCorners.All);
+ }
+
+ public abstract void DrawRowCursor (Cairo.Context cr, int x, int y, int width, int height, Cairo.Color color, CairoCorners corners);
+
+ public abstract void DrawRowSelection (Cairo.Context cr, int x, int y, int width, int height, bool filled, bool stroked, Cairo.Color color, CairoCorners corners);
+
+ public abstract void DrawRowRule (Cairo.Context cr, int x, int y, int width, int height);
+
+ public Cairo.Color ViewFill {
+ get { return view_fill; }
+ }
+
+ public Cairo.Color ViewFillTransparent {
+ get { return view_fill_transparent; }
+ }
+
+ public Cairo.Color SelectionFill {
+ get { return selection_fill; }
+ }
+
+ public Cairo.Color SelectionStroke {
+ get { return selection_stroke; }
+ }
+
+ public virtual int BorderWidth {
+ get { return 1; }
+ }
+
+ public virtual int InnerBorderWidth {
+ get { return 4; }
+ }
+
+ public int TotalBorderWidth {
+ get { return BorderWidth + InnerBorderWidth; }
+ }
+
+ #endregion
+
+ #region Contexts
+
+ public void PushContext ()
+ {
+ PushContext (new ThemeContext ());
+ }
+
+ public void PushContext (ThemeContext context)
+ {
+ lock (this) {
+ contexts.Push (context);
+ }
+ }
+
+ public ThemeContext PopContext ()
+ {
+ lock (this) {
+ return contexts.Pop ();
+ }
+ }
+
+ public ThemeContext Context {
+ get {
+ lock (this) {
+ return contexts.Peek ();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Static Utilities
+
+ public static double Clamp (double min, double max, double value)
+ {
+ return Math.Max (min, Math.Min (max, value));
+ }
+
+ #endregion
+
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeContext.cs
new file mode 100644
index 0000000000..2ab2799458
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeContext.cs
@@ -0,0 +1,78 @@
+//
+// Theme.cs
+//
+// Author:
+// Aaron Bockover <abockover@novell.com>
+//
+// Copyright (C) 2007-2008 Novell, Inc.
+//
+// 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 Cairo;
+
+namespace MonoDevelop.Components.Theming
+{
+ public class ThemeContext
+ {
+ private double radius = 3.0;
+ public double Radius {
+ get { return radius; }
+ set { radius = value; }
+ }
+
+ private double fill_alpha = 1.0;
+ public double FillAlpha {
+ get { return fill_alpha; }
+ set { fill_alpha = Theme.Clamp (0.0, 1.0, value); }
+ }
+
+ private double line_width = 1.0;
+ public double LineWidth {
+ get { return line_width; }
+ set { line_width = value; }
+ }
+
+ private bool show_stroke = true;
+ public bool ShowStroke {
+ get { return show_stroke; }
+ set { show_stroke = value; }
+ }
+
+ private double x;
+ public double X {
+ get { return x; }
+ set { x = value; }
+ }
+
+ private double y;
+ public double Y {
+ get { return y; }
+ set { y = value; }
+ }
+
+ private Cairo.Context cairo;
+ public Cairo.Context Cairo {
+ get { return cairo; }
+ set { cairo = value; }
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeEngine.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeEngine.cs
new file mode 100644
index 0000000000..044079e17b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/ThemeEngine.cs
@@ -0,0 +1,45 @@
+//
+// ThemeEngine.cs
+//
+// Author:
+// Aaron Bockover <abockover@novell.com>
+//
+// Copyright 2009 Aaron Bockover
+//
+// 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.Theming
+{
+ public static class ThemeEngine
+ {
+ private static Type theme_type;
+
+ public static void SetCurrentTheme<T> () where T : Theme
+ {
+ theme_type = typeof(T);
+ }
+
+ public static Theme CreateTheme (Gtk.Widget widget)
+ {
+ return theme_type == null ? new GtkTheme (widget) : (Theme)Activator.CreateInstance (theme_type, new object[] { widget });
+ }
+ }
+}