// 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.Windows.Forms.Design;
using System.Diagnostics;
using System.ComponentModel;
using System.Security.Permissions;
using System.Drawing.Design;
using System.Drawing.Drawing2D;

#endregion

namespace VisualHint.SmartPropertyGrid
{
    /// <summary>
    /// Inplace control shown in the value column when the usual Microsoft UITypeEditor selector is needed.
    /// </summary>
    [ToolboxItem(false)]
    public class PropInPlaceUITypeEditor : Control, IInPlaceControl
    {
        internal class DropDownForm : Form
        {
            private bool _canceled = false;
//            private bool _closeDropDownCalled;

            private PropInPlaceUITypeEditor _inPlaceCtrl;

            private enum MoveDirection
            {
                None = 0,
                Vertical = 1,
                NESW = 2,
                NWSE = 3
            }

            private MoveDirection _currentMoveDirection = MoveDirection.None;

            private bool _resizable;

            public static readonly int ResizeGripHeight = 0;

            public static readonly Size MinSize;

            public bool ResizeInProgress
            {
                get { return (_currentMoveDirection != MoveDirection.None); }
            }

            private Point _resizeStartPoint = Point.Empty;

            private bool _resizeTop;

            public bool ResizeTop
            {
                get { return _resizeTop; }

                set
                {
                    DockPadding.Bottom = 0;
                    DockPadding.Top = 0;

                    _resizeTop = value;
                    if (_resizable)
                    {
                        _resizeGripImage = null;

                        if (value)
                            DockPadding.Top = ResizeGripHeight + 1;
                        else
                            DockPadding.Bottom = ResizeGripHeight + 1;
                    }
                }
            }

            static DropDownForm()
            {
                ResizeGripHeight = SystemInformation.HorizontalScrollBarHeight;
                MinSize = new Size(SystemInformation.VerticalScrollBarWidth * 4, SystemInformation.HorizontalScrollBarHeight * 4);
            }

            public DropDownForm(PropInPlaceUITypeEditor inPlaceCtrl, bool resizable)
            {
                FormBorderStyle = FormBorderStyle.None;
                ShowInTaskbar = false;
                KeyPreview = true;
                StartPosition = FormStartPosition.Manual;
                _inPlaceCtrl = inPlaceCtrl;
                _resizable = resizable;
            }

