diff options
author | Lluis Sanchez <lluis@novell.com> | 2010-03-17 15:35:28 +0300 |
---|---|---|
committer | Lluis Sanchez <lluis@novell.com> | 2010-03-17 15:35:28 +0300 |
commit | 8fa37870e9cc22ffccdd494fa951b2c3807d7978 (patch) | |
tree | bda14806802947c51676c183b08f166878964c40 /main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid | |
parent | f1a8582658af8aeb0f6fa459965a2e4d0684c347 (diff) | |
parent | 585086f0ea0a49166046bb8f48d2def87907d0e0 (diff) |
Merged MD.Projects into MD.Core, and MD.Projects.Gui, MD.Core.Gui and MD.Components into MD.Ide.
svn path=/trunk/monodevelop/; revision=153730
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid')
10 files changed, 2176 insertions, 0 deletions
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*
+ */
+}
|