// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU General Public License(GPL).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

namespace UVTexIntegra.CustomControls
{
    //! @addtogroup UVTexIntegra-CustomControls名前空間
    //! @{

    //! @brief GLControlをラッピングするユーザーコントロールクラスです。
    //! @details このクラスは以下の機能を実装します。
    //! - ウィンドウ変形時のアスペクト比とレターボックス表示
    //! - コントロールがフォーカス不能（CanFocusすなわちVisible/Enabledがfalse）な場合のレンダリングメソッドの呼び出し停止
    //! - .Netグラフィックス標準の背景レンダリングのサポート
    public partial class GLPanel : System.Windows.Forms.UserControl
    {
        public enum CantFocusRenderMode
        {
            Invisible,
            BackColor,
            DefaultBackground,
        }

        public enum PrePaintStatus
        {
            NotAlready,
            Canceled,
            Complete
        }

        private class StylableGLControl : OpenTK.GLControl
        {
            public new void SetStyle(System.Windows.Forms.ControlStyles flag, bool value) { base.SetStyle(flag, value); }
            public new bool GetStyle(System.Windows.Forms.ControlStyles flag) { return base.GetStyle(flag); }
            public new void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) { base.OnPaintBackground(e); }
        }

        //! @brief GLのアスペクト比を設定します
        [System.ComponentModel.DefaultValue(typeof(System.Drawing.Size), "1, 1"),
        System.ComponentModel.Description("GLのアスペクト比を設定します。要素にゼロまたはマイナスを設定するとアスペクト維持を停止します。")]
        public System.Drawing.Size GLAspect
        {
            get { return m_glAspect; }
            set
            {
                value.Width = System.Math.Max(0, value.Width);
                value.Height = System.Math.Max(0, value.Height);
                lock (m_paramsLock)
                {
                    if (m_glAspect != value)
                    {
                        m_glAspect = value;
                        ResetGLLayout(null, null);
                    }
                }
            }
        }

        //! @brief GLPaintイベントが未登録、あるいはGLをMakeCurrentしてフォーカスできない場合のクリア動作を指定します。
        //! @details Invisible-一時的にVisibleを落とします。/BackColor-背景色でクリアします。/DefaultBackground-背景色と画像を使った標準のクリア処理を行います。
        [System.ComponentModel.DefaultValue(typeof(CantFocusRenderMode), "BackColor"),
        System.ComponentModel.Description("GLPaintイベントが未登録、あるいはGLをMakeCurrentしてフォーカスできない場合のクリア動作を指定します。Invisible-一時的にVisibleを落とします。/BackColor-背景色でクリアします。/DefaultBackground-背景色と画像を使った標準のクリア処理を行います。")]
        public CantFocusRenderMode GLCantFocusRender {
            get { return m_glCantFocusRenderMode; }
            set
            {
                lock (m_paramsLock)
                {
                    if (m_glCantFocusRenderMode != value)
                    {
                        m_glCantFocusRenderMode = value;
                        GLControl.Invalidate();
                    }
                }
            }
        }

        //! @brief GLのDockスタイルを取得/設定します。
        [System.ComponentModel.DefaultValue(typeof(System.Windows.Forms.DockStyle), "None"),
        System.ComponentModel.Description("GLのDockスタイルを取得,設定します。")]
        public System.Windows.Forms.DockStyle GLDock
        {
            get { return m_glDock; }
            set
            {
                lock (m_paramsLock)
                {
                    if (m_glDock != value)
                    {
                        m_glDock = value;
                        ResetGLLayout(null, null);
                    }
                }
            }
        }

        //! @brief GLアスペクト調整時、長辺をコントロール内にフィットさせてレターボックスを表示するか、短辺をコントロール内にフィットさせてオーバーフローさせるか設定します。
        //! @details trueの時レターボックスが表示されます。
        [System.ComponentModel.DefaultValue(true),
        System.ComponentModel.Description("GLアスペクト調整時、長辺をコントロール内にフィットさせてレターボックスを表示するか、短辺をコントロール内にフィットさせてオーバーフローさせるか取得,設定します。trueの時レターボックスが表示されます。")]
        public bool GLLetterBoxEnabled
        {
            get { return m_glLetterBoxEnabled; }
            set
            {
                lock (m_paramsLock)
                {
                    if (m_glLetterBoxEnabled != value)
                    {
                        m_glLetterBoxEnabled = value;
                        ResetGLLayout(null, null);
                    }
                }
            }
        }

