* TreeNode.cs: Add the 2.0 collapse method.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / PropertyGridView.cs
1 #define DOUBLEBUFFER
2
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 // 
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 //
22 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 //
24 // Authors:
25 //      Jonathan Chambers       (jonathan.chambers@ansys.com)
26 //
27 //
28
29 // NOT COMPLETE
30
31 using System;
32 using System.Collections;
33 using System.ComponentModel.Design;
34 using System.Drawing;
35 using System.Drawing.Design;
36 using System.ComponentModel;
37 using System.Threading;
38 using System.Windows.Forms.Design;
39
40 namespace System.Windows.Forms.PropertyGridInternal {
41         internal class PropertyGridView : ScrollableControl, IWindowsFormsEditorService {
42
43                 #region Private Members
44                 private double splitter_percent = .5;
45                 private const int V_INDENT = 16;
46                 private int row_height;
47                 private int font_height_padding = 3;
48                 private const int RESIZE_WIDTH = 3;
49                 private const int BUTTON_WIDTH = 25;
50                 private PropertyGridTextBox grid_textbox;
51                 internal PropertyGrid property_grid;
52                 private bool resizing_grid;
53                 private int open_grid_item_count = -1;
54                 private int skipped_grid_items;
55                 private PropertyGridDropDown dropdown_form;
56                 private Form dialog_form;
57                 private ImplicitVScrollBar vbar;
58                 private StringFormat string_format;
59                 private Font bold_font;
60 #if !DOUBLEBUFFER
61                 private int cached_splitter_location;
62 #endif
63                 #endregion
64
65                 #region Contructors
66                 public PropertyGridView (PropertyGrid propertyGrid) {
67                         property_grid = propertyGrid;
68
69                         property_grid.SelectedGridItemChanged += new SelectedGridItemChangedEventHandler (SelectedGridItemChanged);
70                         property_grid.PropertyValueChanged += new PropertyValueChangedEventHandler (PropertyValueChanged);
71                         property_grid.SelectedObjectsChanged += new EventHandler (SelectedObjectsChanged);
72
73                         string_format = new StringFormat();
74                         string_format.FormatFlags = StringFormatFlags.NoWrap;
75                         string_format.Trimming = StringTrimming.None;
76
77                         grid_textbox = new PropertyGridTextBox();
78                         grid_textbox.DropDownButtonClicked +=new EventHandler(DropDownButtonClicked);
79                         grid_textbox.DialogButtonClicked +=new EventHandler(DialogButtonClicked);
80
81                         dropdown_form = new PropertyGridDropDown();
82                         dropdown_form.FormBorderStyle = FormBorderStyle.None;
83                         dropdown_form.StartPosition = FormStartPosition.Manual;
84                         dropdown_form.ShowInTaskbar = false;
85
86                         dialog_form = new Form ();
87                         dialog_form.StartPosition = FormStartPosition.Manual;
88                         dialog_form.FormBorderStyle = FormBorderStyle.None;
89                         dialog_form.ShowInTaskbar = false;
90
91                         skipped_grid_items = 0;
92                         row_height = Font.Height + font_height_padding;
93
94                         grid_textbox.Visible = false;
95                         grid_textbox.Font = this.Font;
96                         grid_textbox.BackColor = this.BackColor;
97                         // Not working at all, used to??
98                         grid_textbox.Validating += new CancelEventHandler(TextBoxValidating);
99                         grid_textbox.ToggleValue+=new EventHandler(grid_textbox_ToggleValue);
100                         this.Controls.Add(grid_textbox);
101
102                         vbar = new ImplicitVScrollBar();
103                         vbar.Visible = false;
104                         vbar.ValueChanged+=new EventHandler(HandleValueChanged);
105                         vbar.Dock = DockStyle.Right;
106                         this.Controls.AddImplicit(vbar);
107
108                         resizing_grid = false;
109
110                         bold_font = new Font(this.Font, FontStyle.Bold);
111
112                         ForeColorChanged+=new EventHandler(RedrawEvent);
113                         BackColorChanged+=new System.EventHandler(RedrawEvent);
114                         FontChanged+=new EventHandler(RedrawEvent);
115                         
116                         SetStyle(ControlStyles.Selectable, true);
117 #if DOUBLEBUFFER
118                         SetStyle(ControlStyles.DoubleBuffer, true);
119 #endif
120                         SetStyle(ControlStyles.UserPaint, true);
121                         SetStyle(ControlStyles.AllPaintingInWmPaint, true);
122 #if DOUBLEBUFFER
123                         SetStyle(ControlStyles.ResizeRedraw, true);
124 #endif
125                 }
126
127                 #endregion
128
129                 #region Protected Instance Methods
130
131                 protected override void OnFontChanged(EventArgs e) {
132                         base.OnFontChanged (e);
133
134                         bold_font = new Font(this.Font, FontStyle.Bold);
135                         row_height = Font.Height + font_height_padding;
136                 }
137
138                 void InvalidateGridItemLabel (GridItem item)
139                 {
140                         Invalidate (new Rectangle (0, item.Top, SplitterLocation, row_height));
141                 }
142
143 #if !DOUBLEBUFFER
144                 void InvalidateGridItem (GridItem item)
145                 {
146                         Invalidate (new Rectangle (0, item.Top, Width, row_height));
147                 }
148 #endif
149
150                 void CalcHeightOfGridItems (GridItemCollection col, ref int amount)
151                 {
152                         foreach (GridItem i in col) {
153                                 amount += row_height;
154                                 if (i.Expanded)
155                                         CalcHeightOfGridItems (i.GridItems, ref amount);
156                         }
157                 }
158
159                 public void RedrawBelowItemOnExpansion (GridItem item)
160                 {
161                         grid_textbox_Hide ();
162 #if DOUBLEBUFFER
163                         Invalidate(new Rectangle (0, item.Top, Width, Height - item.Top));
164 #else
165                         // ugh, we need to recurse down into all our
166                         // group items to figure out the space to
167                         // scroll..
168                         int amount = 0;
169
170                         CalcHeightOfGridItems (item.GridItems, ref amount);
171
172                         if (item.Expanded) {
173                                 XplatUI.ScrollWindow (Handle,
174                                                       new Rectangle (0, item.Top + row_height,
175                                                                      Width, Height - item.Top - row_height),
176                                                       0, amount, false);
177                         }
178                         else {
179                                 XplatUI.ScrollWindow (Handle,
180                                                       new Rectangle (0, item.Top + row_height,
181                                                                      Width, Height - item.Top - row_height),
182                                                       0, -amount, false);
183                         }
184                         InvalidateGridItem (item);
185                         Update();
186 #endif
187                         grid_textbox_Show (property_grid.SelectedGridItem);
188                 }
189
190                 protected override void OnDoubleClick(EventArgs e) {
191                         if (property_grid.SelectedGridItem.Expandable) {
192                                 property_grid.SelectedGridItem.Expanded = !property_grid.SelectedGridItem.Expanded;
193                         }
194                         else {
195                                 GridItem item = property_grid.SelectedGridItem;
196                                 if (item.GridItemType == GridItemType.Property
197                                     && !item.PropertyDescriptor.IsReadOnly) {
198                                         ToggleValue();
199                                         Invalidate();
200                                 }
201                         }
202                 }
203
204                 protected override void OnPaint(PaintEventArgs e) {
205                         // Decide if we need a scrollbar
206                         open_grid_item_count = 0;
207
208                         // Background
209                         e.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
210                         
211                         int yLoc = -vbar.Value*row_height;
212
213                         if (property_grid.root_grid_item != null)
214                                 DrawGridItems(property_grid.root_grid_item.GridItems, e, 1, ref yLoc);
215
216                         UpdateScrollBar();
217                 }
218
219                 protected override void OnMouseWheel(MouseEventArgs e) {
220                         if (vbar == null || !vbar.Visible) {
221                                 return;
222                         }
223
224                         if (e.Delta < 0) {
225                                 vbar.Value = Math.Min(vbar.Value + SystemInformation.MouseWheelScrollLines, vbar.Maximum);
226                         } else {
227                                 vbar.Value = Math.Max(0, vbar.Value - SystemInformation.MouseWheelScrollLines);
228                         }
229                         base.OnMouseWheel (e);
230                 }
231
232
233                 protected override void OnMouseMove (MouseEventArgs e) {
234                         if (property_grid.root_grid_item == null)
235                                 return;
236
237                         if (resizing_grid) {
238                                 int loc = Math.Max(e.X,2*V_INDENT);
239                                 SplitterPercent = 1.0*loc/Width;
240                         }
241                         if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) 
242                                 this.Cursor = Cursors.SizeWE;
243                         else
244                                 this.Cursor = Cursors.Default;
245                         base.OnMouseMove (e);
246                 }
247
248                 protected override void OnMouseDown (MouseEventArgs e) {
249                         if (property_grid.root_grid_item == null)
250                                 return;
251
252                         if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) {
253                                 resizing_grid = true;
254                         }
255                         else {
256                                 int offset = -vbar.Value*row_height;
257                                 GridItem foundItem = GetSelectedGridItem(property_grid.root_grid_item.GridItems, e.Y, ref offset);
258                                 
259                                 if (foundItem != null) {
260                                         if (foundItem.Expandable) {
261                                                 if (e.X >=3 && e.X <= 11 && (e.Y % row_height >= row_height/2-2 && e.Y % row_height <= row_height/2+4)) {
262                                                         foundItem.Expanded = !foundItem.Expanded;
263                                                 }
264                                         }
265                                         this.property_grid.SelectedGridItem = foundItem;
266                                 }
267                                 
268                                 base.OnMouseDown (e);
269                         }
270                 }
271
272                 protected override void OnMouseUp (MouseEventArgs e) {
273                         resizing_grid = false;
274                         base.OnMouseUp (e);
275                 }
276
277                 protected override void OnResize(EventArgs e) {
278                         SuspendLayout ();
279                         grid_textbox.Hide ();
280                         ResumeLayout (false);
281
282 #if !DOUBLEBUFFER
283                         // we need to explicitly handle this redraw
284                         // before getting to the scrollwindow, or we
285                         // end up having to redraw the entire item
286                         // value area because the exposed areas are
287                         // unioned.
288                         Update ();
289 #endif
290
291                         base.OnResize (e);
292 #if !DOUBLEBUFFER
293                         if (cached_splitter_location != SplitterLocation) {
294                                 int x = cached_splitter_location > SplitterLocation ? SplitterLocation : cached_splitter_location;
295                                 XplatUI.ScrollWindow (Handle,
296                                                       new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height),
297                                                       SplitterLocation - cached_splitter_location, 0, false);
298                                 Update();
299                         }
300                         cached_splitter_location = SplitterLocation;
301 #endif
302                         SuspendLayout ();
303                         grid_textbox_Show (property_grid.SelectedGridItem);
304                         ResumeLayout (false);
305                 }
306
307                 private void UnfocusSelection ()
308                 {
309                         Select (this);
310                 }
311
312                 protected override bool ProcessDialogKey (Keys keyData) {
313                         GridItem selectedItem = property_grid.SelectedGridItem;
314                         if (selectedItem != null
315                             && grid_textbox.Visible
316                             /* if the textbox has focus? */) {
317                                 switch (keyData) {
318                                 case Keys.Enter:
319                                         SetPropertyValue(selectedItem.PropertyDescriptor.Converter.ConvertFromString(grid_textbox.Text));
320                                         UnfocusSelection ();
321                                         return true;
322                                 case Keys.Escape:
323                                         grid_textbox.Text = selectedItem.PropertyDescriptor.Converter.ConvertToString(selectedItem.Value);
324                                         UnfocusSelection ();
325                                         return true;
326                                 default:
327                                         return false;
328                                 }
329                         }
330                         return base.ProcessDialogKey (keyData);
331                 }
332
333
334                 protected override bool IsInputKey (Keys keyData) {
335                         switch (keyData) {
336                         case Keys.Left:
337                         case Keys.Right:
338                         case Keys.Enter:
339                         case Keys.Escape:
340                         case Keys.Up:
341                         case Keys.Down:
342                         case Keys.PageDown:
343                         case Keys.PageUp:
344                         case Keys.Home:
345                         case Keys.End:
346                                 return true;
347                         default:
348                                 return false;
349                         }
350                 }
351
352                 private GridEntry MoveUpFromItem (GridEntry item, int up_count)
353                 {
354                         GridItemCollection items;
355                         int index;
356
357                         /* move back up the visible rows (and up the hierarchy as necessary) until
358                            up_count == 0, or we reach the top of the display */
359                         while (up_count > 0) {
360                                 items = item.Parent != null ? item.Parent.GridItems : property_grid.root_grid_item.GridItems;
361                                 index = items.IndexOf (item);
362
363                                 if (index == 0) {
364                                         if (item.Parent.GridItemType == GridItemType.Root) // we're at the top row
365                                                 return item;
366                                         item = (GridEntry)item.Parent;
367                                         up_count --;
368                                 }
369                                 else {
370                                         GridEntry prev_item = (GridEntry)items[index-1];
371                                         if (prev_item.Expandable && prev_item.Expanded) {
372                                                 item = (GridEntry)prev_item.GridItems[prev_item.GridItems.Count - 1];
373                                         }
374                                         else {
375                                                 item = prev_item;
376                                         }
377                                         up_count --;
378                                 }
379                         }
380                         return item;
381                 }
382
383                 private GridEntry MoveDownFromItem (GridEntry item, int down_count)
384                 {
385                         while (down_count > 0) {
386                                 /* if we're a parent node and we're expanded, move to our first child */
387                                 if (item.Expandable && item.Expanded) {
388                                         item = (GridEntry)item.GridItems[0];
389                                         down_count--;
390                                 }
391                                 else {
392                                         GridItem searchItem = item;
393                                         GridItemCollection searchItems = searchItem.Parent.GridItems;
394                                         int searchIndex = searchItems.IndexOf (searchItem);
395
396                                         while (searchIndex == searchItems.Count - 1) {
397                                                 searchItem = searchItem.Parent;
398
399                                                 if (searchItem == null || searchItem.Parent == null)
400                                                         break;
401
402                                                 searchItems = searchItem.Parent.GridItems;
403                                                 searchIndex = searchItems.IndexOf (searchItem);
404                                         }
405
406                                         if (searchIndex == searchItems.Count - 1) {
407                                                 /* if we got all the way back to the root with no nodes after
408                                                    us, the original item was the last one */
409                                                 return item;
410                                         }
411                                         else {
412                                                 item = (GridEntry)searchItems[searchIndex+1];
413                                                 down_count--;
414                                         }
415                                 }
416                         }
417
418                         return item;
419                 }
420
421                 protected override void OnKeyDown(KeyEventArgs e) {
422                         GridEntry selectedItem = (GridEntry)property_grid.SelectedGridItem;
423
424                         if (selectedItem == null) {
425                                 /* XXX not sure what MS does, but at least we shouldn't crash */
426                                 base.OnKeyDown (e);
427                                 return;
428                         }
429
430                         switch (e.KeyData & Keys.KeyCode) {
431                         case Keys.Left:
432                                 if (e.Control) {
433                                         if (SplitterLocation > 2 * V_INDENT)
434                                                 SplitterPercent -= 0.01;
435
436                                         e.Handled = true;
437                                         break;
438                                 }
439                                 else {
440                                         /* if the node is expandable and is expanded, collapse it.
441                                            otherwise, act just like the user pressed up */
442                                         if (selectedItem.Expandable && selectedItem.Expanded) {
443                                                 selectedItem.Expanded = false;
444                                                 e.Handled = true;
445                                                 break;
446                                         }
447                                         else
448                                                 goto case Keys.Up;
449                                 }
450                         case Keys.Right:
451                                 if (e.Control) {
452                                         if (SplitterLocation < Width)
453                                                 SplitterPercent += 0.01;
454
455                                         e.Handled = true;
456                                         break;
457                                 }
458                                 else {
459                                         /* if the node is expandable and not expanded, expand it.
460                                            otherwise, act just like the user pressed down */
461                                         if (selectedItem.Expandable && !selectedItem.Expanded) {
462                                                 selectedItem.Expanded = true;
463                                                 e.Handled = true;
464                                                 break;
465                                         }
466                                         else
467                                                 goto case Keys.Down;
468                                 }
469                         case Keys.Enter:
470                                 /* toggle the expanded state of the selected item */
471                                 if (selectedItem.Expandable) {
472                                         selectedItem.Expanded = !selectedItem.Expanded;
473                                 }
474                                 e.Handled = true;
475                                 break;
476                         case Keys.Up:
477                                 property_grid.SelectedGridItem = MoveUpFromItem (selectedItem, 1);
478                                 e.Handled = true;
479                                 break;
480                         case Keys.Down:
481                                 property_grid.SelectedGridItem = MoveDownFromItem (selectedItem, 1);
482                                 e.Handled = true;
483                                 break;
484                         case Keys.PageUp:
485                                 property_grid.SelectedGridItem = MoveUpFromItem (selectedItem, vbar.LargeChange);
486                                 e.Handled = true;
487                                 break;
488                         case Keys.PageDown:
489                                 property_grid.SelectedGridItem = MoveDownFromItem (selectedItem, vbar.LargeChange);
490                                 e.Handled = true;
491                                 break;
492                         case Keys.End:
493                                 /* find the last, most deeply nested visible item */
494                                 GridEntry item = (GridEntry)property_grid.root_grid_item.GridItems[property_grid.root_grid_item.GridItems.Count - 1];
495                                 while (item.Expandable && item.Expanded)
496                                         item = (GridEntry)item.GridItems[item.GridItems.Count - 1];
497                                 property_grid.SelectedGridItem = item;
498                                 e.Handled = true;
499                                 break;
500                         case Keys.Home:
501                                 property_grid.SelectedGridItem = property_grid.root_grid_item.GridItems[0];
502                                 e.Handled = true;
503                                 break;
504                         }
505
506                         base.OnKeyDown (e);
507                 }
508
509                 #endregion
510
511                 #region Private Helper Methods
512
513                 private int SplitterLocation{
514                         get {
515                                 return (int)(splitter_percent*Width);
516                         }
517                 }
518
519                 private double SplitterPercent{
520                         set {
521                                 int old_splitter_location = SplitterLocation;
522                                 
523                                 splitter_percent = Math.Max(Math.Min(value, .9),.1);
524
525                                 if (old_splitter_location != SplitterLocation) {
526                                         grid_textbox_Hide ();
527                                         int x = old_splitter_location > SplitterLocation ? SplitterLocation : old_splitter_location;
528 #if DOUBLEBUFFER
529                                         Invalidate(new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height));
530 #else
531                                         XplatUI.ScrollWindow (Handle,
532                                                               new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height),
533                                                               SplitterLocation - old_splitter_location, 0, false);
534                                         Update ();
535 #endif
536                                         grid_textbox_Show (property_grid.SelectedGridItem);
537                                 }
538
539 #if !DOUBLEBUFFER
540                                 cached_splitter_location = SplitterLocation;
541 #endif
542                         }
543                         get {
544                                 return splitter_percent;
545                         }
546                 }
547
548                 private GridItem GetSelectedGridItem (GridItemCollection grid_items, int y, ref int current) {
549                         foreach (GridItem child_grid_item in grid_items) {
550                                 if (y > current && y < current + row_height) {
551                                         return child_grid_item;
552                                 }
553                                 current += row_height;
554                                 if (child_grid_item.Expanded) {
555                                         GridItem foundItem = GetSelectedGridItem(child_grid_item.GridItems, y, ref current);
556                                         if (foundItem != null)
557                                                 return foundItem;
558                                 }
559                         }
560                         return null;
561                 }
562
563                 private void UpdateScrollBar() {
564                         int visible_rows = this.ClientRectangle.Height/row_height;
565                         if (open_grid_item_count > visible_rows) {
566                                 vbar.Visible = true;
567                                 vbar.SmallChange = 1;
568                                 vbar.Minimum = 0;
569                                 vbar.Maximum = open_grid_item_count-1;
570                                 vbar.LargeChange = visible_rows;
571                         }
572                         else {
573                                 vbar.Visible = false;
574                         }
575
576                 }
577
578                 #region Drawing Code
579
580                 private void DrawGridItems(GridItemCollection grid_items, PaintEventArgs pevent, int depth, ref int yLoc) {
581                         foreach (GridItem grid_item in grid_items) {
582                                 DrawGridItem (grid_item, pevent, depth, ref yLoc);
583                                 if (grid_item.Expanded)
584                                         DrawGridItems(grid_item.GridItems, pevent, (grid_item.GridItemType == GridItemType.Category) ? depth : depth+1, ref yLoc);
585                         }
586                 }
587
588                 private void DrawGridItemLabel(GridItem grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
589                         Font font = this.Font;
590                         Brush brush;
591
592                         if (grid_item.GridItemType == GridItemType.Category) {
593                                 font = bold_font;
594                                 brush = SystemBrushes.ControlDark;
595
596                                 pevent.Graphics.DrawString (grid_item.Label, font, brush, rect.X + 1, rect.Y + 2);
597                                 if (grid_item == property_grid.SelectedGridItem) {
598                                         SizeF size = pevent.Graphics.MeasureString (grid_item.Label, font);
599                                         ControlPaint.DrawFocusRectangle (pevent.Graphics, new Rectangle(rect.X + 1, rect.Y+2, (int)size.Width, (int)size.Height));
600                                 }
601                         }
602                         else {
603                                 if (grid_item == property_grid.SelectedGridItem) {
604                                         Rectangle highlight = rect;
605                                         if (depth > 1) {
606                                                 highlight.X -= V_INDENT;
607                                                 highlight.Width += V_INDENT;
608                                         }
609                                         pevent.Graphics.FillRectangle (SystemBrushes.Highlight, highlight);
610                                         // Label
611                                         brush = SystemBrushes.HighlightText;
612                                 }
613                                 else {
614                                         brush = SystemBrushes.WindowText;
615                                         if (grid_item.PropertyDescriptor.IsReadOnly
616                                             && !grid_item.Expandable)
617                                                 brush = SystemBrushes.InactiveCaption;
618                                 }
619                         }
620
621                         pevent.Graphics.DrawString (grid_item.Label, font, brush,
622                                                     new Rectangle (rect.X + 1, rect.Y + 2, rect.Width - 2, rect.Height - 2),
623                                                     string_format);
624                 }
625
626                 private void DrawGridItemValue(GridItem grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
627                         // Value
628                         if (grid_item.PropertyDescriptor != null) {
629
630                                 bool paintsValue = false;
631                                 UITypeEditor editor = (UITypeEditor)grid_item.PropertyDescriptor.GetEditor(typeof(UITypeEditor));
632                                 if (editor != null) {
633                                         paintsValue = editor.GetPaintValueSupported();
634                                 }
635
636                                 int xLoc = SplitterLocation+1;
637                                 if (paintsValue) {
638                                         pevent.Graphics.DrawRectangle(Pens.Black, SplitterLocation+2,rect.Y+2, 20, row_height-4);
639                                         try {
640                                                 editor.PaintValue(grid_item.Value, pevent.Graphics, new Rectangle(SplitterLocation+3,rect.Y+3, 19, row_height-5));
641                                         }
642                                         catch (Exception) {
643                                         }
644                                         xLoc += 27;
645                                 }
646
647                                 try {
648                                         if (grid_item.PropertyDescriptor.Converter != null) {
649                                                 Font font = this.Font;
650                                                 Brush brush;
651
652                                                 string value = grid_item.PropertyDescriptor.Converter.ConvertToString(grid_item.Value);
653                                                 if (((GridEntry)grid_item).CanResetValue ())
654                                                         font = bold_font;
655
656                                                 brush = SystemBrushes.WindowText;
657                                                 if (grid_item.PropertyDescriptor.IsReadOnly
658                                                     && !grid_item.Expandable)
659                                                         brush = SystemBrushes.InactiveCaption;
660
661                                                 pevent.Graphics.DrawString(value, font,
662                                                                            brush,
663                                                                            new RectangleF(xLoc, rect.Y+2,
664                                                                                           ClientRectangle.Width-(xLoc), row_height),string_format);
665                                         }
666                                         else {
667                                                 Console.WriteLine("No converter for type {0}",grid_item.PropertyDescriptor.PropertyType);
668                                         }
669
670                                 }
671                                 catch (Exception) {
672                                 }
673                         }
674                 }
675
676                 private void DrawGridItem (GridItem grid_item, PaintEventArgs pevent, int depth, ref int yLoc) {
677                         if (yLoc > -row_height && yLoc < ClientRectangle.Height) {
678
679                                 // Left column
680                                 pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor),
681                                                                0, yLoc, V_INDENT, row_height);
682                         
683                                 if (grid_item.Expandable) {
684                                         grid_item.PlusMinusBounds = DrawPlusMinus(pevent.Graphics, 3, yLoc+row_height/2-3, grid_item.Expanded, grid_item.GridItemType == GridItemType.Category);
685                                 }
686                         
687                                 if (grid_item.GridItemType == GridItemType.Category) {
688                                         pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor), depth*V_INDENT,yLoc,ClientRectangle.Width-(depth*V_INDENT), row_height);
689                                 }
690
691                                 DrawGridItemLabel(grid_item, pevent,
692                                                   depth,
693                                                   new Rectangle(depth * V_INDENT, yLoc, SplitterLocation - depth * V_INDENT, row_height));
694                                 DrawGridItemValue(grid_item, pevent,
695                                                   depth,
696                                                   new Rectangle(SplitterLocation + 2 , yLoc, ClientRectangle.Width - SplitterLocation - 2 - (vbar.Visible ? vbar.Width : 0), row_height));
697
698                                 if (grid_item.GridItemType != GridItemType.Category) {
699                                         Pen pen = ThemeEngine.Current.ResPool.GetPen(property_grid.LineColor);
700                                         // vertical divider line
701                                         pevent.Graphics.DrawLine(pen, SplitterLocation, yLoc, SplitterLocation, yLoc + row_height);
702                         
703                                         // draw the horizontal line
704                                         pevent.Graphics.DrawLine(pen, 0, yLoc + row_height, ClientRectangle.Width, yLoc + row_height);
705                                 }
706                         }
707                         grid_item.Top = yLoc;
708                         yLoc += row_height;
709                         open_grid_item_count++;
710                 }
711
712                 private Rectangle DrawPlusMinus (Graphics g, int x, int y, bool expanded, bool category) {
713                         Rectangle bounds = new Rectangle(x, y, 8, 8);
714                         if (!category) g.FillRectangle (Brushes.White, bounds);
715                         Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.ViewForeColor);
716                         g.DrawRectangle (pen, bounds);
717                         g.DrawLine (pen, x+2, y+4, x + 6, y+4);
718                         if (!expanded)
719                                 g.DrawLine (pen, x+4, y+2, x+4, y+6);
720
721                         return bounds;
722                 }
723
724                 #endregion
725
726                 #region Event Handling
727                 private void RedrawEvent (object sender, System.EventArgs e) {
728                         Refresh();
729                 }
730
731                 private void TextBoxValidating (object sender, CancelEventArgs e) {
732                         if (this.property_grid.SelectedGridItem != null) {
733                                 PropertyDescriptor desc = property_grid.SelectedGridItem.PropertyDescriptor;
734                                 if (desc != null) {
735                                         try {
736                                                 if (desc.Converter != null) {
737                                                         SetPropertyValue(desc.Converter.ConvertFromString(grid_textbox.Text));
738                                                 }
739                                                 else {
740                                                         Console.WriteLine("No converter for type {0}",desc.PropertyType);
741                                                 }
742                                         }
743                                         catch (Exception) {
744                                                 Console.WriteLine("Error converting string");
745                                         }
746                                 }
747                         }
748                 }
749
750                 #endregion
751
752                 private void ToggleValue() {
753                         if (property_grid.SelectedGridItem.GridItemType == GridItemType.Property) {
754                                 if (property_grid.SelectedGridItem.PropertyDescriptor != null) {
755                                         if (property_grid.SelectedGridItem.PropertyDescriptor.PropertyType == typeof(bool))
756                                                 SetPropertyValue(!(bool)property_grid.SelectedGridItem.Value);
757                                         else if (property_grid.SelectedGridItem.PropertyDescriptor.Converter.GetStandardValuesSupported()){
758                                                 System.ComponentModel.TypeConverter.StandardValuesCollection coll = 
759                                                         (System.ComponentModel.TypeConverter.StandardValuesCollection)property_grid.SelectedGridItem.PropertyDescriptor.Converter.GetStandardValues();
760                                                 for (int i = 0; i < coll.Count; i++) {
761                                                         if (property_grid.SelectedGridItem.Value.Equals(coll[i])){
762                                                                 if (i < coll.Count-1)
763                                                                         SetPropertyValue(coll[i+1]);
764                                                                 else
765                                                                         SetPropertyValue(coll[0]);
766                                                                 break;
767                                                         }
768
769                                                 }
770                                         }
771                                 }
772                         }
773                 }
774
775                 private void listBox_MouseUp(object sender, MouseEventArgs e) {
776                         AcceptListBoxSelection (sender);
777                 }
778
779                 private void listBox_KeyDown(object sender, KeyEventArgs e)
780                 {
781                         switch (e.KeyData & Keys.KeyCode) {
782                         case Keys.Enter:
783                                 AcceptListBoxSelection (sender);
784                                 return;
785                         case Keys.Escape:
786                                 CloseDropDown ();
787                                 return;
788                         }
789                 }
790
791                 void AcceptListBoxSelection (object sender) {
792                         if (this.property_grid.SelectedGridItem != null) {
793                                 PropertyDescriptor desc = property_grid.SelectedGridItem.PropertyDescriptor;
794                                 if (desc != null) {
795                                         SetPropertyValue(((ListBox)sender).SelectedItem);
796                                 }
797                         }
798                         CloseDropDown ();
799                 }
800
801                 private void SetPropertyValue(object newVal) {
802                         if (property_grid.SelectedGridItem == null)
803                                 return;
804
805                         PropertyDescriptor desc = property_grid.SelectedGridItem.PropertyDescriptor;
806                         if (desc == null)
807                                 return;
808
809                         for (int i = 0; i < ((GridEntry)property_grid.SelectedGridItem).SelectedObjects.Length; i ++) {
810                                 object target = property_grid.GetTarget (property_grid.SelectedGridItem, i);
811                                 desc.SetValue(target, newVal);
812                         }
813                 }
814
815                 private void DropDownButtonClicked (object sender, EventArgs e) {
816                         UITypeEditor editor = property_grid.SelectedGridItem.PropertyDescriptor.GetEditor (typeof (UITypeEditor)) as UITypeEditor;
817                         if (editor == null) {
818                                 if (dropdown_form.Visible) {
819                                         CloseDropDown ();
820                                 }
821                                 else {
822                                         TypeConverter converter = property_grid.SelectedGridItem.PropertyDescriptor.Converter;
823                                         ICollection std_values;
824
825                                         if (!converter.GetStandardValuesSupported ())
826                                                 return;
827
828                                         std_values = converter.GetStandardValues();
829                                         if (std_values == null)
830                                                 return;
831
832                                         ListBox listBox = new ListBox();
833                                         listBox.BorderStyle = BorderStyle.FixedSingle;
834                                         int selected_index = 0;
835                                         int i = 0;
836                                         object selected_value = property_grid.SelectedGridItem.Value;
837                                         foreach (object obj in std_values) {
838                                                 listBox.Items.Add(obj);
839                                                 if (selected_value != null && selected_value.Equals(obj))
840                                                         selected_index = i;
841                                                 i++;
842                                         }
843                                         listBox.Height = row_height * Math.Min (listBox.Items.Count, 15);
844                                         listBox.SelectedIndex = selected_index;
845                                         listBox.KeyDown += new KeyEventHandler(listBox_KeyDown);
846                                         listBox.MouseUp+=new MouseEventHandler(listBox_MouseUp);
847
848                                         DropDownControl (listBox);
849                                 }
850                         } else { // use editor
851                                 SetPropertyValueFromUITypeEditor (editor);
852                         }
853                 }
854
855                 void SetPropertyValueFromUITypeEditor (UITypeEditor editor)
856                 {
857                         ServiceContainer service_container = new ServiceContainer ();
858                         service_container.AddService (typeof (IWindowsFormsEditorService), this);
859                         object initial_value = property_grid.SelectedGridItem.Value;
860                         object value = editor.EditValue (
861                                 (ITypeDescriptorContext)property_grid.SelectedGridItem,
862                                 service_container,
863                                 initial_value);
864                         if (!Object.Equals (value, initial_value)) {
865                                 SetPropertyValue (value);
866                         }
867                 }
868                 
869                 private void DialogButtonClicked(object sender, EventArgs e) {
870                         UITypeEditor editor = property_grid.SelectedGridItem.PropertyDescriptor.GetEditor (typeof (UITypeEditor)) as UITypeEditor;
871                         if (editor != null)
872                                 SetPropertyValueFromUITypeEditor (editor);
873                 }
874
875                 private void HandleValueChanged(object sender, EventArgs e) {
876                         if (vbar.Value <= 0) {
877                                 vbar.Value = 0;
878                         }
879                         if (vbar.Value > vbar.Maximum-ClientRectangle.Height/row_height) {
880                                 vbar.Value = vbar.Maximum-ClientRectangle.Height/row_height+1;
881                         }
882
883                         int scroll_amount = (skipped_grid_items-vbar.Value)*row_height;
884
885                         if (scroll_amount == 0)
886                                 return;
887
888                         grid_textbox_Hide ();
889
890                         skipped_grid_items = vbar.Value;
891                         XplatUI.ScrollWindow(Handle, 0, scroll_amount, false);
892 #if DOUBLEBUFFER
893                         Invalidate ();
894 #endif
895                         Update ();
896
897                         if (property_grid.SelectedGridItem != null)
898                                 grid_textbox_Show (property_grid.SelectedGridItem);
899                 }
900
901                 private void grid_textbox_ToggleValue(object sender, EventArgs e) {
902                         if (!property_grid.SelectedGridItem.PropertyDescriptor.IsReadOnly) {
903                                 ToggleValue();
904                                 Invalidate();
905                         }
906                 }
907
908                 internal void grid_textbox_Show (GridItem forItem)
909                 {
910                         if (forItem == null || forItem.PropertyDescriptor == null)
911                                 return;
912
913                         SuspendLayout ();
914
915                         if (((GridEntry)forItem).CanResetValue ())
916                                 grid_textbox.Font = bold_font;
917                         else
918                                 grid_textbox.Font = this.Font;
919
920                         grid_textbox.ReadOnly = false;
921                         grid_textbox.DropDownButtonVisible = false;
922                         grid_textbox.DialogButtonVisible = false;
923
924                         bool paintsValue = false;
925                         UITypeEditor editor = (UITypeEditor)forItem.PropertyDescriptor.GetEditor(typeof(UITypeEditor));
926                         if (editor != null) {
927                                 paintsValue = editor.GetPaintValueSupported();
928                         }
929
930                         if (editor != null) {
931                                 UITypeEditorEditStyle style = editor.GetEditStyle();
932                                         
933                                 switch (style) {
934                                 case UITypeEditorEditStyle.DropDown:
935                                         grid_textbox.DropDownButtonVisible = true;
936                                         break;
937                                 case UITypeEditorEditStyle.Modal:
938                                         grid_textbox.DialogButtonVisible = true;
939                                         break;
940                                 }
941                         }
942                         else {
943                                 try {
944                                         if (forItem.PropertyDescriptor.Converter != null) {
945                                                 if (forItem.PropertyDescriptor.Converter.GetStandardValuesSupported()) {
946                                                         
947                                                         grid_textbox.DropDownButtonVisible = true;
948                                                         grid_textbox.ReadOnly = true;
949                                                 }
950                                         }
951                                         else {
952                                                 Console.WriteLine("Converter not available for type {0}",forItem.PropertyDescriptor.PropertyType);
953                                         }
954                                                 
955                                 }
956                                 catch (Exception) {
957                                 }
958                         }
959                                 
960                         grid_textbox.ReadOnly = grid_textbox.ReadOnly || forItem.PropertyDescriptor.IsReadOnly;
961
962                         int xloc = SplitterLocation + 1 + (paintsValue ? 27 : 0);
963                         grid_textbox.SetBounds (xloc,
964                                                 forItem.Top + 2,
965                                                 ClientRectangle.Width - xloc - (vbar.Visible ? vbar.Width : 0),
966                                                 row_height - 2);
967                         grid_textbox.Visible = true;
968
969                         ResumeLayout (false);
970                 }
971
972                 private void grid_textbox_Hide ()
973                 {
974                         SuspendLayout ();
975                         grid_textbox.Bounds = Rectangle.Empty;
976                         grid_textbox.Visible = false;
977                         ResumeLayout (false);
978                 }
979
980                 void EnsureItemIsVisible (GridItem item)
981                 {
982                         if (item.Top < 0) {
983                                 // the new item is above the viewable area
984                                 vbar.Value += item.Top / row_height;
985                         }
986                         else if (item.Top + row_height > Height) {
987                                 // the new item is below the viewable area
988                                 vbar.Value += ((item.Top + row_height) - Height) / row_height + 1;
989                         }
990                         else {
991                                 // do nothing
992                         }
993                 }
994
995
996                 private void SelectedObjectsChanged (object sender, EventArgs args) {
997                         grid_textbox_Hide ();
998                 }
999
1000                 private void SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e) {
1001                         if (e.OldSelection != null)
1002                                 InvalidateGridItemLabel (e.OldSelection);
1003                         InvalidateGridItemLabel (e.NewSelection);
1004
1005                         EnsureItemIsVisible (e.NewSelection);
1006
1007                         if (e.NewSelection.GridItemType == GridItemType.Property) {
1008                                 if (e.NewSelection.PropertyDescriptor != null) {
1009
1010                                         if (e.NewSelection.Value == null)
1011                                                 grid_textbox.Text = "";
1012                                         else
1013                                                 grid_textbox.Text = e.NewSelection.PropertyDescriptor.Converter.ConvertToString(e.NewSelection.Value);
1014                                         if (((GridEntry)e.NewSelection).CanResetValue ())
1015                                                 grid_textbox.Font = bold_font;
1016                                         else
1017                                                 grid_textbox.Font = this.Font;
1018
1019                                         grid_textbox_Show (e.NewSelection);
1020                                 }
1021                         }
1022                         else {
1023                                 grid_textbox_Hide ();
1024                         }
1025                 }
1026
1027                 private void PropertyValueChanged(object s, PropertyValueChangedEventArgs e) {
1028                         if (e.ChangedItem.PropertyDescriptor != null) {
1029                                 grid_textbox.Text = e.ChangedItem.PropertyDescriptor.Converter.ConvertToString(e.ChangedItem.Value);
1030                                 if (((GridEntry)e.ChangedItem).CanResetValue())
1031                                         grid_textbox.Font = bold_font;
1032                                 else
1033                                         grid_textbox.Font = this.Font;
1034                         }
1035                 }
1036
1037                 private void DropDownControl(Control control, bool block) {
1038                         Object  queue_id;
1039
1040                         dropdown_form.Size = control.Size;
1041                         control.Dock = DockStyle.Fill;
1042                         dropdown_form.Controls.Clear();
1043                         dropdown_form.Controls.Add(control);
1044                         dropdown_form.Location = PointToScreen(new Point(SplitterLocation,grid_textbox.Location.Y+row_height));
1045                         dropdown_form.Width = ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0);
1046
1047                         FindForm ().AddOwnedForm (dropdown_form);
1048
1049                         dropdown_form.Show();
1050
1051                         if (block) {
1052                                 System.Windows.Forms.MSG msg = new MSG();
1053                                 queue_id = XplatUI.StartLoop(Thread.CurrentThread);
1054                                 while (dropdown_form.Visible && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
1055
1056                                         if ((msg.message == Msg.WM_NCLBUTTONDOWN ||
1057                                              msg.message == Msg.WM_NCMBUTTONDOWN ||
1058                                              msg.message == Msg.WM_NCRBUTTONDOWN ||
1059                                              msg.message == Msg.WM_LBUTTONDOWN ||
1060                                              msg.message == Msg.WM_MBUTTONDOWN ||
1061                                              msg.message == Msg.WM_RBUTTONDOWN)
1062                                             && !HwndInControl (dropdown_form, msg.hwnd)) {
1063                                                 CloseDropDown ();
1064                                                 break;
1065                                         }
1066                                         XplatUI.TranslateMessage(ref msg);
1067                                         XplatUI.DispatchMessage(ref msg);
1068                                 }
1069                         }
1070                 }
1071
1072                 private bool HwndInControl (Control c, IntPtr hwnd)
1073                 {
1074                         if (hwnd == c.window.Handle)
1075                                 return true;
1076                         foreach (Control cc in c.Controls.GetAllControls ())
1077                                 if (HwndInControl (cc, hwnd))
1078                                         return true;
1079                         return false;
1080                 }
1081
1082                 #endregion
1083
1084                 #region IWindowsFormsEditorService Members
1085
1086                 public void CloseDropDown() {
1087                         Control c = dropdown_form.Controls[0];
1088                         c.Capture = false;
1089                         dropdown_form.Hide();
1090                 }
1091
1092                 public void DropDownControl(Control control) {
1093                         DropDownControl (control, true);
1094                 }
1095
1096                 public System.Windows.Forms.DialogResult ShowDialog(Form dialog) {
1097                         return dialog.ShowDialog(this);
1098                 }
1099
1100                 #endregion
1101
1102                 /*
1103                         class ComboListBox
1104                 */
1105                 internal class PropertyGridDropDown : Form {
1106                         protected override CreateParams CreateParams {
1107                                 get {
1108                                         CreateParams cp = base.CreateParams;
1109                                         cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
1110                                         cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);                            
1111                                         return cp;
1112                                 }
1113                         }
1114
1115                 }
1116         }
1117 }