Click here to Skip to main content
15,887,683 members
Articles / Programming Languages / Visual Basic

A Tiny Parser Generator v1.2

Rate me:
Please Sign up or sign in to vote.
4.94/5 (201 votes)
21 Sep 2010CPOL25 min read 664.6K   17.5K   465  
@TinyPG is a utility that makes it easier to write and try out your own parser/compiler
// Copyright 2008 - 2010 Herre Kuijpers - <herre.kuijpers@gmail.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace TinyPG.Controls
{
    /// <summary>
    /// this is the publicly exposed interface of the floating window (floaty)
    /// add more methods/properties here for your own needs, so these are exposed to the client
    /// the main goal is to keep the floaty form internal
    /// </summary>
    public interface IFloaty
    {
        /// <summary>
        /// show the floaty 
        /// </summary>
        void Show();

        /// <summary>
        /// hide the floaty
        /// </summary>
        void Hide();

        /// <summary>
        /// dock the floaty (works only if it is floating)
        /// </summary>
        void Dock();

        /// <summary>
        /// float the floaty (works only if it is docked)
        /// </summary>
        void Float();

        /// <summary>
        /// set a caption for the floaty
        /// </summary>
        String Text {get; set; }

        /// <summary>
        /// indicates if a floaty may dock only on the host docking control (e.g. the form)
        /// and not inside other floaties
        /// </summary>
        bool DockOnHostOnly { get; set; }

        /// <summary>
        /// indicates if a floaty may dock on the inside or on the outside of a form/control
        /// default is true
        /// </summary>
        bool DockOnInside { get; set; }

        event EventHandler Docking;
    }

    /// <summary>
    /// this class contains basically all the logic for making a control floating and dockable
    /// note that it is an internal class, only it's IFloaty interface is exposed to the client
    /// </summary>
    internal sealed class Floaty : Form, IFloaty
    {
        #region a teeny weeny tiny bit of API functions used
        private const int WM_NCLBUTTONDBLCLK = 0x00A3;
        private const int WM_LBUTTONUP = 0x0202;
        private const int WM_SYSCOMMAND = 0x0112;
        private const int SC_MOVE = 0xF010;

        // NOTE: I don't like using API's in .Net... so I try to avoid them if possible.
        // this time there was no way around it.

        // this function is used to be able to send some very specific (uncommon) messages
        // to the floaty forms. It is used particularly to switch between start dragging a docked panel
        // to dragging a floaty form.
        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        private static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
        #endregion private members

        #region private members

        // this is the orgigional state of the panel. This state is used to reset a control to its
        // origional state if it was floating
        private DockState _dockState;

        // this is a flag that indicates if a control can start floating
        private bool _startFloating;

        // indicates if the container is floating or docked
        private bool _isFloating; 

        // this is the dockmananger that manages this floaty.
        private DockExtender _dockExtender;

        private bool _dockOnHostOnly;
        private bool _dockOnInside;

        #endregion private members

        #region initialization
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="DockExtender">requires the DockExtender</param>
        public Floaty(DockExtender DockExtender)
        {
            _dockExtender = DockExtender;
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // Floaty
            // 
            this.ClientSize = new System.Drawing.Size(178, 122);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
            this.MaximizeBox = false;
            this.Name = "Floaty";
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
            this.ResumeLayout(false);
            this._dockOnInside = true;
            this._dockOnHostOnly = true; // keep it simple for now
        }

        #endregion initialization

        #region properties
        internal DockState DockState 
        {
            get { return _dockState; }
        }

        public bool DockOnHostOnly
        {
            get { return _dockOnHostOnly; }
            set { _dockOnHostOnly = value; }
        }

        public bool DockOnInside
        {
            get { return _dockOnInside; }
            set { _dockOnInside = value; }
        }
        
        #endregion properties

        #region overrides
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_NCLBUTTONDBLCLK) // doubleclicked on border, so reset.
            {
                DockFloaty();
            }
            base.WndProc(ref m);
        }

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

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

        protected override void OnResizeEnd(EventArgs e)
        {
            
            if (_dockExtender.Overlay.Visible == true && _dockExtender.Overlay.DockHostControl != null) //ok found new docking position
            {
                _dockState.OrgDockingParent = _dockExtender.Overlay.DockHostControl;
                _dockState.OrgBounds = _dockState.Container.RectangleToClient(_dockExtender.Overlay.Bounds);
                _dockState.OrgDockStyle = _dockExtender.Overlay.Dock;
                _dockExtender.Overlay.Hide();
                DockFloaty(); // dock the container
            }
            _dockExtender.Overlay.DockHostControl = null;
            _dockExtender.Overlay.Hide();
            base.OnResizeEnd(e);
        }

        protected override void OnMove(EventArgs e)
        {
            if (IsDisposed) return;

            Point pt = Cursor.Position;
            Point pc = PointToClient(pt);
            if (pc.Y < -21 || pc.Y > 0) return;
            if (pc.X < -1 || pc.X > Width) return;

            Control t = _dockExtender.FindDockHost(this, pt);
            if (t == null) 
            {
                _dockExtender.Overlay.Hide();
            }
            else
            {
                SetOverlay(t, pt);
            }
            base.OnMove(e);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            e.Cancel = true;
            this.Hide(); // hide but don't close
            base.OnClosing(e);
        }

        #endregion overrides

        #region public methods (implements IFloaty)
        // override base method, this control only allows one way of showing.
        public new void Show()
        {
            if (!this.Visible && _isFloating)
                base.Show(_dockState.OrgDockHost);

            _dockState.Container.Show();

            if (_dockState.Splitter != null)
                _dockState.Splitter.Show();
        }

        public new void Hide()
        {
            if (this.Visible)
                base.Hide();

            _dockState.Container.Hide();
            if (_dockState.Splitter != null)
                _dockState.Splitter.Hide();
        }

        // this this member
        public new void Show(IWin32Window win)
        {
            Show();
        }

        public new void Dock()
        {
            if (!_isFloating) return;
            DockFloaty();

        }

        public void Float()
        {
            if (_isFloating) return;
            Text = _dockState.Handle.Text;

            Point pt = _dockState.Container.PointToScreen(new Point(0,0));
            Size sz = _dockState.Container.Size;
            if (_dockState.Container.Equals(_dockState.Handle))
            {
                sz.Width += 18;
                sz.Height += 28;
            }
            if (sz.Width > 600) sz.Width = 600;
            if (sz.Height > 600) sz.Height = 600;

            _dockState.OrgDockingParent = _dockState.Container.Parent;
            _dockState.OrgBounds = _dockState.Container.Bounds;
            _dockState.OrgDockStyle = _dockState.Container.Dock;
            _dockState.Handle.Hide();
            _dockState.Container.Parent = this;
            _dockState.Container.Dock = DockStyle.Fill;

            if (_dockState.Splitter != null)
            {
                _dockState.Splitter.Visible = false; // hide splitter
                _dockState.Splitter.Parent = this;
            }
            this.Bounds = new Rectangle(pt, sz);
            _isFloating = true;
            Show();
        }


        #endregion

        #region helper functions - this contains most of the logic

        /// <summary>
        /// determines the client area of the control. The area of docked controls are excluded
        /// </summary>
        /// <param name="c">the control to which to determine the client area</param>
        /// <returns>returns the docking area in screen coordinates</returns>
        private Rectangle GetDockingArea(Control c)
        {
            Rectangle r = c.Bounds;
           
            if (c.Parent != null)
                r = c.Parent.RectangleToScreen(r);

            Rectangle rc = c.ClientRectangle;

            int borderwidth = (r.Width - rc.Width) / 2;
            r.X += borderwidth;
            r.Y += (r.Height - rc.Height) - borderwidth;

            if (!_dockOnInside)
            {
                rc.X += r.X;
                rc.Y += r.Y;
                return rc;
            }

            foreach (Control cs in c.Controls)
            {
                if (!cs.Visible) continue;
                switch (cs.Dock)
                {
                    case DockStyle.Left:
                        rc.X += cs.Width;
                        rc.Width -= cs.Width;
                        break;
                    case DockStyle.Right:
                            rc.Width -= cs.Width;
                        break;
                    case DockStyle.Top:
                        rc.Y += cs.Height;
                        rc.Height -= cs.Height;
                        break;
                    case DockStyle.Bottom:
                            rc.Height -= cs.Height;
                        break;
                    default:
                        break;
                }
            }
            rc.X += r.X;
            rc.Y += r.Y;

            //Console.WriteLine("Client = " + c.Name + " " + rc.ToString());

            return rc;
        }

        /// <summary>
        /// This method will check if the overlay needs to be displayed or not
        /// for display it will position the overlay
        /// </summary>
        /// <param name="c"></param>
        /// <param name="p">position of cursor in screen coordinates</param>
        private void SetOverlay(Control c, Point pc)
        {

            Rectangle r = GetDockingArea(c);
            Rectangle rc = r;

            //determine relative coordinates
            float rx = (pc.X - r.Left) / (float)(r.Width);
            float ry = (pc.Y - r.Top) / (float)(r.Height);

            //Console.WriteLine("Moving over " + c.Name + " " +  rx.ToString() + "," + ry.ToString());

            _dockExtender.Overlay.Dock = DockStyle.None; // keep floating

            // this section determines when the overlay is to be displayed.
            // it depends on the position of the mouse cursor on the client area.
            // the overlay is currently only shown if the mouse is moving over either the Northern, Western, 
            // Southern or Eastern parts of the client area.
            // when the mouse is in the center or in the NE, NW, SE or SW, no overlay preview is displayed, hence
            // allowing the user to dock the container.

            // dock to left, checks the Western area
            if (rx > 0 && rx < ry && rx < 0.25 && ry < 0.75 && ry > 0.25)
            {
                r.Width = r.Width / 2;
                if (r.Width > this.Width)
                    r.Width = this.Width;

                _dockExtender.Overlay.Dock = DockStyle.Left; // dock to left
            }

            // dock to the right, checks the Easter area
            if (rx < 1 && rx > ry && rx > 0.75 && ry < 0.75 && ry > 0.25)
            {
                r.Width = r.Width / 2;
                if (r.Width > this.Width)
                    r.Width = this.Width;
                r.X = rc.X + rc.Width - r.Width;
                _dockExtender.Overlay.Dock = DockStyle.Right;
            }

            // dock to top, checks the Northern area
            if (ry > 0 && ry < rx && ry < 0.25 && rx < 0.75 && rx > 0.25)
            {
                r.Height = r.Height / 2;
                if (r.Height > this.Height)
                    r.Height = this.Height;
                _dockExtender.Overlay.Dock = DockStyle.Top;
            }

            // dock to the bottom, checks the Southern area
            if (ry < 1 && ry > rx && ry > 0.75 && rx < 0.75 && rx > 0.25)
            {
                r.Height = r.Height / 2;
                if (r.Height > this.Height)
                    r.Height = this.Height;
                r.Y = rc.Y + rc.Height - r.Height;
                _dockExtender.Overlay.Dock = DockStyle.Bottom;
            }
            if (_dockExtender.Overlay.Dock != DockStyle.None)
                _dockExtender.Overlay.Bounds = r;
            else
                _dockExtender.Overlay.Hide();

            if (!_dockExtender.Overlay.Visible && _dockExtender.Overlay.Dock != DockStyle.None)
            {
                _dockExtender.Overlay.DockHostControl = c;
                _dockExtender.Overlay.Show(_dockState.OrgDockHost);
                BringToFront();
            }
        }

        internal void Attach(DockState dockState)
        {
            // track the handle's mouse movements
            _dockState = dockState;
            Text = _dockState.Handle.Text;
            _dockState.Handle.MouseMove += new MouseEventHandler(Handle_MouseMove);
            _dockState.Handle.MouseHover += new EventHandler(Handle_MouseHover);
            _dockState.Handle.MouseLeave += new EventHandler(Handle_MouseLeave);
        }

        /// <summary>
        /// makes the docked control floatable in this Floaty form
        /// </summary>
        /// <param name="dockState"></param>
        /// <param name="offsetx"></param>
        /// <param name="offsety"></param>
        private void MakeFloatable(DockState dockState, int offsetx, int offsety)
        {
            Point ps = Cursor.Position;
            _dockState = dockState;
            Text = _dockState.Handle.Text;

            Size sz = _dockState.Container.Size;
            if (_dockState.Container.Equals(_dockState.Handle))
            {
                sz.Width += 18;
                sz.Height += 28;
            }
            if (sz.Width > 600) sz.Width = 600;
            if (sz.Height > 600) sz.Height = 600;



            _dockState.OrgDockingParent = _dockState.Container.Parent;
            _dockState.OrgBounds = _dockState.Container.Bounds;
            _dockState.OrgDockStyle = _dockState.Container.Dock;
            //_dockState.OrgDockingParent.Controls.Remove(_dockState.Container);
            //Controls.Add(_dockState.Container);
            _dockState.Handle.Hide();
            _dockState.Container.Parent = this;
            _dockState.Container.Dock = DockStyle.Fill;
            //_dockState.Handle.Visible = false; // hide it for now
            if (_dockState.Splitter != null)
            {
                _dockState.Splitter.Visible = false; // hide splitter
                _dockState.Splitter.Parent = this;
            }
            // allow redraw of floaty and container
            //Application.DoEvents();  

            // this is kind of tricky
            // disable the mousemove events of the handle
            SendMessage(_dockState.Handle.Handle.ToInt32(), WM_LBUTTONUP, 0, 0);
            ps.X -= offsetx;
            ps.Y -= offsety;


            this.Bounds = new Rectangle(ps, sz);
            _isFloating = true;
            Show();
            // enable the mousemove events of the new floating form, start dragging the form immediately
            SendMessage(this.Handle.ToInt32(), WM_SYSCOMMAND, SC_MOVE | 0x02, 0);
        }

        /// <summary>
        /// this will dock the floaty control
        /// </summary>
        private void DockFloaty()
        {
            // bring dockhost to front first to prevent flickering
            _dockState.OrgDockHost.TopLevelControl.BringToFront();
            this.Hide();
            _dockState.Container.Visible = false; // hide it temporarely
            _dockState.Container.Parent = _dockState.OrgDockingParent;
            _dockState.Container.Dock = _dockState.OrgDockStyle;
            _dockState.Container.Bounds = _dockState.OrgBounds;
            _dockState.Handle.Visible = true; // show handle again
            _dockState.Container.Visible = true; // it's good, show it

            if (_dockOnInside)
                _dockState.Container.BringToFront(); // set to front

            //show splitter
            if (_dockState.Splitter != null && _dockState.OrgDockStyle != DockStyle.Fill && _dockState.OrgDockStyle != DockStyle.None)
            {
                _dockState.Splitter.Parent = _dockState.OrgDockingParent;
                _dockState.Splitter.Dock = _dockState.OrgDockStyle;
                _dockState.Splitter.Visible = true; // show splitter

                if (_dockOnInside)
                    _dockState.Splitter.BringToFront();
                else
                    _dockState.Splitter.SendToBack();
            }

            if (!_dockOnInside)
                _dockState.Container.SendToBack(); // set to back

            _isFloating = false;

            if (Docking != null)
                Docking.Invoke(this, new EventArgs());
        }

        private void DetachHandle()
        {
            _dockState.Handle.MouseMove -= new MouseEventHandler(Handle_MouseMove);
            _dockState.Handle.MouseHover -= new EventHandler(Handle_MouseHover);
            _dockState.Handle.MouseLeave -= new EventHandler(Handle_MouseLeave);
            _dockState.Container = null;
            _dockState.Handle = null;
        }

        #endregion helper functions

        #region Container Handle tracking methods
        void Handle_MouseHover(object sender, EventArgs e)
        {
            _startFloating = true;
        }

        void Handle_MouseLeave(object sender, EventArgs e)
        {
            _startFloating = false;
        }

        void Handle_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && _startFloating)
            {
                Point ps = _dockState.Handle.PointToScreen(new Point(e.X, e.Y));
                MakeFloatable(_dockState, e.X, e.Y);
            }
        }
        #endregion Container Handle tracking methods


        #region events

        public event EventHandler Docking = null;

        #endregion
    }

    /// <summary>
    /// define a Floaty collection used for enumerating all defined floaties
    /// </summary>
    public class Floaties : List<IFloaty>
    {
        public IFloaty Find(Control container)
        {
            foreach (Floaty f in this)
            {
                if (f.DockState.Container.Equals(container))
                    return f;
            }
            return null;
        }
    }

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Rubicon
Netherlands Netherlands
Currently Herre Kuijpers is employed at Rubicon. During his career he developed skills with all kinds of technologies, methodologies and programming languages such as c#, ASP.Net, .Net Core, VC++, Javascript, SQL, Agile, Scrum, DevOps, ALM. Currently he fulfills the role of software architect in various projects.

Herre Kuijpers is a very experienced software architect with deep knowledge of software design and development on the Microsoft .Net platform. He has a broad knowledge of Microsoft products and knows how these, in combination with custom software, can be optimally implemented in the often complex environment of the customer.

Comments and Discussions