            protected override CreateParams CreateParams
            {
                [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get
                {
                    CreateParams cp = base.CreateParams;

                    cp.ExStyle = Win32Calls.WS_EX_TOOLWINDOW | Win32Calls.WS_EX_TOPMOST;
                    cp.Style = Win32Calls.WS_POPUP | Win32Calls.WS_BORDER;

                    if (OSFeature.IsPresent(SystemParameter.DropShadow))
                        cp.ClassStyle |= Win32Calls.CS_DROPSHADOW;

                    return cp;
                }
            }

            private Control _innerControl;

            public void SetControl(Control ctl)
            {
                Controls.Clear();

                _innerControl = ctl;
                Controls.Add(_innerControl);
            }

            public Control GetControl()
            {
                return _innerControl;
            }

            public bool Canceled
            {
                get { return _canceled; }
            }

            public void CloseDropDown()
            {
                Hide();
            }

            protected override void OnVisibleChanged(EventArgs e)
            {
                if (Visible)
                    _canceled = false;

                _resizeGripImage = null;

                base.OnVisibleChanged(e);
            }

            protected override void OnKeyDown(KeyEventArgs e)
            {
                base.OnKeyDown(e);

                if (e.KeyCode == Keys.Escape)
                    _canceled = true;

                if ((e.KeyCode == Keys.Escape) || (e.KeyCode == Keys.F4) ||
                    (e.Alt && ((e.KeyCode == Keys.Down) || (e.KeyCode == Keys.Up))))
                    Hide();
            }

            private MoveDirection GetMoveDirection(Point pt)
            {
                if (ResizeTop)
                {
                    Rectangle rect = new Rectangle(0, 0, ResizeGripHeight, ResizeGripHeight);
                    if (rect.Contains(pt))
                        return MoveDirection.NWSE;
                }
                else
                {
                    Rectangle rect = new Rectangle(0, Height - ResizeGripHeight, ResizeGripHeight, ResizeGripHeight);
                    if (rect.Contains(pt))
                        return MoveDirection.NESW;
                }

                int halfHeightNS = ResizeGripHeight / 2;
                Rectangle vertRect = new Rectangle(0, ResizeTop ? 0 : Height - halfHeightNS, Width, halfHeightNS);
                if (vertRect.Contains(pt))
                    return MoveDirection.Vertical;

                return MoveDirection.None;
            }

            protected override void OnMouseDown(MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                {
                    _currentMoveDirection = GetMoveDirection(new Point(e.X, e.Y));
                    if (_currentMoveDirection != MoveDirection.None)
                    {
                        _resizeStartPoint = PointToScreen(e.Location);
                        Capture = true;
                    }
                }

                base.OnMouseDown(e);
            }

            protected override void OnMouseMove(MouseEventArgs e)
            {
                if (_currentMoveDirection != MoveDirection.None)
                {
                    Point pt = PointToScreen(e.Location);

                    int deltaY = pt.Y - _resizeStartPoint.Y;
                    int deltaX = (_currentMoveDirection != MoveDirection.Vertical ? pt.X - _resizeStartPoint.X : 0);

                    if (Width - deltaX < MinSize.Width)
                    {
                        deltaX = Width - MinSize.Width;
                        _resizeStartPoint.X = Right - MinSize.Width + (_resizeStartPoint.X - Left);
                    }
                    else
                        _resizeStartPoint.X = pt.X;

                    if (ResizeTop)
                    {
                        if (Height - deltaY < MinSize.Height)
                        {
                            deltaY = Height - MinSize.Height;
                            _resizeStartPoint.Y = Bottom - MinSize.Height + (_resizeStartPoint.Y - Top);
                        }
                        else
                            _resizeStartPoint.Y = pt.Y;
                    }
                    else
                    {
                        if (Height + deltaY < MinSize.Height)
                        {
                            deltaY = MinSize.Height - Height;
                            _resizeStartPoint.Y = Top + MinSize.Height + (_resizeStartPoint.Y - Bottom);
                        }
                        else
                            _resizeStartPoint.Y = pt.Y;
                    }

                    Rectangle oldBounds = Bounds;
                    if (ResizeTop)
                        Bounds = new Rectangle(Left + deltaX, Top + deltaY, Width - deltaX, Height - deltaY);
                    else
                        Bounds = new Rectangle(Left + deltaX, Top, Width - deltaX, Height + deltaY);

                    if (oldBounds != Bounds)
                        Refresh();
                }
                else
                {
                    MoveDirection md = GetMoveDirection(e.Location);
                    switch (md)
                    {
                        case MoveDirection.Vertical:
                            Cursor = Cursors.SizeNS;
                            break;
                        case MoveDirection.NESW:
                            Cursor = Cursors.SizeNESW;
                            break;
                        case MoveDirection.NWSE:
                            Cursor = Cursors.SizeNWSE;
                            break;
                        default:
                            Cursor = null;
                            break;
                    }
                }

                base.OnMouseMove(e);
            }

            protected override void OnMouseLeave(EventArgs e)
            {
                Cursor = null;
                base.OnMouseLeave(e);
            }

            protected override void OnMouseUp(MouseEventArgs e)
            {
                base.OnMouseUp(e);

                if (e.Button == MouseButtons.Left)
                {
                    _currentMoveDirection = MoveDirection.None;
                    Capture = false;
                }
            }

            protected override void OnDeactivate(EventArgs e)
            {
                // We set the Owner to null BEFORE calling the base class.
                // If we wouldn't do it, the Picker form would lose focus after 
                // the dropdown is closed.
                Owner = null;

                base.OnDeactivate(e);

                // If the form was closed by any other means as the CloseDropDown call, it is because
                // the user clicked outside the form, or pressed the ESC key.
//                if (!_closeDropDownCalled)
  //                  _canceled = true;

                Hide();

                // Click outside the app or ALT-TAB ?
                if (ActiveForm == null)
                    _inPlaceCtrl.OwnerPropertyEnumerator.Property.ParentGrid.Grid.HideInPlaceControl();
            }

            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);

                if (_resizable)
                {
                    Rectangle rectangle1 = new Rectangle(0, ResizeTop ? -1 : (Height - ResizeGripHeight - 3), ResizeGripHeight, ResizeGripHeight);
                    e.Graphics.DrawImage(GetResizeGripImage(e.Graphics), rectangle1);
                    int lineY = ResizeTop ? GetControl().Top - 1 : GetControl().Bottom;
                    Pen pen = new Pen(SystemColors.ControlDark, 1f);
                    e.Graphics.DrawLine(pen, 0, lineY, Width, lineY);
                    pen.Dispose();
                }
            }