        //! @brief GLのレンダリングの左上端の位置を取得/設定します
        //! @details 書き込みを有効にするには、GLDockをNone、GLAspectを無効な値に設定してください。
        [System.ComponentModel.Description("GLのレンダリングの左上端の位置を取得,設定します。書き込みを有効にするには、GLDockをNone、GLAspectを無効な値に設定してください。")]
        public System.Drawing.Point GLLocation
        {
            get { return GLControl.Location; }
            set
            {
                lock (m_paramsLock)
                {
                    if ((GLDock == System.Windows.Forms.DockStyle.None) && (GLAspectEnabled == false) && (GLControl.Location != value))
                    {
                        ResetGLLayout(value, null);
                    }
                }
            }
        }

        //! @brief GLの背景色を取得/設定します。
        [System.ComponentModel.Description("GLの背景色を取得,設定します。")]
        public System.Drawing.Color GLBackColor
        {
            get { return GLControl.BackColor; }
            set
            {
                lock (m_paramsLock)
                {
                    if (GLControl.BackColor != value) GLControl.BackColor = value;
                }
            }
        }

        //! @brief GLの背景画像を取得/設定します。
        [System.ComponentModel.Description("GLの背景画像を取得,設定します。")]
        public System.Drawing.Image GLBackgroundImage
        {
            get { return GLControl.BackgroundImage; }
            set
            {
                lock (m_paramsLock)
                {
                    if (GLControl.BackgroundImage != value) GLControl.BackgroundImage = value;
                }
            }
        }

        //! @brief GLの背景のレイアウトスタイルを取得/設定します。
        [System.ComponentModel.DefaultValue(typeof(System.Windows.Forms.ImageLayout), "Tile"),
        System.ComponentModel.Description("GLの背景のレイアウトスタイルを取得,設定します。")]
        public System.Windows.Forms.ImageLayout GLBackgroundImageLayout
        {
            get { return GLControl.BackgroundImageLayout; }
            set
            {
                lock (m_paramsLock)
                {
                    if (GLControl.BackgroundImageLayout != value) GLControl.BackgroundImageLayout = value;
                }
            }
        }

        //! @brief 描画前のクリア処理を自分で行うかどうか。
        //! @details 背景を自動で処理させず、GLPaintイベントで自ら実装する時は、trueにして下さい。
        //! このプロパティをtrueに設定すると、GLSoftwareDoubleBufferは連動してfalseになります。
        [System.ComponentModel.DefaultValue(false),
        System.ComponentModel.Description("GL描画前のクリア処理を自分で行うかどうか取得,設定します。背景を自動で処理させず、GLPaintイベント内で全て実装する時は、trueにして下さい。")]
        public bool GLBackgroundSelfRender
        {
            get { return m_glBackgroundSelfRender; }
            set
            {
                lock (m_paramsLock)
                {
                    if (m_glBackgroundSelfRender != value)
                    {
                        m_glBackgroundSelfRender = value;
                        if (value == false) GLSoftwareDoubleBuffer = true;
                    }
                }
            }
        }

        [System.ComponentModel.Browsable(false)]
        public OpenTK.Graphics.IGraphicsContext GLContext { get { return GLControl.Context; } }

        //! @brief GL描画不能時に最後に更新した時のフレームを再描画するか取得,設定します。
        [System.ComponentModel.DefaultValue(false),
        System.ComponentModel.Description("GL描画不能時に最後に更新した時のフレームを再描画するか取得,設定します。毎フレームバッファリングするので、処理速度が必要ないときに使用してください。この値がfalse(または過去の画像データが無い)ならCantFocusRenderプロパティが評価されます。")]
        public bool GLLastFrameBuffer
        {
            get { return m_lastFrameBuffer; }
            set { lock (m_paramsLock) m_lastFrameBuffer = value; }
        }

