6966ef0a43536b82ca2e63e56be803fd9546388b
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / PropertyGridView.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) 2005-2008 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Jonathan Chambers       (jonathan.chambers@ansys.com)
24 //      Ivan N. Zlatev          (contact@i-nz.net)
25 //
26 //
27
28 // NOT COMPLETE
29
30 using System;
31 using System.Collections;
32 using System.ComponentModel.Design;
33 using System.Drawing;
34 using System.Drawing.Design;
35 using System.ComponentModel;
36 using System.Threading;
37 using System.Windows.Forms.Design;
38
39 namespace System.Windows.Forms.PropertyGridInternal {
40         internal class PropertyGridView : ScrollableControl, IWindowsFormsEditorService {
41
42                 #region Private Members
43                 private const int V_INDENT = 16;
44                 private const int ENTRY_SPACING = 2;
45                 private const int RESIZE_WIDTH = 3;
46                 private const int BUTTON_WIDTH = 25;
47                 private const int VALUE_PAINT_WIDTH = 19;
48                 private const int VALUE_PAINT_INDENT = 27;
49                 private double splitter_percent = .5;
50                 private int row_height;
51                 private int font_height_padding = 3;
52                 private PropertyGridTextBox grid_textbox;
53                 private PropertyGrid property_grid;
54                 private bool resizing_grid;
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                 private Brush inactive_text_brush;
61                 #endregion
62
63                 #region Contructors
64                 public PropertyGridView (PropertyGrid propertyGrid) {
65                         property_grid = propertyGrid;
66
67                         string_format = new StringFormat ();
68                         string_format.FormatFlags = StringFormatFlags.NoWrap;
69                         string_format.Trimming = StringTrimming.None;
70
71                         grid_textbox = new PropertyGridTextBox ();
72                         grid_textbox.DropDownButtonClicked +=new EventHandler (DropDownButtonClicked);
73                         grid_textbox.DialogButtonClicked +=new EventHandler (DialogButtonClicked);
74
75                         dropdown_form = new PropertyGridDropDown ();
76                         dropdown_form.FormBorderStyle = FormBorderStyle.None;
77                         dropdown_form.StartPosition = FormStartPosition.Manual;
78                         dropdown_form.ShowInTaskbar = false;
79
80                         dialog_form = new Form ();
81                         dialog_form.StartPosition = FormStartPosition.Manual;
82                         dialog_form.FormBorderStyle = FormBorderStyle.None;
83                         dialog_form.ShowInTaskbar = false;
84
85                         row_height = Font.Height + font_height_padding;
86
87                         grid_textbox.Visible = false;
88                         grid_textbox.Font = this.Font;
89                         grid_textbox.BackColor = SystemColors.Window;
90                         // Not working at all, used to??
91                         // grid_textbox.Validating += new CancelEventHandler (TextBoxValidating);
92                         grid_textbox.ToggleValue+=new EventHandler (grid_textbox_ToggleValue);
93                         this.Controls.Add (grid_textbox);
94
95                         vbar = new ImplicitVScrollBar ();
96                         vbar.Visible = false;
97                         vbar.Value = 0;
98                         vbar.ValueChanged+=new EventHandler (VScrollBar_HandleValueChanged);
99                         vbar.Dock = DockStyle.Right;
100                         this.Controls.AddImplicit (vbar);
101
102                         resizing_grid = false;
103
104                         bold_font = new Font (this.Font, FontStyle.Bold);
105                         inactive_text_brush = new SolidBrush (ThemeEngine.Current.ColorGrayText);
106
107                         ForeColorChanged+=new EventHandler (RedrawEvent);
108                         BackColorChanged+=new System.EventHandler (RedrawEvent);
109                         FontChanged+=new EventHandler (RedrawEvent);
110                         
111                         SetStyle (ControlStyles.Selectable, true);
112                         SetStyle (ControlStyles.DoubleBuffer, true);
113                         SetStyle (ControlStyles.UserPaint, true);
114                         SetStyle (ControlStyles.AllPaintingInWmPaint, true);
115                         SetStyle (ControlStyles.ResizeRedraw, true);
116                 }
117
118                 #endregion
119
120                 #region Protected Instance Methods
121
122                 protected override void OnFontChanged (EventArgs e) {
123                         base.OnFontChanged (e);
124
125                         bold_font = new Font (this.Font, FontStyle.Bold);
126                         row_height = Font.Height + font_height_padding;
127                 }
128
129                 private void InvalidateItemLabel (GridEntry item)
130                 {
131                         Invalidate (new Rectangle (0, ((GridEntry)item).Top, SplitterLocation, row_height));
132                 }
133
134                 private void InvalidateItem (GridEntry item)
135                 {
136                         if (item == null)
137                                 return;
138
139                         Rectangle rect = new Rectangle (0, item.Top, Width, row_height);
140                         Invalidate (rect);
141
142                         if (item.Expanded) {
143                                 rect = new Rectangle (0, item.Top + row_height, Width,
144                                                       Height - (item.Top + row_height));
145                                 Invalidate (rect);
146                         }
147                 }
148
149                 protected override void OnDoubleClick (EventArgs e) 
150                 {
151                         if (property_grid.SelectedGridItem != null && property_grid.SelectedGridItem.Expandable)
152                                 property_grid.SelectedGridItem.Expanded = !property_grid.SelectedGridItem.Expanded;
153                         else
154                                 ToggleValue ((GridEntry)property_grid.SelectedGridItem);
155                 }
156
157                 protected override void OnPaint (PaintEventArgs e) 
158                 {
159                         // Background
160                         e.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
161                         
162                         int yLoc = -vbar.Value*row_height;
163                         if (property_grid.RootGridItem != null)
164                                 DrawGridItems (property_grid.RootGridItem.GridItems, e, 1, ref yLoc);
165
166                         UpdateScrollBar ();
167                 }
168
169                 protected override void OnMouseWheel (MouseEventArgs e) 
170                 {
171                         if (vbar == null || !vbar.Visible)
172                                 return;
173                         if (e.Delta < 0)
174                                 vbar.Value = Math.Min (vbar.Value + SystemInformation.MouseWheelScrollLines, vbar.Maximum - vbar.SmallChange);
175                         else
176                                 vbar.Value = Math.Max (0, vbar.Value - SystemInformation.MouseWheelScrollLines);
177                         base.OnMouseWheel (e);
178                 }
179
180
181                 protected override void OnMouseMove (MouseEventArgs e) {
182                         if (property_grid.RootGridItem == null)
183                                 return;
184
185                         if (resizing_grid) {
186                                 int loc = Math.Max (e.X,2*V_INDENT);
187                                 SplitterPercent = 1.0*loc/Width;
188                         }
189                         if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) 
190                                 this.Cursor = Cursors.SizeWE;
191                         else
192                                 this.Cursor = Cursors.Default;
193                         base.OnMouseMove (e);
194                 }
195
196                 protected override void OnMouseDown (MouseEventArgs e) 
197                 {
198                         base.OnMouseDown (e);
199                         if (property_grid.RootGridItem == null)
200                                 return;
201
202                         if (!TrySetEntry ((GridEntry)property_grid.SelectedGridItem, grid_textbox.Text)) {
203                                 FocusSelection ();
204                                 return;
205                         }
206
207                         if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) {
208                                 resizing_grid = true;
209                         }
210                         else {
211                                 int offset = -vbar.Value*row_height;
212                                 GridItem foundItem = GetSelectedGridItem (property_grid.RootGridItem.GridItems, e.Y, ref offset);
213
214                                 if (foundItem != null) {
215                                         if (foundItem.Expandable && ((GridEntry)foundItem).PlusMinusBounds.Contains (e.X, e.Y))
216                                                 foundItem.Expanded = !foundItem.Expanded;
217                                         
218                                         this.property_grid.SelectedGridItem = foundItem;
219                                         if (!GridLabelHitTest (e.X)) {
220                                                 // send mouse down so we get the carret under cursor
221                                                 grid_textbox.SendMouseDown (PointToScreen (e.Location));
222                                         }
223                                 }
224                         }
225                 }
226
227                 protected override void OnMouseUp (MouseEventArgs e) {
228                         resizing_grid = false;
229                         base.OnMouseUp (e);
230                 }
231
232                 protected override void OnResize (EventArgs e) {
233                         base.OnResize (e);
234                         if (property_grid.SelectedGridItem != null) { // initialized already
235                                 UpdateView ();
236                                 // MS scrolls to the currently selected item on resize, even
237                                 // when it's not in the visible area.
238                                 // 
239                                 ScrollToItem ((GridEntry)property_grid.SelectedGridItem);
240                         }
241                 }
242
243                 private void UnfocusSelection ()
244                 {
245                         Select (this);
246                 }
247
248                 private void FocusSelection ()
249                 {
250                         Select (grid_textbox);
251                 }
252
253                 protected override bool ProcessDialogKey (Keys keyData) {
254                         GridEntry selectedItem = (GridEntry) property_grid.SelectedGridItem;
255                         if (selectedItem != null
256                             && grid_textbox.Visible) {
257                                 switch (keyData) {
258                                 case Keys.Enter:
259                                         if (TrySetEntry (selectedItem, grid_textbox.Text))
260                                                 UnfocusSelection ();
261                                         return true;
262                                 case Keys.Escape:
263                                         if (selectedItem.IsEditable)
264                                                 UpdateItem (selectedItem); // reset value
265                                         UnfocusSelection ();
266                                         return true;
267                                 case Keys.Tab:
268                                         FocusSelection ();
269                                         return true;
270                                 default:
271                                         return false;
272                                 }
273                         }
274                         return base.ProcessDialogKey (keyData);
275                 }
276
277                 private bool TrySetEntry (GridEntry entry, object value)
278                 {
279                         if (entry == null || grid_textbox.Text.Equals (entry.ValueText))
280                                 return true;
281
282                         if (entry.IsEditable || !entry.IsEditable && (entry.HasCustomEditor || entry.AcceptedValues != null) ||
283                             !entry.IsMerged || entry.HasMergedValue || 
284                             (!entry.HasMergedValue && grid_textbox.Text != String.Empty)) {
285                                 string error = null;
286                                 bool changed = entry.SetValue (value, out error);
287                                 if (!changed && error != null) {
288                                         if (property_grid.ShowError (error, MessageBoxButtons.OKCancel) == DialogResult.Cancel)
289                                                 UpdateItem (entry); // restore value, repaint, etc
290                                         else
291                                                 return false;
292                                 }
293                         }
294                         return true;
295                 }
296
297                 protected override bool IsInputKey (Keys keyData) {
298                         switch (keyData) {
299                         case Keys.Left:
300                         case Keys.Right:
301                         case Keys.Enter:
302                         case Keys.Escape:
303                         case Keys.Up:
304                         case Keys.Down:
305                         case Keys.PageDown:
306                         case Keys.PageUp:
307                         case Keys.Home:
308                         case Keys.End:
309                                 return true;
310                         default:
311                                 return false;
312                         }
313                 }
314
315                 private GridEntry MoveUpFromItem (GridEntry item, int up_count)
316                 {
317                         GridItemCollection items;
318                         int index;
319
320                         /* move back up the visible rows (and up the hierarchy as necessary) until
321                            up_count == 0, or we reach the top of the display */
322                         while (up_count > 0) {
323                                 items = item.Parent != null ? item.Parent.GridItems : property_grid.RootGridItem.GridItems;
324                                 index = items.IndexOf (item);
325
326                                 if (index == 0) {
327                                         if (item.Parent.GridItemType == GridItemType.Root) // we're at the top row
328                                                 return item;
329                                         item = (GridEntry)item.Parent;
330                                         up_count --;
331                                 }
332                                 else {
333                                         GridEntry prev_item = (GridEntry)items[index-1];
334                                         if (prev_item.Expandable && prev_item.Expanded) {
335                                                 item = (GridEntry)prev_item.GridItems[prev_item.GridItems.Count - 1];
336                                         }
337                                         else {
338                                                 item = prev_item;
339                                         }
340                                         up_count --;
341                                 }
342                         }
343                         return item;
344                 }
345
346                 private GridEntry MoveDownFromItem (GridEntry item, int down_count)
347                 {
348                         while (down_count > 0) {
349                                 /* if we're a parent node and we're expanded, move to our first child */
350                                 if (item.Expandable && item.Expanded) {
351                                         item = (GridEntry)item.GridItems[0];
352                                         down_count--;
353                                 }
354                                 else {
355                                         GridItem searchItem = item;
356                                         GridItemCollection searchItems = searchItem.Parent.GridItems;
357                                         int searchIndex = searchItems.IndexOf (searchItem);
358
359                                         while (searchIndex == searchItems.Count - 1) {
360                                                 searchItem = searchItem.Parent;
361
362                                                 if (searchItem == null || searchItem.Parent == null)
363                                                         break;
364
365                                                 searchItems = searchItem.Parent.GridItems;
366                                                 searchIndex = searchItems.IndexOf (searchItem);
367                                         }
368
369                                         if (searchIndex == searchItems.Count - 1) {
370                                                 /* if we got all the way back to the root with no nodes after
371                                                    us, the original item was the last one */
372                                                 return item;
373                                         }
374                                         else {
375                                                 item = (GridEntry)searchItems[searchIndex+1];
376                                                 down_count--;
377                                         }
378                                 }
379                         }
380
381                         return item;
382                 }
383
384                 protected override void OnKeyDown (KeyEventArgs e) 
385                 {
386                         GridEntry selectedItem = (GridEntry)property_grid.SelectedGridItem;
387
388                         if (selectedItem == null) {
389                                 /* XXX not sure what MS does, but at least we shouldn't crash */
390                                 base.OnKeyDown (e);
391                                 return;
392                         }
393
394                         if (!TrySetEntry (selectedItem, grid_textbox.Text)) {
395                                 FocusSelection ();
396                                 return;
397                         }
398
399                         switch (e.KeyData & Keys.KeyCode) {
400                         case Keys.Left:
401                                 if (e.Control) {
402                                         if (SplitterLocation > 2 * V_INDENT)
403                                                 SplitterPercent -= 0.01;
404
405                                         e.Handled = true;
406                                         break;
407                                 }
408                                 else {
409                                         /* if the node is expandable and is expanded, collapse it.
410                                            otherwise, act just like the user pressed up */
411                                         if (selectedItem.Expandable && selectedItem.Expanded) {
412                                                 selectedItem.Expanded = false;
413                                                 e.Handled = true;
414                                                 break;
415                                         }
416                                         else
417                                                 goto case Keys.Up;
418                                 }
419                         case Keys.Right:
420                                 if (e.Control) {
421                                         if (SplitterLocation < Width)
422                                                 SplitterPercent += 0.01;
423
424                                         e.Handled = true;
425                                         break;
426                                 }
427                                 else {
428                                         /* if the node is expandable and not expanded, expand it.
429                                            otherwise, act just like the user pressed down */
430                                         if (selectedItem.Expandable && !selectedItem.Expanded) {
431                                                 selectedItem.Expanded = true;
432                                                 e.Handled = true;
433                                                 break;
434                                         }
435                                         else
436                                                 goto case Keys.Down;
437                                 }
438                         case Keys.Enter:
439                                 /* toggle the expanded state of the selected item */
440                                 if (selectedItem.Expandable) {
441                                         selectedItem.Expanded = !selectedItem.Expanded;
442                                 }
443                                 e.Handled = true;
444                                 break;
445                         case Keys.Up:
446                                 property_grid.SelectedGridItem = MoveUpFromItem (selectedItem, 1);
447                                 e.Handled = true;
448                                 break;
449                         case Keys.Down:
450                                 property_grid.SelectedGridItem = MoveDownFromItem (selectedItem, 1);
451                                 e.Handled = true;
452                                 break;
453                         case Keys.PageUp:
454                                 property_grid.SelectedGridItem = MoveUpFromItem (selectedItem, vbar.LargeChange);
455                                 e.Handled = true;
456                                 break;
457                         case Keys.PageDown:
458                                 property_grid.SelectedGridItem = MoveDownFromItem (selectedItem, vbar.LargeChange);
459                                 e.Handled = true;
460                                 break;
461                         case Keys.End:
462                                 /* find the last, most deeply nested visible item */
463                                 GridEntry item = (GridEntry)property_grid.RootGridItem.GridItems[property_grid.RootGridItem.GridItems.Count - 1];
464                                 while (item.Expandable && item.Expanded)
465                                         item = (GridEntry)item.GridItems[item.GridItems.Count - 1];
466                                 property_grid.SelectedGridItem = item;
467                                 e.Handled = true;
468                                 break;
469                         case Keys.Home:
470                                 property_grid.SelectedGridItem = property_grid.RootGridItem.GridItems[0];
471                                 e.Handled = true;
472                                 break;
473                         }
474
475                         base.OnKeyDown (e);
476                 }
477
478                 #endregion
479
480                 #region Private Helper Methods
481
482                 private int SplitterLocation {
483                         get {
484                                 return (int)(splitter_percent*Width);
485                         }
486                 }
487
488                 private double SplitterPercent {
489                         set {
490                                 int old_splitter_location = SplitterLocation;
491                                 
492                                 splitter_percent = Math.Max (Math.Min (value, .9),.1);
493
494                                 if (old_splitter_location != SplitterLocation) {
495                                         int x = old_splitter_location > SplitterLocation ? SplitterLocation : old_splitter_location;
496                                         Invalidate (new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height));
497                                         UpdateItem ((GridEntry)property_grid.SelectedGridItem);
498                                 }
499                         }
500                         get {
501                                 return splitter_percent;
502                         }
503                 }
504
505                 private bool GridLabelHitTest (int x)
506                 {
507                         if (0 <= x && x <= splitter_percent * this.Width)
508                                 return true;
509                         return false;
510                 }
511
512                 private GridItem GetSelectedGridItem (GridItemCollection grid_items, int y, ref int current) {
513                         foreach (GridItem child_grid_item in grid_items) {
514                                 if (y > current && y < current + row_height) {
515                                         return child_grid_item;
516                                 }
517                                 current += row_height;
518                                 if (child_grid_item.Expanded) {
519                                         GridItem foundItem = GetSelectedGridItem (child_grid_item.GridItems, y, ref current);
520                                         if (foundItem != null)
521                                                 return foundItem;
522                                 }
523                         }
524                         return null;
525                 }
526
527                 private int GetVisibleItemsCount (GridEntry entry)
528                 {
529                         if (entry == null)
530                                 return 0;
531
532                         int count = 0;
533                         foreach (GridEntry e in entry.GridItems) {
534                                 count += 1;
535                                 if (e.Expandable && e.Expanded)
536                                         count += GetVisibleItemsCount (e);
537                         }
538                         return count;
539                 }
540
541                 private int GetVisibleRowsCount ()
542                 {
543                         return this.Height / row_height;
544                 }
545
546                 private void UpdateScrollBar ()
547                 {
548                         if (property_grid.RootGridItem == null)
549                                 return;
550
551                         int visibleRows = GetVisibleRowsCount ();
552                         int openedItems = GetVisibleItemsCount ((GridEntry)property_grid.RootGridItem);
553                         if (openedItems > visibleRows) {
554                                 vbar.Visible = true;
555                                 vbar.SmallChange = 1;
556                                 vbar.Minimum = 0;
557                                 vbar.Maximum = openedItems - 1;
558                                 vbar.LargeChange = visibleRows;
559                         } else {
560                                 vbar.Value = 0;
561                                 vbar.Visible = false;
562                         }
563                         UpdateGridTextBoxBounds ((GridEntry)property_grid.SelectedGridItem);
564                 }
565
566                 // private bool GetScrollBarVisible ()
567                 // {
568                 //      if (property_grid.RootGridItem == null)
569                 //              return false;
570                 // 
571                 //      int visibleRows = GetVisibleRowsCount ();
572                 //      int openedItems = GetVisibleItemsCount ((GridEntry)property_grid.RootGridItem);
573                 //      if (openedItems > visibleRows)
574                 //              return true;
575                 //      return false;
576                 // }
577                 #region Drawing Code
578
579                 private void DrawGridItems (GridItemCollection grid_items, PaintEventArgs pevent, int depth, ref int yLoc) {
580                         foreach (GridItem grid_item in grid_items) {
581                                 DrawGridItem ((GridEntry)grid_item, pevent, depth, ref yLoc);
582                                 if (grid_item.Expanded)
583                                         DrawGridItems (grid_item.GridItems, pevent, (grid_item.GridItemType == GridItemType.Category) ? depth : depth+1, ref yLoc);
584                         }
585                 }
586
587                 private void DrawGridItemLabel (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
588                         Font font = this.Font;
589                         Brush brush;
590
591                         if (grid_item.GridItemType == GridItemType.Category) {
592                                 font = bold_font;
593                                 brush = SystemBrushes.ControlText;
594
595                                 pevent.Graphics.DrawString (grid_item.Label, font, brush, rect.X + 1, rect.Y + ENTRY_SPACING);
596                                 if (grid_item == property_grid.SelectedGridItem) {
597                                         SizeF size = pevent.Graphics.MeasureString (grid_item.Label, font);
598                                         ControlPaint.DrawFocusRectangle (pevent.Graphics, new Rectangle (rect.X + 1, rect.Y+ENTRY_SPACING, (int)size.Width, (int)size.Height));
599                                 }
600                         }
601                         else {
602                                 if (grid_item == property_grid.SelectedGridItem) {
603                                         Rectangle highlight = rect;
604                                         if (depth > 1) {
605                                                 highlight.X -= V_INDENT;
606                                                 highlight.Width += V_INDENT;
607                                         }
608                                         pevent.Graphics.FillRectangle (SystemBrushes.Highlight, highlight);
609                                         // Label
610                                         brush = SystemBrushes.HighlightText;
611                                 }
612                                 else {
613                                         brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
614                                 }
615                         }
616                         pevent.Graphics.DrawString (grid_item.Label, font, brush,
617                                                     new Rectangle (rect.X + 1, rect.Y + ENTRY_SPACING, rect.Width - ENTRY_SPACING, rect.Height - ENTRY_SPACING),
618                                                     string_format);
619                 }
620
621                 private void DrawGridItemValue (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect) 
622                 {
623                         if (grid_item.PropertyDescriptor == null)
624                                 return; 
625
626                         int xLoc = SplitterLocation+ENTRY_SPACING;
627                         if (grid_item.PaintValueSupported) {
628                                 pevent.Graphics.DrawRectangle (Pens.Black, SplitterLocation + ENTRY_SPACING, 
629                                                                rect.Y + 2, VALUE_PAINT_WIDTH + 1, row_height - ENTRY_SPACING*2);
630                                 grid_item.PaintValue (pevent.Graphics, 
631                                                       new Rectangle (SplitterLocation + ENTRY_SPACING + 1, 
632                                                                      rect.Y + ENTRY_SPACING + 1,
633                                                                      VALUE_PAINT_WIDTH, row_height - (ENTRY_SPACING*2 +1)));
634                                 xLoc += VALUE_PAINT_INDENT;
635                         }
636
637                         Font font = this.Font;
638                         if (grid_item.IsResetable || !grid_item.HasDefaultValue)
639                                 font = bold_font;
640                         Brush brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
641                         string valueText = grid_item.IsMerged && !grid_item.HasMergedValue ? String.Empty : grid_item.ValueText;
642                         pevent.Graphics.DrawString (valueText, font,
643                                                     brush,
644                                                     new RectangleF (xLoc + ENTRY_SPACING, rect.Y + ENTRY_SPACING,
645                                                                     ClientRectangle.Width-(xLoc), row_height - ENTRY_SPACING*2), 
646                                                     string_format);
647                 }
648
649                 private void DrawGridItem (GridEntry grid_item, PaintEventArgs pevent, int depth, ref int yLoc) {
650                         if (yLoc > -row_height && yLoc < ClientRectangle.Height) {
651                                 // Left column
652                                 pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor),
653                                                                0, yLoc, V_INDENT, row_height);
654                         
655                                 if (grid_item.GridItemType == GridItemType.Category) {
656                                         pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.CategoryForeColor), depth*V_INDENT,yLoc,ClientRectangle.Width-(depth*V_INDENT), row_height);
657                                 }
658
659                                 DrawGridItemLabel (grid_item, pevent,
660                                                    depth,
661                                                   new Rectangle (depth * V_INDENT, yLoc, SplitterLocation - depth * V_INDENT, row_height));
662                                 DrawGridItemValue (grid_item, pevent,
663                                                   depth,
664                                                   new Rectangle (SplitterLocation + ENTRY_SPACING , yLoc, 
665                                                                  ClientRectangle.Width - SplitterLocation - ENTRY_SPACING - (vbar.Visible ? vbar.Width : 0), 
666                                                                  row_height));
667
668                                 if (grid_item.GridItemType != GridItemType.Category) {
669                                         Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.LineColor);
670                                         // vertical divider line
671                                         pevent.Graphics.DrawLine (pen, SplitterLocation, yLoc, SplitterLocation, yLoc + row_height);
672                         
673                                         // draw the horizontal line
674                                         pevent.Graphics.DrawLine (pen, 0, yLoc + row_height, ClientRectangle.Width, yLoc + row_height);
675                                 }                               
676                                 
677                                 if (grid_item.Expandable) {
678                                         int y = yLoc + row_height / 2 - ENTRY_SPACING + 1;
679                                         grid_item.PlusMinusBounds = DrawPlusMinus (pevent.Graphics, (depth - 1) * V_INDENT + ENTRY_SPACING + 1, 
680                                                                                    y, grid_item.Expanded, grid_item.GridItemType == GridItemType.Category);
681                                 }
682
683                         }
684                         grid_item.Top = yLoc;
685                         yLoc += row_height;
686                 }
687
688                 private Rectangle DrawPlusMinus (Graphics g, int x, int y, bool expanded, bool category) {
689                         Rectangle bounds = new Rectangle (x, y, 8, 8);
690                         if (!category) g.FillRectangle (Brushes.White, bounds);
691                         Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.ViewForeColor);
692                         g.DrawRectangle (pen, bounds);
693                         g.DrawLine (pen, x+2, y+4, x + 6, y+4);
694                         if (!expanded)
695                                 g.DrawLine (pen, x+4, y+2, x+4, y+6);
696
697                         return bounds;
698                 }
699
700                 #endregion
701
702                 #region Event Handling
703                 private void RedrawEvent (object sender, System.EventArgs e) 
704                 {
705                         Refresh ();
706                 }
707
708                 // private void TextBoxValidating (object sender, CancelEventArgs e)
709                 // {
710                 //      GridEntry entry = (GridEntry) property_grid.SelectedGridItem;
711                 //      if (entry != null && entry.IsEditable)
712                 //              TrySetEntry (entry, grid_textbox.Text);
713                 // }
714
715                 #endregion
716
717                 private void listBox_MouseUp (object sender, MouseEventArgs e) {
718                         AcceptListBoxSelection (sender);
719                 }
720
721                 private void listBox_KeyDown (object sender, KeyEventArgs e)
722                 {
723                         switch (e.KeyData & Keys.KeyCode) {
724                         case Keys.Enter:
725                                 AcceptListBoxSelection (sender);
726                                 return;
727                         case Keys.Escape:
728                                 CloseDropDown ();
729                                 return;
730                         }
731                 }
732
733                 void AcceptListBoxSelection (object sender) 
734                 {
735                         GridEntry entry = this.property_grid.SelectedGridItem as GridEntry;
736                         if (entry != null) {
737                                 grid_textbox.Text = (string) ((ListBox) sender).SelectedItem;
738                                 if (TrySetEntry (entry, (string) ((ListBox) sender).SelectedItem))
739                                         UnfocusSelection ();
740                         }
741                         CloseDropDown ();
742                 }
743
744                 private void DropDownButtonClicked (object sender, EventArgs e) 
745                 {
746                         GridEntry entry = property_grid.SelectedGridItem as GridEntry;
747                         if (entry == null)
748                                 return;
749
750                         if (entry.HasCustomEditor) {
751                                 entry.EditValue ((IWindowsFormsEditorService) this);
752                         } else {
753                                 if (dropdown_form.Visible) {
754                                         CloseDropDown ();
755                                 }
756                                 else {
757                                         ICollection std_values = entry.AcceptedValues;
758                                         if (std_values != null) {
759                                                 ListBox listBox = new ListBox ();
760                                                 listBox.BorderStyle = BorderStyle.FixedSingle;
761                                                 int selected_index = 0;
762                                                 int i = 0;
763                                                 foreach (object obj in std_values) {
764                                                         listBox.Items.Add (obj);
765                                                         if (entry.ValueText != null && entry.ValueText.Equals (obj))
766                                                                 selected_index = i;
767                                                         i++;
768                                                 }
769                                                 listBox.Height = row_height * Math.Min (listBox.Items.Count, 15);
770                                                 listBox.Width = ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0);
771                                                 listBox.KeyDown += new KeyEventHandler (listBox_KeyDown);
772                                                 listBox.MouseUp+=new MouseEventHandler (listBox_MouseUp);
773                                                 if (std_values.Count > 0)
774                                                         listBox.SelectedIndex = selected_index;
775                                                 DropDownControl (listBox);
776                                         }
777                                 }
778                         }
779                 }
780
781                 private void DialogButtonClicked (object sender, EventArgs e) 
782                 {
783                         GridEntry entry = property_grid.SelectedGridItem as GridEntry;
784                         if (entry != null && entry.HasCustomEditor)
785                                 entry.EditValue ((IWindowsFormsEditorService) this);
786                 }
787
788                 private void VScrollBar_HandleValueChanged (object sender, EventArgs e) 
789                 {
790                         UpdateView ();
791                 }
792
793                 private void grid_textbox_ToggleValue (object sender, EventArgs args) 
794                 {
795                         ToggleValue ((GridEntry)property_grid.SelectedGridItem);
796                 }
797
798                 private void ToggleValue (GridEntry entry)
799                 {
800                         if (entry != null && !entry.IsReadOnly && entry.GridItemType == GridItemType.Property)
801                                 entry.ToggleValue ();
802                 }
803
804                 internal void UpdateItem (GridEntry entry)
805                 {
806                         if (entry == null || entry.GridItemType == GridItemType.Category || 
807                             entry.GridItemType == GridItemType.Root) {
808                                 grid_textbox.Visible = false;
809                                 InvalidateItem (entry);
810                                 return;
811                         }
812
813                         if (property_grid.SelectedGridItem == entry) {
814                                 SuspendLayout ();
815                                 grid_textbox.Visible = false;
816                                 if (entry.IsResetable || !entry.HasDefaultValue)
817                                         grid_textbox.Font = bold_font;
818                                 else
819                                         grid_textbox.Font = this.Font;
820
821                                 if (entry.IsReadOnly) {
822                                         grid_textbox.DropDownButtonVisible = false;
823                                         grid_textbox.DialogButtonVisible = false;
824                                         grid_textbox.ReadOnly = true;
825                                         grid_textbox.ForeColor = SystemColors.GrayText;
826                                 } else {
827                                         grid_textbox.DropDownButtonVisible = entry.AcceptedValues != null || 
828                                                 entry.EditorStyle == UITypeEditorEditStyle.DropDown;
829                                         grid_textbox.DialogButtonVisible = entry.EditorStyle == UITypeEditorEditStyle.Modal;
830                                         grid_textbox.ForeColor = SystemColors.ControlText;
831                                         grid_textbox.ReadOnly = !entry.IsEditable;
832                                 }
833                                 UpdateGridTextBoxBounds (entry);
834                                 grid_textbox.Text = entry.IsMerged && !entry.HasMergedValue ? String.Empty : entry.ValueText;
835                                 grid_textbox.Visible = true;
836                                 InvalidateItem (entry);
837                                 ResumeLayout (false);
838                         } else {
839                                 grid_textbox.Visible = false;
840                         }
841                 }
842
843                 private void UpdateGridTextBoxBounds (GridEntry entry)
844                 {
845                         int y = -vbar.Value*row_height;
846                         CalculateItemY (entry, property_grid.RootGridItem.GridItems, ref y);
847                         int x = SplitterLocation + ENTRY_SPACING + (entry.PaintValueSupported ? VALUE_PAINT_INDENT : 0);
848                         grid_textbox.SetBounds (x + ENTRY_SPACING, y + ENTRY_SPACING,
849                                                 ClientRectangle.Width - ENTRY_SPACING - x - (vbar.Visible ? vbar.Width : 0),
850                                                 row_height - ENTRY_SPACING);
851                 }
852
853                 // Calculates the sum of the heights of all items before the one
854                 //
855                 private bool CalculateItemY (GridEntry entry, GridItemCollection items, ref int y)
856                 {
857                         foreach (GridItem item in items) {
858                                 if (item == entry)
859                                         return true;
860                                 y += row_height;
861                                 if (item.Expandable && item.Expanded)
862                                         if (CalculateItemY (entry, item.GridItems, ref y))
863                                                 return true;
864                         }
865                         return false;
866                 }
867
868                 private void ScrollToItem (GridEntry item)
869                 {
870                         if (item == null)
871                                 return;
872
873                         int itemY = -vbar.Value*row_height;
874                         int value = vbar.Value;;
875                         CalculateItemY (item, property_grid.RootGridItem.GridItems, ref itemY);
876                         if (itemY < 0) // the new item is above the viewable area
877                                 value += itemY / row_height;
878                         else if (itemY + row_height > Height) // the new item is below the viewable area
879                                 value += ((itemY + row_height) - Height) / row_height + 1;
880                         if (value >= vbar.Minimum && value <= vbar.Maximum)
881                                 vbar.Value = value;
882                 }
883
884                 internal void SelectItem (GridEntry oldItem, GridEntry newItem) 
885                 {
886                         if (oldItem != null)
887                                 InvalidateItemLabel (oldItem);
888                         if (newItem != null) {
889                                 UpdateItem (newItem);
890                                 ScrollToItem (newItem);
891                         } else
892                                 grid_textbox.Visible = false;
893                 }
894
895                 internal void UpdateView ()
896                 {
897                         UpdateScrollBar ();
898                         UpdateItem ((GridEntry)property_grid.SelectedGridItem);
899                         Invalidate ();
900                         Update ();
901                 }
902
903                 internal void ExpandItem (GridEntry item)
904                 {
905                         UpdateItem ((GridEntry)property_grid.SelectedGridItem);
906                         Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
907                 }
908
909                 internal void CollapseItem (GridEntry item)
910                 {
911                         UpdateItem ((GridEntry)property_grid.SelectedGridItem);
912                         Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
913                 }
914
915                 private void ShowDropDownControl (Control control, bool block) 
916                 {
917                         Object  queue_id;
918
919                         Form owner = FindForm ();
920
921                         Point location;
922                         dropdown_form.Size = control.Size;
923                         control.Dock = DockStyle.Fill;
924                         dropdown_form.Controls.Add (control);
925                         dropdown_form.Width = Math.Max (ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0), 
926                                                         control.Width);
927                         dropdown_form.Location = PointToScreen (new Point (grid_textbox.Right - control.Width, grid_textbox.Location.Y + row_height));
928                         RepositionInScreenWorkingArea (dropdown_form);
929                         location = dropdown_form.Location;
930
931                         owner.AddOwnedForm (dropdown_form);
932
933                         dropdown_form.Show ();
934
935                         if (dropdown_form.Location != location) {
936                                 dropdown_form.Location = location;
937                         }
938                         if (block) {
939                                 System.Windows.Forms.MSG msg = new MSG ();
940                                 queue_id = XplatUI.StartLoop (Thread.CurrentThread);
941                                 while (dropdown_form.Visible && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
942                                         switch (msg.message) {
943                                                 case Msg.WM_NCLBUTTONDOWN:
944                                             case Msg.WM_NCMBUTTONDOWN:
945                                             case Msg.WM_NCRBUTTONDOWN:
946                                             case Msg.WM_LBUTTONDOWN:
947                                             case Msg.WM_MBUTTONDOWN:
948                                             case Msg.WM_RBUTTONDOWN:
949                                                 if (!HwndInControl (dropdown_form, msg.hwnd))
950                                                                 CloseDropDown ();
951                                                 break;
952                                                 case Msg.WM_ACTIVATE:
953                                                 case Msg.WM_NCPAINT:
954                                                         if (owner.window.Handle == msg.hwnd)
955                                                                 CloseDropDown ();
956                                                 break;                                          
957                                         }
958                                         XplatUI.TranslateMessage (ref msg);
959                                         XplatUI.DispatchMessage (ref msg);
960                                 }
961                                 XplatUI.EndLoop (Thread.CurrentThread);                 
962                         }
963                 }
964
965                 private void RepositionInScreenWorkingArea (Form form)
966                 {
967                         Rectangle workingArea = Screen.FromControl (form).WorkingArea;
968                         if (!workingArea.Contains (form.Bounds)) {
969                                 int x, y;
970                                 x = form.Location.X;
971                                 y = form.Location.Y;
972
973                                 if (form.Location.X < workingArea.X)
974                                         x = workingArea.X;
975
976                                 if (form.Location.Y + form.Size.Height > workingArea.Height) {
977                                         Point aboveTextBox = PointToScreen (new Point (grid_textbox.Right - form.Width, grid_textbox.Location.Y));
978                                         y = aboveTextBox.Y - form.Size.Height;
979                                 }
980
981                                 form.Location = new Point (x, y);
982                         }
983                 }
984
985                 private bool HwndInControl (Control c, IntPtr hwnd)
986                 {
987                         if (hwnd == c.window.Handle)
988                                 return true;
989                         foreach (Control cc in c.Controls.GetAllControls ()) {
990                                 if (HwndInControl (cc, hwnd))
991                                         return true;
992                         }
993                         return false;
994                 }
995                 #endregion
996
997                 #region IWindowsFormsEditorService Members
998
999                 public void CloseDropDown () {
1000                         Control c = dropdown_form.Controls[0];
1001                         c.Capture = false;
1002                         dropdown_form.Hide ();
1003                         dropdown_form.Controls.Clear ();
1004                 }
1005
1006                 public void DropDownControl (Control control) {
1007                         ShowDropDownControl (control, true);
1008                 }
1009
1010                 public System.Windows.Forms.DialogResult ShowDialog (Form dialog) {
1011                         return dialog.ShowDialog (this);
1012                 }
1013
1014                 #endregion
1015
1016                 /*
1017                         class ComboListBox
1018                 */
1019                 internal class PropertyGridDropDown : Form {
1020                         protected override CreateParams CreateParams {
1021                                 get {
1022                                         CreateParams cp = base.CreateParams;
1023                                         cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
1024                                         cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);                            
1025                                         return cp;
1026                                 }
1027                         }
1028
1029                 }
1030         }
1031 }