context: ////////////////////////////////////////////////////////////////////////////////
  // StickyWindows
  // 
  // Copyright (c) 2004 Corneliu I. Tusnea
  //
  // This software is provided 'as-is', without any express or implied warranty.
  // In no event will the author be held liable for any damages arising from 
  // the use of this software.
  // Permission to use, copy, modify, distribute and sell this software for any 
  // purpose is hereby granted without fee, provided that the above copyright 
  // notice appear in all copies and that both that copyright notice and this 
  // permission notice appear in supporting documentation.
  //
  // Notice: Check CodeProject for details about using this class
  //
  //////////////////////////////////////////////////////////////////////////////////
  
  using System;
  
  #region Blue.Win32Imports
  
  namespace Blue.Private.Win32Imports
  {
  	using System.Runtime.InteropServices;
  
  	/// <summary>
  	/// Win32 is just a placeholder for some Win32 imported definitions
  	/// </summary>
  	public class Win32
  	{
  		[DllImport("user32.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
  		public static extern short GetAsyncKeyState(int vKey);
  		[DllImport("user32.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
  		public static extern IntPtr GetDesktopWindow();
  
  		/// <summary>
  		/// VK is just a placeholder for VK (VirtualKey) general definitions
  		/// </summary>
  		public class VK
  		{
  			public const int VK_SHIFT          = 0x10;
  			public const int VK_CONTROL        = 0x11;
  			public const int VK_MENU           = 0x12;
  			public const int VK_ESCAPE         = 0x1B;
  
  			public static bool IsKeyPressed ( int KeyCode )
  			{
  				return (GetAsyncKeyState(KeyCode) & 0x0800)==0;
  			}
  		}
  
  		/// <summary>
  		/// WM is just a placeholder class for WM (WindowMessage) definitions
  		/// </summary>
  		public class WM
  		{
  			public const int WM_MOUSEMOVE                    = 0x0200;
  			public const int WM_NCMOUSEMOVE                  = 0x00A0;
  			public const int WM_NCLBUTTONDOWN                = 0x00A1;
  			public const int WM_NCLBUTTONUP                  = 0x00A2;
  			public const int WM_NCLBUTTONDBLCLK              = 0x00A3;
  			public const int WM_LBUTTONDOWN                  = 0x0201;
  			public const int WM_LBUTTONUP                    = 0x0202;
  			public const int WM_KEYDOWN                      = 0x0100;
  		}
  
  		/// <summary>
  		/// HT is just a placeholder for HT (HitTest) definitions
  		/// </summary>
  		public class HT
  		{
  			public const int HTERROR             = (-2);
  			public const int HTTRANSPARENT       = (-1);
  			public const int HTNOWHERE           = 0;
  			public const int HTCLIENT            = 1;
  			public const int HTCAPTION           = 2;
  			public const int HTSYSMENU           = 3;
  			public const int HTGROWBOX           = 4;
  			public const int HTSIZE              = HTGROWBOX;
  			public const int HTMENU              = 5;
  			public const int HTHSCROLL           = 6;
  			public const int HTVSCROLL           = 7;
  			public const int HTMINBUTTON         = 8;
  			public const int HTMAXBUTTON         = 9;
  			public const int HTLEFT              = 10;
  			public const int HTRIGHT             = 11;
  			public const int HTTOP               = 12;
  			public const int HTTOPLEFT           = 13;
  			public const int HTTOPRIGHT          = 14;
  			public const int HTBOTTOM            = 15;
  			public const int HTBOTTOMLEFT        = 16;
  			public const int HTBOTTOMRIGHT       = 17;
  			public const int HTBORDER            = 18;
  			public const int HTREDUCE            = HTMINBUTTON;
  			public const int HTZOOM              = HTMAXBUTTON;
  			public const int HTSIZEFIRST         = HTLEFT;
  			public const int HTSIZELAST          = HTBOTTOMRIGHT;
  
  			public const int HTOBJECT            = 19;
  			public const int HTCLOSE             = 20;
  			public const int HTHELP              = 21;
  		}
  
  		public class Bit
  		{
  			public static int HiWord ( int iValue )
  			{
  				return ( (iValue >> 16 ) & 0xFFFF );
  			}
  			public static int LoWord( int iValue )
  			{
  				return ( iValue & 0xFFFF );
  			}
  		}
  	}
  }
  #endregion
  
  
  namespace Blue.Windows
  {
  	using System.Windows.Forms;
  	using System.Drawing;
  	using System.Collections;
  
  	using Blue.Private.Win32Imports;
  
  	/// <summary>
  	/// A windows that Sticks to other windows of the same type when moved or resized.
  	/// You get a nice way of organizing multiple top-level windows.
  	/// Quite similar with WinAmp 2.x style of sticking the windows
  	/// </summary>
  	public class StickyWindow : System.Windows.Forms.NativeWindow
  	{
  		/// <summary>
  		/// Global List of registered StickyWindows
  		/// </summary>
  		private static ArrayList	GlobalStickyWindows = new ArrayList();
  
  		#region ResizeDir
  		private enum ResizeDir
  		{
  			Top		= 2,
  			Bottom	= 4,
  			Left	= 8,
  			Right	= 16
  		};
  		#endregion
  
  		#region Message Processor
  		// Internal Message Processor
  		private delegate bool ProcessMessage( ref Message m );
  		private ProcessMessage	MessageProcessor;
  
  		// Messages processors based on type
  		private ProcessMessage	DefaultMessageProcessor;
  		private ProcessMessage	MoveMessageProcessor;
  		private ProcessMessage	ResizeMessageProcessor;
  		#endregion
  
  		#region Internal properties
  		// Move stuff
  		private bool		movingForm;
  		private Point		formOffsetPoint;	// calculated offset rect to be added !! (min distances in all directions!!)
  		private Point		offsetPoint;		// primary offset
  
  		// Resize stuff
  		private bool		resizingForm;
  		private ResizeDir	resizeDirection;
  		private Rectangle	formOffsetRect;		// calculated rect to fix the size
  		private Point		mousePoint;			// mouse position
  
  		// General Stuff
  		private Form		originalForm;		// the form
  		private Rectangle	formRect;			// form bounds
  		private Rectangle	formOriginalRect;	// bounds before last operation started
  		#endregion
  
  		// public properties
  		private static int	stickGap = 20;		// distance to stick
  		private bool		stickOnResize;
  		private bool		stickOnMove;
  		private bool		stickToScreen;
  		private bool		stickToOther;
  
  		#region Public operations and properties
  
  		/// <summary>
  		/// Distance in pixels betwen two forms or a form and the screen where the sticking should start
  		/// Default value = 20
  		/// </summary>
  		public int	StickGap
  		{
  			get { return stickGap; }
  			set { stickGap = value; }
  		}
  		/// <summary>
  		/// Allow the form to stick while resizing
  		/// Default value = true
  		/// </summary>
  		public bool StickOnResize
  		{
  			get { return stickOnResize; }
  			set { stickOnResize = value; }
  		}
  		/// <summary>
  		/// Allow the form to stick while moving
  		/// Default value = true
  		/// </summary>
  		public bool StickOnMove
  		{
  			get { return stickOnMove; }
  			set { stickOnMove = value; }
  		}
  		/// <summary>
  		/// Allow sticking to Screen Margins
  		/// Default value = true
  		/// </summary>
  		public bool StickToScreen
  		{
  			get { return stickToScreen; }
  			set { stickToScreen = value; }
  		}
  		/// <summary>
  		/// Allow sticking to other StickWindows
  		/// Default value = true
  		/// </summary>
  		public bool StickToOther
  		{
  			get { return stickToOther; }
  			set { stickToOther = value; }
  		}
  
  		/// <summary>
  		/// Register a new form as an external reference form.
  		/// All Sticky windows will try to stick to the external references
  		/// Use this to register your MainFrame so the child windows try to stick to it, when your MainFrame is NOT a sticky window
  		/// </summary>
  		/// <param name="frmExternal">External window that will be used as reference</param>
  		public static void RegisterExternalReferenceForm ( Form frmExternal )
  		{
  			GlobalStickyWindows.Add ( frmExternal );
  		}
  		/// <summary>
  		/// Unregister a form from the external references.
  		/// <see cref="RegisterExternalReferenceForm"/>
  		/// </summary>
  		/// <param name="frmExternal">External window that will was used as reference</param>
  		public static void UnregisterExternalReferenceForm ( Form frmExternal )
  		{
  			GlobalStickyWindows.Remove ( frmExternal );
  		}
  
  		#endregion
  
  		#region StickyWindow Constructor
  		/// <summary>
  		/// Make the form Sticky
  		/// </summary>
  		/// <param name="form">Form to be made sticky</param>
  		public StickyWindow(Form form)
  		{
  			resizingForm	= false;
  			movingForm		= false;
  
  			originalForm	= form;
  
  			formRect		= Rectangle.Empty;
  			formOffsetRect	= Rectangle.Empty;
  
  			formOffsetPoint	= Point.Empty;
  			offsetPoint		= Point.Empty;
  			mousePoint		= Point.Empty;
  
  			stickOnMove		= true;
  			stickOnResize	= true;
  			stickToScreen	= true;
  			stickToOther	= true;
  
  			DefaultMessageProcessor	= new ProcessMessage ( DefaultMsgProcessor );
  			MoveMessageProcessor	= new ProcessMessage ( MoveMsgProcessor );
  			ResizeMessageProcessor	= new ProcessMessage ( ResizeMsgProcessor );
  			MessageProcessor		= DefaultMessageProcessor;
  
  			AssignHandle ( originalForm.Handle );
  		}
  		#endregion
  
  		#region OnHandleChange
  		[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
  		protected override void OnHandleChange()
  		{
  			if ( (int)this.Handle != 0 )
  			{
  				GlobalStickyWindows.Add ( this.originalForm );
  			} 
  			else
  			{
  				GlobalStickyWindows.Remove ( this.originalForm );
  			}
  		}
  		#endregion
  
  		#region WndProc
  		[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
  		protected override void WndProc(ref Message m)
  		{
  			if ( !MessageProcessor ( ref m ) )
  				base.WndProc (ref m);
  		}
  		#endregion
  
  		#region DefaultMsgProcessor
  		/// <summary>
  		/// Processes messages during normal operations (while the form is not resized or moved)
  		/// </summary>
  		/// <param name="m"></param>
  		/// <returns></returns>
  		private bool DefaultMsgProcessor ( ref Message m )
  		{
  			switch ( m.Msg )
  			{
  				case Win32.WM.WM_NCLBUTTONDOWN:
  				{
  					originalForm.Activate();
  					mousePoint.X = (short)Win32.Bit.LoWord ( (int)m.LParam );
  					mousePoint.Y = (short)Win32.Bit.HiWord ( (int)m.LParam );
  					if ( OnNCLButtonDown( (int)m.WParam, mousePoint ) )
  					{
  						//m.Result = new IntPtr ( (resizingForm || movingForm) ? 1 : 0 );
  						m.Result = (IntPtr)( (resizingForm || movingForm) ? 1 : 0 );
  						return true;
  					}
  					break;
  				}
  			}
  
  			return false;
  		}
  		#endregion
  
  		#region OnNCLButtonDown
  		/// <summary>
  		/// Checks where the click was in the NC area and starts move or resize operation
  		/// </summary>
  		/// <param name="iHitTest"></param>
  		/// <param name="point"></param>
  		/// <returns></returns>
  		private bool OnNCLButtonDown ( int iHitTest, Point point )
  		{
  			Rectangle rParent	= originalForm.Bounds;
  			offsetPoint			= point;
  
  			switch ( iHitTest )
  			{
  				case Win32.HT.HTCAPTION:
  				{	// request for move
  					if ( stickOnMove )
  					{
  						offsetPoint.Offset ( -rParent.Left, -rParent.Top );
  						StartMove();
  						return true;
  					}
  					else
  						return false;	// leave default processing
  					
  				}
  
  				// requests for resize
  				case Win32.HT.HTTOPLEFT:
  					return StartResize ( ResizeDir.Top | ResizeDir.Left );
  				case Win32.HT.HTTOP:
  					return StartResize ( ResizeDir.Top );
  				case Win32.HT.HTTOPRIGHT:
  					return StartResize ( ResizeDir.Top | ResizeDir.Right );
  				case Win32.HT.HTRIGHT:
  					return StartResize ( ResizeDir.Right );
  				case Win32.HT.HTBOTTOMRIGHT:
  					return StartResize ( ResizeDir.Bottom | ResizeDir.Right );
  				case Win32.HT.HTBOTTOM:
  					return StartResize ( ResizeDir.Bottom );
  				case Win32.HT.HTBOTTOMLEFT:
  					return StartResize ( ResizeDir.Bottom | ResizeDir.Left );
  				case Win32.HT.HTLEFT:
  					return StartResize ( ResizeDir.Left );
  			}
  
  			return false;
  		}
  		#endregion
  
  		#region ResizeOperations
  		private bool StartResize(ResizeDir resDir)
  		{
  			if ( stickOnResize )
  			{
  				resizeDirection = resDir;
  				formRect = originalForm.Bounds;
  				formOriginalRect = originalForm.Bounds;	// save the old bounds
  
  				if ( !originalForm.Capture )	// start capturing messages
  					originalForm.Capture = true;
  
  				MessageProcessor = ResizeMessageProcessor;
  
  				return true;	// catch the message
  			} else
  				return false;	// leave default processing !
  		}
  		private bool ResizeMsgProcessor(ref Message m)
  		{
  			if ( !originalForm.Capture )
  			{
  				Cancel();
  				return false;
  			}
  
  			switch ( m.Msg )
  			{
  				case Win32.WM.WM_LBUTTONUP:
  				{	// ok, resize finished !!!
  					EndResize();
  					break;
  				}
  				case Win32.WM.WM_MOUSEMOVE:
  				{
  					mousePoint.X = (short)Win32.Bit.LoWord ( (int)m.LParam );
  					mousePoint.Y = (short)Win32.Bit.HiWord ( (int)m.LParam );
  
gt: 					Resize( mousePoint );