        //! @brief GLPaintイベントを一時的に休止する時trueに設定してください。
        //! @details 通常、GLPanel.Enabledをfalseに設定すればGL描画を休止することが出来ますが、この方法ではマウスイベントも止まってしまうため、プログラムから描画を停止するには、このフラグを管理する事を推奨しています。
        [System.ComponentModel.Browsable(false)]
        public bool GLPaintSuspend
        {
            get { return (0 < m_glPaintSuspendCount); }
            set
            {
                int addValue = value ? 1 : -1;
                lock (m_paramsLock) m_glPaintSuspendCount = System.Math.Max(m_glPaintSuspendCount + addValue, 0);
            }
        }

        //! @brief GLPrePaintOnceイベントが未処理/成功/失敗したか取得します。
        [System.ComponentModel.Browsable(false)]
        public PrePaintStatus GLPrePaintStatus
        {
            get;
            private set;
        }

        //! @brief GLのレンダリングサイズを取得/設定します
        //! @details 書き込みを有効にするには、GLDockをNone、GLAspectを無効な値に設定してください。
        [System.ComponentModel.Description("GLのレンダリングサイズを取得,設定します。書き込みを有効にするには、GLDockをNone、GLAspectを無効な値に設定してください。")]
        public System.Drawing.Size GLSize
        {
            get { return GLControl.Size; }
            set
            {
                lock (m_paramsLock)
                {
                    if ((GLDock == System.Windows.Forms.DockStyle.None) && (GLAspectEnabled == false) && (GLControl.Size != value))
                    {
                        ResetGLLayout(null, value);
                    }
                }
            }
        }

