using System;
using System.Text;
using System.ComponentModel;
using System.Collections;
using System.Windows.Forms;
using System.Drawing;
namespace DevComponents.DotNetBar
{
    /// 
    /// Represents the context menu bar that provides the context menus for the System.Windows.Forms.Control inherited controls on the form.
    /// 
    [ToolboxItem(true), Designer("DevComponents.DotNetBar.Design.ContextMenuBarDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf"), ProvideProperty("ContextMenuEx", typeof(Control)), System.Runtime.InteropServices.ComVisible(false), DefaultEvent("ItemClick")]
    public class ContextMenuBar : Bar, System.ComponentModel.IExtenderProvider
    {
        #region Private Variables
        private Hashtable m_ContextExMenus = new Hashtable();
        private Hashtable m_ContextExHandlers = new Hashtable();
        private bool m_ContextMenuSubclass = true;
        #endregion
        #region Internal Implementation
        public ContextMenuBar() : base()
        {
            this.Visible = false;
            this.WrapItemsDock = true;
            this.WrapItemsFloat = true;
        }
        protected override bool IsContextPopup(BaseItem popup)
        {
            if (this.Items.Contains(popup)) return true;
            return base.IsContextPopup(popup);
        }
        #endregion
        #region Property Hiding
        /// 
        /// Gets/Sets whether Bar is visible or not.
        /// 
        [DevCoBrowsable(false), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new bool Visible
        {
            get { return base.Visible; }
            set { base.Visible = value; }
        }
        #endregion
        #region Extender Implementation
        // *********************************************************************
        //
        // Extended Property ContextMenuEx implementation code
        //
        // *********************************************************************
        bool IExtenderProvider.CanExtend(object target)
        {
            if (target is Control)
                return true;
            return false;
        }
        private delegate void WmContextEventHandler(object sender, WmContextEventArgs e);
        /// 
        /// Returns the instance of the BaseItem that is assigned as context menu to the control.
        /// 
        /// Control to return context menu for.
        /// Instance of the BaseItem used as context menu for the control.
        [DefaultValue(null), Editor(typeof(ContextExMenuTypeEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public BaseItem GetContextMenuEx(Control control)
        {
            if (control is DevComponents.DotNetBar.Controls.RichTextBoxEx)
                control = ((DevComponents.DotNetBar.Controls.RichTextBoxEx)control).RichTextBox;
            BaseItem item = (BaseItem)m_ContextExMenus[control];
            return item;
        }
        /// 
        /// Assigns the context menu to a control.
        /// 
        /// Control to assign the context menu to.
        /// Instance of PopupItem derived class usually ButtonItem to act as context menu for a control. The SubItems collection of the item specified here actually defines the visible context menu items.
        public void SetContextMenuEx(Control control, BaseItem value)
        {
            if (control is DevComponents.DotNetBar.Controls.RichTextBoxEx)
                control = ((DevComponents.DotNetBar.Controls.RichTextBoxEx)control).RichTextBox;
            if (value == null)
            {
                if (m_ContextExMenus.Contains(control))
                {
                    if (m_ContextExHandlers.Contains(control))
                    {
                        ContextMessageHandler h = m_ContextExHandlers[control] as ContextMessageHandler;
                        if (h != null)
                        {
                            h.ContextMenu -= new WmContextEventHandler(this.OnContextMenu);
                            h.ReleaseHandle();
                            h = null;
                        }
                        m_ContextExHandlers.Remove(control);
                    }
                    m_ContextExMenus.Remove(control);
                    control.MouseUp -= new MouseEventHandler(this.ContextExMouseUp);
                    try
                    {
                        control.HandleDestroyed -= new EventHandler(this.ContextExHandleDestroy);
                    }
                    catch { }
                    try
                    {
                        control.HandleCreated -= new EventHandler(this.ContextExHandleCreate);
                    }
                    catch { }
                    try { control.MouseUp -= new MouseEventHandler(this.ContextExMouseUp); }
                    catch { }
                }
            }
            else
            {
                if (m_ContextExMenus.Contains(control))
                {
                    m_ContextExMenus[control] = value;
                }
                else
                {
                    m_ContextExMenus[control] = value;
                    if (!m_ContextExHandlers.Contains(control) && !this.DesignMode)
                    {
                        if (!(control is System.Windows.Forms.TreeView) && !(control is System.Windows.Forms.Form) && 
                            !(control is System.Windows.Forms.Panel
#if FRAMEWORK20
                            || control is System.Windows.Forms.DataGridView
#endif
                            ) && 
                            m_ContextMenuSubclass)
                        {
                            if (control.IsHandleCreated)
                            {
                                ContextMessageHandler h = new ContextMessageHandler();
                                h.ContextMenu += new WmContextEventHandler(this.OnContextMenu);
                                h.ParentControl = control;
                                h.AssignHandle(control.Handle);
                                m_ContextExHandlers[control] = h;
                            }
                            control.HandleDestroyed += new EventHandler(this.ContextExHandleDestroy);
                            control.HandleCreated += new EventHandler(this.ContextExHandleCreate);
                        }
                        if (control is ComboBox)
                        {
                            ComboBox cbo = control as ComboBox;
                            cbo.ContextMenu = new ContextMenu();
                        }
                    }
                    try { control.MouseUp += new MouseEventHandler(this.ContextExMouseUp); }
                    catch { }
                }
            }
        }
        internal bool HasContextMenu(Control ctrl)
        {
            return m_ContextExMenus.Contains(ctrl);
        }
        private void ContextExHandleDestroy(object sender, EventArgs e)
        {
            Control control = sender as Control;
            if (control == null)
                return;
            ContextMessageHandler h = m_ContextExHandlers[control] as ContextMessageHandler;
            if (h != null)
            {
                h.ContextMenu -= new WmContextEventHandler(this.OnContextMenu);
                h.ReleaseHandle();
                h = null;
            }
            m_ContextExHandlers.Remove(control);
        }
        private void ContextExHandleCreate(object sender, EventArgs e)
        {
            Control control = sender as Control;
            if (control == null)
                return;
            if (m_ContextExHandlers.Contains(control))
                return;
            ContextMessageHandler h = new ContextMessageHandler();
            h.ContextMenu += new WmContextEventHandler(this.OnContextMenu);
            h.ParentControl = control;
            h.AssignHandle(control.Handle);
            m_ContextExHandlers[control] = h;
        }
        private void ContextExMouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Right)
                return;
            Control ctrl = sender as Control;
            if (ctrl == null)
                return;
            // Find it in pop-ups
            PopupItem popup = GetContextExItem(ctrl) as PopupItem;
            if (popup == null) return;
            if (!popup.Expanded)
            {
                popup.Style = this.Style;
                popup.SetSourceControl(ctrl);
                popup.Popup(ctrl.PointToScreen(new Point(e.X, e.Y)));
            }
        }
        private Control FindControl(Control parent, IntPtr handle)
        {
            foreach (Control ctrl in parent.Controls)
            {
                if (ctrl.Handle == handle)
                    return ctrl;
                if (ctrl.Controls.Count > 0)
                {
                    Control ret = FindControl(ctrl, handle);
                    if (ret != null)
                        return ret;
                }
            }
            return parent;
        }
        private BaseItem GetContextExItem(Control ctrl)
        {
            BaseItem item = (BaseItem)m_ContextExMenus[ctrl];
            return item;
        }
        private void OnContextMenu(object sender, WmContextEventArgs e)
        {
            ContextMessageHandler h = sender as ContextMessageHandler;
            if (h == null)
                return;
            BaseItem contextItem = GetContextExItem(h.ParentControl);
            if (contextItem == null)
                return;
            // Check whether context menu originated elsewhere in particular a Panel which handles MouseUp
            if (e.Handle != e.SourceHandle && e.SourceHandle != IntPtr.Zero)
            {
                Control ctrl = Control.FromChildHandle(e.Handle);
                if (ctrl != null && ctrl is Panel && this.GetContextExItem(ctrl) != null) return; // WM_CONTEXT bubbles up and Panel handles MouseUp
            }
            // Find it in pop-ups
            PopupItem popup = contextItem as PopupItem;
            popup.Style = this.Style;
            if (e.Button == MouseButtons.None)
            {
                // Get the control with focus
                Control ctrl = Control.FromChildHandle(e.Handle);
                if (ctrl != null && ctrl.Handle != e.Handle)
                {
                    ctrl = FindControl(ctrl, e.Handle);
                }
                popup.SetSourceControl(h.ParentControl);
                if (ctrl != null)
                {
                    if (ctrl.ClientRectangle.Contains(ctrl.PointToClient(Control.MousePosition)))
                        popup.Popup(Control.MousePosition);
                    else
                        popup.Popup(ctrl.PointToScreen(Point.Empty));
                }
                else
                    popup.Popup(Control.MousePosition);
                // We need to eat the message in OnSysKeyUp for Shift+F10 case
                if (this.IgnoreSysKeyUp)
                {
                    this.IgnoreSysKeyUp = false;
                    this.EatSysKeyUp = true;
                }
            }
            else
            {
                // This is handled by the WM_RBUTTONUP just eat it
                if (!e.WmContext)
                {
                    popup.SetSourceControl(h.ParentControl);
                    popup.Popup(e.X, e.Y);
                }
            }
            e.Handled = true;
        }
        private class ContextMessageHandler : NativeWindow
        {
            public event WmContextEventHandler ContextMenu;
            private const int WM_CONTEXTMENU = 0x007B;
            private const int WM_RBUTTONUP = 0x0205;
            private const int WM_NCRBUTTONUP = 0x00A5;
            private const int WM_RBUTTONDOWN = 0x0204;
            public Control ParentControl = null;
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_CONTEXTMENU)
                {
                    if (ContextMenu != null)
                    {
                        int ilParam = WinApi.ToInt(m.LParam);
                        int y = ilParam >> 16;
                        int x = (short)(ilParam & 0xFFFF);
                        IntPtr hWnd = m.WParam;
                        if (hWnd == IntPtr.Zero)
                            hWnd = m.HWnd;
                        bool context = true;
                        if (m.HWnd != m.WParam)
                            context = false;
                        WmContextEventArgs e = new WmContextEventArgs(hWnd, x, y, ((x == -1 && y == -1) ? MouseButtons.None : MouseButtons.Right), context, m.HWnd);
                        ContextMenu(this, e);
                        if (e.Handled)
                            return;
                    }
                }
                // This case was taken out becouse the message was not generated for the listview control and possibly for
                // treview control so this code was moved to the MouseUp event of the Control see ContextExMouseUp
                //				else if(m.Msg==WM_RBUTTONUP || m.Msg==WM_NCRBUTTONUP)
                //				{
                //					if(ContextMenu!=null)
                //					{
                //						int ilParam=m.LParam.ToInt32();
                //						int y=ilParam>>16;
                //						int x=ilParam & 0xFFFF;
                //						Point p=ParentControl.PointToScreen(new Point(x,y));
                //						WmContextEventArgs e=new WmContextEventArgs(m.HWnd,p.X,p.Y,MouseButtons.Right,false);
                //						ContextMenu(this,e);
                //					}
                //				}
                base.WndProc(ref m);
            }
        }
        private class WmContextEventArgs : EventArgs
        {
            private readonly int x;
            private readonly int y;
            private readonly MouseButtons button = 0;
            private readonly IntPtr hwnd;
            private readonly bool wmcontext;
            public bool Handled = false;
            private IntPtr sourceHandle;
            public WmContextEventArgs(IntPtr phwnd, int ix, int iy, MouseButtons eButton, bool WmContextMessage, IntPtr sourceHandle)
            {
                this.x = ix;
                this.y = iy;
                this.button = eButton;
                this.hwnd = phwnd;
                this.wmcontext = WmContextMessage;
                this.sourceHandle = sourceHandle;
            }
            public int X
            {
                get { return this.x; }
            }
            public int Y
            {
                get { return this.y; }
            }
            public MouseButtons Button
            {
                get { return this.button; }
            }
            public IntPtr Handle
            {
                get { return this.hwnd; }
            }
            public bool WmContext
            {
                get { return this.wmcontext; }
            }
            public IntPtr SourceHandle
            {
                get
                {
                    return this.sourceHandle;
                }
            }
        }
        #endregion
    }
}