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