context: #region Copyright (c) PureComponents, All Rights Reserved
  
  /* ---------------------------------------------------------------------*
  *                           PureComponents                              *
  *              Copyright (c) All Rights reserved                        *
  *                                                                       *
  *                                                                       *
  * This file and its contents are protected by Czech and                 *
  * International copyright laws.  Unauthorized reproduction and/or       *
  * distribution of all or any portion of the code contained herein       *
  * is strictly prohibited and will result in severe civil and criminal   *
  * penalties.  Any violations of this copyright will be prosecuted       *
  * to the fullest extent possible under law.                             *
  *                                                                       *
  * THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
  * TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
  * TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
  * CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
  * THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF PURECOMPONENYS     *
  *                                                                       *
  * UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
  * PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME,     *
  * SIMILAR, SUBSTANTIALLY SIMILAR OR THE SAME, FUNCTIONALITY AS ANY      *
  * PURECOMPONENTS PRODUCT.                                               *
  *                                                                       *
  * THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
  * CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF PURECOMPONENTS.    *
  * THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO             *
  * INSURE ITS CONFIDENTIALITY.                                           *
  *                                                                       *
  * THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
  * PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
  * EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
  * THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
  * SOURCE CODE CONTAINED HEREIN.                                         *
  *                                                                       *
  * THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
  * --------------------------------------------------------------------- *
  */
  
  #endregion Copyright (c) PureComponents, All Rights Reserved
  
  using System;
  using System.IO;
  using System.Drawing;
  using System.Reflection;
  using System.Drawing.Text;
  using System.Collections;
  using System.Windows.Forms;
  using System.ComponentModel;
  using System.ComponentModel.Design;
  using System.Drawing.Imaging;
  using System.Drawing.Drawing2D;
  using System.Runtime.InteropServices;
  
  namespace PureComponents.TreeView.Design
  {
  	/// <summary>
  	/// The action menu item object
  	/// </summary>
  	internal class ActionMenuItem
  	{
  		public string Text;
  		public bool Selected = false;
  		public ActionMenuGroup MenuGroup;
  	}
  
  	/// <summary>
  	/// The action menu group object
  	/// </summary>
  	internal class ActionMenuGroup
  	{
  		public ArrayList Items = new ArrayList();
  
  		public string Title;
  		public bool Expanded = false;		
  	}
  	
  	internal class TreeViewSelector : NativeWindow
  	{
  		private Size m_oCurrentSize;	
  		private Point m_oCurrentPoint;
  		private Point m_oScreenPos;
  		private bool m_exitLoop = false;	
  
  		internal bool Visible = false;
  		internal ISelectionService SelectionService;
  		internal TreeView TreeView;
  
  		private Image m_SelectorImage;
  
  		private static Image LoadImage(string imageName)
  		{			
  			Stream strm = Type.GetType("PureComponents.TreeView.TreeView").Assembly.GetManifestResourceStream
  				("PureComponents.TreeView.Design." + imageName);
  			
  			Image ic = null;
  			if(strm != null)
  			{
  				ic = System.Drawing.Image.FromStream(strm);
  				strm.Close();
  			}
  
  			return ic;
  		}
  
  		protected void RecalcLayout()
  		{
  			m_oCurrentSize = new Size(13, 13);
  		}
  
  		protected void UpdateLayeredWindow()
  		{
  			UpdateLayeredWindow(m_oCurrentPoint, m_oCurrentSize, 255);
  		}
  
  		protected void UpdateLayeredWindow(byte alpha)
  		{
  			UpdateLayeredWindow(m_oCurrentPoint, m_oCurrentSize, alpha);
  		}
  
  		protected void UpdateLayeredWindow(Point point, Size size, byte alpha)
  		{
  			Graphics g = Graphics.FromHwnd(this.Handle);
  			g.DrawImage(m_SelectorImage, 0, 0);
  			
  			g.Dispose();
  		}
  
  		protected POINT MousePositionToClient(POINT point)
  		{
  			POINT localPoint;
  			localPoint.x = point.x;
  			localPoint.y = point.y;
  			User32.ScreenToClient(this.Handle, ref localPoint);
  
  			return localPoint;
  		}
  
  		protected POINT MousePositionToScreen(POINT point)
  		{
  			POINT localPoint;
  			localPoint.x = point.x;
  			localPoint.y = point.y;
  
  			User32.ClientToScreen(this.Handle, ref localPoint);
  
  			return localPoint;
  		}
  
  		protected POINT MousePositionToScreen(MSG msg)
  		{
  			POINT screenPos;
  			screenPos.x = (short)((uint)msg.lParam & 0x0000FFFFU);
  			screenPos.y = (short)(((uint)msg.lParam & 0xFFFF0000U) >> 16);
  
  			// Convert the mouse position to screen coordinates, 
  			// but not for non-client messages
  			if ((msg.message != (int)Msgs.WM_NCLBUTTONUP) &&
  				(msg.message != (int)Msgs.WM_NCMBUTTONUP) &&
  				(msg.message != (int)Msgs.WM_NCRBUTTONUP) &&
  				(msg.message != (int)Msgs.WM_NCXBUTTONUP) &&
  				(msg.message != (int)Msgs.WM_NCLBUTTONDOWN) &&
  				(msg.message != (int)Msgs.WM_NCMBUTTONDOWN) &&
  				(msg.message != (int)Msgs.WM_NCRBUTTONDOWN) &&
  				(msg.message != (int)Msgs.WM_NCXBUTTONDOWN))	
  			{
  				// Convert the mouse position to screen coordinates
  				User32.ClientToScreen(msg.hwnd, ref screenPos);
  			}
  
  			return screenPos;
  		}
  
  		protected POINT MousePositionToScreen(Message msg)
  		{
  			POINT screenPos;
  			screenPos.x = (short)((uint)msg.LParam & 0x0000FFFFU);
  			screenPos.y = (short)(((uint)msg.LParam & 0xFFFF0000U) >> 16);
  
  			// Convert the mouse position to screen coordinates, 
  			// but not for non-client messages
  			if ((msg.Msg != (int)Msgs.WM_NCLBUTTONUP) &&
  				(msg.Msg != (int)Msgs.WM_NCMBUTTONUP) &&
  				(msg.Msg != (int)Msgs.WM_NCRBUTTONUP) &&
  				(msg.Msg != (int)Msgs.WM_NCXBUTTONUP) &&
  				(msg.Msg != (int)Msgs.WM_NCLBUTTONDOWN) &&
  				(msg.Msg != (int)Msgs.WM_NCMBUTTONDOWN) &&
  				(msg.Msg != (int)Msgs.WM_NCRBUTTONDOWN) &&
  				(msg.Msg != (int)Msgs.WM_NCXBUTTONDOWN))	
  			{
  				// Convert the mouse position to screen coordinates
  				User32.ClientToScreen(msg.HWnd, ref screenPos);
  			}
  
  			return screenPos;
  		}
  
  		public void Show(int nX, int nY)
  		{
  			Visible = true;
  			m_SelectorImage = LoadImage("Selector.gif");
  
  			// Decide if we need layered windows			
  			CreateParams cp = new CreateParams();
  	
  			// Any old title will do as it will not be shown
  			cp.Caption = "PureComponents.TreeViewSelector";
  
  			RecalcLayout();
  		
  			// get the max screen size. if the nX + Width or nY + Height is bigger than the area, them move it elsewhere and recalculate the
  			// position
  			Screen oScreen = Screen.FromHandle(this.Handle);
  			if (nX + m_oCurrentSize.Width > oScreen.Bounds.Width)
  				nX = oScreen.Bounds.Width - m_oCurrentSize.Width;
  	
  			if (nY + m_oCurrentSize.Height > oScreen.Bounds.Height)
  				nY = oScreen.Bounds.Height - m_oCurrentSize.Height;
  	
  			// set the position
  			m_oScreenPos = new Point(nX, nY);
  	
  			Size winSize = m_oCurrentSize;
  			Point screenPos = m_oScreenPos;						
  
  			// Define the screen position/size			
  			cp.X = nX;
  			cp.Y = nY;
  			cp.Height = winSize.Height;
  			cp.Width = winSize.Width;
  	
  			// As a top-level window it has no parent
  			cp.Parent = IntPtr.Zero;
  				
  			// Appear as a top-level window
  			cp.Style = unchecked((int)(uint)WindowStyles.WS_POPUP);
  				
  			// Set styles so that it does not have a caption bar and is above all other 
  			// windows in the ZOrder, i.e. TOPMOST
  			cp.ExStyle = (int)WindowExStyles.WS_EX_TOPMOST + 
  				(int)WindowExStyles.WS_EX_TOOLWINDOW;
  	
  			// Create the actual window
  			if (this.Handle == IntPtr.Zero)
  				this.CreateHandle(cp);
  	
  			// Remember the correct screen drawing details
  			m_oCurrentSize = winSize;
  			m_oCurrentPoint = screenPos;
  			// Show the window without activating it (i.e. do not take focus)
  			User32.ShowWindow(this.Handle, (short)ShowWindowStyles.SW_SHOWNOACTIVATE);	
  
  			UpdateLayeredWindow();
  		
  			MSG msg = new MSG();	
  			m_exitLoop = false;
  	
  			while (!m_exitLoop)
  			{
  				// Suspend thread until a windows message has arrived
  				if (User32.WaitMessage())
  				{
  					// Take a peek at the message details without removing from queue
  					while(!m_exitLoop && User32.PeekMessage(ref msg, 0, 0, 0, (int)PeekMessageFlags.PM_NOREMOVE))
  					{
  						// test if the mouse up is in the area of the menu
  						int localWidth = m_oCurrentSize.Width;
  						int localHeight = m_oCurrentSize.Height;						
  						
  						bool eatMessage = false;
  						
  						#region mouse move
  						if (msg.message == (int)Msgs.WM_MOUSEMOVE)
  						{
  							POINT screenPosMsg = MousePositionToScreen(msg);
  							
  							// Is the POINT inside the Popup window rectangle
  							if ((screenPosMsg.x >= m_oCurrentPoint.X) && (screenPosMsg.x <= (m_oCurrentPoint.X + localWidth)) &&
  								(screenPosMsg.y >= m_oCurrentPoint.Y) && (screenPosMsg.y <= (m_oCurrentPoint.Y + localHeight)))
  							{
  								eatMessage = true;
  
  								//OnWM_MOUSEMOVE(screenPosMsg.x, screenPosMsg.y);
  							}
  							else
  							{
  								if (User32.GetMessage(ref msg, 0, 0, 0))
  								{
  									User32.TranslateMessage(ref msg);
  									User32.DispatchMessage(ref msg);
  								}
  							}
  						}
  						#endregion
  
  						#region left button up
  						if (msg.message == (int)Msgs.WM_LBUTTONUP)
  						{
  							POINT screenPosMsg = MousePositionToScreen(msg);
  							
  							// Is the POINT inside the Popup window rectangle
  							if ((screenPosMsg.x >= m_oCurrentPoint.X) && (screenPosMsg.x <= (m_oCurrentPoint.X + localWidth)) &&
  								(screenPosMsg.y >= m_oCurrentPoint.Y) && (screenPosMsg.y <= (m_oCurrentPoint.Y + localHeight)))
  							{
  								eatMessage = true;
  
  								Hide();
  
  								POINT localPoint = MousePositionToClient(screenPosMsg); 
  
  								//if (SelectionService != null)
  								//	SelectionService.SetSelectedComponents(new Component [] {TreeView}, SelectionTypes.Replace);
  							}
  							else
  							{
  								//Hide();
  
  								//m_exitLoop = true;								
  							}
  						}						
  						#endregion												
  
  						if (eatMessage == true)
  						{
  							MSG eat = new MSG();
  							User32.GetMessage(ref eat, 0, 0, 0);
  
  							eatMessage = false;
  						}
  						else						
  						{
  							if (User32.GetMessage(ref msg, 0, 0, 0))
  							{
  								User32.TranslateMessage(ref msg);
  								User32.DispatchMessage(ref msg);
  							}
  						}
  					}
  				}
  			}
  		}
  
  		public void Hide()
  		{
  			m_exitLoop = true;
  			Visible = false;	
  
  			User32.ShowWindow(this.Handle, (short)ShowWindowStyles.SW_HIDE);					
  		}
  
  		protected override void WndProc(ref Message m)
  		{
  			switch(m.Msg)
  			{					
  				case (int)Msgs.WM_ACTIVATEAPP:
  					Hide();
  					base.WndProc(ref m);
  					break;
  
  				default:
  					base.WndProc(ref m);
  					break;
  			}			
  		}
  	}
  
  	/// <summary>
  	/// Summary description for ActionMenuNative.
  	/// </summary>
  	internal class ActionMenuNative : NativeWindow
  	{
  		#region private members
  		private bool m_bLayered;
  		private Color m_oBackColor;
  		private Color m_oControlLL;
  		private Size m_oCurrentSize;	
  		private Point m_oScreenPos;
  		private Point m_oCurrentPoint;
  		private SolidBrush m_oLBrush;
  		private SolidBrush m_oEBrush;
  		private SolidBrush m_oLLBrush;
  		private Point m_oLastMousePos;
  
  		protected bool m_bMouseOver = false;
  
  		private static int m_nImageWidth = 16;
  		private static int m_nShadowLength = 4;
  		private static Bitmap m_oShadowCache = null;
  		private static int m_nShadowCacheWidth = 0;
  		private static int m_nShadowCacheHeight = 0;
  
  		private bool m_bSupportsLayered = false;
  
  		private Color m_oBorderColor = Color.FromArgb(0xAA, 0x8E, 0x7D);		
  		private Color m_oSelectColor = Color.Azure;
  		private string m_sTitle = "Context Menu";
  
  		private int m_nWidth = 150;
  		private Point m_oLastMouseDown;		
  
  		private ArrayList m_aGroups = new ArrayList();
  		private Hashtable m_mapGroupBox2Rect = new Hashtable();
  		private Hashtable m_mapRect2GroupBox = new Hashtable();
  		private Hashtable m_mapItem2Rect = new Hashtable();
  		private Hashtable m_mapRect2Item = new Hashtable();
  
  		private ActionMenuItem m_oSelectedItem = null;
  		private ActionMenuGroup m_oHighlightedGroup = null;
  		private bool m_bExpandCollapseStrafeSelect = false;	
  		private bool m_bDrag = false;	
  		#endregion
  
  		#region properties
  		[
  		Category("ActionMenu"),
  		Description("Font of the context menu"),
  		]
  		public int Width
  		{
  			set
  			{
  				m_nWidth = value;				
  			}			
  		}
  
  		[
  		Category("ActionMenu"),
  		Description("Font of the context menu"),
  		]
  		public Font Font
  		{
  			get
  			{
  				return new Font("Microsoft Sans Serif", (float)8.25);
  			}			
  		}
  
  		[
  		Category("ActionMenu"),
  		Description("Title of the context menu"),
  		]
  		public string Title
  		{
  			get
  			{
  				return m_sTitle;
  			}
  
  			set
  			{
  				m_sTitle = value;
  			}
  		}
  
  		[
  		Category("ActionMenu"),
  		Description("The color of the border"),
  		]
  		public Color BorderColor
  		{
  			get
  			{
  				return m_oBorderColor;
  			}
  
  			set
  			{
  				m_oBorderColor = value;
  			}
  		}
  
  		[
  		Category("ActionMenu"),
  		Description("The color of the selected item"),
  		]
  		public Color SelectColor
  		{
  			get
  			{
  				return m_oSelectColor;
  			}
  
  			set
  			{
  				m_oSelectColor = value;
  			}
  		}
  		#endregion		
  
  		#region helper structs
  		private enum PaintItem
  		{
  			BT = 0,	BL = 1, BB = 2, BR = 3, IGT = 4, IGL = 5, IGB = 6, IGR = 7,	TGL	= 8, TGR = 9,
  			SMGL = 10, SMW = 11, SBGR = 12, SPH	= 13, SPW = 14, SG = 15, SW = 16, SH = 17, EWG = 18,
  			EHG	= 19, ERG = 20,	ER = 21
  		}		
  		
  		private static readonly int[,] m_aPosition = { 
  														{2, 1, 0, 1, 2, 3, 3, 5, 4, 4, 2, 6, 5, 5, 1, 10, 4, 4, 2, 2, 0, 0},
  														{1, 0, 1, 2, 2, 1, 3, 4, 3, 3, 2, 8, 5, 5, 5, 10, 0, 0, 2, 2, 2, 5}	
  													 };
  		#endregion
  
  		#region construction
  		public ActionMenuNative()
  		{			
  			m_bSupportsLayered = (OSFeature.Feature.GetVersionPresent(OSFeature.LayeredWindows) != null);
  
  			m_oBackColor = Color.White;
  			m_oControlLL = CalculateColor(SystemColors.Window, m_oBackColor, 220);
  			m_oLBrush = new SolidBrush(CalculateColor(m_oBackColor, Color.White, 200));
  			m_oEBrush = new SolidBrush(CalculateColor(m_oBackColor, Color.White, 150));
  			m_oLLBrush = new SolidBrush(m_oControlLL);			
  		}
  		#endregion
  
  		#region implementation
  		public ActionMenuGroup AddMenuGroup(string sGroup)
  		{
  			ActionMenuGroup oGroup = new ActionMenuGroup();
  			oGroup.Title = sGroup;
  
  			m_aGroups.Add(oGroup);
  
  			RecalcLayout();
  
  			Invalidate();
  
  			return oGroup;
  		}
  
  		public void RemoveMenuGroup(ActionMenuGroup oGroup)
  		{
  			m_aGroups.Remove(oGroup);
  
  			RecalcLayout();
  
  			Invalidate();
  		}
  
  		public void ClearMenuGroups()
  		{			
  			m_aGroups.Clear();
  		}		
  
  		public ActionMenuItem AddMenuItem(ActionMenuGroup oGroup, string sItem)
  		{
  			ActionMenuItem oItem = new ActionMenuItem();
  
  			oItem.Text = sItem;
  			oItem.MenuGroup = oGroup;
  			oGroup.Items.Add(oItem);
  
  			return oItem;
  		}
  		
  		internal bool AllGroupsExpanded()
  		{
  			foreach (ActionMenuGroup oGroup in m_aGroups)
  				if (oGroup.Expanded == false)
  					return false;
  
  			return true;
  		}
  		#endregion			
  
  		#region defined events
  		internal delegate void ItemClickEventHandler(ActionMenuItem oItem);
  		internal event ItemClickEventHandler ItemClick;
  		protected virtual void OnItemClick(ActionMenuItem oItem) { if (ItemClick != null) ItemClick(oItem); }
  		#endregion
  
  		#region native window implementation
  		public void Show(int nX, int nY)
  		{
  			// Decide if we need layered windows
  			m_bLayered = (m_bSupportsLayered);					
  
  			CreateParams cp = new CreateParams();
  
  			// Any old title will do as it will not be shown
  			cp.Caption = "PureComponents.ActionMenu";
  
  			RecalcLayout();
  
  			// get the max screen size. if the nX + Width or nY + Height is bigger than the area, them move it elsewhere and recalculate the
  			// position
  			Screen oScreen = Screen.FromHandle(this.Handle);
  
  			if (nX + m_oCurrentSize.Width > oScreen.Bounds.Width)
  				nX = oScreen.Bounds.Width - m_oCurrentSize.Width;
  
  			if (nY + m_oCurrentSize.Height > oScreen.Bounds.Height)
  				nY = oScreen.Bounds.Height - m_oCurrentSize.Height;
  
  			// set the position
  			m_oScreenPos = new Point(nX, nY);
  
  			Size winSize = m_oCurrentSize;
  			Point screenPos = m_oScreenPos;
  			
  			// Define the screen position/size			
  			cp.X = nX;
  			cp.Y = nY;
  			cp.Height = winSize.Height;
  			cp.Width = winSize.Width;
  
  			// As a top-level window it has no parent
  			cp.Parent = IntPtr.Zero;
  			
  			// Appear as a top-level window
  			cp.Style = unchecked((int)(uint)WindowStyles.WS_POPUP);
  			
  			// Set styles so that it does not have a caption bar and is above all other 
  			// windows in the ZOrder, i.e. TOPMOST
  			cp.ExStyle = (int)WindowExStyles.WS_EX_TOPMOST + 
  				(int)WindowExStyles.WS_EX_TOOLWINDOW;
  
  			// OS specific style
  			if (m_bLayered)
  			{
  				// If not on NT then we are going to use alpha blending on the shadow border
  				// and so we need to specify the layered window style so the OS can handle it
  
gt: 				cp.ExStyle += (int)WindowExStyles.WS_EX_LAYERED;