            private Bitmap _resizeGripImage = null;

            private Bitmap GetResizeGripImage(Graphics g)
            {
                if (_resizeGripImage == null)
                {
                    _resizeGripImage = new Bitmap(ResizeGripHeight, ResizeGripHeight, g);
                    using (Graphics graphics = Graphics.FromImage(_resizeGripImage))
                    {
                        Matrix matrix = new Matrix();
                        matrix.Translate(ResizeGripHeight + 1, ResizeTop ? ResizeGripHeight + 1 : 0f);
                        matrix.Scale(-1f, ResizeTop ? -1f : 1f);
                        graphics.Transform = matrix;
                        ControlPaint.DrawSizeGrip(graphics, BackColor, 0, 0, ResizeGripHeight, ResizeGripHeight);
                        graphics.ResetTransform();
                    }
                    _resizeGripImage.MakeTransparent(BackColor);
                }
                return _resizeGripImage;
            }
        }

        // This class actually hosts the UITypeEditor by implementing the IWindowsFormsEditorService.
        private class EditorService : IWindowsFormsEditorService, IServiceProvider
        {
            // The associated  picker control.
            private PropInPlaceUITypeEditor _inPlaceCtrl;

            // Reference to the drop down, which hosts the UITypeEditor control.
            private DropDownForm _dropDownHolder;

            internal DropDownForm DropDownHolder
            {
                get { return _dropDownHolder; }
            }

            // Cached _DropDownHolder.Canceled flag in order to allow it to be inspected
            // by the owning Picker control.
            private bool _canceled;

            public EditorService(PropInPlaceUITypeEditor inPlaceCtrl)
            {
                _inPlaceCtrl = inPlaceCtrl;
            }

            public bool Canceled
            {
                get { return _canceled; }
            }

            public void CloseDropDown()
            {
                if (_dropDownHolder != null)
                    _dropDownHolder.CloseDropDown();
            }

            public void DropDownControl(Control control)
            {
                if (_dropDownHolder == null)
                    _dropDownHolder = new DropDownForm(_inPlaceCtrl, _inPlaceCtrl.Resizable);

                _canceled = false;

                _dropDownHolder.SetControl(control);

                // Initialize the hosting form for the control.
                Rectangle rect = control.Bounds;
                rect.Inflate(1,1);
                if (_inPlaceCtrl.Resizable)
                    rect.Height += DropDownForm.ResizeGripHeight + 1;
                _dropDownHolder.Bounds = rect;
                _dropDownHolder.Width = Math.Max(_inPlaceCtrl.Width + 1, _dropDownHolder.Width);

                // Lookup a parent form for the Picker control and make the dropdown form to be owned
                // by it. This prevents to show the dropdown form icon when the user presses the At+Tab system 
                // key while the dropdown form is displayed.
                Control pickerForm = GetParentForm(_inPlaceCtrl);
                if ((pickerForm != null) && (pickerForm is Form))
                    _dropDownHolder.Owner = (Form)pickerForm;

                // Ensure the whole drop-down UI is displayed on the screen and show it.
                PositionDropDownHolder();

                control.Dock = DockStyle.Fill;

                control.Resize += new EventHandler(OnUIControlResize);
                _dropDownHolder.Show(); // ShowDialog would disable clicking outside the dropdown area!
                control.Refresh();

                // Wait for the user to select a new value (in which case the UITypeEditor calls our CloseDropDown
                // method) or cancel the selection (no CloseDropDown called, the Cancel flag is set to True).
                DoModalLoop();

                // Remember the cancel flag
                _canceled = _dropDownHolder.Canceled;
            }

            void OnUIControlResize(object sender, EventArgs e)
            {
                if (_dropDownHolder.ResizeInProgress == false)
                {
                    Rectangle rect = _dropDownHolder.GetControl().Bounds;
                    rect.Inflate(1, 1);
                    if (_inPlaceCtrl.Resizable)
                    {
                        int delta = DropDownForm.ResizeGripHeight + 1;
                        rect.Height += delta;
                    }
                    _dropDownHolder.Size = rect.Size;
                }
            }

