diff options
| author | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
|---|---|---|
| committer | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
| commit | d40fed5ce2bc806a91245adb18039634eac13ed0 (patch) | |
| tree | f1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Widgets/PropertyGridView.cs | |
| parent | f1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff) | |
| download | shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2 shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip | |
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Widgets/PropertyGridView.cs')
| -rw-r--r-- | source/ShiftUI/Widgets/PropertyGridView.cs | 1101 |
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; + } + } + + } + } +} |
