// This is a part of the Smart PropertyGrid Library
// Copyright (C) 2001-2006 VisualHint
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions 
// of the accompanying license agreement.

#region Using directives

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Windows.Forms.VisualStyles;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Security.Permissions;

#endregion

namespace VisualHint.SmartPropertyGrid
{
    /// <summary>
    /// Inplace control shown in the value column when a textbox for a numerical value plus a combobox for its
    /// unit are needed.
    /// </summary>
    [ToolboxItem(false)]
    public class PropInPlaceUnit : Control, IInPlaceControl
    {
        private PropInPlaceTextbox _edit = null;

        private bool _dontTransferFocus = false;

        private bool _closeByClickOnArrow = false;

        private PropertyEnumerator _ownerPropertyEnum;

        public PropertyEnumerator OwnerPropertyEnumerator
        {
            set
            {
                _ownerPropertyEnum = (PropertyEnumerator)value.Clone();

                if (_edit != null)
                    _edit.OwnerPropertyEnumerator = value;

                _editorContainer.Visible = false;
            }

            get { return _ownerPropertyEnum; }
        }

        /// <summary>
        /// True when the focus is lost because of typing Return or Escape, false otherwise.
        /// </summary>
        private bool _leaveKey = false;

        public bool ReadOnly
        {
            get { return _ownerPropertyEnum.Property.Value.IsReadOnly(_ownerPropertyEnum); }
        }

        public bool RealtimeChange
        {
            set { }
            get { return true; }
        }

        private bool _pushed = false;

        private bool _mouseOver = false;

        /// <summary>
        /// Because Control.OnLostFocus and Control.OnGotFocus doesn't provide a reference to the
        /// window that lost or gained the focus, this variable will contain this missing information.
        /// It is initialized in WndProc when processing WM_FILLFOCUS and WM_SETFOCUS messages.
        /// It is also embedded in a FocusEventArgs instance for event listeners.
        /// </summary>
        private Control _wmFocusMissingArg;

        private PropInPlaceContainer _editorContainer = new PropInPlaceContainer();

        public override string Text
        {
            set { _edit.Text = value; }
            get { return _edit.Text; }
        }

        public override Font Font
        {
            get { return base.Font; }
            set
            {
                base.Font = value;
                if (_edit != null)
                    _edit.Font = value;
            }
        }

        public override Color BackColor
        {
            get { return base.BackColor; }
            set
            {
                base.BackColor = value;

                if (_edit != null)
                    _edit.BackColor = value;
            }
        }

        public override Color ForeColor
        {
            get { return base.ForeColor; }
            set
            {
                base.ForeColor = value;

                if (_edit != null)
                    _edit.ForeColor = value;
            }
        }

        public PropInPlaceUnit()
        {
            _edit = new PropInPlaceTextbox(false);
            _edit.LostFocus += new EventHandler(OnChildLostFocus);
            _edit.Parent = this;
            _edit.TabStop = false;

            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint |
            ControlStyles.OptimizedDoubleBuffer | ControlStyles.Opaque | ControlStyles.ResizeRedraw, true);

            Cursor = Cursors.Arrow;
        }

        protected int GetButtonWidth(Graphics graphics)
        {
            if (ReadOnly)
                return 0;

            return SystemInformation.VerticalScrollBarWidth;
        }

