context: // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
  //
  // Authors:
  //	Jackson Harper (jackson@ximian.com)
  //
  //
  
  
  using System;
  using System.Drawing;
  using System.Runtime.InteropServices;
  
  
  namespace System.Windows.Forms {
  
  	internal abstract class InternalWindowManager {
  		private TitleButtons title_buttons;
  		internal Form form;
  
  		// moving windows
  		internal Point start;
  		internal State state;
  		protected Point clicked_point;
  		private FormPos sizing_edge;
  		internal Rectangle virtual_position;
  
  		private Rectangle normal_bounds;
  		private Rectangle iconic_bounds;
  		
  
  		public enum State {
  			Idle,
  			Moving,
  			Sizing,
  		}
  
  		[Flags]
  		public enum FormPos {
  			None,
  
  			TitleBar = 1,
  
  			Top = 2,
  			Left = 4,
  			Right = 8,
  			Bottom = 16,
  
  			TopLeft = Top | Left,
  			TopRight = Top | Right,
  
  			BottomLeft = Bottom | Left,
  			BottomRight = Bottom | Right,
  
  			AnyEdge = Top | Left | Right | Bottom,
  		}
  
  		public InternalWindowManager (Form form)
  		{
  			this.form = form;
  
  			form.SizeChanged += new EventHandler (FormSizeChangedHandler);
  
  			title_buttons = new TitleButtons (form);
  			ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
  		}
  
  		public Form Form {
  			get { return form; }
  		}
  		
  		public int IconWidth {
  			get { return TitleBarHeight - 5; }
  		}
  
  		public TitleButtons TitleButtons {
  			get {
  				return title_buttons;
  			}
  		}
  		internal Rectangle NormalBounds {
  			get {
  				return normal_bounds;
  			}
  			set {
  				normal_bounds = value;
  			}
  		}
  		internal Size IconicSize {
  			get {
  				return SystemInformation.MinimizedWindowSize;
  			}
  		}
  		
  		internal Rectangle IconicBounds {
  			get {
  				if (iconic_bounds == Rectangle.Empty)
  					return Rectangle.Empty;
  				Rectangle result = iconic_bounds;
  				result.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y;
  				return result;
  			}
  			set {
  				iconic_bounds = value;
  				iconic_bounds.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y;
  			}
  		}
  
  		internal virtual Rectangle MaximizedBounds {
  			get {
  				return Form.Parent.ClientRectangle;
  			}
  		}
  				
  		public virtual void UpdateWindowState (FormWindowState old_window_state, FormWindowState new_window_state, bool force)
  		{
  			if (old_window_state == FormWindowState.Normal) {
  				NormalBounds = form.Bounds;
  			} else if (old_window_state == FormWindowState.Minimized) {
  				IconicBounds = form.Bounds;
  			}
  
  			switch (new_window_state) {
  			case FormWindowState.Minimized:
  				if (IconicBounds == Rectangle.Empty) {
  					Size size = IconicSize;
  					Point location = new Point (0, Form.Parent.ClientSize.Height - size.Height);
  					IconicBounds = new Rectangle (location, size);
  				}
  				form.Bounds = IconicBounds;
  				break;
  			case FormWindowState.Maximized:
  				form.Bounds = MaximizedBounds;
  				break;
  			case FormWindowState.Normal:
  				form.Bounds = NormalBounds;
  				break;
  			}
  
  			UpdateWindowDecorations (new_window_state);
  			form.ResetCursor ();
  		}
  		
  		public virtual void UpdateWindowDecorations (FormWindowState window_state)
  		{
  			ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
  			if (form.IsHandleCreated)
  				XplatUI.RequestNCRecalc (form.Handle);
  		}
  		
  		public virtual bool WndProc (ref Message m)
  		{
  #if debug
  			Console.WriteLine(DateTime.Now.ToLongTimeString () + " " + this.GetType () .Name + " (Handle={0},Text={1}) received message {2}", form.IsHandleCreated ? form.Handle : IntPtr.Zero,  form.Text, m.ToString ());
  #endif
  
  			switch ((Msg)m.Msg) {
  
  
  				// The mouse handling messages are actually
  				// not WM_NC* messages except for the first button and NCMOVEs
  				// down because we capture on the form
  
  			case Msg.WM_MOUSEMOVE:
  				return HandleMouseMove (form, ref m);
  
  			case Msg.WM_LBUTTONUP:
  				HandleLButtonUp (ref m);
  				break;
  
  			case Msg.WM_RBUTTONDOWN:
  				return HandleRButtonDown (ref m);
  				
  			case Msg.WM_LBUTTONDOWN:
  				return HandleLButtonDown (ref m);
  				
  			case Msg.WM_LBUTTONDBLCLK:
  				return HandleLButtonDblClick (ref m);
  				
  			case Msg.WM_PARENTNOTIFY:
  				if (Control.LowOrder(m.WParam.ToInt32()) == (int) Msg.WM_LBUTTONDOWN) 
  					Activate ();
  				break;
  
  			case Msg.WM_NCHITTEST: 
  				return HandleNCHitTest (ref m);
  
  				// Return true from these guys, otherwise win32 will mess up z-order
  			case Msg.WM_NCLBUTTONUP:
  				HandleNCLButtonUp (ref m);
  				return true;
  
  			case Msg.WM_NCLBUTTONDOWN:
  				HandleNCLButtonDown (ref m);
  				return true;
  
  			case Msg.WM_NCMOUSEMOVE:
  				HandleNCMouseMove (ref m);
  				return true;
  				
  			case Msg.WM_NCLBUTTONDBLCLK:
  				HandleNCLButtonDblClick (ref m);
  				break;
  
  			case Msg.WM_NCMOUSELEAVE:
  				HandleNCMouseLeave (ref m);
  				break;
  			
  			case Msg.WM_MOUSELEAVE:
  				HandleMouseLeave (ref m);
  				break;
  
  			case Msg.WM_NCCALCSIZE:
  				return HandleNCCalcSize (ref m);
  
  			case Msg.WM_NCPAINT:
  				return HandleNCPaint (ref m);
  			}
  
  			return false;
  		}
  
  		protected virtual bool HandleNCPaint (ref Message m)
  		{
  			PaintEventArgs pe = XplatUI.PaintEventStart (ref m, form.Handle, false);
  
  			Rectangle clip;
  			
  			if (form.ActiveMenu != null) {
  				Point pnt;
  
  				pnt = GetMenuOrigin ();
  
  				// The entire menu has to be in the clip rectangle because the 
  				// control buttons are right-aligned and otherwise they would
  				// stay painted when the window gets resized.
  				clip = new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0);
  				clip = Rectangle.Union (clip, pe.ClipRectangle);
  				pe.SetClip (clip);
  				pe.Graphics.SetClip (clip);
  
  				form.ActiveMenu.Draw (pe, new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0));
  			}
  			if (HasBorders || IsMinimized && !(Form.IsMdiChild && IsMaximized)) {
  				// clip region is not correct on win32.
  				// use the entire form's area.
  				clip = new Rectangle (0, 0, form.Width, form.Height);
  				ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this);
  			}
  			XplatUI.PaintEventEnd (ref m, form.Handle, false);
  			return true;
  		}
  
  		protected virtual bool HandleNCCalcSize (ref Message m)
  		{
  			XplatUIWin32.NCCALCSIZE_PARAMS ncp;
  			XplatUIWin32.RECT rect;
  
  			if (m.WParam == (IntPtr)1) {
  				ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam,
  						typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
  				
  				ncp.rgrc1 = NCCalcSize (ncp.rgrc1);
  
  				Marshal.StructureToPtr (ncp, m.LParam, true);
  			} else {
  				rect = (XplatUIWin32.RECT) Marshal.PtrToStructure (m.LParam, typeof (XplatUIWin32.RECT));
  				
  				rect = NCCalcSize (rect);
  				
  				Marshal.StructureToPtr (rect, m.LParam, true);
  			}
  			
  			return true;
  		}
  
  		protected virtual XplatUIWin32.RECT NCCalcSize (XplatUIWin32.RECT proposed_window_rect)
  		{
  			int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
  
  			if (HasBorders) {
  				proposed_window_rect.top += TitleBarHeight + bw;
  				proposed_window_rect.bottom -= bw;
  				proposed_window_rect.left += bw;
  				proposed_window_rect.right -= bw;
  			}
  
  			if (XplatUI.RequiresPositiveClientAreaSize) {
  				// This is necessary for Linux, can't handle <= 0-sized 
  				// client areas correctly.
  				if (proposed_window_rect.right <= proposed_window_rect.left) {
  					proposed_window_rect.right += proposed_window_rect.left - proposed_window_rect.right + 1;
  				}
  				if (proposed_window_rect.top >= proposed_window_rect.bottom) {
  					proposed_window_rect.bottom += proposed_window_rect.top - proposed_window_rect.bottom + 1;
  				}
  			}
  
  			return proposed_window_rect;
  		}
  
  		protected virtual bool HandleNCHitTest (ref Message m)
  		{
  
  			int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
  			int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
  
  			NCPointToClient (ref x, ref y);
  
  			FormPos pos = FormPosForCoords (x, y);
  
  			if (pos == FormPos.TitleBar) {
  				m.Result = new IntPtr ((int)HitTest.HTCAPTION);
  				return true;
  			}
  
  			if (!IsSizable)
  				return false;
  
  			switch (pos) {
  			case FormPos.Top:
  				m.Result = new IntPtr ((int)HitTest.HTTOP);
  				break;
  			case FormPos.Left:
  				m.Result = new IntPtr ((int)HitTest.HTLEFT);
  				break;
  			case FormPos.Right:
  				m.Result = new IntPtr ((int)HitTest.HTRIGHT);
  				break;
  			case FormPos.Bottom:
  				m.Result = new IntPtr ((int)HitTest.HTBOTTOM);
  				break;
  			case FormPos.TopLeft:
  				m.Result = new IntPtr ((int)HitTest.HTTOPLEFT);
  				break;
  			case FormPos.TopRight:
  				m.Result = new IntPtr ((int)HitTest.HTTOPRIGHT);
  				break;
  			case FormPos.BottomLeft:
  				m.Result = new IntPtr ((int)HitTest.HTBOTTOMLEFT);
  				break;
  			case FormPos.BottomRight:
  				m.Result = new IntPtr ((int)HitTest.HTBOTTOMRIGHT);
  				break;
  			default:
  				// We return false so that DefWndProc handles things
  				return false;
  			}
  			return true;
  		}
  
  		public virtual void UpdateBorderStyle (FormBorderStyle border_style)
  		{
  			if (form.IsHandleCreated) {
  				XplatUI.SetBorderStyle (form.Handle, border_style);
  			}
  
  			if (ShouldRemoveWindowManager (border_style)) {
  				form.RemoveWindowManager ();
  				return;
  			}
  				
  			ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
  		}
  
  		
  		
  		public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state)
  		{
  			UpdateWindowState (old_state, window_state, false);
  		}
  
  		public virtual FormWindowState GetWindowState ()
  		{
  			return form.window_state;
  		}
  
  		public virtual void PointToClient (ref int x, ref int y)
  		{
  			// toolwindows stay in screencoords we just have to make sure
  			// they obey the working area
  			Rectangle working = SystemInformation.WorkingArea;
  
  			if (x > working.Right)
  				x = working.Right;
  			if (x < working.Left)
  				x = working.Left;
  
  			if (y < working.Top)
  				y = working.Top;
  
gt: 			if (y > working.Bottom)