            public DialogResult ShowDialog(Form dialog)
            {
                _inPlaceCtrl.Parent.Focus();

                // Lookup a parent form for the Picker control and make the dropdown form to be owned
                // by it. This prevents to show the dropdown form icon when the user presses the At+Tab system 
                // key while the dropdown form is displayed.
                Control pickerForm = GetParentForm(_inPlaceCtrl);
                return dialog.ShowDialog(pickerForm);
            }

            public object GetService(Type serviceType)
            {
                if (serviceType.Equals(typeof(IWindowsFormsEditorService)))
                    return this;

                return null;
            }

            private void DoModalLoop()
            {
                Debug.Assert(_dropDownHolder != null);

                while(_dropDownHolder.Visible)
                {
                    Application.DoEvents();
                    Win32Calls.MsgWaitForMultipleObjects(1, IntPtr.Zero, true, 5, 255);
                }
            }

            private void PositionDropDownHolder()
            {
                // Convert _Picker location to screen coordinates.
                Point loc = _inPlaceCtrl.Parent.PointToScreen(_inPlaceCtrl.Location);
                loc.X -= _dropDownHolder.Width - _inPlaceCtrl.Width;

                Rectangle workingArea = Screen.FromControl(_inPlaceCtrl).WorkingArea;
                //                Rectangle workingArea = Screen.PrimaryScreen.WorkingArea;

                // Position the dropdown X coordinate in order to be displayed in its entirety.
                if (loc.X < workingArea.X)
                    loc.X = workingArea.X;
                else if (loc.X + _dropDownHolder.Width > workingArea.Right)
                    loc.X = workingArea.Right - _dropDownHolder.Width;

                // Do the same for the Y coordinate.
                if (loc.Y + _inPlaceCtrl.Height + _dropDownHolder.Height > workingArea.Bottom)
                {
                    loc.Offset(0, -_dropDownHolder.Height);  // dropdown will be above the picker control
                    _dropDownHolder.ResizeTop = true;
                }
                else
                {
                    loc.Offset(0, _inPlaceCtrl.Height); // dropdown will be below the picker
                    _dropDownHolder.ResizeTop = false;
                }

                _dropDownHolder.Location = loc;
            }

            private Control GetParentForm(Control ctl)
            {
                do
                {
                    if (ctl.Parent == null)
                        return ctl;
                    else
                        ctl = ctl.Parent;
                }
                while (true);
            }
        }

        // This is the  type editor - it displays the drop-down UI calling
        // our IWindowsFormsEditorService implementation.
        private UITypeEditor _editor = null;

        private EditorService _editorService;

        private PropInPlaceTextbox _edit = null;

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

        private bool _dontTransferFocus = false;
        
        private PropertyEnumerator _ownerPropertyEnum;

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