        protected Rectangle GetButtonRect(Graphics graphics)
        {
            int buttonWidth = GetButtonWidth(graphics);
            return new Rectangle(Width - buttonWidth, 0, buttonWidth, Height);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // Draw the background

            _ownerPropertyEnum.Property.ParentGrid.DrawManager.DrawPropertyValueBackground(e.Graphics,
                ClientRectangle, _ownerPropertyEnum);

            // Draw the combobox arrow

            Rectangle buttonRect = GetButtonRect(e.Graphics);
            int listboxWidth = GetListboxWidth();

            Color valueColor;
            if (_ownerPropertyEnum.Property.Enabled == false)
                valueColor = SystemColors.GrayText;
            else
            {
                if (Focused)
                    valueColor = SystemColors.HighlightText;
                else
                    valueColor = _ownerPropertyEnum.Property.Value.ForeColor;
            }

            if (!ReadOnly)
            {
                if (Application.RenderWithVisualStyles)
                {
                    VisualStyleElement vsElement;
                    if (_pushed)
                    {
                        if (_mouseOver)
                            vsElement = VisualStyleElement.ComboBox.DropDownButton.Pressed;
                        else
                            vsElement = VisualStyleElement.ComboBox.DropDownButton.Hot;
                    }
                    else
                    {
                        if (_mouseOver)
                            vsElement = VisualStyleElement.ComboBox.DropDownButton.Hot;
                        else
                            vsElement = VisualStyleElement.ComboBox.DropDownButton.Normal;
                    }

                    VisualStyleRenderer renderer = new VisualStyleRenderer(vsElement);
                    renderer.DrawBackground(e.Graphics, buttonRect);
                }
                else
                {
                    ControlPaint.DrawComboButton(e.Graphics, buttonRect, (_pushed && _mouseOver) ?
                        ButtonState.Pushed : ButtonState.Normal);
                }

                if (Focused)
                {
                    Rectangle focusRect = buttonRect;
                    focusRect.Inflate(-3, -3);
                    ControlPaint.DrawFocusRectangle(e.Graphics, focusRect);
                }
            }

            // Draw the listbox content

            Rectangle listRect = ClientRectangle;
            listRect.X = listRect.Right - listboxWidth;
            listRect.Width = listboxWidth - GetButtonWidth(null);
            Rectangle listRect2 = listRect;
            listRect2.Inflate(0, -1);
            listRect2.Width--;
            if (Focused)
            {
                e.Graphics.FillRectangle(SystemBrushes.FromSystemColor(SystemColors.Highlight), listRect2);
                ControlPaint.DrawFocusRectangle(e.Graphics, listRect2);
            }
            else
            {
                Brush listBrush = new SolidBrush(_ownerPropertyEnum.Property.ParentGrid.GridColor);
                e.Graphics.FillRectangle(listBrush, listRect2);
                listBrush.Dispose();
            }

            PropertyValue unitValue = _ownerPropertyEnum.Property.GetValue(PropertyUnitLook.UnitValue);
            Rectangle strRect = listRect;
            strRect.X += PropertyGrid.GlobalTextMargin;
            strRect.Width = strRect.Right - strRect.Left - PropertyGrid.GlobalTextMargin;
            strRect.Y++;
            strRect.Height--;
            Win32Calls.DrawText(e.Graphics, unitValue.DisplayString, ref strRect, Font, valueColor,
                Win32Calls.DT_VCENTER | Win32Calls.DT_SINGLELINE | Win32Calls.DT_NOPREFIX);

            // Draw the value
            if (_edit == null)
            {
                Rectangle valueRect = ClientRectangle;
                valueRect.Width -= listboxWidth;

                _ownerPropertyEnum.Property.Value.DrawValue(e.Graphics, valueRect, valueColor, _ownerPropertyEnum, Text);
            }

            base.OnPaint(e);
        }

        public bool OnForbidClickThrough(Point pt)
        {
            Graphics graphics = CreateGraphics();
            bool result = GetButtonRect(graphics).Contains(pt);
            graphics.Dispose();

            return result;
        }

        public int GetListboxWidth()
        {
            PropertyValue unitValue = _ownerPropertyEnum.Property.GetValue(PropertyUnitLook.UnitValue);

            string[] units = unitValue.GetDisplayedValues();

            Graphics graphics = CreateGraphics();
            int maxWidth = 0;
            foreach (string unit in units)
            {
                Size size = Win32Calls.GetTextExtent(graphics, unit, Font);
                if (size.Width > maxWidth)
                    maxWidth = size.Width;
            }

            int buttonWidth = GetButtonWidth(graphics);
            int width = maxWidth + 2 * PropertyGrid.GlobalTextMargin + buttonWidth;
            graphics.Dispose();

            if (width > ClientRectangle.Width - buttonWidth - 20)
                width = ClientRectangle.Width - buttonWidth - 20;
            if (width < buttonWidth)
                width = buttonWidth;

            return width;
        }

        protected override void OnMove(EventArgs e)
        {
            base.OnMove(e);
            OnSizeChanged(e);
        }

