aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Widgets/GridEntry.cs
diff options
context:
space:
mode:
authorMichael VanOverbeek <[email protected]>2016-07-25 12:57:52 -0400
committerGitHub <[email protected]>2016-07-25 12:57:52 -0400
commit46c1c31302f111a1f3ec23a70e6f3986a9aa2a27 (patch)
treef00af7ea3f6ad2641fb26fa1d310fd8b7179b39c /source/ShiftUI/Widgets/GridEntry.cs
parentaf48e774189596b8d7a058c564a7d6d75205ca03 (diff)
parent6fa16209519896de09949a27425dff00ebf2970a (diff)
downloadshiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.tar.gz
shiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.tar.bz2
shiftos-c--46c1c31302f111a1f3ec23a70e6f3986a9aa2a27.zip
Merge pull request #17 from MichaelTheShifter/shiftui_integration
Shiftui integration
Diffstat (limited to 'source/ShiftUI/Widgets/GridEntry.cs')
-rw-r--r--source/ShiftUI/Widgets/GridEntry.cs860
1 files changed, 860 insertions, 0 deletions
diff --git a/source/ShiftUI/Widgets/GridEntry.cs b/source/ShiftUI/Widgets/GridEntry.cs
new file mode 100644
index 0000000..619bb4c
--- /dev/null
+++ b/source/ShiftUI/Widgets/GridEntry.cs
@@ -0,0 +1,860 @@
+// 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.
+//
+// Copyright (c) 2004-2008 Novell, Inc.
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+// Ivan N. Zlatev ([email protected])
+//
+
+// NOT COMPLETE
+
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Drawing.Design;
+using ShiftUI;
+using ShiftUI.Design;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Globalization;
+
+namespace ShiftUI.PropertyGridInternal
+{
+ internal class GridEntry : GridItem, ITypeDescriptorContext
+ {
+ #region Local Variables
+ private PropertyGrid property_grid;
+ private bool expanded;
+ private GridItemCollection grid_items;
+ private GridItem parent;
+ private PropertyDescriptor[] property_descriptors;
+ private int top;
+ private Rectangle plus_minus_bounds;
+ private GridItemCollection child_griditems_cache;
+ #endregion // Local Variables
+
+ #region Contructors
+ protected GridEntry (PropertyGrid propertyGrid, GridEntry parent)
+ {
+ if (propertyGrid == null)
+ throw new ArgumentNullException ("propertyGrid");
+ property_grid = propertyGrid;
+ plus_minus_bounds = new Rectangle (0,0,0,0);
+ top = -1;
+ grid_items = new GridItemCollection ();
+ expanded = false;
+ this.parent = parent;
+ child_griditems_cache = null;
+ }
+
+ // Cannot use one PropertyDescriptor for all owners, because the
+ // propertydescriptors might have different Invokees. Check
+ // ReflectionPropertyDescriptor.GetInvokee and how it's used.
+ //
+ public GridEntry (PropertyGrid propertyGrid, PropertyDescriptor[] properties,
+ GridEntry parent) : this (propertyGrid, parent)
+ {
+ if (properties == null || properties.Length == 0)
+ throw new ArgumentNullException ("prop_desc");
+ property_descriptors = properties;
+ }
+ #endregion // Constructors
+
+
+ public override bool Expandable {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (converter == null || !converter.GetPropertiesSupported ((ITypeDescriptorContext)this))
+ return false;
+
+ if (GetChildGridItemsCached ().Count > 0)
+ return true;
+
+ return false;
+ }
+ }
+
+ public override bool Expanded {
+ get { return expanded; }
+ set {
+ if (expanded != value) {
+ expanded = value;
+ PopulateChildGridItems ();
+ if (value)
+ property_grid.OnExpandItem (this);
+ else
+ property_grid.OnCollapseItem (this);
+ }
+ }
+ }
+
+ public override GridItemCollection GridItems {
+ get {
+ PopulateChildGridItems ();
+ return grid_items;
+ }
+ }
+
+ public override GridItemType GridItemType {
+ get { return GridItemType.Property; }
+ }
+
+ public override string Label {
+ get {
+ PropertyDescriptor property = this.PropertyDescriptor;
+ if (property != null) {
+ string label = property.DisplayName;
+ ParenthesizePropertyNameAttribute parensAttr =
+ property.Attributes[typeof (ParenthesizePropertyNameAttribute)] as ParenthesizePropertyNameAttribute;
+ if (parensAttr != null && parensAttr.NeedParenthesis)
+ label = "(" + label + ")";
+ return label;
+ }
+ return String.Empty;
+ }
+ }
+
+ public override GridItem Parent {
+ get { return parent; }
+ }
+
+ public GridEntry ParentEntry {
+ get {
+ if (parent != null && parent.GridItemType == GridItemType.Category)
+ return parent.Parent as GridEntry;
+ return parent as GridEntry;
+ }
+ }
+
+ public override PropertyDescriptor PropertyDescriptor {
+ get { return property_descriptors != null ? property_descriptors[0] : null; }
+ }
+
+ public PropertyDescriptor[] PropertyDescriptors {
+ get { return property_descriptors; }
+ }
+
+ public object PropertyOwner {
+ get {
+ object[] owners = PropertyOwners;
+ if (owners != null)
+ return owners[0];
+ return null;
+ }
+ }
+
+ public object[] PropertyOwners {
+ get {
+ if (ParentEntry == null)
+ return null;
+
+ object[] owners = ParentEntry.Values;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ object newOwner = null;
+ for (int i=0; i < owners.Length; i++) {
+ if (owners[i] is ICustomTypeDescriptor) {
+ newOwner = ((ICustomTypeDescriptor)owners[i]).GetPropertyOwner (properties[i]);
+ if (newOwner != null)
+ owners[i] = newOwner;
+ }
+ }
+ return owners;
+ }
+ }
+
+ // true if the value is the same among all properties
+ public bool HasMergedValue {
+ get {
+ if (!IsMerged)
+ return false;
+
+ object[] values = this.Values;
+ for (int i=0; i+1 < values.Length; i++) {
+ if (!Object.Equals (values[i], values[i+1]))
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public virtual bool IsMerged {
+ get { return (PropertyDescriptors != null && PropertyDescriptors.Length > 1); }
+ }
+
+ // If IsMerged this will return all values for all properties in all owners
+ public virtual object[] Values {
+ get {
+ if (PropertyDescriptor == null || this.PropertyOwners == null)
+ return null;
+ if (IsMerged) {
+ object[] owners = this.PropertyOwners;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ object[] values = new object[owners.Length];
+ for (int i=0; i < owners.Length; i++)
+ values[i] = properties[i].GetValue (owners[i]);
+ return values;
+ } else {
+ return new object[] { this.Value };
+ }
+ }
+ }
+
+ // Returns the first value for the first propertyowner and propertydescriptor
+ //
+ public override object Value {
+ get {
+ if (PropertyDescriptor == null || PropertyOwner == null)
+ return null;
+
+ return PropertyDescriptor.GetValue (PropertyOwner);
+ }
+ set
+ {
+ PropertyDescriptor.SetValue(PropertyOwner, value);
+ }
+ }
+
+ public string ValueText {
+ get {
+ string text = null;
+ try {
+ text = ConvertToString (this.Value);
+ if (text == null)
+ text = String.Empty;
+ } catch {
+ text = String.Empty;
+ }
+ return text;
+ }
+ }
+
+ public override bool Select ()
+ {
+ property_grid.SelectedGridItem = this;
+ return true;
+ }
+
+ #region ITypeDescriptorContext
+ void ITypeDescriptorContext.OnComponentChanged ()
+ {
+ }
+
+ bool ITypeDescriptorContext.OnComponentChanging ()
+ {
+ return false;
+ }
+
+ IContainer ITypeDescriptorContext.Container {
+ get {
+ if (PropertyOwner == null)
+ return null;
+
+ IComponent component = property_grid.SelectedObject as IComponent;
+ if (component != null && component.Site != null)
+ return component.Site.Container;
+ return null;
+ }
+ }
+
+ object ITypeDescriptorContext.Instance {
+ get {
+ if (ParentEntry != null && ParentEntry.PropertyOwner != null)
+ return ParentEntry.PropertyOwner;
+ return PropertyOwner;
+ }
+ }
+
+ PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor {
+ get {
+ if (ParentEntry != null && ParentEntry.PropertyDescriptor != null)
+ return ParentEntry.PropertyDescriptor;
+ return PropertyDescriptor;
+ }
+ }
+ #endregion
+
+ #region IServiceProvider Members
+
+ object IServiceProvider.GetService (Type serviceType) {
+ IComponent selectedComponent = property_grid.SelectedObject as IComponent;
+ if (selectedComponent != null && selectedComponent.Site != null)
+ return selectedComponent.Site.GetService (serviceType);
+ return null;
+ }
+
+ #endregion
+
+ internal int Top {
+ get { return top; }
+ set {
+ if (top != value)
+ top = value;
+ }
+ }
+
+ internal Rectangle PlusMinusBounds {
+ get { return plus_minus_bounds; }
+ set { plus_minus_bounds = value; }
+ }
+
+ public void SetParent (GridItem parent)
+ {
+ this.parent = parent;
+ }
+
+ public ICollection AcceptedValues {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (PropertyDescriptor != null && converter != null &&
+ converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
+ ArrayList values = new ArrayList ();
+ string stringVal = null;
+ ICollection standardValues = converter.GetStandardValues ((ITypeDescriptorContext)this);
+ if (standardValues != null) {
+ foreach (object value in standardValues) {
+ stringVal = ConvertToString (value);
+ if (stringVal != null)
+ values.Add (stringVal);
+ }
+ }
+ return values.Count > 0 ? values : null;
+ }
+ return null;
+ }
+ }
+
+ private string ConvertToString (object value)
+ {
+ if (value is string)
+ return (string)value;
+
+ if (PropertyDescriptor != null && PropertyDescriptor.Converter != null &&
+ PropertyDescriptor.Converter.CanConvertTo ((ITypeDescriptorContext)this, typeof (string))) {
+ try {
+ return PropertyDescriptor.Converter.ConvertToString ((ITypeDescriptorContext)this, value);
+ } catch {
+ // XXX: Happens too often...
+ // property_grid.ShowError ("Property value of '" + property_descriptor.Name + "' is not convertible to string.");
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ public bool HasCustomEditor {
+ get { return EditorStyle != UITypeEditorEditStyle.None; }
+ }
+
+ public UITypeEditorEditStyle EditorStyle {
+ get {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ return editor.GetEditStyle ((ITypeDescriptorContext)this);
+ } catch {
+ // Some of our Editors throw NotImplementedException
+ }
+ }
+ return UITypeEditorEditStyle.None;
+ }
+ }
+
+ public bool EditorResizeable {
+ get {
+ if (this.EditorStyle == UITypeEditorEditStyle.DropDown) {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null && editor.IsDropDownResizable)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public bool EditValue (IWindowsFormsEditorService service)
+ {
+ if (service == null)
+ throw new ArgumentNullException ("service");
+
+ IServiceContainer parent = ((ITypeDescriptorContext)this).GetService (typeof (IServiceContainer)) as IServiceContainer;
+ ServiceContainer container = null;
+
+ if (parent != null)
+ container = new ServiceContainer (parent);
+ else
+ container = new ServiceContainer ();
+
+ container.AddService (typeof (IWindowsFormsEditorService), service);
+
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ object value = editor.EditValue ((ITypeDescriptorContext)this,
+ container,
+ this.Value);
+ string error = null;
+ return SetValue (value, out error);
+ } catch { //(Exception e) {
+ // property_grid.ShowError (e.Message + Environment.NewLine + e.StackTrace);
+ }
+ }
+ return false;
+ }
+
+ private UITypeEditor GetEditor ()
+ {
+ if (PropertyDescriptor != null) {
+ try { // can happen, because we are missing some editors
+ if (PropertyDescriptor != null)
+ return (UITypeEditor) PropertyDescriptor.GetEditor (typeof (UITypeEditor));
+ } catch {
+ // property_grid.ShowError ("Unable to load UITypeEditor for property '" + PropertyDescriptor.Name + "'.");
+ }
+ }
+ return null;
+ }
+
+ private TypeConverter GetConverter ()
+ {
+ if (PropertyDescriptor != null)
+ return PropertyDescriptor.Converter;
+ return null;
+ }
+
+ public bool ToggleValue ()
+ {
+ if (IsReadOnly || (IsMerged && !HasMergedValue))
+ return false;
+
+ bool success = false;
+ string error = null;
+ object value = this.Value;
+ if (PropertyDescriptor.PropertyType == typeof(bool))
+ success = SetValue (!(bool)value, out error);
+ else {
+ TypeConverter converter = GetConverter ();
+ if (converter != null &&
+ converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
+ TypeConverter.StandardValuesCollection values =
+ (TypeConverter.StandardValuesCollection) converter.GetStandardValues ((ITypeDescriptorContext)this);
+ if (values != null) {
+ for (int i = 0; i < values.Count; i++) {
+ if (value != null && value.Equals (values[i])){
+ if (i < values.Count-1)
+ success = SetValue (values[i+1], out error);
+ else
+ success = SetValue (values[0], out error);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!success && error != null)
+ property_grid.ShowError (error);
+ return success;
+ }
+
+ public bool SetValue (object value, out string error)
+ {
+ error = null;
+ if (this.IsReadOnly)
+ return false;
+
+ if (SetValueCore (value, out error)) {
+ InvalidateChildGridItemsCache ();
+ property_grid.OnPropertyValueChangedInternal (this, this.Value);
+ return true;
+ }
+ return false;
+ }
+
+ protected virtual bool SetValueCore (object value, out string error)
+ {
+ error = null;
+
+ TypeConverter converter = GetConverter ();
+ Type valueType = value != null ? value.GetType () : null;
+ // if the new value is not of the same type try to convert it
+ if (valueType != null && this.PropertyDescriptor.PropertyType != null &&
+ !this.PropertyDescriptor.PropertyType.IsAssignableFrom (valueType)) {
+ bool conversionError = false;
+ try {
+ if (converter != null &&
+ converter.CanConvertFrom ((ITypeDescriptorContext)this, valueType))
+ value = converter.ConvertFrom ((ITypeDescriptorContext)this,
+ CultureInfo.CurrentCulture, value);
+ else
+ conversionError = true;
+ } catch (Exception e) {
+ error = e.Message;
+ conversionError = true;
+ }
+ if (conversionError) {
+ string valueText = ConvertToString (value);
+ string errorShortDescription = null;
+ if (valueText != null) {
+ errorShortDescription = "Property value '" + valueText + "' of '" +
+ PropertyDescriptor.Name + "' is not convertible to type '" +
+ this.PropertyDescriptor.PropertyType.Name + "'";
+
+ } else {
+ errorShortDescription = "Property value of '" +
+ PropertyDescriptor.Name + "' is not convertible to type '" +
+ this.PropertyDescriptor.PropertyType.Name + "'";
+ }
+ error = errorShortDescription + Environment.NewLine + Environment.NewLine + error;
+ return false;
+ }
+ }
+
+ bool changed = false;
+ bool current_changed = false;
+ object[] propertyOwners = this.PropertyOwners;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ for (int i=0; i < propertyOwners.Length; i++) {
+ object currentVal = properties[i].GetValue (propertyOwners[i]);
+ current_changed = false;
+ if (!Object.Equals (currentVal, value)) {
+ if (this.ShouldCreateParentInstance) {
+ Hashtable updatedParentProperties = new Hashtable ();
+ PropertyDescriptorCollection parentProperties = TypeDescriptor.GetProperties (propertyOwners[i]);
+ foreach (PropertyDescriptor property in parentProperties) {
+ if (property.Name == properties[i].Name)
+ updatedParentProperties[property.Name] = value;
+ else
+ updatedParentProperties[property.Name] = property.GetValue (propertyOwners[i]);
+ }
+ object updatedParentValue = this.ParentEntry.PropertyDescriptor.Converter.CreateInstance (
+ (ITypeDescriptorContext)this, updatedParentProperties);
+ if (updatedParentValue != null)
+ current_changed = this.ParentEntry.SetValueCore (updatedParentValue, out error);
+ } else {
+ try {
+ properties[i].SetValue (propertyOwners[i], value);
+ } catch {
+ // MS seems to swallow this
+ //
+ // string valueText = ConvertToString (value);
+ // if (valueText != null)
+ // error = "Property value '" + valueText + "' of '" + properties[i].Name + "' is invalid.";
+ // else
+ // error = "Property value of '" + properties[i].Name + "' is invalid.";
+ return false;
+ }
+
+ if (IsValueType (this.ParentEntry))
+ current_changed = ParentEntry.SetValueCore (propertyOwners[i], out error);
+ else
+ current_changed = Object.Equals (properties[i].GetValue (propertyOwners[i]), value);
+ }
+ }
+ if (current_changed)
+ changed = true;
+ }
+ return changed;
+ }
+
+ private bool IsValueType (GridEntry item)
+ {
+ if (item != null && item.PropertyDescriptor != null &&
+ (item.PropertyDescriptor.PropertyType.IsValueType ||
+ item.PropertyDescriptor.PropertyType.IsPrimitive))
+ return true;
+ return false;
+ }
+
+ public bool ResetValue ()
+ {
+ if (IsResetable) {
+ object[] owners = this.PropertyOwners;
+ PropertyDescriptor[] properties = PropertyDescriptors;
+ for (int i=0; i < owners.Length; i++) {
+ properties[i].ResetValue (owners[i]);
+ if (IsValueType (this.ParentEntry)) {
+ string error = null;
+ if (!ParentEntry.SetValueCore (owners[i], out error) && error != null)
+ property_grid.ShowError (error);
+ }
+ }
+ property_grid.OnPropertyValueChangedInternal (this, this.Value);
+ return true;
+ }
+ return false;
+ }
+
+ public bool HasDefaultValue {
+ get {
+ if (PropertyDescriptor != null)
+ return !PropertyDescriptor.ShouldSerializeValue (PropertyOwner);
+ return false;
+ }
+ }
+
+ // Determines if the current value can be reset
+ //
+ public virtual bool IsResetable {
+ get { return (!IsReadOnly && PropertyDescriptor.CanResetValue (PropertyOwner)); }
+
+ }
+
+ // If false the entry can be modified only by the means of a predefined values
+ // and not such inputed by the user.
+ //
+ public virtual bool IsEditable {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (PropertyDescriptor == null)
+ return false;
+ else if (PropertyDescriptor.PropertyType.IsArray)
+ return false;
+ else if (PropertyDescriptor.IsReadOnly && !this.ShouldCreateParentInstance)
+ return false;
+ else if (converter == null ||
+ !converter.CanConvertFrom ((ITypeDescriptorContext)this, typeof (string)))
+ return false;
+ else if (converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
+ converter.GetStandardValuesExclusive ((ITypeDescriptorContext)this))
+ return false;
+ else
+ return true;
+ }
+ }
+
+ // If true the the entry cannot be modified at all
+ //
+ public virtual bool IsReadOnly {
+ get {
+ TypeConverter converter = GetConverter ();
+ if (PropertyDescriptor == null || PropertyOwner == null)
+ return true;
+ else if (PropertyDescriptor.IsReadOnly &&
+ (EditorStyle != UITypeEditorEditStyle.Modal || PropertyDescriptor.PropertyType.IsValueType) &&
+ !this.ShouldCreateParentInstance)
+ return true;
+ else if (PropertyDescriptor.IsReadOnly &&
+ TypeDescriptor.GetAttributes (PropertyDescriptor.PropertyType)
+ [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes))
+ return true;
+ else if (ShouldCreateParentInstance && ParentEntry.IsReadOnly)
+ return true;
+ else if (!HasCustomEditor && converter == null)
+ return true;
+ else if (converter != null &&
+ !converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
+ !converter.CanConvertFrom ((ITypeDescriptorContext)this,
+ typeof (string)) &&
+ !HasCustomEditor) {
+ return true;
+ } else if (PropertyDescriptor.PropertyType.IsArray && !HasCustomEditor)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ public bool IsPassword {
+ get {
+ if (PropertyDescriptor != null)
+ return PropertyDescriptor.Attributes.Contains (PasswordPropertyTextAttribute.Yes);
+ return false;
+ }
+ }
+ // This is a way to set readonly properties (e.g properties without a setter).
+ // The way it works is that if CreateInstance is supported by the parent's converter
+ // it gets passed a list of properties and their values which it uses to create an
+ // instance (e.g by passing them to the ctor of that object type).
+ //
+ // This is used for e.g Font
+ //
+ public virtual bool ShouldCreateParentInstance {
+ get {
+ if (this.ParentEntry != null && ParentEntry.PropertyDescriptor != null) {
+ TypeConverter parentConverter = ParentEntry.GetConverter ();
+ if (parentConverter != null && parentConverter.GetCreateInstanceSupported ((ITypeDescriptorContext)this))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public virtual bool PaintValueSupported {
+ get {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ return editor.GetPaintValueSupported ();
+ } catch {
+ // Some of our Editors throw NotImplementedException
+ }
+ }
+ return false;
+ }
+ }
+
+ public virtual void PaintValue (Graphics gfx, Rectangle rect)
+ {
+ UITypeEditor editor = GetEditor ();
+ if (editor != null) {
+ try {
+ editor.PaintValue (this.Value, gfx, rect);
+ } catch {
+ // Some of our Editors throw NotImplementedException
+ }
+ }
+ }
+
+#region Population
+ protected void PopulateChildGridItems ()
+ {
+ grid_items = GetChildGridItemsCached ();
+ }
+
+ private void InvalidateChildGridItemsCache ()
+ {
+ if (child_griditems_cache != null) {
+ child_griditems_cache = null;
+ PopulateChildGridItems ();
+ }
+ }
+
+ private GridItemCollection GetChildGridItemsCached ()
+ {
+ if (child_griditems_cache == null) {
+ child_griditems_cache = GetChildGridItems ();
+ // foreach (GridEntry item in child_griditems_cache)
+ // PrintDebugInfo (item);
+ }
+
+ return child_griditems_cache;
+ }
+
+ // private static void PrintDebugInfo (GridEntry item)
+ // {
+ // if (item.PropertyDescriptor != null) {
+ // Console.WriteLine ("=== [" + item.PropertyDescriptor.Name + "] ===");
+ // try {
+ // TypeConverter converter = item.GetConverter ();
+ // Console.WriteLine ("IsReadOnly: " + item.IsReadOnly);
+ // Console.WriteLine ("IsEditable: " + item.IsEditable);
+ // Console.WriteLine ("PropertyDescriptor.IsReadOnly: " + item.PropertyDescriptor.IsReadOnly);
+ // if (item.ParentEntry != null)
+ // Console.WriteLine ("ParentEntry.IsReadOnly: " + item.ParentEntry.IsReadOnly);
+ // Console.WriteLine ("ImmutableObjectAttribute.Yes: " + TypeDescriptor.GetAttributes (item.PropertyDescriptor.PropertyType)
+ // [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes));
+ // UITypeEditor editor = item.GetEditor ();
+ // Console.WriteLine ("Editor: " + (editor == null ? "none" : editor.GetType ().Name));
+ // if (editor != null)
+ // Console.WriteLine ("Editor.EditorStyle: " + editor.GetEditStyle ((ITypeDescriptorContext)item));
+ // Console.WriteLine ("Converter: " + (converter == null ? "none" : converter.GetType ().Name));
+ // if (converter != null) {
+ // Console.WriteLine ("Converter.GetStandardValuesSupported: " + converter.GetStandardValuesSupported ((ITypeDescriptorContext)item).ToString ());
+ // Console.WriteLine ("Converter.GetStandardValuesExclusive: " + converter.GetStandardValuesExclusive ((ITypeDescriptorContext)item).ToString ());
+ // Console.WriteLine ("ShouldCreateParentInstance: " + item.ShouldCreateParentInstance);
+ // Console.WriteLine ("CanConvertFrom (string): " + converter.CanConvertFrom ((ITypeDescriptorContext)item, typeof (string)));
+ // }
+ // Console.WriteLine ("IsArray: " + item.PropertyDescriptor.PropertyType.IsArray.ToString ());
+ // } catch { /* Some converters and editor throw NotImplementedExceptions */ }
+ // }
+ // }
+
+ private GridItemCollection GetChildGridItems ()
+ {
+ object[] propertyOwners = this.Values;
+ string[] propertyNames = GetMergedPropertyNames (propertyOwners);
+ GridItemCollection items = new GridItemCollection ();
+
+ foreach (string propertyName in propertyNames) {
+ PropertyDescriptor[] properties = new PropertyDescriptor[propertyOwners.Length];
+ for (int i=0; i < propertyOwners.Length; i++)
+ properties[i] = GetPropertyDescriptor (propertyOwners[i], propertyName);
+ items.Add (new GridEntry (property_grid, properties, this));
+ }
+
+ return items;
+ }
+
+ private bool IsPropertyMergeable (PropertyDescriptor property)
+ {
+ if (property == null)
+ return false;
+
+ MergablePropertyAttribute attrib = property.Attributes [typeof (MergablePropertyAttribute)] as MergablePropertyAttribute;
+ if (attrib != null && !attrib.AllowMerge)
+ return false;
+
+ return true;
+ }
+
+ private string[] GetMergedPropertyNames (object [] objects)
+ {
+ if (objects == null || objects.Length == 0)
+ return new string[0];
+
+ ArrayList intersection = new ArrayList ();
+ for (int i = 0; i < objects.Length; i ++) {
+ if (objects [i] == null)
+ continue;
+
+ PropertyDescriptorCollection properties = GetProperties (objects[i], property_grid.BrowsableAttributes);
+ ArrayList new_intersection = new ArrayList ();
+
+ foreach (PropertyDescriptor currentProperty in (i == 0 ? (ICollection)properties : (ICollection)intersection)) {
+ PropertyDescriptor matchingProperty = (i == 0 ? currentProperty : properties [currentProperty.Name]);
+ if (objects.Length > 1 && !IsPropertyMergeable (matchingProperty))
+ continue;
+ if (matchingProperty.PropertyType == currentProperty.PropertyType)
+ new_intersection.Add (matchingProperty);
+ }
+
+ intersection = new_intersection;
+ }
+
+ string[] propertyNames = new string [intersection.Count];
+ for (int i=0; i < intersection.Count; i++)
+ propertyNames[i] = ((PropertyDescriptor)intersection[i]).Name;
+
+ return propertyNames;
+ }
+
+ private PropertyDescriptor GetPropertyDescriptor (object propertyOwner, string propertyName)
+ {
+ if (propertyOwner == null || propertyName == null)
+ return null;
+
+ PropertyDescriptorCollection properties = GetProperties (propertyOwner, property_grid.BrowsableAttributes);
+ if (properties != null)
+ return properties[propertyName];
+ return null;
+ }
+
+ private PropertyDescriptorCollection GetProperties (object propertyOwner, AttributeCollection attributes)
+ {
+ if (propertyOwner == null || property_grid.SelectedTab == null)
+ return new PropertyDescriptorCollection (null);
+
+ Attribute[] atts = new Attribute[attributes.Count];
+ attributes.CopyTo (atts, 0);
+ return property_grid.SelectedTab.GetProperties ((ITypeDescriptorContext)this, propertyOwner, atts);
+ }
+#endregion
+ }
+}