        //! @brief GLのソフト側ダブルバッファの有効/無効を取得,設定します。
        //! @details GLのソフト側ダブルバッファの有効/無効を取得,設定します。Forms.ControlStyles.OptimizedDoubleBufferの設定を書き換えます。
        //! GLPaintに記述する処理においてGLハードウェア領域に描き込まず、Graphicオブジェクトに描き込みを行い、なおかつダブルバッファを要求したい時はtrueに設定してください。
        [System.ComponentModel.DefaultValue(true),
        System.ComponentModel.Description("GLのソフト側ダブルバッファの有効/無効を取得,設定します。Forms.ControlStyles.OptimizedDoubleBufferの設定を書き換えます。チラツキに効果がある時があります。GLBackgroundSelfRenderをfalseにする時はtrueにしてください。")]
        public bool GLSoftwareDoubleBuffer
        {
            get { return GLControl.GetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer); }
            set
            {
                lock (m_paramsLock)
                {
                    if (GLControl.GetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer) != value)
                    {
                        GLControl.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, value);
                    }
                }
            }
        }

        [System.ComponentModel.DefaultValue(false),
        System.ComponentModel.Description("GLのControlStyles.UserMouseスタイルを取得,設定します。")]
        public bool GLUserMouse
        {
            get { return GLControl.GetStyle(System.Windows.Forms.ControlStyles.UserMouse); }
            set
            {
                lock (m_paramsLock)
                {
                    if (GLControl.GetStyle(System.Windows.Forms.ControlStyles.UserMouse) != value)
                    {
                        GLControl.SetStyle(System.Windows.Forms.ControlStyles.UserMouse, value);
                    }
                }
            }
        }

        [System.ComponentModel.Browsable(false)]
        public OpenTK.Platform.IWindowInfo GLWindowInfo { get { return GLControl.WindowInfo; } }

        public System.Drawing.Point GLPointToClient(System.Drawing.Point screenPosition) { return GLControl.PointToClient(screenPosition); }
        public System.Drawing.Point GLPointToScreen(System.Drawing.Point localPosition) { return GLControl.PointToScreen(localPosition); }

        //! @brief GLのレンダリングイベント。
        //! @details GLを使ってレンダリングを行うには、このイベントにメソッドを追加します。senderはGLPanelではなくOpenTK.GLControlがセットされます。
        [System.ComponentModel.Description("GLを使ってレンダリングを行うには、このイベントにメソッドを追加します。senderはGLPanelではなくOpenTK.GLControlがセットされます。")]
        public event System.Windows.Forms.PaintEventHandler GLPaint;

        //! @brief GLのファーストプリレンダリングイベント
        //! @details GLPaintが最初に呼び出される前に一度だけ実行されます。senderはGLPanelではなくOpenTK.GLControlがセットされます。
        [System.ComponentModel.Description("GLPaintが最初に呼び出される前に一度だけ実行されます。senderはGLPanelではなくOpenTK.GLControlがセットされます。")]
        public event System.ComponentModel.CancelEventHandler GLPrePaintOnce;

        public event System.EventHandler GLLayout;
        
        public event System.Windows.Forms.MouseEventHandler GLMouseDown
        {
            add { GLControl.MouseDown += value; }
            remove { GLControl.MouseDown -= value; }
        }

        public event System.Windows.Forms.MouseEventHandler GLMouseMove
        {
            add { GLControl.MouseMove += value; }
            remove { GLControl.MouseMove -= value; }
        }

        public GLPanel()
        {
            m_paramsLock = new System.Object();
            m_paintLock = new System.Object();
            m_glAspect = new System.Drawing.Size(1, 1);
            m_glCantFocusRenderMode = CantFocusRenderMode.BackColor;
            m_glDock = System.Windows.Forms.DockStyle.None;
            m_glBackgroundSelfRender = false;
            m_glLetterBoxEnabled = true;
            GLPrePaintStatus = PrePaintStatus.NotAlready;
            InitializeComponent();
            GLSoftwareDoubleBuffer = true;
        }

        protected void OnGLLayout(System.EventArgs e) { if (GLLayout != null) lock(GLLayout) GLLayout(GLControl, e);  }

        protected void OnGLPaint(System.Windows.Forms.PaintEventArgs e)
        {
            lock (m_paintLock)
            {
                bool isPainted = false;

                // ready parameter snap
                bool isLastFrameBuffer = m_lastFrameBuffer;

                if ((GLPaintSuspend == false) && (CanFocus == true) && (GLPaint != null))
                {
                    if (GLControl.Visible != true) GLControl.Visible = true;
                    if (GLControl.Enabled != true) GLControl.Enabled = true;
                    if (GLPrePaintStatus == PrePaintStatus.NotAlready)
                    {
                        System.ComponentModel.CancelEventArgs args = new System.ComponentModel.CancelEventArgs();
                        OnGLPrePaintOnce(args);
                        GLPrePaintStatus = args.Cancel ? PrePaintStatus.Canceled : PrePaintStatus.Complete;
                    }
                    if (GLPrePaintStatus == PrePaintStatus.Complete)
                    {
                        System.Windows.Forms.PaintEventArgs registed_eventargs = null;

                        if (isLastFrameBuffer == false)
                        {
                            // delete last frame buffer
                            if (m_lastDrawFrame != null) m_lastDrawFrame.Dispose();
                            m_lastDrawFrame = null;
                        }
                        else
                        {
                            if ((m_lastDrawFrame == null) ||
                                (m_lastDrawFrame.Width != e.ClipRectangle.Size.Width) ||
                                (m_lastDrawFrame.Height != e.ClipRectangle.Size.Height) ||
                                (m_lastDrawFrame.HorizontalResolution != e.Graphics.DpiX) ||
                                (m_lastDrawFrame.VerticalResolution != e.Graphics.DpiY))
                            {
                                if (m_lastDrawFrame != null) m_lastDrawFrame.Dispose();
                                m_lastDrawFrame = new System.Drawing.Bitmap(e.ClipRectangle.Size.Width, e.ClipRectangle.Size.Height, e.Graphics);
                            }
                            // swap target
                            registed_eventargs = e;
                            e = new System.Windows.Forms.PaintEventArgs(System.Drawing.Graphics.FromImage(m_lastDrawFrame), registed_eventargs.ClipRectangle);
                        }
                        
                        if (GLBackgroundSelfRender == false) GLControl.OnPaintBackground(e);

                        // execute event
                        GLPaint(GLControl, e);

                        // update last frame buffer
                        if (registed_eventargs != null) {
                            e = registed_eventargs;
                            System.Drawing.Drawing2D.CompositingMode mode = registed_eventargs.Graphics.CompositingMode;
                            System.Drawing.Drawing2D.InterpolationMode scaleMode = registed_eventargs.Graphics.InterpolationMode;
                            try
                            {
                                if (mode != System.Drawing.Drawing2D.CompositingMode.SourceCopy)
                                {
                                    registed_eventargs.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                                }
                                if (scaleMode != System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor)
                                {
                                    registed_eventargs.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                                }
                                registed_eventargs.Graphics.DrawImage(m_lastDrawFrame, registed_eventargs.ClipRectangle);
                            }
                            finally
                            {
                                if (registed_eventargs.Graphics.CompositingMode != mode) registed_eventargs.Graphics.CompositingMode = mode;
                                if (registed_eventargs.Graphics.InterpolationMode != scaleMode) registed_eventargs.Graphics.InterpolationMode = scaleMode;
                                e.Dispose();
                                e = registed_eventargs;
                            }
                        }

                        // complete flag
                        isPainted = true;
                    }
                }

                if (isPainted == false)
                {
                    if (isLastFrameBuffer && (m_lastDrawFrame != null))
                    {
                        System.Drawing.Drawing2D.CompositingMode mode = e.Graphics.CompositingMode;
                        System.Drawing.Drawing2D.InterpolationMode scaleMode = e.Graphics.InterpolationMode;
                        try
                        {
                            if (mode != System.Drawing.Drawing2D.CompositingMode.SourceCopy)
                            {
                                e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                            }
                            if (scaleMode != System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor)
                            {
                                e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                            }
                            e.Graphics.DrawImage(m_lastDrawFrame, e.ClipRectangle);
                        }
                        finally
                        {
                            if (e.Graphics.CompositingMode != mode) e.Graphics.CompositingMode = mode;
                            if (e.Graphics.InterpolationMode != scaleMode) e.Graphics.InterpolationMode = scaleMode;
                        }
                    }
                    else
                    {
                        switch (m_glCantFocusRenderMode)
                        {
                            default:
                            case CantFocusRenderMode.Invisible:
                                if (GLControl.Visible != false) GLControl.Visible = false;
                                break;
                            case CantFocusRenderMode.BackColor:
                                if (GLControl.Visible != true) GLControl.Visible = true;
                                e.Graphics.Clear(GLControl.BackColor);
                                break;
                            case CantFocusRenderMode.DefaultBackground:
                                if (GLControl.Visible != true) GLControl.Visible = true;
                                GLControl.OnPaintBackground(e);
                                break;
                        }
                    }
                }
            }
        }

        protected void OnGLPrePaintOnce(System.ComponentModel.CancelEventArgs e)
        {
            if (GLPrePaintOnce != null) lock (GLPrePaintOnce) GLPrePaintOnce(GLControl, e);
        }

        protected override void OnGotFocus(System.EventArgs e)
        {
            base.OnGotFocus(e);
            GLControl.Focus();
        }

        protected override void OnLayout(System.Windows.Forms.LayoutEventArgs e)
        {
            base.OnLayout(e);
            ResetGLLayout(null, null);
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e);
            GLControl.Refresh();
        }

        private readonly System.Object m_paramsLock;
        private readonly System.Object m_paintLock;
        private System.Drawing.Size m_glAspect;
        private System.Windows.Forms.DockStyle m_glDock;
        private CantFocusRenderMode m_glCantFocusRenderMode;
        private bool m_glBackgroundSelfRender;
        private bool m_glLetterBoxEnabled;
        private bool m_lastFrameBuffer;
        private int m_glPaintSuspendCount;
        private System.Drawing.Bitmap m_lastDrawFrame;

        private bool GLAspectEnabled { get { return (0 < m_glAspect.Width) && (0 < m_glAspect.Height); } }

        //! @brief GLControlの独自のレイアウト処理を行います。不必要には呼び出さないで下さい。
        //! @details アスペクト比率とDockStyleは既に更新された情報でGLLayoutを呼び出しますが、変更したい位置とサイズ情報はパラメータで渡され、
        //! GLLayout内でGLControlに適用されます。そのため、location,sizeをnullで省略する場合は現在のGLControlの値がそのまま再設定されます。
        //! 尚、GLLayout内の処理はスレッドロックしないので、呼び出し元で lock(m_lock) する必要があります。
        private void ResetGLLayout(System.Drawing.Point? location, System.Drawing.Size? size)
        {
            System.Drawing.Point setLoc = GLControl.Location;
            System.Drawing.Size setSize = GLControl.Size;

            if (location.HasValue) setLoc = location.Value;
            if (size.HasValue) setSize = size.Value;

            SuspendLayout();
            try
            {
                if (GLAspectEnabled)
                {
                    int ClientEdge = System.Math.Max(ClientSize.Width, ClientSize.Height);
                    if (m_glLetterBoxEnabled) ClientEdge = System.Math.Min(ClientSize.Width, ClientSize.Height);

                    if (m_glAspect.Width > m_glAspect.Height)
                    {
                        setSize.Width = ClientEdge;
                        setSize.Height = ClientEdge * m_glAspect.Height / m_glAspect.Width;
                    }
                    else
                    {
                        setSize.Width = ClientEdge * m_glAspect.Width / m_glAspect.Height;
                        setSize.Height = ClientEdge;
                    }

                    switch (m_glDock) // aspected mode
                    {
                        default:
                        case System.Windows.Forms.DockStyle.None:
                        case System.Windows.Forms.DockStyle.Fill:
                            // centering
                            setLoc.X = (ClientSize.Width - setSize.Width) / 2;
                            if ((setLoc.X * 2 + setSize.Width) < ClientSize.Width) setSize.Width += 1;
                            setLoc.Y = (ClientSize.Height - setSize.Height) / 2;
                            if ((setLoc.Y * 2 + setSize.Height) < ClientSize.Height) setSize.Height += 1;
                            break;

                        case System.Windows.Forms.DockStyle.Bottom:
                            setLoc.X = (ClientSize.Width - setSize.Width) / 2;
                            if ((setLoc.X * 2 + setSize.Width) < ClientSize.Width) setSize.Width += 1;
                            setLoc.Y = ClientSize.Height - setSize.Height;
                            break;

                        case System.Windows.Forms.DockStyle.Left:
                            setLoc.X = 0;
                            setLoc.Y = (ClientSize.Height - setSize.Height) / 2;
                            if ((setLoc.Y * 2 + setSize.Height) < ClientSize.Height) setSize.Height += 1;
                            break;

                        case System.Windows.Forms.DockStyle.Right:
                            setLoc.X = ClientSize.Width - setSize.Width;
                            setLoc.Y = (ClientSize.Height - setSize.Height) / 2;
                            if ((setLoc.Y * 2 + setSize.Height) < ClientSize.Height) setSize.Height += 1;
                            break;

                        case System.Windows.Forms.DockStyle.Top:
                            setLoc.X = (ClientSize.Width - setSize.Width) / 2;
                            if ((setLoc.X * 2 + setSize.Width) < ClientSize.Width) setSize.Width += 1;
                            setLoc.Y = 0;
                            break;
                    }

                    GLControl.Location = setLoc;
                    GLControl.Size = setSize;
                }
                else
                {
                    switch (m_glDock) // non-aspected mode
                    {
                        case System.Windows.Forms.DockStyle.Bottom:
                            GLControl.Location = new System.Drawing.Point(0, ClientSize.Height - setSize.Height);
                            GLControl.Size = new System.Drawing.Size(ClientSize.Width, setSize.Height);
                            break;

                        default:
                        case System.Windows.Forms.DockStyle.Fill: // fill to just size
                            GLControl.Location = new System.Drawing.Point(0, 0);
                            GLControl.Size = ClientSize;
                            break;

                        case System.Windows.Forms.DockStyle.Left:
                            GLControl.Location = new System.Drawing.Point(0, 0);
                            GLControl.Size = new System.Drawing.Size(setSize.Width, ClientSize.Height);
                            break;

                        case System.Windows.Forms.DockStyle.None: // plane apply
                            GLControl.Location = location.Value;
                            GLControl.Size = size.Value;
                            break;

                        case System.Windows.Forms.DockStyle.Right:
                            GLControl.Location = new System.Drawing.Point(ClientSize.Width - setSize.Width, 0);
                            GLControl.Size = new System.Drawing.Size(setSize.Width, ClientSize.Height);
                            break;

                        case System.Windows.Forms.DockStyle.Top:
                            GLControl.Location = new System.Drawing.Point(0, 0);
                            GLControl.Size = new System.Drawing.Size(ClientSize.Width, setSize.Height);
                            break;
                    }
                }

                OnGLLayout(System.EventArgs.Empty);
            }
            finally { ResumeLayout(); }
        }

        private void GLControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { OnGLPaint(e); }
    }

    //! @}
}