        public void RepositionChildren()
        {
            if (_edit != null)
            {
                Rectangle clientRect = ClientRectangle;
                if (clientRect.Width > 0)
                {
                    // Get the margins of the editbox
                    int rawMargins = Win32Calls.LoWord(Win32Calls.SendMessage(_edit.Handle, Win32Calls.EM_GETMARGINS, 0, 0));
                    int margins = Win32Calls.LoWord(rawMargins) + Win32Calls.HiWord(rawMargins);

                    int leftPos = (_ownerPropertyEnum.Property.Value.EditboxLeftMargin == -1 ? 0 :
                        PropertyGrid.GlobalTextMargin - _ownerPropertyEnum.Property.Value.EditboxLeftMargin);

                    int listboxWidth = GetListboxWidth();
                    Rectangle editRect = new Rectangle(leftPos, 0, clientRect.Width - listboxWidth - leftPos, clientRect.Height);
                    editRect.Y++;
                    editRect.Height--;

                    // The width of the editbox can't be smaller than the margins or the margins would be void and the text misplaced
                    if (editRect.Width <= margins)
                        _edit.Visible = false;
                    else
                    {
                        _edit.Visible = true;

                        Win32Calls.TEXTMETRIC tm = new Win32Calls.TEXTMETRIC();
                        Graphics graphics = CreateGraphics();
                        Win32Calls.GetTextMetrics(graphics, Font, ref tm);
                        graphics.Dispose();
                        int extraHeight = editRect.Height - tm.tmHeight;
                        editRect.Y += extraHeight / 2;
                        editRect.Height -= extraHeight / 2;

                        _edit.Invalidate();
                        _edit.Bounds = editRect;
                    }
                }
            }

            Invalidate(false);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);