                if (_edit != null)
                {
                    _edit.OwnerPropertyEnumerator = value;
                    _edit.Text = Text;
                }
            }

            get { return _ownerPropertyEnum; }
        }

        public bool ReadOnly
        {
            get
            {
                // A modal editor always gives the chance to edit the content of collections
                if (_editor.GetEditStyle(_ownerPropertyEnum.Property.Value.GetTypeDescriptorContext(
                    _ownerPropertyEnum)) == UITypeEditorEditStyle.Modal)
                    return false;

                return _ownerPropertyEnum.Property.Value.IsReadOnly(_ownerPropertyEnum);
            }
        }

        private bool _realtimeChange = true;

        public bool RealtimeChange
        {
            set { _realtimeChange = value; }
            get { return _realtimeChange; }
        }

        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;

        public string ActualText
        {
            set
            {
                if (_edit == null)
                    Text = value;
                else
                    _edit.Text = value;
                Refresh();
            }

            get
            {
                if (_edit == null)
                    return Text;
                else
                    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 PropInPlaceUITypeEditor(UITypeEditor _editor, bool editable)
        {
            this._editor = _editor;

            if (editable)
            {
                _edit = new PropInPlaceTextbox(false);
                _edit.LostFocus += new EventHandler(OnChildLostFocus);
                _edit.Parent = this;
                _edit.Text = Text;
                _edit.TabStop = false;
            }

            _editorService = new EditorService(this);

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

            Cursor = Cursors.Arrow;
        }

        public bool Resizable
        {
            get { return _editor.IsDropDownResizable; }
        }

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

            return SystemInformation.VerticalScrollBarWidth;
        }

        protected Rectangle GetButtonRect(Graphics graphics)
        {
            if (ReadOnly)
                return Rectangle.Empty;

            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 value
            Rectangle buttonRect = GetButtonRect(e.Graphics);

            Rectangle valueRect = ClientRectangle;
            valueRect.Width -= buttonRect.Width + 1;

            if (_edit == null)
            {
                if (Focused && (_ownerPropertyEnum.Property.InPlaceCtrlInAction == null))
                {
                    Rectangle fillRect = _ownerPropertyEnum.Property.Value.GetStringValueRect(e.Graphics, valueRect, Point.Empty);
                    fillRect.Y++;
                    fillRect.Height -= 2;
                    fillRect.X -= PropertyGrid.GlobalTextMargin / 2;
                    fillRect.Width = buttonRect.Left - 1 - fillRect.Left;
                    e.Graphics.FillRectangle(SystemBrushes.FromSystemColor(SystemColors.Highlight), fillRect);
                }
            }

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

            _ownerPropertyEnum.Property.Value.DrawValue(e.Graphics, valueRect, valueColor, _ownerPropertyEnum, null/*ActualText*/);

            // Draw the combobox arrow

            if (!ReadOnly)
            {
                UITypeEditorEditStyle uiStyle = _editor.GetEditStyle(_ownerPropertyEnum.Property.Value.GetTypeDescriptorContext(_ownerPropertyEnum));

                if (Application.RenderWithVisualStyles)
                {
                    if (uiStyle == UITypeEditorEditStyle.DropDown)
                    {
                        VisualStyleRenderer renderer1 = new VisualStyleRenderer(_mouseOver ? VisualStyleElement.ComboBox.DropDownButton.Hot : VisualStyleElement.ComboBox.DropDownButton.Normal);
                        if (_pushed)
                            renderer1.SetParameters(VisualStyleElement.ComboBox.DropDownButton.Pressed);
                        renderer1.DrawBackground(e.Graphics, buttonRect);
                    }
                    else if (uiStyle == UITypeEditorEditStyle.Modal)
                    {
                        Rectangle ellipsisButtonRect = buttonRect;
                        ellipsisButtonRect.Inflate(1, 0);

                        VisualStyleRenderer renderer1 = new VisualStyleRenderer(_mouseOver ? VisualStyleElement.Button.PushButton.Hot : VisualStyleElement.Button.PushButton.Normal);
                        if (_pushed)
                            renderer1.SetParameters(VisualStyleElement.Button.PushButton.Pressed);
                        renderer1.DrawBackground(e.Graphics, ellipsisButtonRect);

                        renderer1.DrawText(e.Graphics, ellipsisButtonRect, "...", false, TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
                    }
                }
                else
                {
                    if (uiStyle == UITypeEditorEditStyle.DropDown)
                        ControlPaint.DrawComboButton(e.Graphics, buttonRect, (_pushed && _mouseOver) ?
                            ButtonState.Pushed : ButtonState.Normal);
                    else if (uiStyle == UITypeEditorEditStyle.Modal)
                    {
                        ControlPaint.DrawButton(e.Graphics, buttonRect, (_pushed && _mouseOver) ? ButtonState.Pushed : ButtonState.Normal);
                        StringFormat stringFormat = (StringFormat)StringFormat.GenericDefault.Clone();
                        stringFormat.Alignment = StringAlignment.Center;
                        stringFormat.LineAlignment = StringAlignment.Center;
                        Brush brush = new SolidBrush(Color.Black);
                        e.Graphics.DrawString("...", Font, brush, buttonRect, stringFormat);
                        brush.Dispose();
                    }
                }

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

            base.OnPaint(e);
        }

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

            return result;
        }

        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);

                    UITypeEditorEditStyle uiStyle = _editor.GetEditStyle(_ownerPropertyEnum.Property.Value.GetTypeDescriptorContext(_ownerPropertyEnum));
                    int buttonWidth = 0;
                    if (uiStyle != UITypeEditorEditStyle.None)
                        buttonWidth = GetButtonWidth(null);

//                    Rectangle editRect = new Rectangle(0, 0, clientRect.Width - buttonWidth, clientRect.Height);
  //                  editRect.Inflate(0, -1);
                    Rectangle valueRect = ClientRectangle;
                    valueRect.Width -= buttonWidth + 1;
                    valueRect.Height++; // for GetStringValueRect()
                    Graphics graphics = CreateGraphics();
                    valueRect = _ownerPropertyEnum.Property.Value.GetStringValueRect(graphics, valueRect, Point.Empty);
                    valueRect.Height--;

                    int leftPos = valueRect.Left - PropertyGrid.GlobalTextMargin + (_ownerPropertyEnum.Property.Value.EditboxLeftMargin == -1 ? 0 :
                        PropertyGrid.GlobalTextMargin - _ownerPropertyEnum.Property.Value.EditboxLeftMargin);
                    Rectangle editRect = new Rectangle(leftPos, 0, clientRect.Width - buttonWidth - 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();
                        Win32Calls.GetTextMetrics(graphics, Font, ref tm);
                        int extraHeight = editRect.Height - tm.tmHeight;
                        editRect.Y += extraHeight / 2;
                        editRect.Height -= extraHeight / 2;

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

                    graphics.Dispose();
                }
            }

            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 != null) && (_editorService.DropDownHolder != _wmFocusMissingArg.TopLevelControl))
                            _edit.Focus();
                    }
                    else if (e.FocusControl == _edit)
                        Parent.Focus();
                }
            }
            else
                Invalidate();
        }

        protected void ValidateTextInternal()
        {
            //            if (mEdit == null)
            {
                PropertyValue value = _ownerPropertyEnum.Property.Value;

                // Store the previous value
                string oldValue = "";
                if (value.HasMultipleValues == false)
                    oldValue = value.GetStringValue();

                if (oldValue != ActualText)
                {
                    // 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(ActualText);

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

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

                    ActualText = value.DisplayString;

                    Parent.Invalidate();
                    Invalidate();
                }
            }
        }

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

        protected override void OnLostFocus(EventArgs e)
        {
            _pushed = false;
            _mouseOver = false;

            if (_edit == null)
                ValidateTextInternal();

            if (e is FocusEventArgs)
                base.OnLostFocus(e);
            else
                base.OnLostFocus(new FocusEventArgs(_wmFocusMissingArg, _leaveKey));

            _leaveKey = false;
        }

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

            _leaveKey = (e as FocusEventArgs).LeaveKey;
            if (_leaveKey)
                Parent.Focus();
            else
                base.OnLostFocus(e);
        }

        [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]))
                    {
                        int j;
                        for (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
                {
                    Graphics graphics = CreateGraphics();
                    if (GetButtonRect(graphics).Contains(e.Location))
                    {
                        _pushed = true;
                        _mouseOver = true;
                        Capture = true;
                        Invalidate(false);
                        Update();
                    }
                    graphics.Dispose();

                    _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.KeyData == Keys.Space)
            {
                _mouseOver = true;
                Invalidate(false);
            }
            else if ((e.KeyData == Keys.Up) || (e.KeyData == Keys.Down))
            {
                ProcessArrowKey(e.KeyData);
                Invalidate();
            }
        }

        protected void ProcessArrowKey(Keys key)
        {
            if (ReadOnly)
                return;

            string[] displayStrings = _ownerPropertyEnum.Property.Value.GetDisplayedValues();

            if (_ownerPropertyEnum.Property.Value.HasMultipleValues)
            {
                if (key == Keys.Up)
                {
                    ActualText = displayStrings[displayStrings.Length - 1].ToString();
                    _ownerPropertyEnum.Property.Value.ImageIndex = displayStrings.Length - 1;
                }
                else if (key == Keys.Down)
                {
                    ActualText = displayStrings[0].ToString();
                    _ownerPropertyEnum.Property.Value.ImageIndex = 0;
                }

                RepositionChildren();

                if (RealtimeChange)
                    ValidateTextInternal();
                else
                    Invalidate();
            }
            else
            {
                for (int i = 0; i < displayStrings.Length; i++)
                {
                    if (displayStrings[i].Equals(ActualText))
                    {
                        bool change = false;

                        if ((key == Keys.Up) && (i > 0))
                        {
                            ActualText = displayStrings[i - 1];
                            change = true;
                        }
                        else if ((key == Keys.Down) && (i < displayStrings.Length - 1))
                        {
                            ActualText = displayStrings[i + 1];
                            change = true;
                        }

                        if (change && _realtimeChange)
                            ValidateTextInternal();

                        break;
                    }
                }
            }
        }

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

                RunButton();
            }
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if (!ReadOnly)
            {
                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)
        {
            if (keyData == Keys.Return)
            {
                ValidateTextInternal();

                _pushed = false;
                _mouseOver = false;

                _leaveKey = true;
                Parent.Focus();
                return true;
            }

            Keys key = (keyData & Keys.KeyCode);

            if (key == Keys.Escape)
            {
                // Restore the old string in the edit box
                PropertyValue value = _ownerPropertyEnum.Property.Value;
                if (value != null)
                    Text = value.DisplayString;

                _pushed = false;
                _mouseOver = false;

                _leaveKey = true;
                Parent.Focus();
                Invalidate();
                return true;
            }
            else if ((key == Keys.F4) || ((key == Keys.Down) && ((keyData & Keys.Alt) != Keys.None)))
            {
                _pushed = false;
                _mouseOver = false;

                Parent.Focus();
                OnF4();
                return true;
            }
            else if (key == Keys.Tab)
            {
                if ((_edit != null) && !ReadOnly)
                {
                    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);
        }

        [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);
                    _edit.SelectAll();
                    _edit.Refresh();
                    return true;
                }
            }

            return base.ProcessKeyPreview(ref m);
        }

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

