2008-03-27 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
38 namespace System.Windows.Forms.PropertyGridInternal
39 {
40         internal class GridEntry : GridItem, ITypeDescriptorContext
41         {
42                 #region Local Variables
43                 private PropertyGrid property_grid;
44                 private bool expanded;
45                 private GridItemCollection grid_items;
46                 private GridItem parent;
47                 private PropertyDescriptor[] property_descriptors;
48                 private int top;
49                 private Rectangle plus_minus_bounds;
50                 #endregion      // Local Variables
51
52                 #region  Contructors
53                 protected GridEntry (PropertyGrid propertyGrid, GridEntry parent)
54                 {
55                         if (propertyGrid == null)
56                                 throw new ArgumentNullException ("propertyGrid");
57                         property_grid = propertyGrid;
58                         plus_minus_bounds = new Rectangle (0,0,0,0);
59                         top = -1;
60                         grid_items = new GridItemCollection ();
61                         expanded = false;
62                         this.parent = parent;
63                 }
64
65                 // Cannot use one PropertyDescriptor for all owners, because the
66                 // propertydescriptors might have different Invokees. Check
67                 // ReflectionPropertyDescriptor.GetInvokee and how it's used.
68                 //
69                 public GridEntry (PropertyGrid propertyGrid, PropertyDescriptor[] properties, 
70                                   GridEntry parent) : this (propertyGrid, parent) 
71                 {
72                         if (properties == null || properties.Length == 0)
73                                 throw new ArgumentNullException ("prop_desc");
74                         property_descriptors = properties;
75                 }
76                 #endregion      // Constructors
77
78
79                 public override bool Expandable {
80                         get { return grid_items.Count > 0; }
81                 }
82
83                 public override bool Expanded {
84                         get { return expanded; }
85                         set {
86                                 if (expanded != value) {
87                                         expanded = value;
88                                         if (value)
89                                                 property_grid.OnExpandItem (this);
90                                         else
91                                                 property_grid.OnCollapseItem (this);
92                                 }
93                         }
94                 }
95
96                 public override GridItemCollection GridItems {
97                         get { return grid_items; }
98                 }
99
100                 public override GridItemType GridItemType {
101                         get { return GridItemType.Property; }
102                 }
103
104                 public override string Label {
105                         get { 
106                                 PropertyDescriptor property = this.PropertyDescriptor;
107                                 if (property != null) {
108                                         string label = property.DisplayName;
109                                         ParenthesizePropertyNameAttribute parensAttr = 
110                                                 property.Attributes[typeof (ParenthesizePropertyNameAttribute)] as ParenthesizePropertyNameAttribute;
111                                         if (parensAttr != null && parensAttr.NeedParenthesis)
112                                                 label = "(" + label + ")";
113                                         return label;
114                                 }
115                                 return String.Empty;
116                         }
117                 }
118
119                 public override GridItem Parent {
120                         get { return parent; }
121                 }
122
123                 public GridEntry ParentEntry {
124                         get { 
125                                 if (parent != null && parent.GridItemType == GridItemType.Category)
126                                         return parent.Parent as GridEntry;
127                                 return parent as GridEntry; 
128                         }
129                 }
130
131                 public override PropertyDescriptor PropertyDescriptor {
132                         get { return property_descriptors != null ? property_descriptors[0] : null; }
133                 }
134
135                 public PropertyDescriptor[] PropertyDescriptors {
136                         get { return property_descriptors; }
137                 }
138
139                 public object PropertyOwner {
140                         get { 
141                                 object[] owners = PropertyOwners;
142                                 if (owners != null)
143                                         return owners[0];
144                                 return null;
145                         }
146                 }
147
148                 public object[] PropertyOwners {
149                         get { 
150                                 if (ParentEntry != null)
151                                         return ParentEntry.Values;
152                                 return null;
153                         }
154                 }
155
156                 // true if the value is the same among all properties
157                 public bool HasMergedValue {
158                         get {
159                                 if (!IsMerged)
160                                         return false;
161
162                                 object[] values = this.Values;
163                                 for (int i=0; i+1 < values.Length; i++) {
164                                         if (!Object.Equals (values[i], values[i+1]))
165                                                 return false;
166                                 }
167                                 return true;
168                         }
169                 }
170
171                 public virtual bool IsMerged {
172                         get { return (PropertyDescriptors != null && PropertyDescriptors.Length > 1); }
173                 }
174
175                 // If IsMerged this will return all values for all properties in all owners
176                 public virtual object[] Values {
177                         get {
178                                 if (PropertyDescriptor == null || this.PropertyOwners == null)
179                                         return null;
180                                 if (IsMerged) {
181                                         object[] owners = this.PropertyOwners;
182                                         PropertyDescriptor[] properties = PropertyDescriptors;
183                                         object[] values = new object[owners.Length];
184                                         for (int i=0; i < owners.Length; i++)
185                                                 values[i] = properties[i].GetValue (owners[i]);
186                                         return values;
187                                 } else {
188                                         return new object[] { this.Value };
189                                 }
190                         }
191                 }
192
193                 // Returns the first value for the first propertyowner and propertydescriptor
194                 //
195                 public override object Value {
196                         get {
197                                 if (PropertyDescriptor == null || PropertyOwner == null)
198                                         return null;
199
200                                 return PropertyDescriptor.GetValue (PropertyOwner);
201                         }
202                 }
203
204                 public string ValueText {
205                         get { 
206                                 string text = null;
207                                 try {
208                                         text = ConvertToString (this.Value);
209                                         if (text == null)
210                                                 text = String.Empty;
211                                 } catch {
212                                         text = String.Empty;
213                                 }
214                                 return text;
215                         }
216                 }
217
218                 public override bool Select ()
219                 {
220                         property_grid.SelectedGridItem = this;
221                         return true;
222                 }
223
224                 #region ITypeDescriptorContext
225                 void ITypeDescriptorContext.OnComponentChanged () 
226                 {
227                 }
228
229                 bool ITypeDescriptorContext.OnComponentChanging () 
230                 {
231                         return false;
232                 }
233
234                 IContainer ITypeDescriptorContext.Container {
235                         get {
236                                 if (PropertyOwner == null)
237                                         return null;
238
239                                 IComponent component = property_grid.SelectedObject as IComponent;
240                                 if (component != null && component.Site != null)
241                                         return component.Site.Container;
242                                 return null;
243                         }
244                 }
245
246                 object ITypeDescriptorContext.Instance {
247                         get { return PropertyOwner; }
248                 }
249
250                 PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor {
251                         get { return PropertyDescriptor; }
252                 }
253                 #endregion
254
255                 #region IServiceProvider Members
256
257                 object IServiceProvider.GetService (Type serviceType) {
258                         IComponent selectedComponent = property_grid.SelectedObject as IComponent;
259                         if (selectedComponent != null && selectedComponent.Site != null)
260                                 return selectedComponent.Site.GetService (serviceType);
261                         return null;
262                 }
263
264                 #endregion
265
266                 internal int Top {
267                         get { return top; }
268                         set {
269                                 if (top != value)
270                                         top = value;
271                         }
272                 }
273
274                 internal Rectangle PlusMinusBounds {
275                         get { return plus_minus_bounds; }
276                         set { plus_minus_bounds = value; }
277                 }
278
279                 public void SetParent (GridItem parent)
280                 {
281                         this.parent = parent;
282                 }
283
284                 public ICollection AcceptedValues {
285                         get {
286                                 if (PropertyDescriptor != null && PropertyDescriptor.Converter != null &&
287                                     PropertyDescriptor.Converter.GetStandardValuesSupported ()) {
288                                         ArrayList values = new ArrayList ();
289                                         string stringVal = null;
290                                         ICollection standardValues = PropertyDescriptor.Converter.GetStandardValues ();
291                                         if (standardValues != null) {
292                                                 foreach (object value in standardValues) {
293                                                         stringVal = ConvertToString (value);
294                                                         if (stringVal != null)
295                                                                 values.Add (stringVal);
296                                                 }
297                                         }
298                                         return values.Count > 0 ? values : null;
299                                 }
300                                 return null;
301                         }
302                 }
303
304                 private string ConvertToString (object value)
305                 {
306                         if (value is string)
307                                 return (string)value;
308
309                         if (PropertyDescriptor != null && PropertyDescriptor.Converter != null &&
310                             PropertyDescriptor.Converter.CanConvertTo (typeof (string))) {
311                                 try {
312                                         return PropertyDescriptor.Converter.ConvertToString (value);
313                                 } catch {
314                                         // XXX: Happens too often...
315                                         // property_grid.ShowError ("Property value of '" + property_descriptor.Name + "' is not convertible to string.");
316                                         return null;
317                                 }
318                         }
319
320                         return null;
321                 }
322
323                 public bool HasCustomEditor {
324                         get { return GetEditor() != null; }
325                 }
326
327                 public UITypeEditorEditStyle EditorStyle {
328                         get {
329                                 UITypeEditor editor = GetEditor ();
330                                 if (editor != null) {
331                                         try {
332                                                 return editor.GetEditStyle ((ITypeDescriptorContext)this);
333                                         } catch {
334                                                 // Some of our Editors throw NotImplementedException
335                                         }
336                                 }
337                                 return UITypeEditorEditStyle.None;
338                         }
339                 }
340
341                 public bool EditorResizeable {
342                         get {
343 #if NET_2_0
344                                 if (this.EditorStyle == UITypeEditorEditStyle.DropDown) {
345                                         UITypeEditor editor = GetEditor ();
346                                         if (editor != null && editor.IsDropDownResizable)
347                                                 return true;
348                                 }
349 #endif
350                                 return false;
351                         }
352                 }
353
354                 public bool EditValue (IWindowsFormsEditorService service)
355                 {
356                         if (service == null)
357                                 throw new ArgumentNullException ("service");
358
359                         IServiceContainer parent = ((ITypeDescriptorContext)this).GetService (typeof (IServiceContainer)) as IServiceContainer;
360                         ServiceContainer container = null;
361
362                         if (parent != null)
363                                 container = new ServiceContainer (parent);
364                         else
365                                 container = new ServiceContainer ();
366
367                         container.AddService (typeof (IWindowsFormsEditorService), service);
368
369                         UITypeEditor editor = GetEditor ();
370                         if (editor != null) {
371                                 try {
372                                         object value = editor.EditValue ((ITypeDescriptorContext)this,
373                                                                          container,
374                                                                          this.Value);
375                                         string error = null;
376                                         return SetValue (value, out error);
377                                 } catch { //(Exception e) {
378                                         // property_grid.ShowError (e.Message + Environment.NewLine + e.StackTrace);
379                                 }
380                         }
381                         return false;
382                 }
383
384                 private UITypeEditor GetEditor ()
385                 {
386                         if (PropertyDescriptor != null) {
387                                 try { // can happen, because we are missing some editors
388                                         return PropertyDescriptor.GetEditor (typeof (UITypeEditor)) as UITypeEditor;
389                                 } catch {
390                                         // property_grid.ShowError ("Unable to load UITypeEditor for property '" + PropertyDescriptor.Name + "'.");
391                                 }
392                         }
393                         return null;
394                 }
395
396                 public bool ToggleValue ()
397                 {
398                         if (IsReadOnly || (IsMerged && !HasMergedValue))
399                                 return false;
400
401                         bool success = false;
402                         string error = null;
403                         object value = this.Value;
404                         if (PropertyDescriptor.PropertyType == typeof(bool))
405                                 success = SetValue (!(bool)value, out error);
406                         else if (PropertyDescriptor.Converter != null && 
407                                  PropertyDescriptor.Converter.GetStandardValuesSupported ()) {
408                                 TypeConverter.StandardValuesCollection values = 
409                                         (TypeConverter.StandardValuesCollection) PropertyDescriptor.Converter.GetStandardValues();
410                                 if (values != null) {
411                                         for (int i = 0; i < values.Count; i++) {
412                                                 if (value != null && value.Equals (values[i])){
413                                                         if (i < values.Count-1)
414                                                                 success = SetValue (values[i+1], out error);
415                                                         else
416                                                                 success = SetValue (values[0], out error);
417                                                         break;
418                                                 }
419                                         }
420                                 }
421                         }
422                         if (!success && error != null)
423                                 property_grid.ShowError (error);
424                         return success;
425                 }
426
427                 public bool SetValue (object value, out string error)
428                 {
429                         error = null;
430                         if (this.IsReadOnly)
431                                 return false;
432
433                         if (SetValueCore (value, out error)) {
434                                 property_grid.OnPropertyValueChangedInternal (this, this.Value);
435                                 return true;
436                         }
437                         return false;
438                 }
439
440                 protected virtual bool SetValueCore (object value, out string error)
441                 {
442                         error = null;
443
444                         TypeConverter converter = PropertyDescriptor.Converter;
445                         Type valueType = value != null ? value.GetType () : null;
446                         // if the new value is not of the same type try to convert it
447                         if (valueType != null && this.PropertyDescriptor.PropertyType != null &&
448                             !this.PropertyDescriptor.PropertyType.IsAssignableFrom (valueType)) {
449                                 bool conversionError = false;
450                                 try {
451                                         if (converter != null &&
452                                             converter.CanConvertFrom (valueType))
453                                                 value = converter.ConvertFrom (value);
454                                         else
455                                                 conversionError = true;
456                                 } catch {
457                                         conversionError = true;
458                                 }
459                                 if (conversionError) {
460                                         string valueText = ConvertToString (value);
461                                         if (valueText != null) {
462                                                 error = "Property value '" + valueText + "' of '" +
463                                                         PropertyDescriptor.Name + "' is not convertible to type '" +
464                                                         this.PropertyDescriptor.PropertyType.Name + "'";
465
466                                         } else {
467                                                 error = "Property value of '" +
468                                                         PropertyDescriptor.Name + "' is not convertible to type '" +
469                                                         this.PropertyDescriptor.PropertyType.Name + "'";
470                                         }
471                                         return false;
472                                 }
473                         }
474
475                         bool changed = false;
476                         bool current_changed = false;
477                         object[] propertyOwners = this.PropertyOwners;
478                         PropertyDescriptor[] properties = PropertyDescriptors;
479                         for (int i=0; i < propertyOwners.Length; i++) {
480                                 object currentVal = properties[i].GetValue (propertyOwners[i]);
481                                 current_changed = false;
482                                 if (!Object.Equals (currentVal, value)) {
483                                         if (this.ShouldCreateParentInstance) {
484                                                 Hashtable updatedParentProperties = new Hashtable ();
485                                                 PropertyDescriptorCollection parentProperties = TypeDescriptor.GetProperties (propertyOwners[i]);
486                                                 foreach (PropertyDescriptor property in parentProperties) {
487                                                         if (property.Name == properties[i].Name)
488                                                                 updatedParentProperties[property.Name] = value;
489                                                         else
490                                                                 updatedParentProperties[property.Name] = property.GetValue (propertyOwners[i]);
491                                                 }
492                                                 object updatedParentValue = this.ParentEntry.PropertyDescriptor.Converter.CreateInstance (updatedParentProperties);
493                                                 if (updatedParentValue != null)
494                                                         current_changed = this.ParentEntry.SetValueCore (updatedParentValue, out error);
495                                         } else {
496                                                 try {
497                                                         properties[i].SetValue (propertyOwners[i], value);
498                                                 } catch {
499                                                         // MS seems to swallow this
500                                                         //
501                                                         // string valueText = ConvertToString (value);
502                                                         // if (valueText != null)
503                                                         //      error = "Property value '" + valueText + "' of '" + properties[i].Name + "' is invalid.";
504                                                         // else
505                                                         //      error = "Property value of '" + properties[i].Name + "' is invalid.";
506                                                         return false;
507                                                 }
508
509                                                 if (IsValueType (this.ParentEntry)) 
510                                                         current_changed = ParentEntry.SetValueCore (propertyOwners[i], out error);
511                                                 else
512                                                         current_changed = Object.Equals (properties[i].GetValue (propertyOwners[i]), value);
513                                         }
514                                         // restore original value if doesn't get set
515                                         if (!current_changed && !PropertyDescriptor.IsReadOnly)
516                                                 properties[i].SetValue (propertyOwners[i], currentVal);
517                                 }
518                                 if (current_changed)
519                                         changed = true;
520                         }
521                         return changed;
522                 }
523
524                 private bool IsValueType (GridEntry item)
525                 {
526                         if (item != null && item.PropertyDescriptor != null && 
527                             (item.PropertyDescriptor.PropertyType.IsValueType ||
528                              item.PropertyDescriptor.PropertyType.IsPrimitive))
529                                 return true;
530                         return false;
531                 }
532
533                 public bool ResetValue ()
534                 {
535                         if (IsResetable) {
536                                 object[] owners = this.PropertyOwners;
537                                 PropertyDescriptor[] properties = PropertyDescriptors;
538                                 for (int i=0; i < owners.Length; i++) {
539                                         properties[i].ResetValue (owners[i]);
540                                         if (IsValueType (this.ParentEntry)) {
541                                                 string error = null;
542                                                 if (!ParentEntry.SetValueCore (owners[i], out error) && error != null)
543                                                         property_grid.ShowError (error);
544                                         }
545                                 }
546                                 property_grid.OnPropertyValueChangedInternal (this, this.Value);
547                                 return true;
548                         }
549                         return false;
550                 }
551
552                 public bool HasDefaultValue {
553                         get {
554                                 if (PropertyDescriptor != null && 
555                                     PropertyDescriptor.Attributes[typeof (DefaultValueAttribute)] != null)
556                                     return true;
557                                 return false;
558                         }
559                 }
560
561                 // Determines if the current value can be reset
562                 //
563                 public virtual bool IsResetable {
564                         get { return (!IsReadOnly && PropertyDescriptor.CanResetValue (PropertyOwner)); }
565
566                 }
567
568                 // If false the entry can be modified only by the means of a predefined values
569                 // and not such inputed by the user.
570                 //
571                 public virtual bool IsEditable {
572                         get {
573                                 if (PropertyDescriptor == null)
574                                         return false;
575                                 else if (PropertyDescriptor.PropertyType.IsArray)
576                                         return false;
577                                 else if (PropertyDescriptor.IsReadOnly && this.ShouldCreateParentInstance)
578                                         return true;
579                                 else if (PropertyDescriptor.Converter == null || 
580                                          !PropertyDescriptor.Converter.CanConvertFrom (this, typeof (string)))
581                                         return false;
582                                 else if (PropertyDescriptor.Converter.GetStandardValuesSupported () &&
583                                          PropertyDescriptor.Converter.GetStandardValuesExclusive ())
584                                         return false;
585                                 else
586                                         return true;
587                         }
588                 }
589
590                 // If true the the entry cannot be modified at all
591                 //
592                 public virtual bool IsReadOnly {
593                         get {
594                                 // if (PropertyDescriptor != null) {
595                                 //      Console.WriteLine ("=== [" + PropertyDescriptor.Name + "]");
596                                 //      Console.WriteLine ("PropertyDescriptor.IsReadOnly: " + PropertyDescriptor.IsReadOnly);
597                                 //      Console.WriteLine ("Editor: " + (GetEditor () == null ? "none" : GetEditor ().GetType ().Name));
598                                 //      Console.WriteLine ("Converter: " + (PropertyDescriptor.Converter == null ? "none" : PropertyDescriptor.Converter.GetType ().Name));
599                                 //      Console.WriteLine ("Converter.GetStandardValuesSupported: " + PropertyDescriptor.Converter.GetStandardValuesSupported ().ToString ());
600                                 //      Console.WriteLine ("Converter.GetStandardValuesExclusive: " + PropertyDescriptor.Converter.GetStandardValuesExclusive ().ToString ());
601                                 //      Console.WriteLine ("ShouldCreateParentInstance: " + this.ShouldCreateParentInstance);
602                                 //      Console.WriteLine ("CanConvertFrom (string): " + PropertyDescriptor.Converter.CanConvertFrom ((ITypeDescriptorContext)this, typeof (string)));
603                                 //      Console.WriteLine ("IsArray: " + PropertyDescriptor.PropertyType.IsArray.ToString ());
604                                 // }
605                                 if (ParentEntry != null && ParentEntry.GridItemType == GridItemType.Property 
606                                     && ParentEntry.IsReadOnly)
607                                         return true;
608                                 else if (PropertyDescriptor == null || PropertyOwner == null ||
609                                     (PropertyDescriptor.IsReadOnly && !this.ShouldCreateParentInstance))
610                                         return true;
611                                 else if (!HasCustomEditor && PropertyDescriptor.Converter == null)
612                                         return true;
613                                 else if (PropertyDescriptor.Converter != null &&
614                                          !PropertyDescriptor.Converter.GetStandardValuesSupported () &&
615                                          !PropertyDescriptor.Converter.CanConvertFrom ((ITypeDescriptorContext)this,
616                                                                                        typeof (string)) &&
617                                          !HasCustomEditor) {
618                                         return true;
619                                 } else if (PropertyDescriptor.PropertyType.IsArray && !HasCustomEditor)
620                                         return true;
621                                 else
622                                         return false;
623                         }
624                 }
625
626                 public bool IsPassword {
627                         get {
628 #if NET_2_0
629                                 if (PropertyDescriptor != null)
630                                         return PropertyDescriptor.Attributes.Contains (PasswordPropertyTextAttribute.Yes);
631 #endif
632                                 return false;
633                         }
634                 }
635                 // This is a way to set readonly properties (e.g properties without a setter).
636                 // The way it works is that if CreateInstance is supported by the parent's converter  
637                 // it gets passed a list of properties and their values which it uses to create an 
638                 // instance (e.g by passing them to the ctor of that object type).
639                 // 
640                 // This is used for e.g Font
641                 //
642                 public virtual bool ShouldCreateParentInstance {
643                         get {
644                                 if (this.ParentEntry != null && ParentEntry.PropertyDescriptor != null) {
645                                         TypeConverter parentConverter = Parent.PropertyDescriptor.Converter;
646                                         if (parentConverter != null && parentConverter.GetCreateInstanceSupported ((ITypeDescriptorContext)this))
647                                                 return true;
648                                 }
649                                 return false;
650                         }
651                 }
652
653                 public virtual bool PaintValueSupported {
654                         get {
655                                 UITypeEditor editor = GetEditor ();
656                                 if (editor != null) {
657                                         try {
658                                                 return editor.GetPaintValueSupported ();
659                                         } catch {
660                                                 // Some of our Editors throw NotImplementedException
661                                         }
662                                 }
663                                 return false;
664                         }
665                 }
666
667                 public virtual void PaintValue (Graphics gfx, Rectangle rect)
668                 {
669                         UITypeEditor editor = GetEditor ();
670                         if (editor != null) {
671                                 try {
672                                         editor.PaintValue (this.Value, gfx, rect);
673                                 } catch {
674                                         // Some of our Editors throw NotImplementedException
675                                 }
676                         }
677                 }
678
679         }
680 }