            RepositionChildren();
        }

        protected virtual void OnWmSetFocus(FocusEventArgs e)
        {
            _wmFocusMissingArg = e.FocusControl;

            if (_edit != null)
            {
                if (_dontTransferFocus == false)
                {
                    if (e.FocusControl != _edit)
                    {
                        if (_wmFocusMissingArg != _editorContainer.ContentControl)
                            _edit.Focus();
                    }
                    else if (e.FocusControl == _edit)
                    {
                        if (((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.TabKey) == 0) ||
                            ((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.TabKeyInSubControls) == 0))
                            Parent.Focus();
                    }
                }
            }
            else
                Invalidate();
        }

        protected void ValidateTextInternal()
        {
//            if (mEdit == null)
            {
                Invalidate();

                PropertyValue value = _ownerPropertyEnum.Property.Value;

                // Store the previous value
                string oldValue = value.DisplayString;

                if (oldValue.Equals(Text) == false)
                {
                    // Freeze painting because updating the property may also update other properties in the grid.
                    _ownerPropertyEnum.Property.ParentGrid.BeginUpdate();

                    _ownerPropertyEnum.Property.Value.PreviousValue = _ownerPropertyEnum.Property.Value.GetValue();

                    value.SetValue(Text);

                    // Defreeze painting
                    _ownerPropertyEnum.Property.ParentGrid.EndUpdate();

                    Text = value.DisplayString;

                    // Notify the properties control if the value has really changed
                    if (value.GetStringValue() != oldValue)
                        _ownerPropertyEnum.Property.ParentGrid.NotifyPropertyChanged(new PropertyChangedEventArgs(_ownerPropertyEnum));

                    Parent.Invalidate();
                    Invalidate();
                    if (_edit != null)
                        _edit.Invalidate();
/*                    if (mEdit != null)
                    {
                        RepositionChildren();
                        Refresh();
                    }*/
                }
            }
        }

        protected virtual void OnWmKillFocus(FocusEventArgs e)
        {
            _wmFocusMissingArg = e.FocusControl;
            Invalidate();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            if (_edit == null)
                ValidateTextInternal();

            if (_editorContainer.Contains(_wmFocusMissingArg) == false)
                base.OnLostFocus(new FocusEventArgs(_wmFocusMissingArg, _leaveKey));

            _leaveKey = false;
        }

        void OnChildLostFocus(object source, EventArgs e)
        {
            ValidateTextInternal();

            if (((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.TabKey) != 0) &&
                    ((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.TabKeyInSubControls) != 0))
                _leaveKey = false;
            else
                _leaveKey = (e as FocusEventArgs).LeaveKey;

            if (_leaveKey)
                Parent.Focus();
            else
                base.OnLostFocus(new FocusEventArgs((e as FocusEventArgs).FocusControl, _leaveKey));
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == Win32Calls.WM_SETFOCUS)
                OnWmSetFocus(new FocusEventArgs(FromHandle(m.WParam), false));
            else if (m.Msg == Win32Calls.WM_KILLFOCUS)
                OnWmKillFocus(new FocusEventArgs(FromHandle(m.WParam), false));

            base.WndProc(ref m);
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if ((_edit != null) && (Focused == false))
                Win32Calls.PostMessage(_edit.Handle, Win32Calls.WM_CHAR, e.KeyChar, 0);
            else if (_edit == null)
            {
                // When typing a letter, we try to find the next string after the selected one
                // that begins by this letter

                string[] displayedStrings = _ownerPropertyEnum.Property.Value.GetDisplayedValues();
                char[] chArray = new char[1] { e.KeyChar };
                string letter = new string(chArray);

                for (int i = 0; i < displayedStrings.Length; i++)
                {
                    if (_ownerPropertyEnum.Property.Value.DisplayString.Equals(displayedStrings[i]))
                    {
                        for (int j = 0; j < displayedStrings.Length; j++)
                        {
                            string currentStr = displayedStrings[((i + j) + 1) % displayedStrings.Length];
                            if (string.Compare(currentStr.Substring(0, 1), letter, true, CultureInfo.InvariantCulture) == 0)
                            {
                                Text = currentStr;
                                ValidateTextInternal();
                                break;
                            }
                        }

                        break;
                    }
                }
            }

        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (e.Clicks == 2)
                {
                    Graphics graphics = CreateGraphics();
                    if (GetButtonRect(graphics).Contains(e.Location) == false)
                        ScrollValueOnDoubleClick(!(ModifierKeys == Keys.Shift), false);
                }
                else
                {
                    if (!_closeByClickOnArrow)
                    {
                        Graphics graphics = CreateGraphics();
                        if (GetButtonRect(graphics).Contains(e.Location))
                        {
                            _pushed = true;
                            _mouseOver = true;
                            Capture = true;
                            Invalidate(false);
                        }
                        graphics.Dispose();
                    }
                    else
                        _closeByClickOnArrow = false;

                    _dontTransferFocus = true;
                    Focus();
                    _dontTransferFocus = false;
                }
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _mouseOver = false;
            Invalidate(false);

            base.OnMouseLeave(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            bool oldMouseOver = _mouseOver;
            Graphics graphics = CreateGraphics();
            _mouseOver = GetButtonRect(graphics).Contains(e.Location);
            graphics.Dispose();
            if (oldMouseOver != _mouseOver)
                Invalidate(false);

            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (_pushed && (e.Button == MouseButtons.Left))
            {
                Capture = false;
                _pushed = false;
                _mouseOver = false;
                Invalidate(false);

                Graphics graphics = CreateGraphics();
                if (GetButtonRect(graphics).Contains(e.Location))
                {
                    // Maybe the user typed something in the editbox that has not been validated yet
                    if (_edit != null)
                        _edit.ValidateTextInternal();

                    RunButton();
                }
                graphics.Dispose();
            }

            base.OnMouseUp(e);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Space)
            {
                _mouseOver = true;
                Invalidate(false);
            }
            else if ((e.KeyCode == Keys.Up) || (e.KeyCode == Keys.Down))
            {
                if (e.Alt && (e.KeyCode == Keys.Down))
                {
                    OnF4();
                }
                else if (e.Alt == false)
                {
                    ProcessArrowKey(e.KeyCode, false);
                    Invalidate();
                }
            }
            else if (e.KeyCode == Keys.F4)
                OnF4();
        }

        protected void ProcessArrowKey(Keys key, bool rotate)
        {
            PropertyValue unitValue = _ownerPropertyEnum.Property.GetValue(PropertyUnitLook.UnitValue);

            object[] displayStrings = unitValue.GetDisplayedValues();

            if (unitValue.HasMultipleValues)
            {
                int index = -1;
                if (key == Keys.Up)
                    index = displayStrings.Length - 1;
                else if (key == Keys.Down)
                    index = 0;

                if (index != -1)
                {
                    unitValue.SetValue(displayStrings[index].ToString());

                    _ownerPropertyEnum.Property.ParentGrid.NotifyPropertyChanged(
                        new PropertyChangedEventArgs(_ownerPropertyEnum));
                }
            }
            else
            {
                for (int i = 0; i < displayStrings.Length; i++)
                {
                    if (unitValue.DisplayString.Equals(displayStrings[i]))
                    {
                        int index = -1;

                        if ((key == Keys.Up) && (i > 0))
                            index = i - 1;
                        else if ((key == Keys.Up) && rotate)
                            index = displayStrings.Length - 1;
                        else if ((key == Keys.Down) && (i < displayStrings.Length - 1))
                            index = i + 1;
                        else if ((key == Keys.Down) && rotate)
                            index = 0;

                        if (index != -1)
                        {
                            unitValue.SetValue(displayStrings[index].ToString());

                            _ownerPropertyEnum.Property.ParentGrid.NotifyPropertyChanged(
                                new PropertyChangedEventArgs(_ownerPropertyEnum));
                        }

                        break;
                    }
                }
            }
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            if (e.KeyData == Keys.Space)
            {
                _mouseOver = false;
                Invalidate(false);

                RunButton();
            }
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if ((keyData == Keys.Up) || (keyData == Keys.Down))
                return true;

            return base.IsInputKey(keyData);
        }

        [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
        protected override bool ProcessDialogKey(Keys keyData)
        {
            Keys key = (keyData & Keys.KeyCode);

            if (key == Keys.Return)
            {
                ValidateTextInternal();

                _leaveKey = true;
                Parent.Focus();
                return true;
            }
            else if (key == Keys.Escape)
            {
                if (_edit == null)
                {
                    // Restore the old string in the edit box
                    Text = _ownerPropertyEnum.Property.Value.DisplayString;
                }

                _leaveKey = true;
                Parent.Focus();
                Invalidate();
                return true;
            }
            else if ((key == Keys.F4) || ((key == Keys.Down) && ((keyData & Keys.Alt) != Keys.None)))
            {
                Parent.Focus();
                OnF4();
                return true;
            }
            else if (key == Keys.Tab)
            {
                if (_edit != null)
                {
                    bool forward = ((keyData & Keys.Shift) == Keys.None);

                    if (forward && _edit.Focused)
                    {
                        if (((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.ArrowKeys) != 0) ||
                            (((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.TabKey) != 0) &&
                            ((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.TabKeyInSubControls) != 0)))
                        {
                            _dontTransferFocus = true;
                            Focus();
                            Invalidate(false);
                            _dontTransferFocus = false;
                            return true;
                        }
                    }
                    else if (!forward && _edit.Focused)
                    {
                        if ((_ownerPropertyEnum.Property.ParentGrid.NavigationKeyMode & PropertyGrid.NavigationKeyModes.ArrowKeys) != 0)
                        {
                            Parent.Focus();
                            return true;
                        }
                    }
                    else if (!forward && Focused)
                    {
                        _edit.Focus();
                        _edit.SelectAll();
                        Invalidate(false);
                        return true;
                    }
                }
            }

            return base.ProcessDialogKey(keyData);
        }

        protected void RunButton()
        {
            if (ReadOnly)
                return;

            _ownerPropertyEnum.Property.InPlaceCtrlInAction = this;

            _editorContainer.SetContent(_ownerPropertyEnum, PropertyUnitLook.UnitValue);

            if (_editorContainer.ContentControl == null)
                return;

            _editorContainer.MustBeClosed += new EventHandler<DropDownContentClosedEventArgs>(OnDropDownContentMustBeClosed);

            Win32Calls.SetWindowLong(_editorContainer.Handle, -8, (int)Parent.Handle);

            Rectangle workingArea = Screen.FromControl(this).WorkingArea;
            Point offset = Parent.PointToScreen(new Point(0, 0));

            Rectangle bounds = Bounds;

            // No listbox smaller than the value column
            if (_editorContainer.Width < Size.Width)
                _editorContainer.Width = Size.Width;

            // No listbox left truncated
            Point origin = new Point(bounds.Right - _editorContainer.Size.Width + offset.X, bounds.Bottom + 1 + offset.Y);
            if (origin.X < workingArea.Left)
                origin.X = workingArea.Left;

            // No listbox bottom truncated
            if (origin.Y + _editorContainer.Height > workingArea.Bottom)
                origin.Y = bounds.Top - _editorContainer.Height + offset.Y;

            _editorContainer.Location = origin;
            _editorContainer.ContentControl.Dock = DockStyle.Fill;

            // Show the window but don't activate it
            Win32Calls.ShowWindow(new HandleRef(_editorContainer, _editorContainer.Handle), Win32Calls.SW_SHOWNA);
            _editorContainer.Visible = true;

            _editorContainer.DoModalLoop();
        }

        void OnDropDownContentMustBeClosed(object sender, DropDownContentClosedEventArgs e)
        {
            if (e.ToBeValidated)
            {
                object obj = _editorContainer.DropDownContent.GetValue();
                if (obj is string)
                {
                    PropertyValue unitValue = _ownerPropertyEnum.Property.GetValue(PropertyUnitLook.UnitValue);
                    string oldValue = unitValue.DisplayString;
                    unitValue.PreviousValue = unitValue.GetValue();
                    unitValue.SetValue(obj);

                    if (unitValue.GetStringValue() != oldValue)
                        _ownerPropertyEnum.Property.ParentGrid.NotifyPropertyChanged(new PropertyChangedEventArgs(_ownerPropertyEnum, unitValue.AdditionalValueKey));
                }
            }

            if (e.ReasonLostFocus)
            {
                _wmFocusMissingArg = e.WndTakingFocus;

                // We simulate a LostFocus on this PropInPlaceList
                base.OnLostFocus(new FocusEventArgs(e.WndTakingFocus, false));
            }

            HideDropDownContent();
        }

        protected void HideDropDownContent()
        {
            _ownerPropertyEnum.Property.InPlaceCtrlInAction = null;

            _editorContainer.MustBeClosed -= new EventHandler<DropDownContentClosedEventArgs>(OnDropDownContentMustBeClosed);

            // If the dropdown window is closed because the focus changed to another app,
            // the focus is not reset to the grid
            if (_wmFocusMissingArg != null)
                Parent.Focus();

            _editorContainer.Visible = false;
            _editorContainer.SetContent(null);

            Graphics graphics = CreateGraphics();
            if (GetButtonRect(graphics).Contains(PointToClient(MousePosition)))
                _closeByClickOnArrow = true;
            graphics.Dispose();
        }

        public void OnF4()
        {
            // If the textbox takes the focus it will show a blink, so we prevent that
            _dontTransferFocus = true;

            Focus();
            _dontTransferFocus = false;

            RunButton();
        }

        public bool OnParentKeyDown(KeyEventArgs e)
        {
            if ((_edit != null) && _edit.OnParentKeyDown(e))
                return true;

            return false;
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override bool ProcessKeyPreview(ref Message m)
        {
            if (m.Msg == Win32Calls.WM_KEYDOWN)
            {
                Keys key = (Keys)m.WParam;
                if ((key == Keys.Down) || (key == Keys.Up))
                {
                    ProcessArrowKey(key, false);
                    _edit.SelectAll();
                    return true;
                }
            }

            return base.ProcessKeyPreview(ref m);
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            int nb = e.Delta / 120;
            if (nb > 0)
            {
                for (int i = 0; i < nb; i++)
                    ProcessArrowKey(Keys.Up, false);
            }
            else
            {
                for (int i = 0; i < -nb; i++)
                    ProcessArrowKey(Keys.Down, false);
            }

            Invalidate();

            (e as HandledMouseEventArgs).Handled = true;
        }

        public bool GoToNextPropertyOnTab(bool forward)
        {
            if (forward)
            {
                if (_edit == null)
                    return true;
                else
                    return Focused;
            }
            else
            {
                if (_edit == null)
                    return true;
                else
                    return _edit.Focused;
            }
        }

        public virtual bool ScrollValueOnDoubleClick(bool forward, bool force)
        {
            if (ReadOnly)
                return false;

            if (forward)
                ProcessArrowKey(Keys.Down, true);
            else
                ProcessArrowKey(Keys.Up, true);

            return true;
        }
    }
}