//            try
            {
                if (_editor.GetEditStyle() == UITypeEditorEditStyle.DropDown)
                    _ownerPropertyEnum.Property.InPlaceCtrlInAction = this;

                PropertyValue propertyValue = _ownerPropertyEnum.Property.Value;

                object value;
                PropertyTypeDescriptorContext context = propertyValue.GetTypeDescriptorContext(_ownerPropertyEnum);
                if (propertyValue.HasMultipleValues)
                    value = "";
                else if (propertyValue.TypeConverter.CanConvertFrom(context, typeof(string)))
                    value = propertyValue.TypeConverter.ConvertFromString(context, propertyValue.CultureInfo, ActualText);
                else
                    value = propertyValue.GetValue();

                // Display the UI.
                IWindowsFormsEditorService service1 = (IWindowsFormsEditorService)_editorService.GetService(typeof(IWindowsFormsEditorService));

                object newValue = _editor.EditValue(context, _editorService, value);

                // If the user didn't cancel the selection, remember the new value.
                if ((newValue != null) && (newValue != value) && (!_editorService.Canceled))
                {
                    propertyValue.SetValue(newValue);
                    ActualText = propertyValue.TypeConverter.ConvertToString(context, propertyValue.CultureInfo, newValue);
                    Invalidate();
                    Refresh();
                    _ownerPropertyEnum.Property.ParentGrid.NotifyPropertyChanged(new PropertyChangedEventArgs(_ownerPropertyEnum));
                }

                if (_editor.GetEditStyle() == UITypeEditorEditStyle.DropDown)
                {
                    Parent.Invalidate();
                    if (Focused || !Parent.ContainsFocus)
                        FindForm().ActiveControl = Parent;
                    else
                        Parent.Focus();
                }
            }
//            catch (Exception)
  //          {
    //        }

            if (_editor.GetEditStyle() == UITypeEditorEditStyle.DropDown)
                _ownerPropertyEnum.Property.InPlaceCtrlInAction = null;
        }

        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.ReadOnly && _edit.OnParentKeyDown(e))
                return true;

            return false;
        }

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

            Invalidate();

            (e as HandledMouseEventArgs).Handled = true;
        }

        public bool GoToNextPropertyOnTab(bool forward)
        {
            if (forward)
            {
                if ((_edit == null) || ReadOnly)
                    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;

            string[] displayStrings = _ownerPropertyEnum.Property.Value.GetDisplayedValues();

            for (int i = 0; i < displayStrings.Length; i++)
            {
                if (_ownerPropertyEnum.Property.Value.DisplayString.Equals(displayStrings[i]))
                {
                    int index;
                    if (forward)
                        index = (i < displayStrings.Length - 1 ? i + 1 : 0);
                    else
                        index = (i > 0 ? i - 1 : displayStrings.Length - 1);

                    _ownerPropertyEnum.Property.Value.ImageIndex = index;
                    ActualText = displayStrings[index];
                    ValidateTextInternal();

                    return true;
                }
            }

            return false;
        }
    }
}
