Merge pull request #1612 from esdrubal/socketsh
[mono.git] / mcs / class / System.Windows.Forms / System.Windows.Forms / GridEntry.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2008 Novell, Inc.
21 //
22 // Authors:
23 //      Jonathan Chambers (jonathan.chambers@ansys.com)
24 //      Ivan N. Zlatev    (contact@i-nz.net)
25 //
26
27 // NOT COMPLETE
28
29 using System;
30 using System.Collections;
31 using System.Drawing;
32 using System.Drawing.Design;
33 using System.Windows.Forms;
34 using System.Windows.Forms.Design;
35 using System.ComponentModel;
36 using System.ComponentModel.Design;
37 using System.Globalization;
38
39 namespace System.Windows.Forms.PropertyGridInternal
40 {
41         internal class GridEntry : GridItem, ITypeDescriptorContext
42         {
43                 #region Local Variables
44                 private PropertyGrid property_grid;
45                 private bool expanded;
46                 private GridItemCollection grid_items;
47                 private GridItem parent;
48                 private PropertyDescriptor[] property_descriptors;
49                 private int top;
50                 private Rectangle plus_minus_bounds;
51                 private GridItemCollection child_griditems_cache;
52                 #endregion      // Local Variables
53
54                 #region  Contructors
55                 protected GridEntry (PropertyGrid propertyGrid, GridEntry parent)
56                 {
57                         if (propertyGrid == null)
58                                 throw new ArgumentNullException ("propertyGrid");
59                         property_grid = propertyGrid;
60                         plus_minus_bounds = new Rectangle (0,0,0,0);
61                         top = -1;
62                         grid_items = new GridItemCollection ();
63                         expanded = false;
64                         this.parent = parent;
65                         child_griditems_cache = null;
66                 }
67
68                 // Cannot use one PropertyDescriptor for all owners, because the
69                 // propertydescriptors might have different Invokees. Check
70                 // ReflectionPropertyDescriptor.GetInvokee and how it's used.
71                 //
72                 public GridEntry (PropertyGrid propertyGrid, PropertyDescriptor[] properties, 
73                                   GridEntry parent) : this (propertyGrid, parent) 
74                 {
75                         if (properties == null || properties.Length == 0)
76                                 throw new ArgumentNullException ("prop_desc");
77                         property_descriptors = properties;
78                 }
79                 #endregion      // Constructors
80
81
82                 public override bool Expandable {
83                         get {
84                                 TypeConverter converter = GetConverter ();
85                                 if (converter == null || !converter.GetPropertiesSupported ((ITypeDescriptorContext)this))
86                                         return false;
87
88                                 if (GetChildGridItemsCached ().Count > 0)
89                                         return true;
90
91                                 return false;
92                         }
93                 }
94
95                 public override bool Expanded {
96                         get { return expanded; }
97                         set {
98                                 if (expanded != value) {
99                                         expanded = value;
100                                         PopulateChildGridItems ();
101                                         if (value)
102                                                 property_grid.OnExpandItem (this);
103                                         else
104                                                 property_grid.OnCollapseItem (this);
105                                 }
106                         }
107                 }
108
109                 public override GridItemCollection GridItems {
110                         get {
111                                 PopulateChildGridItems ();
112                                 return grid_items; 
113                         }
114                 }
115
116                 public override GridItemType GridItemType {
117                         get { return GridItemType.Property; }
118                 }
119
120                 public override string Label {
121                         get { 
122                                 PropertyDescriptor property = this.PropertyDescriptor;
123                                 if (property != null) {
124                                         string label = property.DisplayName;
125                                         ParenthesizePropertyNameAttribute parensAttr = 
126                                                 property.Attributes[typeof (ParenthesizePropertyNameAttribute)] as ParenthesizePropertyNameAttribute;
127                                         if (parensAttr != null && parensAttr.NeedParenthesis)
128                                                 label = "(" + label + ")";
129                                         return label;
130                                 }
131                                 return String.Empty;
132                         }
133                 }
134
135                 public override GridItem Parent {
136                         get { return parent; }
137                 }
138
139                 public GridEntry ParentEntry {
140                         get { 
141                                 if (parent != null && parent.GridItemType == GridItemType.Category)
142                                         return parent.Parent as GridEntry;
143                                 return parent as GridEntry; 
144                         }
145                 }
146
147                 public override PropertyDescriptor PropertyDescriptor {
148                         get { return property_descriptors != null ? property_descriptors[0] : null; }
149                 }
150
151                 public PropertyDescriptor[] PropertyDescriptors {
152                         get { return property_descriptors; }
153                 }
154
155                 public object PropertyOwner {
156                         get { 
157                                 object[] owners = PropertyOwners;
158                                 if (owners != null)
159                                         return owners[0];
160                                 return null;
161                         }
162                 }
163
164                 public object[] PropertyOwners {
165                         get { 
166                                 if (ParentEntry == null)
167                                         return null;
168
169                                 object[] owners = ParentEntry.Values;
170                                 PropertyDescriptor[] properties = PropertyDescriptors;
171                                 object newOwner = null;
172                                 for (int i=0; i < owners.Length; i++) {
173                                         if (owners[i] is ICustomTypeDescriptor) {
174                                                 newOwner = ((ICustomTypeDescriptor)owners[i]).GetPropertyOwner (properties[i]);
175                                                 if (newOwner != null)
176                                                         owners[i] = newOwner;
177                                         }
178                                 }
179                                 return owners;
180                         }
181                 }
182
183                 // true if the value is the same among all properties
184                 public bool HasMergedValue {
185                         get {
186                                 if (!IsMerged)
187                                         return false;
188
189                                 object[] values = this.Values;
190                                 for (int i=0; i+1 < values.Length; i++) {
191                                         if (!Object.Equals (values[i], values[i+1]))
192                                                 return false;
193                                 }
194                                 return true;
195                         }
196                 }
197
198                 public virtual bool IsMerged {
199                         get { return (PropertyDescriptors != null && PropertyDescriptors.Length > 1); }
200                 }
201
202                 // If IsMerged this will return all values for all properties in all owners
203                 public virtual object[] Values {
204                         get {
205                                 if (PropertyDescriptor == null || this.PropertyOwners == null)
206                                         return null;
207                                 if (IsMerged) {
208                                         object[] owners = this.PropertyOwners;
209                                         PropertyDescriptor[] properties = PropertyDescriptors;
210                                         object[] values = new object[owners.Length];
211                                         for (int i=0; i < owners.Length; i++)
212                                                 values[i] = properties[i].GetValue (owners[i]);
213                                         return values;
214                                 } else {
215                                         return new object[] { this.Value };
216                                 }
217                         }
218                 }
219
220                 // Returns the first value for the first propertyowner and propertydescriptor
221                 //
222                 public override object Value {
223                         get {
224                                 if (PropertyDescriptor == null || PropertyOwner == null)
225                                         return null;
226
227                                 return PropertyDescriptor.GetValue (PropertyOwner);
228                         }
229                 }
230
231                 public string ValueText {
232                         get { 
233                                 string text = null;
234                                 try {
235                                         text = ConvertToString (this.Value);
236                                         if (text == null)
237                                                 text = String.Empty;
238                                 } catch {
239                                         text = String.Empty;
240                                 }
241                                 return text;
242                         }
243                 }
244
245                 public override bool Select ()
246                 {
247                         property_grid.SelectedGridItem = this;
248                         return true;
249                 }
250
251                 #region ITypeDescriptorContext
252                 void ITypeDescriptorContext.OnComponentChanged () 
253                 {
254                 }
255
256                 bool ITypeDescriptorContext.OnComponentChanging () 
257                 {
258                         return false;
259                 }
260
261                 IContainer ITypeDescriptorContext.Container {
262                         get {
263                                 if (PropertyOwner == null)
264                                         return null;
265
266                                 IComponent component = property_grid.SelectedObject as IComponent;
267                                 if (component != null && component.Site != null)
268                                         return component.Site.Container;
269                                 return null;
270                         }
271                 }
272
273                 object ITypeDescriptorContext.Instance {
274                         get {
275                                 if (ParentEntry != null && ParentEntry.PropertyOwner != null)
276                                         return ParentEntry.PropertyOwner;
277                                 return PropertyOwner;
278                         }
279                 }
280
281                 PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor {
282                         get {
283                                 if (ParentEntry != null && ParentEntry.PropertyDescriptor != null)
284                                         return ParentEntry.PropertyDescriptor; 
285                                 return PropertyDescriptor;
286                         }
287                 }
288                 #endregion
289
290                 #region IServiceProvider Members
291
292                 object IServiceProvider.GetService (Type serviceType) {
293                         IComponent selectedComponent = property_grid.SelectedObject as IComponent;
294                         if (selectedComponent != null && selectedComponent.Site != null)
295                                 return selectedComponent.Site.GetService (serviceType);
296                         return null;
297                 }
298
299                 #endregion
300
301                 internal int Top {
302                         get { return top; }
303                         set {
304                                 if (top != value)
305                                         top = value;
306                         }
307                 }
308
309                 internal Rectangle PlusMinusBounds {
310                         get { return plus_minus_bounds; }
311                         set { plus_minus_bounds = value; }
312                 }
313
314                 public void SetParent (GridItem parent)
315                 {
316                         this.parent = parent;
317                 }
318
319                 public ICollection AcceptedValues {
320                         get {
321                                 TypeConverter converter = GetConverter ();
322                                 if (PropertyDescriptor != null && converter != null &&
323                                     converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
324                                         ArrayList values = new ArrayList ();
325                                         string stringVal = null;
326                                         ICollection standardValues = converter.GetStandardValues ((ITypeDescriptorContext)this);
327                                         if (standardValues != null) {
328                                                 foreach (object value in standardValues) {
329                                                         stringVal = ConvertToString (value);
330                                                         if (stringVal != null)
331                                                                 values.Add (stringVal);
332                                                 }
333                                         }
334                                         return values.Count > 0 ? values : null;
335                                 }
336                                 return null;
337                         }
338                 }
339
340                 private string ConvertToString (object value)
341                 {
342                         if (value is string)
343                                 return (string)value;
344
345                         if (PropertyDescriptor != null && PropertyDescriptor.Converter != null &&
346                             PropertyDescriptor.Converter.CanConvertTo ((ITypeDescriptorContext)this, typeof (string))) {
347                                 try {
348                                         return PropertyDescriptor.Converter.ConvertToString ((ITypeDescriptorContext)this, value);
349                                 } catch {
350                                         // XXX: Happens too often...
351                                         // property_grid.ShowError ("Property value of '" + property_descriptor.Name + "' is not convertible to string.");
352                                         return null;
353                                 }
354                         }
355
356                         return null;
357                 }
358
359                 public bool HasCustomEditor {
360                         get { return EditorStyle != UITypeEditorEditStyle.None; }
361                 }
362
363                 public UITypeEditorEditStyle EditorStyle {
364                         get {
365                                 UITypeEditor editor = GetEditor ();
366                                 if (editor != null) {
367                                         try {
368                                                 return editor.GetEditStyle ((ITypeDescriptorContext)this);
369                                         } catch {
370                                                 // Some of our Editors throw NotImplementedException
371                                         }
372                                 }
373                                 return UITypeEditorEditStyle.None;
374                         }
375                 }
376
377                 public bool EditorResizeable {
378                         get {
379                                 if (this.EditorStyle == UITypeEditorEditStyle.DropDown) {
380                                         UITypeEditor editor = GetEditor ();
381                                         if (editor != null && editor.IsDropDownResizable)
382                                                 return true;
383                                 }
384                                 return false;
385                         }
386                 }
387
388                 public bool EditValue (IWindowsFormsEditorService service)
389                 {
390                         if (service == null)
391                                 throw new ArgumentNullException ("service");
392
393                         IServiceContainer parent = ((ITypeDescriptorContext)this).GetService (typeof (IServiceContainer)) as IServiceContainer;
394                         ServiceContainer container = null;
395
396                         if (parent != null)
397                                 container = new ServiceContainer (parent);
398                         else
399                                 container = new ServiceContainer ();
400
401                         container.AddService (typeof (IWindowsFormsEditorService), service);
402
403                         UITypeEditor editor = GetEditor ();
404                         if (editor != null) {
405                                 try {
406                                         object value = editor.EditValue ((ITypeDescriptorContext)this,
407                                                                          container,
408                                                                          this.Value);
409                                         string error = null;
410                                         return SetValue (value, out error);
411                                 } catch { //(Exception e) {
412                                         // property_grid.ShowError (e.Message + Environment.NewLine + e.StackTrace);
413                                 }
414                         }
415                         return false;
416                 }
417
418                 private UITypeEditor GetEditor ()
419                 {
420                         if (PropertyDescriptor != null) {
421                                 try { // can happen, because we are missing some editors
422                                         if (PropertyDescriptor != null)
423                                                 return (UITypeEditor) PropertyDescriptor.GetEditor (typeof (UITypeEditor));
424                                 } catch {
425                                         // property_grid.ShowError ("Unable to load UITypeEditor for property '" + PropertyDescriptor.Name + "'.");
426                                 }
427                         }
428                         return null;
429                 }
430
431                 private TypeConverter GetConverter ()
432                 {
433                         if (PropertyDescriptor != null)
434                                 return PropertyDescriptor.Converter;
435                         return null;
436                 }
437
438                 public bool ToggleValue ()
439                 {
440                         if (IsReadOnly || (IsMerged && !HasMergedValue))
441                                 return false;
442
443                         bool success = false;
444                         string error = null;
445                         object value = this.Value;
446                         if (PropertyDescriptor.PropertyType == typeof(bool))
447                                 success = SetValue (!(bool)value, out error);
448                         else {
449                                 TypeConverter converter = GetConverter ();
450                                 if (converter != null && 
451                                     converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
452                                         TypeConverter.StandardValuesCollection values = 
453                                                 (TypeConverter.StandardValuesCollection) converter.GetStandardValues ((ITypeDescriptorContext)this);
454                                         if (values != null) {
455                                                 for (int i = 0; i < values.Count; i++) {
456                                                         if (value != null && value.Equals (values[i])){
457                                                                 if (i < values.Count-1)
458                                                                         success = SetValue (values[i+1], out error);
459                                                                 else
460                                                                         success = SetValue (values[0], out error);
461                                                                 break;
462                                                         }
463                                                 }
464                                         }
465                                 }
466                         }
467                         if (!success && error != null)
468                                 property_grid.ShowError (error);
469                         return success;
470                 }
471
472                 public bool SetValue (object value, out string error)
473                 {
474                         error = null;
475                         if (this.IsReadOnly)
476                                 return false;
477
478                         if (SetValueCore (value, out error)) {
479                                 InvalidateChildGridItemsCache ();
480                                 property_grid.OnPropertyValueChangedInternal (this, this.Value);
481                                 return true;
482                         }
483                         return false;
484                 }
485
486                 protected virtual bool SetValueCore (object value, out string error)
487                 {
488                         error = null;
489
490                         TypeConverter converter = GetConverter ();
491                         Type valueType = value != null ? value.GetType () : null;
492                         // if the new value is not of the same type try to convert it
493                         if (valueType != null && this.PropertyDescriptor.PropertyType != null &&
494                             !this.PropertyDescriptor.PropertyType.IsAssignableFrom (valueType)) {
495                                 bool conversionError = false;
496                                 try {
497                                         if (converter != null &&
498                                             converter.CanConvertFrom ((ITypeDescriptorContext)this, valueType))
499                                                 value = converter.ConvertFrom ((ITypeDescriptorContext)this, 
500                                                                                CultureInfo.CurrentCulture, value);
501                                         else
502                                                 conversionError = true;
503                                 } catch (Exception e) {
504                                         error = e.Message;
505                                         conversionError = true;
506                                 }
507                                 if (conversionError) {
508                                         string valueText = ConvertToString (value);
509                                         string errorShortDescription = null;
510                                         if (valueText != null) {
511                                                 errorShortDescription = "Property value '" + valueText + "' of '" +
512                                                         PropertyDescriptor.Name + "' is not convertible to type '" +
513                                                         this.PropertyDescriptor.PropertyType.Name + "'";
514
515                                         } else {
516                                                 errorShortDescription = "Property value of '" +
517                                                         PropertyDescriptor.Name + "' is not convertible to type '" +
518                                                         this.PropertyDescriptor.PropertyType.Name + "'";
519                                         }
520                                         error = errorShortDescription + Environment.NewLine + Environment.NewLine + error;
521                                         return false;
522                                 }
523                         }
524
525                         bool changed = false;
526                         bool current_changed = false;
527                         object[] propertyOwners = this.PropertyOwners;
528                         PropertyDescriptor[] properties = PropertyDescriptors;
529                         for (int i=0; i < propertyOwners.Length; i++) {
530                                 object currentVal = properties[i].GetValue (propertyOwners[i]);
531                                 current_changed = false;
532                                 if (!Object.Equals (currentVal, value)) {
533                                         if (this.ShouldCreateParentInstance) {
534                                                 Hashtable updatedParentProperties = new Hashtable ();
535                                                 PropertyDescriptorCollection parentProperties = TypeDescriptor.GetProperties (propertyOwners[i]);
536                                                 foreach (PropertyDescriptor property in parentProperties) {
537                                                         if (property.Name == properties[i].Name)
538                                                                 updatedParentProperties[property.Name] = value;
539                                                         else
540                                                                 updatedParentProperties[property.Name] = property.GetValue (propertyOwners[i]);
541                                                 }
542                                                 object updatedParentValue = this.ParentEntry.PropertyDescriptor.Converter.CreateInstance (
543                                                         (ITypeDescriptorContext)this, updatedParentProperties);
544                                                 if (updatedParentValue != null)
545                                                         current_changed = this.ParentEntry.SetValueCore (updatedParentValue, out error);
546                                         } else {
547                                                 try {
548                                                         properties[i].SetValue (propertyOwners[i], value);
549                                                 } catch {
550                                                         // MS seems to swallow this
551                                                         //
552                                                         // string valueText = ConvertToString (value);
553                                                         // if (valueText != null)
554                                                         //      error = "Property value '" + valueText + "' of '" + properties[i].Name + "' is invalid.";
555                                                         // else
556                                                         //      error = "Property value of '" + properties[i].Name + "' is invalid.";
557                                                         return false;
558                                                 }
559
560                                                 if (IsValueType (this.ParentEntry)) 
561                                                         current_changed = ParentEntry.SetValueCore (propertyOwners[i], out error);
562                                                 else
563                                                         current_changed = Object.Equals (properties[i].GetValue (propertyOwners[i]), value);
564                                         }
565                                 }
566                                 if (current_changed)
567                                         changed = true;
568                         }
569                         return changed;
570                 }
571
572                 private bool IsValueType (GridEntry item)
573                 {
574                         if (item != null && item.PropertyDescriptor != null && 
575                             (item.PropertyDescriptor.PropertyType.IsValueType ||
576                              item.PropertyDescriptor.PropertyType.IsPrimitive))
577                                 return true;
578                         return false;
579                 }
580
581                 public bool ResetValue ()
582                 {
583                         if (IsResetable) {
584                                 object[] owners = this.PropertyOwners;
585                                 PropertyDescriptor[] properties = PropertyDescriptors;
586                                 for (int i=0; i < owners.Length; i++) {
587                                         properties[i].ResetValue (owners[i]);
588                                         if (IsValueType (this.ParentEntry)) {
589                                                 string error = null;
590                                                 if (!ParentEntry.SetValueCore (owners[i], out error) && error != null)
591                                                         property_grid.ShowError (error);
592                                         }
593                                 }
594                                 property_grid.OnPropertyValueChangedInternal (this, this.Value);
595                                 return true;
596                         }
597                         return false;
598                 }
599
600                 public bool HasDefaultValue {
601                         get {
602                                 if (PropertyDescriptor != null)
603                                     return !PropertyDescriptor.ShouldSerializeValue (PropertyOwner);
604                                 return false;
605                         }
606                 }
607
608                 // Determines if the current value can be reset
609                 //
610                 public virtual bool IsResetable {
611                         get { return (!IsReadOnly && PropertyDescriptor.CanResetValue (PropertyOwner)); }
612
613                 }
614
615                 // If false the entry can be modified only by the means of a predefined values
616                 // and not such inputed by the user.
617                 //
618                 public virtual bool IsEditable {
619                         get {
620                                 TypeConverter converter = GetConverter ();
621                                 if (PropertyDescriptor == null)
622                                         return false;
623                                 else if (PropertyDescriptor.PropertyType.IsArray)
624                                         return false;
625                                 else if (PropertyDescriptor.IsReadOnly && !this.ShouldCreateParentInstance)
626                                         return false;
627                                 else if (converter == null || 
628                                          !converter.CanConvertFrom ((ITypeDescriptorContext)this, typeof (string)))
629                                         return false;
630                                 else if (converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
631                                          converter.GetStandardValuesExclusive ((ITypeDescriptorContext)this))
632                                         return false;
633                                 else
634                                         return true;
635                         }
636                 }
637
638                 // If true the the entry cannot be modified at all
639                 //
640                 public virtual bool IsReadOnly {
641                         get {
642                                 TypeConverter converter = GetConverter ();
643                                 if (PropertyDescriptor == null || PropertyOwner == null)
644                                         return true;
645                                 else if (PropertyDescriptor.IsReadOnly && 
646                                          (EditorStyle != UITypeEditorEditStyle.Modal || PropertyDescriptor.PropertyType.IsValueType) && 
647                                          !this.ShouldCreateParentInstance)
648                                         return true;
649                                 else if (PropertyDescriptor.IsReadOnly && 
650                                          TypeDescriptor.GetAttributes (PropertyDescriptor.PropertyType)
651                                           [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes))
652                                         return true;
653                                 else if (ShouldCreateParentInstance && ParentEntry.IsReadOnly)
654                                         return true;
655                                 else if (!HasCustomEditor && converter == null)
656                                         return true;
657                                 else if (converter != null &&
658                                          !converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
659                                          !converter.CanConvertFrom ((ITypeDescriptorContext)this,
660                                                                     typeof (string)) &&
661                                          !HasCustomEditor) {
662                                         return true;
663                                 } else if (PropertyDescriptor.PropertyType.IsArray && !HasCustomEditor)
664                                         return true;
665                                 else
666                                         return false;
667                         }
668                 }
669
670                 public bool IsPassword {
671                         get {
672                                 if (PropertyDescriptor != null)
673                                         return PropertyDescriptor.Attributes.Contains (PasswordPropertyTextAttribute.Yes);
674                                 return false;
675                         }
676                 }
677                 // This is a way to set readonly properties (e.g properties without a setter).
678                 // The way it works is that if CreateInstance is supported by the parent's converter  
679                 // it gets passed a list of properties and their values which it uses to create an 
680                 // instance (e.g by passing them to the ctor of that object type).
681                 // 
682                 // This is used for e.g Font
683                 //
684                 public virtual bool ShouldCreateParentInstance {
685                         get {
686                                 if (this.ParentEntry != null && ParentEntry.PropertyDescriptor != null) {
687                                         TypeConverter parentConverter = ParentEntry.GetConverter ();
688                                         if (parentConverter != null && parentConverter.GetCreateInstanceSupported ((ITypeDescriptorContext)this))
689                                                 return true;
690                                 }
691                                 return false;
692                         }
693                 }
694
695                 public virtual bool PaintValueSupported {
696                         get {
697                                 UITypeEditor editor = GetEditor ();
698                                 if (editor != null) {
699                                         try {
700                                                 return editor.GetPaintValueSupported ();
701                                         } catch {
702                                                 // Some of our Editors throw NotImplementedException
703                                         }
704                                 }
705                                 return false;
706                         }
707                 }
708
709                 public virtual void PaintValue (Graphics gfx, Rectangle rect)
710                 {
711                         UITypeEditor editor = GetEditor ();
712                         if (editor != null) {
713                                 try {
714                                         editor.PaintValue (this.Value, gfx, rect);
715                                 } catch {
716                                         // Some of our Editors throw NotImplementedException
717                                 }
718                         }
719                 }
720
721 #region Population
722                 protected void PopulateChildGridItems ()
723                 {
724                         grid_items = GetChildGridItemsCached ();
725                 }
726
727                 private void InvalidateChildGridItemsCache ()
728                 {
729                         if (child_griditems_cache != null) {
730                                 child_griditems_cache = null;
731                                 PopulateChildGridItems ();
732                         }
733                 }
734
735                 private GridItemCollection GetChildGridItemsCached ()
736                 {
737                         if (child_griditems_cache == null) {
738                                 child_griditems_cache = GetChildGridItems ();
739                                 // foreach (GridEntry item in child_griditems_cache)
740                                 //      PrintDebugInfo (item);
741                         }
742
743                         return child_griditems_cache;
744                 }
745
746                 // private static void PrintDebugInfo (GridEntry item)
747                 // {
748                 //      if (item.PropertyDescriptor != null) {
749                 //              Console.WriteLine ("=== [" + item.PropertyDescriptor.Name + "] ===");
750                 //              try {
751                 //                      TypeConverter converter = item.GetConverter ();
752                 //                      Console.WriteLine ("IsReadOnly: " + item.IsReadOnly);
753                 //                      Console.WriteLine ("IsEditable: " + item.IsEditable);
754                 //                      Console.WriteLine ("PropertyDescriptor.IsReadOnly: " + item.PropertyDescriptor.IsReadOnly);
755                 //                      if (item.ParentEntry != null)
756                 //                              Console.WriteLine ("ParentEntry.IsReadOnly: " + item.ParentEntry.IsReadOnly);
757                 //                      Console.WriteLine ("ImmutableObjectAttribute.Yes: " + TypeDescriptor.GetAttributes (item.PropertyDescriptor.PropertyType)
758                 //                                         [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes));
759                 //                      UITypeEditor editor = item.GetEditor ();
760                 //                      Console.WriteLine ("Editor: " + (editor == null ? "none" : editor.GetType ().Name));
761                 //                      if (editor != null)
762                 //                              Console.WriteLine ("Editor.EditorStyle: " + editor.GetEditStyle ((ITypeDescriptorContext)item));
763                 //                      Console.WriteLine ("Converter: " + (converter == null ? "none" : converter.GetType ().Name));
764                 //                      if (converter != null) {
765                 //                              Console.WriteLine ("Converter.GetStandardValuesSupported: " + converter.GetStandardValuesSupported ((ITypeDescriptorContext)item).ToString ());
766                 //                              Console.WriteLine ("Converter.GetStandardValuesExclusive: " + converter.GetStandardValuesExclusive ((ITypeDescriptorContext)item).ToString ());
767                 //                              Console.WriteLine ("ShouldCreateParentInstance: " + item.ShouldCreateParentInstance);
768                 //                              Console.WriteLine ("CanConvertFrom (string): " + converter.CanConvertFrom ((ITypeDescriptorContext)item, typeof (string)));
769                 //                      }
770                 //                      Console.WriteLine ("IsArray: " + item.PropertyDescriptor.PropertyType.IsArray.ToString ());
771                 //              } catch { /* Some converters and editor throw NotImplementedExceptions */ }
772                 //      }
773                 // }
774
775                 private GridItemCollection GetChildGridItems ()
776                 {
777                         object[] propertyOwners = this.Values;
778                         string[] propertyNames = GetMergedPropertyNames (propertyOwners);
779                         GridItemCollection items = new GridItemCollection ();
780
781                         foreach (string propertyName in propertyNames) {
782                                 PropertyDescriptor[] properties = new PropertyDescriptor[propertyOwners.Length];
783                                 for (int i=0; i < propertyOwners.Length; i++)
784                                         properties[i] = GetPropertyDescriptor (propertyOwners[i], propertyName);
785                                 items.Add (new GridEntry (property_grid, properties, this));
786                         }
787
788                         return items;
789                 }
790
791                 private bool IsPropertyMergeable (PropertyDescriptor property)
792                 {
793                         if (property == null)
794                                 return false;
795                                 
796                         MergablePropertyAttribute attrib = property.Attributes [typeof (MergablePropertyAttribute)] as MergablePropertyAttribute;
797                         if (attrib != null && !attrib.AllowMerge)
798                                 return false;
799
800                         return true;
801                 }
802
803                 private string[] GetMergedPropertyNames (object [] objects)
804                 {
805                         if (objects == null || objects.Length == 0)
806                                 return new string[0];
807
808                         ArrayList intersection = new ArrayList ();
809                         for (int i = 0; i < objects.Length; i ++) {
810                                 if (objects [i] == null)
811                                         continue;
812
813                                 PropertyDescriptorCollection properties = GetProperties (objects[i], property_grid.BrowsableAttributes);
814                                 ArrayList new_intersection = new ArrayList ();
815
816                                 foreach (PropertyDescriptor currentProperty in (i == 0 ? (ICollection)properties : (ICollection)intersection)) {
817                                         PropertyDescriptor matchingProperty = (i == 0 ? currentProperty : properties [currentProperty.Name]);
818                                         if (objects.Length > 1 && !IsPropertyMergeable (matchingProperty))
819                                                 continue;
820                                         if (matchingProperty.PropertyType == currentProperty.PropertyType)
821                                                 new_intersection.Add (matchingProperty);
822                                 }
823
824                                 intersection = new_intersection;
825                         }
826
827                         string[] propertyNames = new string [intersection.Count];
828                         for (int i=0; i < intersection.Count; i++)
829                                 propertyNames[i] = ((PropertyDescriptor)intersection[i]).Name;
830                                 
831                         return propertyNames;
832                 }
833
834                 private PropertyDescriptor GetPropertyDescriptor (object propertyOwner, string propertyName)
835                 {
836                         if (propertyOwner == null || propertyName == null)
837                                 return null;
838
839                         PropertyDescriptorCollection properties = GetProperties (propertyOwner, property_grid.BrowsableAttributes);
840                         if (properties != null)
841                                 return properties[propertyName];
842                         return null;
843                 }
844
845                 private PropertyDescriptorCollection GetProperties (object propertyOwner, AttributeCollection attributes)
846                 {
847                         if (propertyOwner == null || property_grid.SelectedTab == null)
848                                 return new PropertyDescriptorCollection (null);
849
850                         Attribute[] atts = new Attribute[attributes.Count];
851                         attributes.CopyTo (atts, 0);
852                         return property_grid.SelectedTab.GetProperties ((ITypeDescriptorContext)this, propertyOwner, atts);
853                 }
854 #endregion
855         }
856 }