aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Widgets/PropertyGridView.cs
diff options
context:
space:
mode:
Diffstat (limited to 'source/ShiftUI/Widgets/PropertyGridView.cs')
-rw-r--r--source/ShiftUI/Widgets/PropertyGridView.cs1101
1 files changed, 1101 insertions, 0 deletions
diff --git a/source/ShiftUI/Widgets/PropertyGridView.cs b/source/ShiftUI/Widgets/PropertyGridView.cs
new file mode 100644
index 0000000..ea26d0e
--- /dev/null
+++ b/source/ShiftUI/Widgets/PropertyGridView.cs
@@ -0,0 +1,1101 @@
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2005-2008 Novell, Inc. (http://www.novell.com)
+//
+// Authors:
+// Jonathan Chambers ([email protected])
+// Ivan N. Zlatev ([email protected])
+//
+//
+
+// NOT COMPLETE
+
+using System;
+using System.Collections;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Drawing.Design;
+using System.ComponentModel;
+using System.Threading;
+using ShiftUI.Design;
+using System.Collections.Generic;
+
+namespace ShiftUI.PropertyGridInternal {
+ internal class PropertyGridView : ScrollableWidget, IWindowsFormsEditorService {
+
+ #region Private Members
+ private const char PASSWORD_PAINT_CHAR = '\u25cf'; // the dot char
+ private const char PASSWORD_TEXT_CHAR = '*';
+ private const int V_INDENT = 16;
+ private const int ENTRY_SPACING = 2;
+ private const int RESIZE_WIDTH = 3;
+ private const int BUTTON_WIDTH = 25;
+ private const int VALUE_PAINT_WIDTH = 19;
+ private const int VALUE_PAINT_INDENT = 27;
+ private double splitter_percent = .5;
+ private int row_height;
+ private int font_height_padding = 3;
+ private PropertyGridTextBox grid_textbox;
+ private PropertyGrid property_grid;
+ private bool resizing_grid;
+ private PropertyGridDropDown dropdown_form;
+ private Form dialog_form;
+ private ImplicitVScrollBar vbar;
+ private StringFormat string_format;
+ private Font bold_font;
+ private Brush inactive_text_brush;
+ private ListBox dropdown_list;
+ private Point last_click;
+ private Padding dropdown_form_padding;
+ #endregion
+
+ #region Contructors
+ public PropertyGridView (PropertyGrid propertyGrid) {
+ property_grid = propertyGrid;
+
+ string_format = new StringFormat ();
+ string_format.FormatFlags = StringFormatFlags.NoWrap;
+ string_format.Trimming = StringTrimming.None;
+
+ grid_textbox = new PropertyGridTextBox ();
+ grid_textbox.DropDownButtonClicked +=new EventHandler (DropDownButtonClicked);
+ grid_textbox.DialogButtonClicked +=new EventHandler (DialogButtonClicked);
+
+ dropdown_form = new PropertyGridDropDown ();
+ dropdown_form.FormBorderStyle = FormBorderStyle.None;
+ dropdown_form.StartPosition = FormStartPosition.Manual;
+ dropdown_form.ShowInTaskbar = false;
+
+ dialog_form = new Form ();
+ dialog_form.StartPosition = FormStartPosition.Manual;
+ dialog_form.FormBorderStyle = FormBorderStyle.None;
+ dialog_form.ShowInTaskbar = false;
+
+ dropdown_form_padding = new Padding (0, 0, 2, 2);
+
+ row_height = Font.Height + font_height_padding;
+
+ grid_textbox.Visible = false;
+ grid_textbox.Font = this.Font;
+ grid_textbox.BackColor = SystemColors.Window;
+ grid_textbox.Validate += new CancelEventHandler (grid_textbox_Validate);
+ grid_textbox.ToggleValue+=new EventHandler (grid_textbox_ToggleValue);
+ grid_textbox.KeyDown+=new KeyEventHandler (grid_textbox_KeyDown);
+ this.Widgets.Add (grid_textbox);
+
+ vbar = new ImplicitVScrollBar ();
+ vbar.Visible = false;
+ vbar.Value = 0;
+ vbar.ValueChanged+=new EventHandler (VScrollBar_HandleValueChanged);
+ vbar.Dock = DockStyle.Right;
+ this.Widgets.AddImplicit (vbar);
+
+ resizing_grid = false;
+
+ bold_font = new Font (this.Font, FontStyle.Bold);
+ inactive_text_brush = new SolidBrush (ThemeEngine.Current.ColorGrayText);
+
+ ForeColorChanged+=new EventHandler (RedrawEvent);
+ BackColorChanged+=new System.EventHandler (RedrawEvent);
+ FontChanged+=new EventHandler (RedrawEvent);
+
+ SetStyle (Widgetstyles.Selectable, true);
+ SetStyle (Widgetstyles.DoubleBuffer, true);
+ SetStyle (Widgetstyles.UserPaint, true);
+ SetStyle (Widgetstyles.AllPaintingInWmPaint, true);
+ SetStyle (Widgetstyles.ResizeRedraw, true);
+ }
+
+ #endregion
+
+ private GridEntry RootGridItem {
+ get { return (GridEntry)property_grid.RootGridItem; }
+ }
+
+ private GridEntry SelectedGridItem {
+ get { return (GridEntry)property_grid.SelectedGridItem; }
+ set { property_grid.SelectedGridItem = value; }
+ }
+
+ #region Protected Instance Methods
+
+ protected override void OnFontChanged (EventArgs e) {
+ base.OnFontChanged (e);
+
+ bold_font = new Font (this.Font, FontStyle.Bold);
+ row_height = Font.Height + font_height_padding;
+ }
+
+ private void InvalidateItemLabel (GridEntry item)
+ {
+ Invalidate (new Rectangle (0, ((GridEntry)item).Top, SplitterLocation, row_height));
+ }
+
+ private void InvalidateItem (GridEntry item)
+ {
+ if (item == null)
+ return;
+
+ Rectangle rect = new Rectangle (0, item.Top, Width, row_height);
+ Invalidate (rect);
+
+ if (item.Expanded) {
+ rect = new Rectangle (0, item.Top + row_height, Width,
+ Height - (item.Top + row_height));
+ Invalidate (rect);
+ }
+ }
+
+ // [+] expanding is handled in OnMouseDown, so in order to prevent
+ // duplicate expanding ignore it here.
+ //
+ protected override void OnDoubleClick (EventArgs e)
+ {
+ if (this.SelectedGridItem != null && this.SelectedGridItem.Expandable &&
+ !this.SelectedGridItem.PlusMinusBounds.Contains (last_click))
+ this.SelectedGridItem.Expanded = !this.SelectedGridItem.Expanded;
+ else
+ ToggleValue (this.SelectedGridItem);
+ }
+
+ protected override void OnPaint (PaintEventArgs e)
+ {
+ // Background
+ e.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
+
+ int yLoc = -vbar.Value*row_height;
+ if (this.RootGridItem != null)
+ DrawGridItems (this.RootGridItem.GridItems, e, 1, ref yLoc);
+
+ UpdateScrollBar ();
+ }
+
+ protected override void OnMouseWheel (MouseEventArgs e)
+ {
+ if (vbar == null || !vbar.Visible)
+ return;
+ if (e.Delta < 0)
+ vbar.Value = Math.Min (vbar.Maximum - GetVisibleRowsCount () + 1, vbar.Value + SystemInformation.MouseWheelScrollLines);
+ else
+ vbar.Value = Math.Max (0, vbar.Value - SystemInformation.MouseWheelScrollLines);
+ base.OnMouseWheel (e);
+ }
+
+
+ protected override void OnMouseMove (MouseEventArgs e) {
+ if (this.RootGridItem == null)
+ return;
+
+ if (resizing_grid) {
+ int loc = Math.Max (e.X,2*V_INDENT);
+ SplitterPercent = 1.0*loc/Width;
+ }
+ if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH)
+ this.Cursor = Cursors.SizeWE;
+ else
+ this.Cursor = Cursors.Default;
+ base.OnMouseMove (e);
+ }
+
+ protected override void OnMouseDown (MouseEventArgs e)
+ {
+ base.OnMouseDown (e);
+ last_click = e.Location;
+ if (this.RootGridItem == null)
+ return;
+
+ if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) {
+ resizing_grid = true;
+ }
+ else {
+ int offset = -vbar.Value*row_height;
+ GridItem foundItem = GetSelectedGridItem (this.RootGridItem.GridItems, e.Y, ref offset);
+
+ if (foundItem != null) {
+ if (foundItem.Expandable && ((GridEntry)foundItem).PlusMinusBounds.Contains (e.X, e.Y))
+ foundItem.Expanded = !foundItem.Expanded;
+
+ this.SelectedGridItem = (GridEntry)foundItem;
+ if (!GridLabelHitTest (e.X)) {
+ // send mouse down so we get the carret under cursor
+ grid_textbox.SendMouseDown (PointToScreen (e.Location));
+ }
+ }
+ }
+ }
+
+ protected override void OnMouseUp (MouseEventArgs e) {
+ resizing_grid = false;
+ base.OnMouseUp (e);
+ }
+
+ protected override void OnResize (EventArgs e) {
+ base.OnResize (e);
+ if (this.SelectedGridItem != null) // initialized already
+ UpdateView ();
+ }
+
+ private void UnfocusSelection ()
+ {
+ Select (this);
+ }
+
+ private void FocusSelection ()
+ {
+ Select (grid_textbox);
+ }
+
+ protected override bool ProcessDialogKey (Keys keyData) {
+ GridEntry selectedItem = this.SelectedGridItem;
+ if (selectedItem != null
+ && grid_textbox.Visible) {
+ switch (keyData) {
+ case Keys.Enter:
+ if (TrySetEntry (selectedItem, grid_textbox.Text))
+ UnfocusSelection ();
+ return true;
+ case Keys.Escape:
+ if (selectedItem.IsEditable)
+ UpdateItem (selectedItem); // reset value
+ UnfocusSelection ();
+ return true;
+ case Keys.Tab:
+ FocusSelection ();
+ return true;
+ default:
+ return false;
+ }
+ }
+ return base.ProcessDialogKey (keyData);
+ }
+
+ private bool TrySetEntry (GridEntry entry, object value)
+ {
+ if (entry == null || grid_textbox.Text.Equals (entry.ValueText))
+ return true;
+
+ if (entry.IsEditable || !entry.IsEditable && (entry.HasCustomEditor || entry.AcceptedValues != null) ||
+ !entry.IsMerged || entry.HasMergedValue ||
+ (!entry.HasMergedValue && grid_textbox.Text != String.Empty)) {
+ string error = null;
+ bool changed = entry.SetValue (value, out error);
+ if (!changed && error != null) {
+ if (property_grid.ShowError (error, MessageBoxButtons.OKCancel) == DialogResult.Cancel) {
+ UpdateItem (entry); // restore value, repaint, etc
+ UnfocusSelection ();
+ }
+ return false;
+ }
+ }
+ UpdateItem (entry); // restore value, repaint, etc
+ return true;
+ }
+
+ protected override bool IsInputKey (Keys keyData) {
+ switch (keyData) {
+ case Keys.Left:
+ case Keys.Right:
+ case Keys.Enter:
+ case Keys.Escape:
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.PageDown:
+ case Keys.PageUp:
+ case Keys.Home:
+ case Keys.End:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private GridEntry MoveUpFromItem (GridEntry item, int up_count)
+ {
+ GridItemCollection items;
+ int index;
+
+ /* move back up the visible rows (and up the hierarchy as necessary) until
+ up_count == 0, or we reach the top of the display */
+ while (up_count > 0) {
+ items = item.Parent != null ? item.Parent.GridItems : this.RootGridItem.GridItems;
+ index = items.IndexOf (item);
+
+ if (index == 0) {
+ if (item.Parent.GridItemType == GridItemType.Root) // we're at the top row
+ return item;
+ item = (GridEntry)item.Parent;
+ up_count --;
+ }
+ else {
+ GridEntry prev_item = (GridEntry)items[index-1];
+ if (prev_item.Expandable && prev_item.Expanded) {
+ item = (GridEntry)prev_item.GridItems[prev_item.GridItems.Count - 1];
+ }
+ else {
+ item = prev_item;
+ }
+ up_count --;
+ }
+ }
+ return item;
+ }
+
+ private GridEntry MoveDownFromItem (GridEntry item, int down_count)
+ {
+ while (down_count > 0) {
+ /* if we're a parent node and we're expanded, move to our first child */
+ if (item.Expandable && item.Expanded) {
+ item = (GridEntry)item.GridItems[0];
+ down_count--;
+ }
+ else {
+ GridItem searchItem = item;
+ GridItemCollection searchItems = searchItem.Parent.GridItems;
+ int searchIndex = searchItems.IndexOf (searchItem);
+
+ while (searchIndex == searchItems.Count - 1) {
+ searchItem = searchItem.Parent;
+
+ if (searchItem == null || searchItem.Parent == null)
+ break;
+
+ searchItems = searchItem.Parent.GridItems;
+ searchIndex = searchItems.IndexOf (searchItem);
+ }
+
+ if (searchIndex == searchItems.Count - 1) {
+ /* if we got all the way back to the root with no nodes after
+ us, the original item was the last one */
+ return item;
+ }
+ else {
+ item = (GridEntry)searchItems[searchIndex+1];
+ down_count--;
+ }
+ }
+ }
+
+ return item;
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ GridEntry selectedItem = this.SelectedGridItem;
+
+ if (selectedItem == null) {
+ /* XXX not sure what MS does, but at least we shouldn't crash */
+ base.OnKeyDown (e);
+ return;
+ }
+
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Left:
+ if (e.Widget) {
+ if (SplitterLocation > 2 * V_INDENT)
+ SplitterPercent -= 0.01;
+
+ e.Handled = true;
+ break;
+ }
+ else {
+ /* if the node is expandable and is expanded, collapse it.
+ otherwise, act just like the user pressed up */
+ if (selectedItem.Expandable && selectedItem.Expanded) {
+ selectedItem.Expanded = false;
+ e.Handled = true;
+ break;
+ }
+ else
+ goto case Keys.Up;
+ }
+ case Keys.Right:
+ if (e.Widget) {
+ if (SplitterLocation < Width)
+ SplitterPercent += 0.01;
+
+ e.Handled = true;
+ break;
+ }
+ else {
+ /* if the node is expandable and not expanded, expand it.
+ otherwise, act just like the user pressed down */
+ if (selectedItem.Expandable && !selectedItem.Expanded) {
+ selectedItem.Expanded = true;
+ e.Handled = true;
+ break;
+ }
+ else
+ goto case Keys.Down;
+ }
+ case Keys.Enter:
+ /* toggle the expanded state of the selected item */
+ if (selectedItem.Expandable) {
+ selectedItem.Expanded = !selectedItem.Expanded;
+ }
+ e.Handled = true;
+ break;
+ case Keys.Up:
+ this.SelectedGridItem = MoveUpFromItem (selectedItem, 1);
+ e.Handled = true;
+ break;
+ case Keys.Down:
+ this.SelectedGridItem = MoveDownFromItem (selectedItem, 1);
+ e.Handled = true;
+ break;
+ case Keys.PageUp:
+ this.SelectedGridItem = MoveUpFromItem (selectedItem, vbar.LargeChange);
+ e.Handled = true;
+ break;
+ case Keys.PageDown:
+ this.SelectedGridItem = MoveDownFromItem (selectedItem, vbar.LargeChange);
+ e.Handled = true;
+ break;
+ case Keys.End:
+ /* find the last, most deeply nested visible item */
+ GridEntry item = (GridEntry)this.RootGridItem.GridItems[this.RootGridItem.GridItems.Count - 1];
+ while (item.Expandable && item.Expanded)
+ item = (GridEntry)item.GridItems[item.GridItems.Count - 1];
+ this.SelectedGridItem = item;
+ e.Handled = true;
+ break;
+ case Keys.Home:
+ this.SelectedGridItem = (GridEntry)this.RootGridItem.GridItems[0];
+ e.Handled = true;
+ break;
+ }
+
+ base.OnKeyDown (e);
+ }
+
+ #endregion
+
+ #region Private Helper Methods
+
+ private int SplitterLocation {
+ get {
+ return (int)(splitter_percent*Width);
+ }
+ }
+
+ private double SplitterPercent {
+ set {
+ int old_splitter_location = SplitterLocation;
+
+ splitter_percent = Math.Max (Math.Min (value, .9),.1);
+
+ if (old_splitter_location != SplitterLocation) {
+ int x = old_splitter_location > SplitterLocation ? SplitterLocation : old_splitter_location;
+ Invalidate (new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height));
+ UpdateItem (this.SelectedGridItem);
+ }
+ }
+ get {
+ return splitter_percent;
+ }
+ }
+
+ private bool GridLabelHitTest (int x)
+ {
+ if (0 <= x && x <= splitter_percent * this.Width)
+ return true;
+ return false;
+ }
+
+ private GridItem GetSelectedGridItem (GridItemCollection grid_items, int y, ref int current) {
+ foreach (GridItem child_grid_item in grid_items) {
+ if (y > current && y < current + row_height) {
+ return child_grid_item;
+ }
+ current += row_height;
+ if (child_grid_item.Expanded) {
+ GridItem foundItem = GetSelectedGridItem (child_grid_item.GridItems, y, ref current);
+ if (foundItem != null)
+ return foundItem;
+ }
+ }
+ return null;
+ }
+
+ private int GetVisibleItemsCount (GridEntry entry)
+ {
+ if (entry == null)
+ return 0;
+
+ int count = 0;
+ foreach (GridEntry e in entry.GridItems) {
+ count += 1;
+ if (e.Expandable && e.Expanded)
+ count += GetVisibleItemsCount (e);
+ }
+ return count;
+ }
+
+ private int GetVisibleRowsCount ()
+ {
+ return this.Height / row_height;
+ }
+
+ private void UpdateScrollBar ()
+ {
+ if (this.RootGridItem == null)
+ return;
+
+ int visibleRows = GetVisibleRowsCount ();
+ int openedItems = GetVisibleItemsCount (this.RootGridItem);
+ if (openedItems > visibleRows) {
+ vbar.Visible = true;
+ vbar.SmallChange = 1;
+ vbar.LargeChange = visibleRows;
+ vbar.Maximum = Math.Max (0, openedItems - 1);
+ } else {
+ vbar.Value = 0;
+ vbar.Visible = false;
+ }
+ UpdateGridTextBoxBounds (this.SelectedGridItem);
+ }
+
+ // private bool GetScrollBarVisible ()
+ // {
+ // if (this.RootGridItem == null)
+ // return false;
+ //
+ // int visibleRows = GetVisibleRowsCount ();
+ // int openedItems = GetVisibleItemsCount (this.RootGridItem);
+ // if (openedItems > visibleRows)
+ // return true;
+ // return false;
+ // }
+ #region Drawing Code
+
+ private void DrawGridItems (GridItemCollection grid_items, PaintEventArgs pevent, int depth, ref int yLoc) {
+ foreach (GridItem grid_item in grid_items) {
+ DrawGridItem ((GridEntry)grid_item, pevent, depth, ref yLoc);
+ if (grid_item.Expanded)
+ DrawGridItems (grid_item.GridItems, pevent, (grid_item.GridItemType == GridItemType.Category) ? depth : depth+1, ref yLoc);
+ }
+ }
+
+ private void DrawGridItemLabel (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
+ Font font = this.Font;
+ Brush brush;
+
+ if (grid_item.GridItemType == GridItemType.Category) {
+ font = bold_font;
+ brush = SystemBrushes.ControlText;
+
+ pevent.Graphics.DrawString (grid_item.Label, font, brush, rect.X + 1, rect.Y + ENTRY_SPACING);
+ if (grid_item == this.SelectedGridItem) {
+ SizeF size = pevent.Graphics.MeasureString (grid_item.Label, font);
+ WidgetPaint.DrawFocusRectangle (pevent.Graphics, new Rectangle (rect.X + 1, rect.Y+ENTRY_SPACING, (int)size.Width, (int)size.Height));
+ }
+ }
+ else {
+ if (grid_item == this.SelectedGridItem) {
+ Rectangle highlight = rect;
+ if (depth > 1) {
+ highlight.X -= V_INDENT;
+ highlight.Width += V_INDENT;
+ }
+ pevent.Graphics.FillRectangle (SystemBrushes.Highlight, highlight);
+ // Label
+ brush = SystemBrushes.HighlightText;
+ }
+ else {
+ brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
+ }
+ }
+ pevent.Graphics.DrawString (grid_item.Label, font, brush,
+ new Rectangle (rect.X + 1, rect.Y + ENTRY_SPACING, rect.Width - ENTRY_SPACING, rect.Height - ENTRY_SPACING),
+ string_format);
+ }
+
+ private void DrawGridItemValue (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect)
+ {
+ if (grid_item.PropertyDescriptor == null)
+ return;
+
+ int xLoc = SplitterLocation+ENTRY_SPACING;
+ if (grid_item.PaintValueSupported) {
+ pevent.Graphics.DrawRectangle (Pens.Black, SplitterLocation + ENTRY_SPACING,
+ rect.Y + 2, VALUE_PAINT_WIDTH + 1, row_height - ENTRY_SPACING*2);
+ grid_item.PaintValue (pevent.Graphics,
+ new Rectangle (SplitterLocation + ENTRY_SPACING + 1,
+ rect.Y + ENTRY_SPACING + 1,
+ VALUE_PAINT_WIDTH, row_height - (ENTRY_SPACING*2 +1)));
+ xLoc += VALUE_PAINT_INDENT;
+ }
+
+ Font font = this.Font;
+ if (grid_item.IsResetable || !grid_item.HasDefaultValue)
+ font = bold_font;
+ Brush brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
+ string valueText = String.Empty;
+ if (!grid_item.IsMerged || grid_item.IsMerged && grid_item.HasMergedValue) {
+ if (grid_item.IsPassword)
+ valueText = new String (PASSWORD_PAINT_CHAR, grid_item.ValueText.Length);
+ else
+ valueText = grid_item.ValueText;
+ }
+ pevent.Graphics.DrawString (valueText, font,
+ brush,
+ new RectangleF (xLoc + ENTRY_SPACING, rect.Y + ENTRY_SPACING,
+ ClientRectangle.Width-(xLoc), row_height - ENTRY_SPACING*2),
+ string_format);
+ }
+
+ private void DrawGridItem (GridEntry grid_item, PaintEventArgs pevent, int depth, ref int yLoc) {
+ if (yLoc > -row_height && yLoc < ClientRectangle.Height) {
+ // Left column
+ pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor),
+ 0, yLoc, V_INDENT, row_height);
+
+ if (grid_item.GridItemType == GridItemType.Category) {
+ pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.CategoryForeColor), depth*V_INDENT,yLoc,ClientRectangle.Width-(depth*V_INDENT), row_height);
+ }
+
+ DrawGridItemLabel (grid_item, pevent,
+ depth,
+ new Rectangle (depth * V_INDENT, yLoc, SplitterLocation - depth * V_INDENT, row_height));
+ DrawGridItemValue (grid_item, pevent,
+ depth,
+ new Rectangle (SplitterLocation + ENTRY_SPACING , yLoc,
+ ClientRectangle.Width - SplitterLocation - ENTRY_SPACING - (vbar.Visible ? vbar.Width : 0),
+ row_height));
+
+ if (grid_item.GridItemType != GridItemType.Category) {
+ Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.LineColor);
+ // vertical divider line
+ pevent.Graphics.DrawLine (pen, SplitterLocation, yLoc, SplitterLocation, yLoc + row_height);
+
+ // draw the horizontal line
+ pevent.Graphics.DrawLine (pen, 0, yLoc + row_height, ClientRectangle.Width, yLoc + row_height);
+ }
+
+ if (grid_item.Expandable) {
+ int y = yLoc + row_height / 2 - ENTRY_SPACING + 1;
+ grid_item.PlusMinusBounds = DrawPlusMinus (pevent.Graphics, (depth - 1) * V_INDENT + ENTRY_SPACING + 1,
+ y, grid_item.Expanded, grid_item.GridItemType == GridItemType.Category);
+ }
+
+ }
+ grid_item.Top = yLoc;
+ yLoc += row_height;
+ }
+
+ private Rectangle DrawPlusMinus (Graphics g, int x, int y, bool expanded, bool category) {
+ Rectangle bounds = new Rectangle (x, y, 8, 8);
+ if (!category) g.FillRectangle (Brushes.White, bounds);
+ Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.ViewForeColor);
+ g.DrawRectangle (pen, bounds);
+ g.DrawLine (pen, x+2, y+4, x + 6, y+4);
+ if (!expanded)
+ g.DrawLine (pen, x+4, y+2, x+4, y+6);
+
+ return bounds;
+ }
+
+ #endregion
+
+ #region Event Handling
+ private void RedrawEvent (object sender, System.EventArgs e)
+ {
+ Refresh ();
+ }
+
+ #endregion
+
+ private void listBox_MouseUp (object sender, MouseEventArgs e) {
+ AcceptListBoxSelection (sender);
+ }
+
+ private void listBox_KeyDown (object sender, KeyEventArgs e)
+ {
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Enter:
+ AcceptListBoxSelection (sender);
+ return;
+ case Keys.Escape:
+ CloseDropDown ();
+ return;
+ }
+ }
+
+ void AcceptListBoxSelection (object sender)
+ {
+ GridEntry entry = this.SelectedGridItem as GridEntry;
+ if (entry != null) {
+ grid_textbox.Text = (string) ((ListBox) sender).SelectedItem;
+ CloseDropDown ();
+ if (TrySetEntry (entry, grid_textbox.Text))
+ UnfocusSelection ();
+ }
+ }
+
+ private void DropDownButtonClicked (object sender, EventArgs e)
+ {
+ DropDownEdit ();
+ }
+
+ private void DropDownEdit ()
+ {
+ GridEntry entry = SelectedGridItem as GridEntry;
+ if (entry == null)
+ return;
+
+ if (entry.HasCustomEditor) {
+ entry.EditValue ((IWindowsFormsEditorService) this);
+ } else {
+ if (dropdown_form.Visible) {
+ CloseDropDown ();
+ }
+ else {
+ ICollection std_values = entry.AcceptedValues;
+ if (std_values != null) {
+ if (dropdown_list == null) {
+ dropdown_list = new ListBox ();
+ dropdown_list.KeyDown += new KeyEventHandler (listBox_KeyDown);
+ dropdown_list.MouseUp += new MouseEventHandler (listBox_MouseUp);
+ }
+ dropdown_list.Items.Clear ();
+ dropdown_list.BorderStyle = BorderStyle.FixedSingle;
+ int selected_index = 0;
+ int i = 0;
+ string valueText = entry.ValueText;
+ foreach (object obj in std_values) {
+ dropdown_list.Items.Add (obj);
+ if (valueText != null && valueText.Equals (obj))
+ selected_index = i;
+ i++;
+ }
+ dropdown_list.Height = row_height * Math.Min (dropdown_list.Items.Count, 15);
+ dropdown_list.Width = ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0);
+ if (std_values.Count > 0)
+ dropdown_list.SelectedIndex = selected_index;
+ DropDownWidget (dropdown_list);
+ }
+ }
+ }
+ }
+
+ private void DialogButtonClicked (object sender, EventArgs e)
+ {
+ GridEntry entry = this.SelectedGridItem as GridEntry;
+ if (entry != null && entry.HasCustomEditor)
+ {
+ entry.Value = GetResultFromEditor(entry.Value);
+ }
+ }
+
+ public object GetResultFromEditor(object val)
+ {
+ var EditorDialogs = new Dictionary<Type, Dialogs.IEditorDialog>();
+ EditorDialogs.Add(typeof(string[]), new Dialogs.StringArrayDialog());
+ EditorDialogs.Add(typeof(ComboBox.ObjectCollection), new Dialogs.ComboBoxEditorDialog((val as ComboBox.ObjectCollection).Owner));
+ object retval = val;
+ try
+ {
+ var dialog = EditorDialogs[retval.GetType()];
+ dialog.ShowEditor();
+ retval = dialog.Value;
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Object {retval} is not valid for the given property. {ex.Message}", "Property Grid");
+ retval = val;
+ }
+ MessageBox.Show($"val: {val.GetType().Name}, retval: {retval.GetType().Name}", "Property Grid");
+ return retval;
+ }
+
+ private void VScrollBar_HandleValueChanged (object sender, EventArgs e)
+ {
+ UpdateView ();
+ }
+
+ private void grid_textbox_ToggleValue (object sender, EventArgs args)
+ {
+ ToggleValue (this.SelectedGridItem);
+ }
+
+ private void grid_textbox_KeyDown (object sender, KeyEventArgs e)
+ {
+ switch (e.KeyData & Keys.KeyCode) {
+ case Keys.Down:
+ if (e.Alt) {
+ DropDownEdit ();
+ e.Handled = true;
+ }
+ break;
+ }
+ }
+
+ private void grid_textbox_Validate (object sender, CancelEventArgs args)
+ {
+ if (!TrySetEntry (this.SelectedGridItem, grid_textbox.Text))
+ args.Cancel = true;
+ }
+
+ private void ToggleValue (GridEntry entry)
+ {
+ if (entry != null && !entry.IsReadOnly && entry.GridItemType == GridItemType.Property)
+ entry.ToggleValue ();
+ }
+
+ internal void UpdateItem (GridEntry entry)
+ {
+ if (entry == null || entry.GridItemType == GridItemType.Category ||
+ entry.GridItemType == GridItemType.Root) {
+ grid_textbox.Visible = false;
+ InvalidateItem (entry);
+ return;
+ }
+
+ if (this.SelectedGridItem == entry) {
+ SuspendLayout ();
+ grid_textbox.Visible = false;
+ if (entry.IsResetable || !entry.HasDefaultValue)
+ grid_textbox.Font = bold_font;
+ else
+ grid_textbox.Font = this.Font;
+
+ if (entry.IsReadOnly) {
+ grid_textbox.DropDownButtonVisible = false;
+ grid_textbox.DialogButtonVisible = false;
+ grid_textbox.ReadOnly = true;
+ grid_textbox.ForeColor = SystemColors.GrayText;
+ } else {
+ grid_textbox.DropDownButtonVisible = entry.AcceptedValues != null ||
+ entry.EditorStyle == UITypeEditorEditStyle.DropDown;
+ grid_textbox.DialogButtonVisible = entry.EditorStyle == UITypeEditorEditStyle.Modal;
+ grid_textbox.ForeColor = SystemColors.ControlText;
+ grid_textbox.ReadOnly = !entry.IsEditable;
+ }
+ UpdateGridTextBoxBounds (entry);
+ grid_textbox.PasswordChar = entry.IsPassword ? PASSWORD_TEXT_CHAR : '\0';
+ grid_textbox.Text = entry.IsMerged && !entry.HasMergedValue ? String.Empty : entry.ValueText;
+ grid_textbox.Visible = true;
+ InvalidateItem (entry);
+ ResumeLayout (false);
+ } else {
+ grid_textbox.Visible = false;
+ }
+ }
+
+ private void UpdateGridTextBoxBounds (GridEntry entry)
+ {
+ if (entry == null || this.RootGridItem == null)
+ return;
+
+ int y = -vbar.Value*row_height;
+ CalculateItemY (entry, this.RootGridItem.GridItems, ref y);
+ int x = SplitterLocation + ENTRY_SPACING + (entry.PaintValueSupported ? VALUE_PAINT_INDENT : 0);
+ grid_textbox.SetBounds (x + ENTRY_SPACING, y + ENTRY_SPACING,
+ ClientRectangle.Width - ENTRY_SPACING - x - (vbar.Visible ? vbar.Width : 0),
+ row_height - ENTRY_SPACING);
+ }
+
+ // Calculates the sum of the heights of all items before the one
+ //
+ private bool CalculateItemY (GridEntry entry, GridItemCollection items, ref int y)
+ {
+ foreach (GridItem item in items) {
+ if (item == entry)
+ return true;
+ y += row_height;
+ if (item.Expandable && item.Expanded)
+ if (CalculateItemY (entry, item.GridItems, ref y))
+ return true;
+ }
+ return false;
+ }
+
+ private void ScrollToItem (GridEntry item)
+ {
+ if (item == null || this.RootGridItem == null)
+ return;
+
+ int itemY = -vbar.Value*row_height;
+ int value = vbar.Value;;
+ CalculateItemY (item, this.RootGridItem.GridItems, ref itemY);
+ if (itemY < 0) // the new item is above the viewable area
+ value += itemY / row_height;
+ else if (itemY + row_height > Height) // the new item is below the viewable area
+ value += ((itemY + row_height) - Height) / row_height + 1;
+ if (value >= vbar.Minimum && value <= vbar.Maximum)
+ vbar.Value = value;
+ }
+
+ internal void SelectItem (GridEntry oldItem, GridEntry newItem)
+ {
+ if (oldItem != null)
+ InvalidateItemLabel (oldItem);
+ if (newItem != null) {
+ UpdateItem (newItem);
+ ScrollToItem (newItem);
+ } else {
+ grid_textbox.Visible = false;
+ vbar.Visible = false;
+ }
+ }
+
+ internal void UpdateView ()
+ {
+ UpdateScrollBar ();
+ Invalidate ();
+ Update ();
+ UpdateItem (this.SelectedGridItem);
+ }
+
+ internal void ExpandItem (GridEntry item)
+ {
+ UpdateItem (this.SelectedGridItem);
+ Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
+ }
+
+ internal void CollapseItem (GridEntry item)
+ {
+ UpdateItem (this.SelectedGridItem);
+ Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
+ }
+
+ private void ShowDropDownControl (Widget control, bool resizeable)
+ {
+ dropdown_form.Size = control.Size;
+ control.Dock = DockStyle.Fill;
+
+ if (resizeable) {
+ dropdown_form.Padding = dropdown_form_padding;
+ dropdown_form.Width += dropdown_form_padding.Right;
+ dropdown_form.Height += dropdown_form_padding.Bottom;
+ dropdown_form.FormBorderStyle = FormBorderStyle.Sizable;
+ dropdown_form.SizeGripStyle = SizeGripStyle.Show;
+ } else {
+ dropdown_form.FormBorderStyle = FormBorderStyle.None;
+ dropdown_form.SizeGripStyle = SizeGripStyle.Hide;
+ dropdown_form.Padding = Padding.Empty;
+ }
+
+ dropdown_form.Widgets.Add (control);
+ dropdown_form.Width = Math.Max (ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0),
+ control.Width);
+ dropdown_form.Location = PointToScreen (new Point (grid_textbox.Right - dropdown_form.Width, grid_textbox.Location.Y + row_height));
+ RepositionInScreenWorkingArea (dropdown_form);
+ Point location = dropdown_form.Location;
+
+ Form owner = FindForm ();
+ owner.AddOwnedForm (dropdown_form);
+ dropdown_form.Show ();
+ if (dropdown_form.Location != location)
+ dropdown_form.Location = location;
+ control.Show();
+ ShiftUI.MSG msg = new MSG ();
+ object queue_id = XplatUI.StartLoop (Thread.CurrentThread);
+ control.Focus ();
+ while (dropdown_form.Visible && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
+ switch (msg.message) {
+ case Msg.WM_NCLBUTTONDOWN:
+ case Msg.WM_NCMBUTTONDOWN:
+ case Msg.WM_NCRBUTTONDOWN:
+ case Msg.WM_LBUTTONDOWN:
+ case Msg.WM_MBUTTONDOWN:
+ case Msg.WM_RBUTTONDOWN:
+ if (!HwndInControl (dropdown_form, msg.hwnd))
+ CloseDropDown ();
+ break;
+ case Msg.WM_ACTIVATE:
+ case Msg.WM_NCPAINT:
+ if (owner.window.Handle == msg.hwnd)
+ CloseDropDown ();
+ break;
+ }
+ XplatUI.TranslateMessage (ref msg);
+ XplatUI.DispatchMessage (ref msg);
+ }
+ XplatUI.EndLoop (Thread.CurrentThread);
+ }
+
+ private void RepositionInScreenWorkingArea (Form form)
+ {
+ Rectangle workingArea = Screen.FromControl (form).WorkingArea;
+ if (!workingArea.Contains (form.Bounds)) {
+ int x, y;
+ x = form.Location.X;
+ y = form.Location.Y;
+
+ if (form.Location.X < workingArea.X)
+ x = workingArea.X;
+
+ if (form.Location.Y + form.Size.Height > workingArea.Height) {
+ Point aboveTextBox = PointToScreen (new Point (grid_textbox.Right - form.Width, grid_textbox.Location.Y));
+ y = aboveTextBox.Y - form.Size.Height;
+ }
+
+ form.Location = new Point (x, y);
+ }
+ }
+
+ private bool HwndInControl (Widget c, IntPtr hwnd)
+ {
+ if (hwnd == c.window.Handle)
+ return true;
+ foreach (Widget cc in c.Widgets.GetAllWidgets ()) {
+ if (HwndInControl (cc, hwnd))
+ return true;
+ }
+ return false;
+ }
+ #endregion
+
+ #region IWindowsFormsEditorService Members
+
+
+ public void CloseDropDown ()
+ {
+ dropdown_form.Hide ();
+ dropdown_form.Widgets.Clear ();
+ }
+
+ public void DropDownWidget (Widget control)
+ {
+ bool resizeable = this.SelectedGridItem != null ? SelectedGridItem.EditorResizeable : false;
+ ShowDropDownControl (control, resizeable);
+ }
+
+ public ShiftUI.DialogResult ShowDialog (Form dialog) {
+ return dialog.ShowDialog (this);
+ }
+
+ #endregion
+
+ internal class PropertyGridDropDown : Form
+ {
+ protected override CreateParams CreateParams {
+ get {
+ CreateParams cp = base.CreateParams;
+ cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
+ cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOPMOST);
+ return cp;
+ }
+ }
+
+ }
+ }
+}