2010-05-06 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / Managed.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 NET_2_0
380                                 if (this.EditorStyle == UITypeEditorEditStyle.DropDown) {
381                                         UITypeEditor editor = GetEditor ();
382                                         if (editor != null && editor.IsDropDownResizable)
383                                                 return true;
384                                 }
385 #endif
386                                 return false;
387                         }
388                 }
389
390                 public bool EditValue (IWindowsFormsEditorService service)
391                 {
392                         if (service == null)
393                                 throw new ArgumentNullException ("service");
394
395                         IServiceContainer parent = ((ITypeDescriptorContext)this).GetService (typeof (IServiceContainer)) as IServiceContainer;
396                         ServiceContainer container = null;
397
398                         if (parent != null)
399                                 container = new ServiceContainer (parent);
400                         else
401                                 container = new ServiceContainer ();
402
403                         container.AddService (typeof (IWindowsFormsEditorService), service);
404
405                         UITypeEditor editor = GetEditor ();
406                         if (editor != null) {
407                                 try {
408                                         object value = editor.EditValue ((ITypeDescriptorContext)this,
409                                                                          container,
410                                                                          this.Value);
411                                         string error = null;
412                                         return SetValue (value, out error);
413                                 } catch { //(Exception e) {
414                                         // property_grid.ShowError (e.Message + Environment.NewLine + e.StackTrace);
415                                 }
416                         }
417                         return false;
418                 }
419
420                 private UITypeEditor GetEditor ()
421                 {
422                         if (PropertyDescriptor != null) {
423                                 try { // can happen, because we are missing some editors
424                                         if (PropertyDescriptor != null)
425                                                 return (UITypeEditor) PropertyDescriptor.GetEditor (typeof (UITypeEditor));
426                                 } catch {
427                                         // property_grid.ShowError ("Unable to load UITypeEditor for property '" + PropertyDescriptor.Name + "'.");
428                                 }
429                         }
430                         return null;
431                 }
432
433                 private TypeConverter GetConverter ()
434                 {
435                         if (PropertyDescriptor != null)
436                                 return PropertyDescriptor.Converter;
437                         return null;
438                 }
439
440                 public bool ToggleValue ()
441                 {
442                         if (IsReadOnly || (IsMerged && !HasMergedValue))
443                                 return false;
444
445                         bool success = false;
446                         string error = null;
447                         object value = this.Value;
448                         if (PropertyDescriptor.PropertyType == typeof(bool))
449                                 success = SetValue (!(bool)value, out error);
450                         else {
451                                 TypeConverter converter = GetConverter ();
452                                 if (converter != null && 
453                                     converter.GetStandardValuesSupported ((ITypeDescriptorContext)this)) {
454                                         TypeConverter.StandardValuesCollection values = 
455                                                 (TypeConverter.StandardValuesCollection) converter.GetStandardValues ((ITypeDescriptorContext)this);
456                                         if (values != null) {
457                                                 for (int i = 0; i < values.Count; i++) {
458                                                         if (value != null && value.Equals (values[i])){
459                                                                 if (i < values.Count-1)
460                                                                         success = SetValue (values[i+1], out error);
461                                                                 else
462                                                                         success = SetValue (values[0], out error);
463                                                                 break;
464                                                         }
465                                                 }
466                                         }
467                                 }
468                         }
469                         if (!success && error != null)
470                                 property_grid.ShowError (error);
471                         return success;
472                 }
473
474                 public bool SetValue (object value, out string error)
475                 {
476                         error = null;
477                         if (this.IsReadOnly)
478                                 return false;
479
480                         if (SetValueCore (value, out error)) {
481                                 InvalidateChildGridItemsCache ();
482                                 property_grid.OnPropertyValueChangedInternal (this, this.Value);
483                                 return true;
484                         }
485                         return false;
486                 }
487
488                 protected virtual bool SetValueCore (object value, out string error)
489                 {
490                         error = null;
491
492                         TypeConverter converter = GetConverter ();
493                         Type valueType = value != null ? value.GetType () : null;
494                         // if the new value is not of the same type try to convert it
495                         if (valueType != null && this.PropertyDescriptor.PropertyType != null &&
496                             !this.PropertyDescriptor.PropertyType.IsAssignableFrom (valueType)) {
497                                 bool conversionError = false;
498                                 try {
499                                         if (converter != null &&
500                                             converter.CanConvertFrom ((ITypeDescriptorContext)this, valueType))
501                                                 value = converter.ConvertFrom ((ITypeDescriptorContext)this, 
502                                                                                CultureInfo.CurrentCulture, value);
503                                         else
504                                                 conversionError = true;
505                                 } catch (Exception e) {
506                                         error = e.Message;
507                                         conversionError = true;
508                                 }
509                                 if (conversionError) {
510                                         string valueText = ConvertToString (value);
511                                         string errorShortDescription = null;
512                                         if (valueText != null) {
513                                                 errorShortDescription = "Property value '" + valueText + "' of '" +
514                                                         PropertyDescriptor.Name + "' is not convertible to type '" +
515                                                         this.PropertyDescriptor.PropertyType.Name + "'";
516
517                                         } else {
518                                                 errorShortDescription = "Property value of '" +
519                                                         PropertyDescriptor.Name + "' is not convertible to type '" +
520                                                         this.PropertyDescriptor.PropertyType.Name + "'";
521                                         }
522                                         error = errorShortDescription + Environment.NewLine + Environment.NewLine + error;
523                                         return false;
524                                 }
525                         }
526
527                         bool changed = false;
528                         bool current_changed = false;
529                         object[] propertyOwners = this.PropertyOwners;
530                         PropertyDescriptor[] properties = PropertyDescriptors;
531                         for (int i=0; i < propertyOwners.Length; i++) {
532                                 object currentVal = properties[i].GetValue (propertyOwners[i]);
533                                 current_changed = false;
534                                 if (!Object.Equals (currentVal, value)) {
535                                         if (this.ShouldCreateParentInstance) {
536                                                 Hashtable updatedParentProperties = new Hashtable ();
537                                                 PropertyDescriptorCollection parentProperties = TypeDescriptor.GetProperties (propertyOwners[i]);
538                                                 foreach (PropertyDescriptor property in parentProperties) {
539                                                         if (property.Name == properties[i].Name)
540                                                                 updatedParentProperties[property.Name] = value;
541                                                         else
542                                                                 updatedParentProperties[property.Name] = property.GetValue (propertyOwners[i]);
543                                                 }
544                                                 object updatedParentValue = this.ParentEntry.PropertyDescriptor.Converter.CreateInstance (
545                                                         (ITypeDescriptorContext)this, updatedParentProperties);
546                                                 if (updatedParentValue != null)
547                                                         current_changed = this.ParentEntry.SetValueCore (updatedParentValue, out error);
548                                         } else {
549                                                 try {
550                                                         properties[i].SetValue (propertyOwners[i], value);
551                                                 } catch {
552                                                         // MS seems to swallow this
553                                                         //
554                                                         // string valueText = ConvertToString (value);
555                                                         // if (valueText != null)
556                                                         //      error = "Property value '" + valueText + "' of '" + properties[i].Name + "' is invalid.";
557                                                         // else
558                                                         //      error = "Property value of '" + properties[i].Name + "' is invalid.";
559                                                         return false;
560                                                 }
561
562                                                 if (IsValueType (this.ParentEntry)) 
563                                                         current_changed = ParentEntry.SetValueCore (propertyOwners[i], out error);
564                                                 else
565                                                         current_changed = Object.Equals (properties[i].GetValue (propertyOwners[i]), value);
566                                         }
567                                 }
568                                 if (current_changed)
569                                         changed = true;
570                         }
571                         return changed;
572                 }
573
574                 private bool IsValueType (GridEntry item)
575                 {
576                         if (item != null && item.PropertyDescriptor != null && 
577                             (item.PropertyDescriptor.PropertyType.IsValueType ||
578                              item.PropertyDescriptor.PropertyType.IsPrimitive))
579                                 return true;
580                         return false;
581                 }
582
583                 public bool ResetValue ()
584                 {
585                         if (IsResetable) {
586                                 object[] owners = this.PropertyOwners;
587                                 PropertyDescriptor[] properties = PropertyDescriptors;
588                                 for (int i=0; i < owners.Length; i++) {
589                                         properties[i].ResetValue (owners[i]);
590                                         if (IsValueType (this.ParentEntry)) {
591                                                 string error = null;
592                                                 if (!ParentEntry.SetValueCore (owners[i], out error) && error != null)
593                                                         property_grid.ShowError (error);
594                                         }
595                                 }
596                                 property_grid.OnPropertyValueChangedInternal (this, this.Value);
597                                 return true;
598                         }
599                         return false;
600                 }
601
602                 public bool HasDefaultValue {
603                         get {
604                                 if (PropertyDescriptor != null)
605                                     return !PropertyDescriptor.ShouldSerializeValue (PropertyOwner);
606                                 return false;
607                         }
608                 }
609
610                 // Determines if the current value can be reset
611                 //
612                 public virtual bool IsResetable {
613                         get { return (!IsReadOnly && PropertyDescriptor.CanResetValue (PropertyOwner)); }
614
615                 }
616
617                 // If false the entry can be modified only by the means of a predefined values
618                 // and not such inputed by the user.
619                 //
620                 public virtual bool IsEditable {
621                         get {
622                                 TypeConverter converter = GetConverter ();
623                                 if (PropertyDescriptor == null)
624                                         return false;
625                                 else if (PropertyDescriptor.PropertyType.IsArray)
626                                         return false;
627                                 else if (PropertyDescriptor.IsReadOnly && !this.ShouldCreateParentInstance)
628                                         return false;
629                                 else if (converter == null || 
630                                          !converter.CanConvertFrom ((ITypeDescriptorContext)this, typeof (string)))
631                                         return false;
632                                 else if (converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
633                                          converter.GetStandardValuesExclusive ((ITypeDescriptorContext)this))
634                                         return false;
635                                 else
636                                         return true;
637                         }
638                 }
639
640                 // If true the the entry cannot be modified at all
641                 //
642                 public virtual bool IsReadOnly {
643                         get {
644                                 TypeConverter converter = GetConverter ();
645                                 if (PropertyDescriptor == null || PropertyOwner == null)
646                                         return true;
647                                 else if (PropertyDescriptor.IsReadOnly && 
648                                          (EditorStyle != UITypeEditorEditStyle.Modal || PropertyDescriptor.PropertyType.IsValueType) && 
649                                          !this.ShouldCreateParentInstance)
650                                         return true;
651                                 else if (PropertyDescriptor.IsReadOnly && 
652                                          TypeDescriptor.GetAttributes (PropertyDescriptor.PropertyType)
653                                           [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes))
654                                         return true;
655                                 else if (ShouldCreateParentInstance && ParentEntry.IsReadOnly)
656                                         return true;
657                                 else if (!HasCustomEditor && converter == null)
658                                         return true;
659                                 else if (converter != null &&
660                                          !converter.GetStandardValuesSupported ((ITypeDescriptorContext)this) &&
661                                          !converter.CanConvertFrom ((ITypeDescriptorContext)this,
662                                                                     typeof (string)) &&
663                                          !HasCustomEditor) {
664                                         return true;
665                                 } else if (PropertyDescriptor.PropertyType.IsArray && !HasCustomEditor)
666                                         return true;
667                                 else
668                                         return false;
669                         }
670                 }
671
672                 public bool IsPassword {
673                         get {
674 #if NET_2_0
675                                 if (PropertyDescriptor != null)
676                                         return PropertyDescriptor.Attributes.Contains (PasswordPropertyTextAttribute.Yes);
677 #endif
678                                 return false;
679                         }
680                 }
681                 // This is a way to set readonly properties (e.g properties without a setter).
682                 // The way it works is that if CreateInstance is supported by the parent's converter  
683                 // it gets passed a list of properties and their values which it uses to create an 
684                 // instance (e.g by passing them to the ctor of that object type).
685                 // 
686                 // This is used for e.g Font
687                 //
688                 public virtual bool ShouldCreateParentInstance {
689                         get {
690                                 if (this.ParentEntry != null && ParentEntry.PropertyDescriptor != null) {
691                                         TypeConverter parentConverter = ParentEntry.GetConverter ();
692                                         if (parentConverter != null && parentConverter.GetCreateInstanceSupported ((ITypeDescriptorContext)this))
693                                                 return true;
694                                 }
695                                 return false;
696                         }
697                 }
698
699                 public virtual bool PaintValueSupported {
700                         get {
701                                 UITypeEditor editor = GetEditor ();
702                                 if (editor != null) {
703                                         try {
704                                                 return editor.GetPaintValueSupported ();
705                                         } catch {
706                                                 // Some of our Editors throw NotImplementedException
707                                         }
708                                 }
709                                 return false;
710                         }
711                 }
712
713                 public virtual void PaintValue (Graphics gfx, Rectangle rect)
714                 {
715                         UITypeEditor editor = GetEditor ();
716                         if (editor != null) {
717                                 try {
718                                         editor.PaintValue (this.Value, gfx, rect);
719                                 } catch {
720                                         // Some of our Editors throw NotImplementedException
721                                 }
722                         }
723                 }
724
725 #region Population
726                 protected void PopulateChildGridItems ()
727                 {
728                         grid_items = GetChildGridItemsCached ();
729                 }
730
731                 private void InvalidateChildGridItemsCache ()
732                 {
733                         if (child_griditems_cache != null) {
734                                 child_griditems_cache = null;
735                                 PopulateChildGridItems ();
736                         }
737                 }
738
739                 private GridItemCollection GetChildGridItemsCached ()
740                 {
741                         if (child_griditems_cache == null) {
742                                 child_griditems_cache = GetChildGridItems ();
743                                 // foreach (GridEntry item in child_griditems_cache)
744                                 //      PrintDebugInfo (item);
745                         }
746
747                         return child_griditems_cache;
748                 }
749
750                 // private static void PrintDebugInfo (GridEntry item)
751                 // {
752                 //      if (item.PropertyDescriptor != null) {
753                 //              Console.WriteLine ("=== [" + item.PropertyDescriptor.Name + "] ===");
754                 //              try {
755                 //                      TypeConverter converter = item.GetConverter ();
756                 //                      Console.WriteLine ("IsReadOnly: " + item.IsReadOnly);
757                 //                      Console.WriteLine ("IsEditable: " + item.IsEditable);
758                 //                      Console.WriteLine ("PropertyDescriptor.IsReadOnly: " + item.PropertyDescriptor.IsReadOnly);
759                 //                      if (item.ParentEntry != null)
760                 //                              Console.WriteLine ("ParentEntry.IsReadOnly: " + item.ParentEntry.IsReadOnly);
761                 //                      Console.WriteLine ("ImmutableObjectAttribute.Yes: " + TypeDescriptor.GetAttributes (item.PropertyDescriptor.PropertyType)
762                 //                                         [typeof(ImmutableObjectAttribute)].Equals (ImmutableObjectAttribute.Yes));
763                 //                      UITypeEditor editor = item.GetEditor ();
764                 //                      Console.WriteLine ("Editor: " + (editor == null ? "none" : editor.GetType ().Name));
765                 //                      if (editor != null)
766                 //                              Console.WriteLine ("Editor.EditorStyle: " + editor.GetEditStyle ((ITypeDescriptorContext)item));
767                 //                      Console.WriteLine ("Converter: " + (converter == null ? "none" : converter.GetType ().Name));
768                 //                      if (converter != null) {
769                 //                              Console.WriteLine ("Converter.GetStandardValuesSupported: " + converter.GetStandardValuesSupported ((ITypeDescriptorContext)item).ToString ());
770                 //                              Console.WriteLine ("Converter.GetStandardValuesExclusive: " + converter.GetStandardValuesExclusive ((ITypeDescriptorContext)item).ToString ());
771                 //                              Console.WriteLine ("ShouldCreateParentInstance: " + item.ShouldCreateParentInstance);
772                 //                              Console.WriteLine ("CanConvertFrom (string): " + converter.CanConvertFrom ((ITypeDescriptorContext)item, typeof (string)));
773                 //                      }
774                 //                      Console.WriteLine ("IsArray: " + item.PropertyDescriptor.PropertyType.IsArray.ToString ());
775                 //              } catch { /* Some converters and editor throw NotImplementedExceptions */ }
776                 //      }
777                 // }
778
779                 private GridItemCollection GetChildGridItems ()
780                 {
781                         object[] propertyOwners = this.Values;
782                         string[] propertyNames = GetMergedPropertyNames (propertyOwners);
783                         GridItemCollection items = new GridItemCollection ();
784
785                         foreach (string propertyName in propertyNames) {
786                                 PropertyDescriptor[] properties = new PropertyDescriptor[propertyOwners.Length];
787                                 for (int i=0; i < propertyOwners.Length; i++)
788                                         properties[i] = GetPropertyDescriptor (propertyOwners[i], propertyName);
789                                 items.Add (new GridEntry (property_grid, properties, this));
790                         }
791
792                         return items;
793                 }
794
795                 private bool IsPropertyMergeable (PropertyDescriptor property)
796                 {
797                         if (property == null)
798                                 return false;
799                                 
800                         MergablePropertyAttribute attrib = property.Attributes [typeof (MergablePropertyAttribute)] as MergablePropertyAttribute;
801                         if (attrib != null && !attrib.AllowMerge)
802                                 return false;
803
804                         return true;
805                 }
806
807                 private string[] GetMergedPropertyNames (object [] objects)
808                 {
809                         if (objects == null || objects.Length == 0)
810                                 return new string[0];
811
812                         ArrayList intersection = new ArrayList ();
813                         for (int i = 0; i < objects.Length; i ++) {
814                                 if (objects [i] == null)
815                                         continue;
816
817                                 PropertyDescriptorCollection properties = GetProperties (objects[i], property_grid.BrowsableAttributes);
818                                 ArrayList new_intersection = new ArrayList ();
819
820                                 foreach (PropertyDescriptor currentProperty in (i == 0 ? (ICollection)properties : (ICollection)intersection)) {
821                                         PropertyDescriptor matchingProperty = (i == 0 ? currentProperty : properties [currentProperty.Name]);
822                                         if (objects.Length > 1 && !IsPropertyMergeable (matchingProperty))
823                                                 continue;
824                                         if (matchingProperty.PropertyType == currentProperty.PropertyType)
825                                                 new_intersection.Add (matchingProperty);
826                                 }
827
828                                 intersection = new_intersection;
829                         }
830
831                         string[] propertyNames = new string [intersection.Count];
832                         for (int i=0; i < intersection.Count; i++)
833                                 propertyNames[i] = ((PropertyDescriptor)intersection[i]).Name;
834                                 
835                         return propertyNames;
836                 }
837
838                 private PropertyDescriptor GetPropertyDescriptor (object propertyOwner, string propertyName)
839                 {
840                         if (propertyOwner == null || propertyName == null)
841                                 return null;
842
843                         PropertyDescriptorCollection properties = GetProperties (propertyOwner, property_grid.BrowsableAttributes);
844                         if (properties != null)
845                                 return properties[propertyName];
846                         return null;
847                 }
848
849                 private PropertyDescriptorCollection GetProperties (object propertyOwner, AttributeCollection attributes)
850                 {
851                         if (propertyOwner == null || property_grid.SelectedTab == null)
852                                 return new PropertyDescriptorCollection (null);
853
854                         Attribute[] atts = new Attribute[attributes.Count];
855                         attributes.CopyTo (atts, 0);
856                         return property_grid.SelectedTab.GetProperties ((ITypeDescriptorContext)this, propertyOwner, atts);
857                 }
858 #endregion
859         }
860 }