Click here to Skip to main content
15,891,372 members
Articles / Desktop Programming / WTL

ATL Snapping Framework (Like VC.NET)

Rate me:
Please Sign up or sign in to vote.
4.86/5 (10 votes)
23 May 200213 min read 157.5K   1.7K   45  
Complete implementation of .NET IDE's snapping windows in ATL/WTL
/////////////////////////////////////////////////////////////////////////////
// ATL Window Snapping Framework
//
// Written by Eugene Polonsky (partnerinflight@hotmail.com)
// Copyright (c) 2002 Eugene Polonsky
// Thanks to Bjarne Viksoe for CMouseHover class, and a template of
// this message. :)
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. 
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs. Really beware of bugs. As this code is... well... 
// rather complex, there is a VERY good chance that there are bugs
// lurking in here. I will be continually working on improving/updating
// this project, and I'm hoping someone out there will do so as well.
//

#pragma once

#include <math.h>

#ifndef __cplusplus
#	error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLAPP_H__
   #error wtlSnapWnd.h requires atlapp.h to be included first
#endif

// need the STL list....
#ifndef _LIST_
#	include <list>
#endif 

// also need algorithms for std::find
#ifndef _ALGORITHM_
#	include <algorithm>
#endif

// fixed. atltypes for VC7, atlmisc for VC6, and everyone's happy.
#if (_ATL_VER < 0x0700)
#	include <atlmisc.h>
#else
#	include <atltypes.h>
#endif


#pragma warning(disable: 4290) // disable the stupid warning about throw being ignored in some functions.

// notes on this whole thing:
// the framework implements the State pattern in dealing with the changing states of autohidden vs floating vs pinned windows.
// That is,  we have a very simple Window class that leaves all the event handling to a particular State class. There are
// two states -- autohidden and floating, and, of course, they do event handling quite differently. 

// the framework class is not meant to function as a standalone window, rather it's meant to play a Role in an existing
// window (i.e. you derive from the CSnapFramework just like you would derive from CUpdateUI. You must also do a
// CHAIN_MSG_MAP(CSnapFramework<yourclass>) as the first message macro in your window's message map. This will ensure that
// the framework gets access to its messages first, and doesn't interfere with your message processing.

// notice that you MUST use this framework on a child window -- i.e. you can't use it on a top level frame. This actually
// makes sense if you think about it -- you wouldn't want the framework to mess with a frame's toolbars/menus,
// and this way you can implement the framework in a context of an arbitrarily nested child window. In terms of the
// project I created this for, it's the perfect behavior.

////////////////////////////////////////////////BEGIN MOUSE HOVER CLASS//////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Mouse Hover helper
// This code is borrowed from the wonderful atldgix.h class by Bjarne Viksoe. 
// I'm enclosing his copyright message right here, just in case.

/////////////////////////////////////////////////////////////////////////////
// Additional GDI/USER wrappers
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2001-2002 Bjarke Viksoe.
// Thanks to Daniel Bowen for COffscreenDrawRect.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. 
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//

// Um, I'm not sure about the "unmodified by any means" bit in there,
// but I hope Bjarne won't mind if I just rip one of the classes from that file....

#ifndef NOTRACKMOUSEEVENT
#ifndef WM_MOUSEENTER
#define WM_MOUSEENTER WM_USER+253
#endif // WM_MOUSEENTER

// define TME_NONCLIENT if we need to...  if you defined winver as 5.0, it'll already be defined.

#ifndef TME_NONCLIENT
#define TME_NONCLIENT 0x00000010
#endif

// To use it, derive from it and chain it in the message map...
// Make sure to set bHandled to FALSE when handling WM_MOUSEMOVE or
// the WM_MOUSELEAVE message!!
template< class T, bool tbLeave, bool tbHover, bool tbNCLeave >
class CMouseHover
{
public:
   // Internal states
   bool m_fMouseOver;

   CMouseHover() : 
      m_fMouseOver(false)
   {
   }

   BEGIN_MSG_MAP(CMouseHover)
      MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
      MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
   END_MSG_MAP()

   LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
   {
      T* pT = static_cast<T*>(this);
      if( !m_fMouseOver )   {
         m_fMouseOver = true;
         pT->SendMessage(WM_MOUSEENTER, wParam, lParam);
         pT->Invalidate();
         pT->UpdateWindow();
         _StartTrackMouseLeave(pT->m_hWnd);
      }
      bHandled = FALSE;
      return 0;
   }
   LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
   {
      T* pT = static_cast<T*>(this);
      if( m_fMouseOver ) {
         m_fMouseOver = false;
         pT->Invalidate();
         pT->UpdateWindow();
      }
      bHandled = FALSE;
      return 0;
   }
   BOOL _StartTrackMouseLeave(HWND hWnd)
   {
      TRACKMOUSEEVENT tme;
      tme.cbSize = sizeof(tme);
      tme.dwFlags = 0;
	  if(tbLeave) tme.dwFlags |= TME_LEAVE;
	  if(tbHover) tme.dwFlags |= TME_HOVER;
	  if(tbNCLeave) tme.dwFlags |= TME_NONCLIENT;
      tme.hwndTrack = hWnd;
	  tme.dwHoverTime = 400;
      return _TrackMouseEvent(&tme);
   }
};

#endif // NOTRACKMOUSEEVENT

///////////////////////////////////////////////////END MOUSE HOVER CLASS//////////////////////////////////////

// some general defines. These change the geometry of the snap bars, geometry of text/whatever drawing
// that sort of stuff. Feel free to mess with it.

#define SNAPBAR_SIZE								24
#define SNAP_DISTANCE								10
#define SNAPBAR_ICON_INTERNAL_OFFSET				3
#define SNAPBAR_ICON_SEPARATION						( 4 + SNAPBAR_ICON_INTERNAL_OFFSET)
#define SNAPBAR_ICON_OFFSET							( 2 + SNAPBAR_ICON_INTERNAL_OFFSET)
#define SNAPPED_WINDOW_CAPTION_SIZE					16
#define SNAPPED_WINDOW_CAPTION_BUTTON_OFFSET_X		4
#define SNAPPED_WINDOW_CAPTION_BUTTON_OFFSET_Y		2
#define SNAPPED_WINDOW_CAPTION_BUTTON_SIZE_XY		12
#define SNAPPED_WINDOW_CAPTION_BUTTON_SEPARATION	4
#define SNAPPED_WINDOW_GRIPPER_SIZE					4
#define UWM_SNAP_WINDOW_DESTROYED					WM_APP + 1024
#define TRACKING_TIMER_ID							WM_APP
#define SNAPPED_WINDOW_GAP_SIZE						40 // offsets from top/bottom for MINMAXINFO stuff
// a couple utility macros
#define IS_FLOATING(a) ((a) == eFloating)
#define IS_SNAPPED(a)  ((a) != eFloating)
#define IS_SNAPPED_HORZ(a) (((a) == eDockedTop) || ((a) == eDockedBottom))
#define IS_SNAPPED_VERT(a) (((a) == eDockedLeft) || ((a) == eDockedRight))


template<class T>
class CSnapFramework
{
private:
	typedef CSnapFramework<T> thisClass;

public: // helper definitions
	enum eSide { eTop=0, eRight, eBottom, eLeft }; // enumeration for the sides active array
	enum eWinState { eNull = -1, eDockedTop=0, eDockedRight, eDockedBottom, eDockedLeft, eFloating }; // enumeration for the state lists

protected:
	// we need to subclass the parent window of the framework. We'll walk the parent list to get the topmost parent when 
	// we do the subclassing. The upshot is that we need that to get the WM_ACTIVATEAPP messages to do correct
	// floater hiding/showing.
	class CParentWindow : public CWindowImpl<CParentWindow>
	{
	protected:
		// base state class. This dude is an abstract base class out of which we'll define the actual states.
	public:
		CParentWindow(CSnapFramework<T>* pParent) : m_pParent(pParent)
		{ 
		}

		BEGIN_MSG_MAP(CParentWindow)
			MESSAGE_HANDLER(WM_ACTIVATEAPP, OnActivateApp)
		END_MSG_MAP()
	protected:
	LRESULT		OnActivateApp(UINT, WPARAM wParam, LPARAM, BOOL& bHandled)
	{
		bHandled = FALSE; // let other people handle this too...
		// tell the parent to do the appropriate action based on this info....
		this->m_pParent->NotifyActivationEvent(wParam == TRUE);
		return 0;
	}
		CSnapFramework<T>* m_pParent;
	};
	friend class CParentWindow;

	// this is the actual window class that'll show people's client classes
	class CSnapBarWindow;
	class CSnapWindow : public CWindowImpl<CSnapWindow>
	{
	protected:
		// base state class. This dude is an abstract base class out of which we'll define the actual states.
		class CSnapWindowStateBase
		{
			friend class CSnapWindow;
		public:
			virtual LRESULT OnCreate(CSnapWindow* parent, BOOL& bHandled) = 0;
			virtual LRESULT OnEraseBkgnd(CSnapWindow* parent, BOOL& bHandled) =0;
			virtual void	OnSize(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled) = 0;
			virtual void	OnNCLeftButtonDown(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)=0;
			virtual void	OnNCLeftButtonUp(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)=0;
			virtual void	OnMouseMove(UINT uiFlags, CPoint pt, CSnapWindow* parent, BOOL& bHandled) =0;
			virtual bool	Create(HWND hWndClient, IN eWinState state, bool bHasCloseCheckBox, CSize szSize,
									CSnapWindow* pParent)=0;
			virtual void	OnPaint(CDCHandle dc, CSnapWindow* pParent) =0;
			virtual void	HandleFrameworkResize(IN CSnapWindow* pParent) =0;
			virtual void	SwitchState(CSnapWindow* pParent, eWinState eNewState, eWinState eOldState) =0;
			virtual LRESULT GetMinMaxInfo(CSnapWindow* pParent, MINMAXINFO* pInfo, BOOL& bHandled) =0;
			virtual int		GetRequiredRealEstate(CSnapWindow* pParent) =0;
		};

		// this is the Floating state derivation.
		class CSnapWindowStateFloating : public CSnapWindowStateBase
		{
		public:
			// we don't handle on create for this one.
			virtual LRESULT OnCreate(CSnapWindow* parent, BOOL& bHandled)
			{
				bHandled = FALSE;
				return 0;
			}
			// eliminate flicker
			virtual LRESULT OnEraseBkgnd(CSnapWindow* parent, BOOL& bHandled)
			{
				// just a standard anti-flicker return.
				return 0;
			}
			virtual void	OnSize(UINT, CPoint, CSnapWindow* parent, BOOL& bHandled)
			{
				CRect rc;
				
				if(::IsWindow(parent->m_hWnd) == FALSE)
					return; // nothing to do here...

				// first let's take care of moving our main window
				// the sizeset flag is used as a way to notify the window that we want to recompute its rect,
				// and resize it. 
				if(!parent->m_bSizeSet) 
				{
					// let's get the window rect we want for this window....
					CRect rcWin = this->GetWindowRect(parent, parent->m_szSize);
					// if it's an empty rect (as, for instance, on creation), we do nothing
					parent->m_bSizeSet = !rcWin.IsRectEmpty();
					if(parent->m_bSizeSet)
					{
						// once we call MoveWindow OnSize will get called again, and it gets rather nasty....
						// this is just a flag to stop that sort of stuff from taking place.
						parent->m_bStopOnSizeRecursion = true;
						parent->MoveWindow(rcWin);
						// bring our window back to the top
						parent->SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
						parent->m_bStopOnSizeRecursion = false;
					}
				}
				// take care of the client window. Resize it to fit the newly sized parent.
				CRect rcClient, rcWin;
				parent->GetClientRect(rcClient);
				parent->GetWindowRect(rcWin);
				if(parent->m_bSizeSet)
					parent->m_szSize.SetSize(rcWin.Width(), rcWin.Height());
				::MoveWindow(parent->m_hwndClient, rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), TRUE);
			}
			virtual void	OnNCLeftButtonDown(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				if(ui == HTCAPTION) // is the user attempting to drag the window?
				{
					if(::DragDetect(parent->m_hWnd, pt) == 0)
					{
						bHandled = FALSE;
						return; // nope. User didn't drag.
					}
					// if the user's dragging, let's let them drag.... We'll take them to our custom message loop
					// to do it. 
					parent->m_pParent->HandleDragging(pt, parent);
				}
				else
					bHandled = FALSE;
			}
			virtual void	OnNCLeftButtonUp(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				// we don't care about this message here.
				bHandled = FALSE;
			}
			virtual void	OnMouseMove(UINT, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				// we don't care about this one either. Dragging will be happening in a separate message loop.
				bHandled = FALSE; 
			}
			virtual void	OnPaint(CDCHandle dc, CSnapWindow* pParent)
			{
				// we don't do anything when the win's floating.
			}

			virtual bool	Create(HWND hWndClient, IN eWinState state, bool bHasCloseCheckBox, 
									CSize szSize, CSnapWindow* pParent)
			{
				CRect rc;
				DWORD dwStyle = WS_POPUP|WS_BORDER|WS_CLIPSIBLINGS|WS_OVERLAPPED|WS_THICKFRAME|WS_DLGFRAME|WS_VISIBLE;
				DWORD dwExStyle = WS_EX_TOOLWINDOW;
				
				dwStyle |= bHasCloseCheckBox ? WS_SYSMENU : 0;

				rc = this->GetWindowRect(pParent, szSize);
				pParent->m_bSizeSet = !rc.IsRectEmpty();
				HWND hWnd = pParent->Create(NULL, rc, pParent->m_cWindowText, dwStyle, dwExStyle);
				if(::IsWindow(hWnd) != FALSE)
				{
					// let's take care of the client.
					::SetParent(hWndClient, hWnd);
					CRect rc;
					pParent->GetClientRect(rc);
					::MoveWindow(hWndClient, rc.left, rc.top, rc.Width(), rc.Height(), TRUE);

					// let's create the font we'll be using for the captions, too.
					pParent->m_fntCaption.CreatePointFont(80, "Tahoma");
					return true;
				}
				return false;
			}

			virtual void	HandleFrameworkResize(IN CSnapWindow* pParent)
			{
				BOOL bHandled;
				if(!pParent->m_bSizeSet)
					this->OnSize(0, CPoint(0,0), pParent, bHandled);
			}

			CRect			GetWindowRect(CSnapWindow* pParent, CSize szDesiredSize)
			{
				CRect rc, rcWin, rcClient;
				// now let's figure out what our rect will be
				// ok first we need to figure out client rect of the framework
				pParent->m_pParent->m_pT->GetWindowRect(rcWin);
				pParent->m_pParent->m_pT->GetClientRect(rcClient);
				if(rcClient.IsRectEmpty())
					return rcClient; // return an emtpy rect
				rc.top = rcWin.top;
				rc.left = rcWin.left;
				rc.right = rc.left + szDesiredSize.cx;
				rc.bottom = rc.top + szDesiredSize.cy;
				return rc;
			}
			virtual void	SwitchState(CSnapWindow* pParent, eWinState eNewState, eWinState eOldState)
			{
				BOOL b;
				if((eOldState != eFloating) && (eOldState != eNull))
				{
					CRect rc;
					CPoint pt;
					// let's hide....
					pParent->ShowWindow(SW_HIDE);
					::GetCursorPos(&pt);
					rc.SetRect(pt.x, pt.y, pt.x+pParent->m_szSize.cx, pt.y+pParent->m_szSize.cy);
					pParent->m_bStopOnSizeRecursion = true;
					// means we're switching from WS_CHILD to WS_POPUP and all that that entails
					pParent->ModifyStyle(WS_CHILD, WS_POPUP|WS_BORDER|WS_OVERLAPPED|WS_DLGFRAME|WS_THICKFRAME, 
										SWP_NOSIZE|SWP_NOMOVE);
					pParent->SetParent(NULL);
					pParent->ModifyStyleEx(0, WS_EX_TOOLWINDOW, SWP_NOSIZE|SWP_NOMOVE);
					pParent->MoveWindow(rc);
					// we need to show ourselves....
					pParent->ShowWindow(SW_SHOW);
					pParent->Invalidate();
					pParent->m_bStopOnSizeRecursion = false;
					pParent->m_bSizeSet = true; // make sure we DON'T try to get the rect...
				}
				this->OnSize(0, 0, pParent, b); // we need the client moved...
			}
			virtual int		GetRequiredRealEstate(CSnapWindow* pParent) { return 0;	} // floaters don't have it.
			// we don't mess with this here...
			virtual LRESULT GetMinMaxInfo(CSnapWindow* pParent, MINMAXINFO* pInfo, BOOL& bHandled)
			{ bHandled = FALSE; return 1; /* I think...*/  }

		};

		// this is the snapping state derivation
		class CSnapWindowStateAuto : public CSnapWindowStateBase
		{
		public:
			virtual LRESULT OnCreate(CSnapWindow* parent, BOOL& bHandled)
			{
				// we also don't handle creation
				bHandled = FALSE;
				return 0;
			}
			virtual LRESULT OnEraseBkgnd(CSnapWindow* parent, BOOL& bHandled)
			{
				// same deal. standard anti-flicker
				return 0;
			}
			virtual void	OnSize(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				CRect rcParentClient, rcClient;
				if(::IsWindow(parent->m_hWnd) == FALSE) // haven't yet created it
					return;

				parent->m_pParent->m_pT->GetClientRect(rcParentClient);
			
				// first let's take care of moving our main window
				// since I can't seem to be able to get reliable MouseUp notification when 
				// resizing, I decided to screw the whole mouse thing. So here's how we 
				// decide what the user is doing. (the user could be resizing the parent window, in which case
				// we'd want to actually move this window according to the stored percentage, or the user could
				// be resizing this window, in which case we dont' move this window again, but update the percentage.)
				// Anyway, the solution's pretty simple. We store the last known ClientRect of the parent. If, in OnSize,
				// we find that the current ClientRect doesn't match the stored one, that means the user must've resized
				// the parent, and we should resize this window. If the clientRects match, that means the user must be
				// resizing us, and we should update the percentage.

				if((rcParentClient != parent->m_rcLastParentClientRect) || (!parent->m_bSizeSet)) // or we've NEVER resized...
					this->MoveWindow(parent);

				// store the clientrect
				parent->m_rcLastParentClientRect = rcParentClient;

				if(parent->m_bSizeSet)
				{
					CRect rcWin;
					parent->GetWindowRect(rcWin);
					// need to modify rcWin -- before we increased the size by 3... now we gotta put it back
					if(IS_SNAPPED_HORZ(parent->m_eState))
						rcWin.bottom -= 3;
					else
						rcWin.right -= 3;
					parent->GetClientRect(rcClient);
					parent->m_szSize.SetSize(
						IS_SNAPPED_VERT(parent->m_eState) ? rcWin.Width() : parent->m_szSize.cx, 
						IS_SNAPPED_HORZ(parent->m_eState) ? rcWin.Height(): parent->m_szSize.cy); // recompute size.
					// now execute the actual sizing part
					this->DoOnSize(parent, rcClient);
				}
				bHandled = FALSE;
			}
			// this function executes common code from CSnapWindowStateAuto and CSnapWindowStatePinned OnSize functions
			// basically we're resizing the client, and all the UI elements to fit the new rect
			void			DoOnSize(IN CSnapWindow* parent, IN const CRect& rcClient)
			{
				::MoveWindow(parent->m_hwndClient, rcClient.left, 
					rcClient.top+SNAPPED_WINDOW_CAPTION_SIZE, rcClient.Width(), 
					rcClient.Height()-SNAPPED_WINDOW_CAPTION_SIZE, TRUE);

				// also, we need to calculate the close button, pushpin, and opacity rects
				// let's go from right to left, so first we do the close button
				parent->m_rcCloseButtonRect.top = rcClient.top + SNAPPED_WINDOW_CAPTION_BUTTON_OFFSET_Y;
				parent->m_rcCloseButtonRect.right = rcClient.right - SNAPPED_WINDOW_CAPTION_BUTTON_OFFSET_X;
				parent->m_rcCloseButtonRect.bottom = parent->m_rcCloseButtonRect.top + SNAPPED_WINDOW_CAPTION_BUTTON_SIZE_XY;
				parent->m_rcCloseButtonRect.left = parent->m_rcCloseButtonRect.right - SNAPPED_WINDOW_CAPTION_BUTTON_SIZE_XY;

				// now let's do the pushpin.
				parent->m_rcPushPinRect.top = parent->m_rcCloseButtonRect.top;
				parent->m_rcPushPinRect.bottom = parent->m_rcCloseButtonRect.bottom;
				parent->m_rcPushPinRect.right = parent->m_rcCloseButtonRect.left - SNAPPED_WINDOW_CAPTION_BUTTON_OFFSET_X;
				parent->m_rcPushPinRect.left = parent->m_rcPushPinRect.right - SNAPPED_WINDOW_CAPTION_BUTTON_SIZE_XY;

				// finally let's do the slider
				parent->m_rcOpacitySliderRect.top = parent->m_rcCloseButtonRect.top;
				parent->m_rcOpacitySliderRect.bottom = parent->m_rcCloseButtonRect.bottom;
				parent->m_rcOpacitySliderRect.right = parent->m_rcPushPinRect.left - SNAPPED_WINDOW_CAPTION_BUTTON_OFFSET_X;
				parent->m_rcOpacitySliderRect.left = parent->m_rcOpacitySliderRect.right - SNAPPED_WINDOW_CAPTION_BUTTON_SIZE_XY*3;

			}
			virtual void	OnNCLeftButtonDown(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				if(parent->m_rcPushPinRect.PtInRect(pt)) // if we're on the pushpin button...
				{
					::SetCapture(parent->m_hWnd); // just capture the mouse and see what the user does...
					parent->m_bPinButtonPushed = true;
					parent->Invalidate(); // redraw though, to reflect state
				}
				else if(parent->m_rcCloseButtonRect.PtInRect(pt) && parent->m_bHasCloseButton) // if we're in the close button
				{ // and the window has the close button enabled
					::SetCapture(parent->m_hWnd);
					parent->m_bCloseButtonPushed = true;
					parent->Invalidate();
				}
				else
				{
					bHandled = FALSE; // otherwise i haven't the faintest what the user's doing. Silly user.
					parent->m_bLeftButtonDown = true;
				}
			}
			virtual void	OnNCLeftButtonUp(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				if(parent->m_bPinButtonPushed && parent->m_rcPushPinRect.PtInRect(pt))
				{ // if the user mouseupped on the pushpin button...
					::ReleaseCapture();
					parent->m_bPinButtonPushed = false;
					parent->Invalidate();
					// assign the new state....
					parent->m_pState = !parent->m_bWindowPinned ? &parent->m_cPinnedState : &parent->m_cAutoState;
					// and switch to that state
					parent->m_pState->SwitchState(parent, parent->m_eState, parent->m_eState);
				}
				else if(parent->m_bCloseButtonPushed && parent->m_rcCloseButtonRect.PtInRect(pt))
				{ // the user mouseupped on the close button. 
					::ReleaseCapture();
					parent->m_bCloseButtonPushed = false;
					parent->Invalidate();
					// take action
					parent->DestroyWindow();
				}
				else
				{ // the user mouseupped elsewhere. not much to do. 
					bHandled = FALSE;
					parent->m_bLeftButtonDown = false;
					if(parent->m_bCloseButtonPushed || parent->m_bPinButtonPushed)
					{ // if the user previously pushed on of the buttons (and then moved the mouse away)
						::ReleaseCapture();
						parent->m_bCloseButtonPushed = parent->m_bPinButtonPushed = false;
						parent->Invalidate();
					}
				}
			}
			virtual void	OnMouseMove(UINT uiFlags, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				// a quick sanity check...
				parent->m_bLeftButtonDown = (uiFlags & MK_LBUTTON) != 0;
				if(!parent->m_bLeftButtonDown && (parent->m_bPinButtonPushed || parent->m_bCloseButtonPushed)) 
				{    // if one of the buttons is pushed...
					// it shouldn't be.
					parent->m_bPinButtonPushed = parent->m_bCloseButtonPushed = false;
					parent->Invalidate();
				}

				if(parent->m_bLeftButtonDown && !(parent->m_bPinButtonPushed || parent->m_bCloseButtonPushed)) // aah. the user must be trying to drag....
				{
					parent->ClientToScreen(&pt);
					parent->m_bLeftButtonDown = false; // just do it in case the mouse goes our of scope....
					parent->m_pSnapParent->StartDragging(pt, parent);
				}
				bHandled = FALSE;
			}
			virtual void	OnPaint(CDCHandle dc, CSnapWindow* pParent)
			{
				// we need to paint the window bar and all that crap
				UINT uiCloseState = DFCS_CAPTIONCLOSE | (pParent->m_bHasCloseButton ? 0 : DFCS_INACTIVE) | 
					(pParent->m_bCloseButtonPushed ? DFCS_PUSHED : 0);
				CRect rcClient;
				HFONT hfOld = dc.SelectFont(pParent->m_fntCaption);
				pParent->GetClientRect(rcClient);
				rcClient.top += SNAPPED_WINDOW_CAPTION_SIZE;
				dc.ExcludeClipRect(rcClient);
				rcClient.top = 0;
				rcClient.bottom = SNAPPED_WINDOW_CAPTION_SIZE;
				dc.FillSolidRect(rcClient, ::GetSysColor(COLOR_BTNFACE));
				dc.TextOut(rcClient.left + SNAPBAR_ICON_INTERNAL_OFFSET, rcClient.top + SNAPBAR_ICON_INTERNAL_OFFSET, 
					pParent->m_cWindowText);
				dc.SelectFont(hfOld);
				// now let's draw the buttons
				dc.DrawFrameControl(&pParent->m_rcCloseButtonRect, DFC_CAPTION, uiCloseState);
				this->DrawPushpinButton((CDCHandle)dc, pParent->m_bWindowPinned, 
					pParent->m_rcPushPinRect, pParent->m_bPinButtonPushed);
				// finally we need to invalidate the snap bar -- it needs to redraw itself with the right icon
				pParent->m_pSnapParent->Invalidate();
			}
// This routine is stolen DIRECTLY from Jens Nilsson's article (http://www.codeproject.com/wtl/wtlsnap.asp). I added the
// code to do a vertical pin.
			void			DrawPushpinButton(CDCHandle dc, bool bVertical, const RECT &rc, bool bPushed)
			{
				if( ::IsRectEmpty(&rc) )
					return;

				RECT rcPushpin = rc;
				dc.DrawFrameControl(&rcPushpin, DFC_BUTTON, DFCS_ADJUSTRECT|DFCS_BUTTONPUSH | (bPushed ? DFCS_PUSHED : 0));

				POINT ptPin = { rcPushpin.left - 1, rcPushpin.top - 1 + (rcPushpin.bottom - rcPushpin.top) / 2};
				if (bPushed)
				{
					ptPin.x += 1;
					ptPin.y += 1;
				}

				POINT ptsPushpin[] = {	{ptPin.x, ptPin.y},
				{ptPin.x + 2, ptPin.y},
				{ptPin.x + 2, ptPin.y - 3},
				{ptPin.x + 4, ptPin.y - 1},
				{ptPin.x + 5, ptPin.y - 2},
				{ptPin.x + 6, ptPin.y - 2},
				{ptPin.x + 6, ptPin.y + 2},
				{ptPin.x + 5, ptPin.y + 2},
				{ptPin.x + 4, ptPin.y + 1},
				{ptPin.x + 2, ptPin.y + 3},
				{ptPin.x + 2, ptPin.y} };

				POINT ptDownPin = { rcPushpin.left -1 + (rcPushpin.right - rcPushpin.left) / 2, rcPushpin.bottom -1 };
			
				if(bPushed)
				{
					ptDownPin.x += 1;
					ptDownPin.y += 1;
				}

				POINT ptsPushPinDown[] = { 
					{ ptDownPin.x, ptDownPin.y },
					{ ptDownPin.x, ptDownPin.y - 2 },
					{ ptDownPin.x - 3, ptDownPin.y - 2 },
					{ ptDownPin.x - 3, ptDownPin.y - 3 },
					{ ptDownPin.x - 1, ptDownPin.y - 6 },
					{ ptDownPin.x - 2, ptDownPin.y - 7 },
					{ ptDownPin.x - 2, ptDownPin.y - 8 },
					{ ptDownPin.x + 2, ptDownPin.y - 8 },
					{ ptDownPin.x + 2, ptDownPin.y - 7 },
					{ ptDownPin.x + 1, ptDownPin.y - 6 },
					{ ptDownPin.x + 3, ptDownPin.y - 3 },
					{ ptDownPin.x + 3, ptDownPin.y - 2 },
					{ ptDownPin.x, ptDownPin.y - 2 }
				};
				if(bPushed)
				{
					ptDownPin.x += 1;
					ptDownPin.y += 1;
				}

				dc.Polyline(bVertical ? &ptsPushPinDown[0] : &ptsPushpin[0], bVertical ? 13 : 11);
			}
			virtual bool	Create(HWND hWndClient, IN eWinState state,
									bool bHasCloseCheckBox, CSize szSize, CSnapWindow* pParent)
			{
				CRect rc = this->GetWindowRect(pParent, state);
				DWORD dwStyle = WS_CHILD|WS_CLIPSIBLINGS|WS_CLIPCHILDREN | WS_THICKFRAME;
				DWORD dwExStyle = 0;//WS_EX_WINDOWEDGE;
				
				dwStyle |= bHasCloseCheckBox ? WS_SYSMENU : 0;

				pParent->m_bSizeSet = !rc.IsRectEmpty();

				HWND hWnd = pParent->Create(pParent->m_pParent->m_pT->m_hWnd, rc, pParent->m_cWindowText, dwStyle, dwExStyle);
				if(::IsWindow(hWnd) != FALSE) // ok this is a little bit nuts. 
					// MSDN says that ::IsWindow returns a NONZERO value as the return. In VC7 that means
					// it returns TRUE. In VC6 it returns a WILDLY nonzero value... hell knows why. Oh well.
				{ 
					::SetParent(hWndClient, hWnd);
					CRect rcTemp;
					pParent->GetClientRect(rcTemp);
					::MoveWindow(hWndClient, rcTemp.left, rcTemp.top, rcTemp.Width(), rcTemp.Height(), TRUE);

					// let's create the font we'll be using for the captions, too.
					pParent->m_fntCaption.CreatePointFont(80, "Tahoma");

					return true;
				}
				return false;
			}
			virtual void	HandleFrameworkResize(IN CSnapWindow* pParent)
			{
				BOOL bHandled;
				pParent->m_bSizeSet = false; // we need to force a move, not a recalculation....
				this->OnSize(0, CPoint(0,0), pParent, bHandled);
			}

			virtual CRect	GetWindowRect(IN CSnapWindow* pParent, IN eWinState state)
			{
				CRect rc, rcClient;

				pParent->m_pParent->m_pT->GetClientRect(rcClient);
				if(rcClient.IsRectEmpty())
					return rcClient; // nothing to do

				// let's figure out where they want this window
				rc.SetRect(state == eDockedRight ? rcClient.right - pParent->m_szSize.cx : rcClient.left,
					state == eDockedBottom ? rcClient.bottom - pParent->m_szSize.cy : rcClient.top,
					state == eDockedLeft ? rcClient.left + pParent->m_szSize.cx : rcClient.right,
					state == eDockedTop ? rcClient.top + pParent->m_szSize.cy : rcClient.bottom);

				return rc;
			}
			virtual void	SwitchState(CSnapWindow* pParent, eWinState eNewState, eWinState eOldState)
			{
				BOOL b;
				// first take care of the transition from floating
				if((eOldState == eFloating))
				{
					// switching FROM Floating TO snapped.
					pParent->SetParent(pParent->m_pParent->m_pT->m_hWnd);
					pParent->ModifyStyleEx(WS_EX_TOOLWINDOW, 0, 0);
					pParent->ModifyStyle(WS_POPUP|WS_BORDER|WS_OVERLAPPED|WS_DLGFRAME, WS_CHILD, 0);
					pParent->m_bSizeSet = false; // unfortunately the ModifyStyle calls will cause an OnSize which will
					// reset that boolean.
					pParent->MoveWindow(this->GetWindowRect(pParent, eNewState), TRUE);
				}
				// now take care of the transition to pinned, if we need to.
				if(::IsWindow(pParent->m_hWnd) && pParent->m_bWindowPinned) // dont' bother if we're setting state on creation...
				{ // we must be switching from pinned...
					pParent->ShowWindow(SW_HIDE); // hide the window TODO: maybe change this?
					pParent->ModifyStyle(0, WS_THICKFRAME, 0);
					pParent->SetParent(pParent->m_pParent->m_pT->m_hWnd);
					pParent->m_pParent->TriggerNCResize();
					pParent->m_bWindowPinned = false;
					pParent->m_bSizeSet = false;
					OnSize(0, 0, pParent, b);
					pParent->ShowWindow(SW_SHOW);
					// notify the snapbar about this change
					pParent->m_pSnapParent->WindowPinnedStateChange(pParent, false);
				}
				else if(::IsWindow(pParent->m_hWnd))// just a simple side switch
				{
					pParent->m_bSizeSet = false;
					OnSize(0,0, pParent, b);
					if(pParent->GetStyle() & WS_VISIBLE) // if we're visible, we need to start tracking....
						pParent->m_pSnapParent->StartTrackingWindow(pParent);
				}
			}
			void			MoveWindow(CSnapWindow* parent)
			{
					CRect rcWin = this->GetWindowRect(parent, parent->m_eState);
					parent->m_bSizeSet = !rcWin.IsRectEmpty();
					if(parent->m_bSizeSet)
					{
						// we need to stretch out our rect to cover up the thick bars in all places except middle of the
						// screen.
						bool bHorz = (parent->m_eState == eDockedTop) || (parent->m_eState == eDockedBottom);
						rcWin.InflateRect((bHorz || (parent->m_eState == eDockedLeft)) ? 3 : 0, 
							(bHorz && (parent->m_eState != eDockedTop)) ? 0 : 3, 
							(bHorz  || (parent->m_eState == eDockedRight))? 3 : 0, 
							(bHorz && (parent->m_eState != eDockedBottom)) ? 0 : 3);
						parent->m_bStopOnSizeRecursion = true;
						parent->MoveWindow(rcWin);
						parent->SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
						parent->m_bStopOnSizeRecursion = false;
					}
			}
			virtual int		GetRequiredRealEstate(CSnapWindow* pParent) { return 0;	} // autos don't have it either

			virtual LRESULT GetMinMaxInfo(CSnapWindow* pParent, MINMAXINFO* pInfo, BOOL& bHandled)
			{
				int iTemp[4];
				pInfo->ptMinTrackSize.x = pInfo->ptMinTrackSize.y = SNAPPED_WINDOW_GAP_SIZE;

				iTemp[eLeft] = pParent->m_pParent->m_arrSnapBarWindows[eLeft].GetStartOffsetForPinnedWindow(pParent);
				iTemp[eRight] = pParent->m_pParent->m_arrSnapBarWindows[eRight].GetStartOffsetForPinnedWindow(pParent);
				iTemp[eTop] = pParent->m_pParent->m_arrSnapBarWindows[eTop].GetStartOffsetForPinnedWindow(pParent);
				iTemp[eBottom] = pParent->m_pParent->m_arrSnapBarWindows[eBottom].GetStartOffsetForPinnedWindow(pParent);

				// the max size is the window client area, minus the width of the snapbars/pinned windows, minus the 
				// gap size we've set
				if(IS_SNAPPED_VERT(pParent->m_eState))
					pInfo->ptMaxTrackSize.x = pParent->m_pParent->m_arrSnapBarRects[eRight].right - 
						pParent->m_pParent->m_arrSnapBarRects[eLeft].left - 
						(iTemp[eLeft]+iTemp[eRight]+SNAPPED_WINDOW_GAP_SIZE);
	
				if(IS_SNAPPED_HORZ(pParent->m_eState))
					pInfo->ptMaxTrackSize.y = pParent->m_pParent->m_arrSnapBarRects[eBottom].bottom - 
						pParent->m_pParent->m_arrSnapBarRects[eTop].top - 
						(iTemp[eTop]+iTemp[eBottom]+SNAPPED_WINDOW_GAP_SIZE);

				return 0;
			}

		};
		// this is the pinned state derivation. Notice it's derived from CSnapWindowStateAuto, not the base.
		// that's because the pinned window has a lot of the same characteristics as the snapping window.
		class CSnapWindowStatePinned : public CSnapWindowStateAuto
		{
		public:
			virtual void	OnSize(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				// let's take care of the global window moving stuff first.
				CRect rcParentClient, rcClient;
				if(::IsWindow(parent->m_hWnd) == FALSE) // haven't yet created it
					return;

				if(parent->m_bWindowBeingSized == true)
					return; // this means we do NOT want internal OnSize to handle this... we must be resizing.

				// we need a kind of roundabout way of figuring out the correct coordinates
				// problem is, we want to translate the client window's coordinates into its parent's 
				// coordinate frame. (I talk more about this in the CSnapFramework's OnSize function.)
				parent->m_pParent->m_pT->GetWindowRect(rcParentClient);
				LPRECT lpRC = (LPRECT)rcParentClient;
				::MapWindowPoints(NULL, parent->m_pParent->m_pT->GetParent(), (LPPOINT)lpRC, sizeof(RECT)/sizeof(POINT));

				// ok now we can continue...
				if((rcParentClient != parent->m_rcLastParentClientRect) || (!parent->m_bSizeSet)) // or we've NEVER resized...
					this->MoveWindow(parent);

				parent->m_rcLastParentClientRect = rcParentClient;

				// now let's deal with our clientrect
				parent->GetClientRect(rcClient);
				if(parent->m_bSizeSet)
				{
					CRect rcWin;
					parent->GetWindowRect(rcWin);
					parent->m_szSize.SetSize(
					IS_SNAPPED_VERT(parent->m_eState) ? rcWin.Width() : parent->m_szSize.cx, 
					IS_SNAPPED_HORZ(parent->m_eState) ? rcWin.Height() : parent->m_szSize.cy); // recompute size.
				}

				// we need to adjust the client rect to make some room for the resizing bars...
				this->AdjustClientRect(rcClient, parent->m_eState);
				// ok let the baseclass handle all the silly drawing stuff
				this->DoOnSize(parent, rcClient);
			}
			// the difference between this GetWindowRect and the parent's one is that in this one we need to
			// adjust the client rect to discount the size of THIS window
			virtual CRect	GetWindowRect(IN CSnapWindow* pParent, IN eWinState state,
											IN bool bCareAboutPinnedWindows = false)
			{
				CRect rc, rcClient;
				int iTemp[4];
				CSize sz = this->GetClientSize(pParent, iTemp);
	
				// we can also use the snapbar rects to base our calculations on, especially since they're already in the
				// correct window's coordinate frame

				// notice that we're using only eTop and eBottom snapbars for calculations because eRight and eLeft
				// are already offset to make room for top/bottom ones. (and can mess up calculations)
				switch(state)
				{
				case eDockedLeft:
					rc.left = pParent->m_pParent->m_arrSnapBarRects[eLeft].left + iTemp[eLeft];
					rc.top  = pParent->m_pParent->m_arrSnapBarRects[eTop].top + iTemp[eTop];
					rc.right = rc.left + pParent->m_szSize.cx;
					rc.bottom = pParent->m_pParent->m_arrSnapBarRects[eBottom].bottom - iTemp[eBottom];
					break;
				case eDockedBottom:
					rc.left = pParent->m_pParent->m_arrSnapBarRects[eBottom].left + 
						(pParent->m_pParent->m_arrSidesActive[eLeft] ? SNAPBAR_SIZE : 0);
					rc.right = pParent->m_pParent->m_arrSnapBarRects[eBottom].right - 
						(pParent->m_pParent->m_arrSidesActive[eRight] ? SNAPBAR_SIZE : 0);
					rc.bottom = pParent->m_pParent->m_arrSnapBarRects[eBottom].bottom - iTemp[eBottom];
					rc.top  = rc.bottom - pParent->m_szSize.cy;
					break;
				case eDockedRight:
					rc.right = pParent->m_pParent->m_arrSnapBarRects[eRight].right - iTemp[eRight];
					rc.top  = pParent->m_pParent->m_arrSnapBarRects[eTop].top + iTemp[eTop];
					rc.left = rc.right - pParent->m_szSize.cx;
					rc.bottom = pParent->m_pParent->m_arrSnapBarRects[eBottom].bottom - iTemp[eBottom];
					break;
				case eDockedTop:
					rc.left = pParent->m_pParent->m_arrSnapBarRects[eTop].left + 
						(pParent->m_pParent->m_arrSidesActive[eLeft] ? SNAPBAR_SIZE : 0);
					rc.right = pParent->m_pParent->m_arrSnapBarRects[eTop].right - 
						(pParent->m_pParent->m_arrSidesActive[eRight] ? SNAPBAR_SIZE : 0);
					rc.top = pParent->m_pParent->m_arrSnapBarRects[eTop].top + iTemp[eTop];
					rc.bottom  = rc.top + pParent->m_szSize.cy;
					break;
				default:
					ATLASSERT(false);
					break;
				}
				return rc;
			}
			// that itemp thing is a bit of a hack -- basically one of the functions that uses this also wants to
			// know the actual offsets used calculating size... so we'll copy them into this variable...
			CSize			GetClientSize(CSnapWindow* pParent, OUT int* iOffsets = NULL)
			{
				int iTemp[4];
				CSize szNCSize, szResult;

				// need to get all the offsets. This is basically taking into account all the other pinned windows
				// the given snapbar has.

				iTemp[eLeft] = pParent->m_pParent->m_arrSnapBarWindows[eLeft].GetStartOffsetForPinnedWindow(pParent);
				iTemp[eRight] = pParent->m_pParent->m_arrSnapBarWindows[eRight].GetStartOffsetForPinnedWindow(pParent);
				iTemp[eTop] = pParent->m_pParent->m_arrSnapBarWindows[eTop].GetStartOffsetForPinnedWindow(pParent);
				iTemp[eBottom] = pParent->m_pParent->m_arrSnapBarWindows[eBottom].GetStartOffsetForPinnedWindow(pParent);

				szNCSize.SetSize(iTemp[eLeft] + iTemp[eRight], iTemp[eTop] + iTemp[eBottom]);
				
				szResult.SetSize(pParent->m_pParent->m_arrSnapBarRects[eRight].right - 
					pParent->m_pParent->m_arrSnapBarRects[eLeft].left - szNCSize.cx,
					pParent->m_pParent->m_arrSnapBarRects[eBottom].bottom - 
					pParent->m_pParent->m_arrSnapBarRects[eTop].top - szNCSize.cy);
				
				if(iOffsets)
					memcpy(iOffsets, iTemp, sizeof(iTemp));

				return szResult;

			}
			virtual void	OnMouseMove(UINT uiFlags, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				CRect rcGripper = this->GetGripperRect(parent);
				CSize szDragInflation(::GetSystemMetrics(SM_CXDRAG), ::GetSystemMetrics(SM_CYDRAG));
				// inflate the gripper a bit so the user has some leeway...
				rcGripper.InflateRect(szDragInflation.cx,szDragInflation.cy,szDragInflation.cx,szDragInflation.cy);

				bHandled = FALSE;
				// a quick sanity check, just in case we missed a mouse up event somehow.
				if(((uiFlags & MK_LBUTTON) == 0) && (parent->m_bLeftButtonDown || parent->m_bWindowBeingSized))
				{
					parent->m_bLeftButtonDown = parent->m_bWindowBeingSized = parent->m_bStopOnSizeRecursion = false;
					if(parent->m_bPinButtonPushed || parent->m_bCloseButtonPushed) // if one of the buttons are pushed...
					{
						// they shouldn't be.
						parent->m_bPinButtonPushed = parent->m_bCloseButtonPushed = false;
						parent->Invalidate();
					}
				}
				
				// don't mess with ANY of this if the user pushed a button...
				if(parent->m_bLeftButtonDown && !(parent->m_bPinButtonPushed || parent->m_bCloseButtonPushed))
				{
					if(rcGripper.PtInRect(pt))
					{
						// ok... they're trying to resize...
						if(::DragDetect(parent->m_hWnd, pt) == 0) // make sure they'll actually move....
							return;
						::SetCapture(parent->m_hWnd); // ok they're moving. 
						parent->m_bWindowBeingSized = true;
						// the idea here is to completely disable windows sizing here... we want to do sizing ourselves.
						// so this flag will do that for us.
						parent->m_bStopOnSizeRecursion = true;
						::SetCursor(LoadCursor(NULL, IS_SNAPPED_HORZ(parent->m_eState) ? IDC_SIZENS : IDC_SIZEWE));
						this->HandleInternalResize(parent, pt);
						bHandled = TRUE;
					}
					else if(parent->m_bWindowBeingSized) // if we're already sizing....
					{
						::SetCursor(LoadCursor(NULL, IS_SNAPPED_HORZ(parent->m_eState) ? IDC_SIZENS : IDC_SIZEWE));
						this->HandleInternalResize(parent, pt);
						bHandled = TRUE;
					}
					else
					{
						parent->ClientToScreen(&pt);
						parent->m_bLeftButtonDown = false; // just do it in case the mouse goes our of scope....
						parent->m_pSnapParent->StartDragging(pt, parent);
					}
				}
				else if(rcGripper.PtInRect(pt))
					::SetCursor(LoadCursor(NULL, IS_SNAPPED_HORZ(parent->m_eState) ? IDC_SIZENS : IDC_SIZEWE));

			}
			virtual void	SwitchState(CSnapWindow* pParent, eWinState eNewState, eWinState eOldState)
			{
				BOOL bSwitchFromFloating = eOldState == eFloating; // are we switching from floating?

				pParent->ShowWindow(SW_HIDE);
				// remove the resizing borders... we'll have to do them ourselves.
				// the reason for this is that when we have a normal, autohidden window, we can just hide the three 
				// unneeded borders (you wouldn't want to resize the window INTO the snapbars, or AWAY from them, right?)
				// by expanding the window rect so the snapbars obscure them. This tactic doesn't work here, and we 
				// have to actually remove all the borders, and then manually add one. The reason it doesn't work is that
				// since a pinned window has the same parent as the snapbars, it can actually obscure the snapbars, showing
				// its borders. 

				pParent->ModifyStyle(WS_THICKFRAME | (bSwitchFromFloating ? WS_POPUP|WS_BORDER|WS_OVERLAPPED|WS_DLGFRAME : 0), 
					bSwitchFromFloating ? WS_CHILD : 0, 0); 
				if(bSwitchFromFloating)
					pParent->ModifyStyleEx(WS_EX_TOOLWINDOW, 0, 0);
				pParent->SetParent(pParent->m_pParent->m_pT->GetParent());
				pParent->m_bSizeSet = false;
				pParent->m_bWindowPinned = true;
				OnSize(0, 0, pParent, bSwitchFromFloating); // using it as a dummy bool... by this point we don't care...
				// need to make the parent window ncsize, so the client's area can be updated.
				pParent->m_pParent->TriggerNCResize();
				pParent->ShowWindow(SW_SHOW);
				// and notify the snapbar that a pinned window's status has changed.
				pParent->m_pSnapParent->WindowPinnedStateChange(pParent, true);
			}
			void			MoveWindow(CSnapWindow* parent)
			{
				// same idea as in the auto, except the GetWindowRect function's different...
					CRect rcWin = this->GetWindowRect(parent, parent->m_eState, true);
					parent->m_bSizeSet = !rcWin.IsRectEmpty();
					if(parent->m_bSizeSet)
					{
						parent->m_bStopOnSizeRecursion = true;
						parent->MoveWindow(rcWin);
						parent->SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
						parent->m_bStopOnSizeRecursion = false;
					}
			}
			// figure out how much NC area this window's taking up. We only need one dimension, which depends
			// on what side the window's docked at.
			virtual int		GetRequiredRealEstate(CSnapWindow* pParent) 
			{ 
				CRect rc;
				pParent->GetClientRect(rc);
				switch(pParent->m_eState)
				{
				case eDockedTop:
				case eDockedBottom:
					return rc.Height();
					break;
				case eDockedLeft:
				case eDockedRight:
					return rc.Width();
					break;
				default:
					ATLASSERT(false);
					return 0;
					break;
				}
			}

			// adjusts the client rect to leave space for the gripper.
			void			AdjustClientRect(IN OUT CRect& rcClient, eWinState eState)
			{
				switch(eState)
				{
				case eDockedTop:
					rcClient.bottom -= SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				case eDockedLeft:
					rcClient.right -= SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				case eDockedBottom:
					rcClient.top += SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				case eDockedRight:
					rcClient.left += SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				default:
					ATLASSERT(false);
					break;
				}
			}
			virtual void	OnPaint(CDCHandle dc, CSnapWindow* pParent)
			{
				int iOldTop;
					// we need to paint the window bar and all that crap
				UINT uiCloseState = DFCS_CAPTIONCLOSE | (pParent->m_bHasCloseButton ? 0 : DFCS_INACTIVE) | 
					(pParent->m_bCloseButtonPushed ? DFCS_PUSHED : 0);
				CRect rcClient;
				HFONT hfOld = dc.SelectFont(pParent->m_fntCaption);
				pParent->GetClientRect(rcClient);
				// adjust the client rect for the gripper
				this->AdjustClientRect(rcClient, pParent->m_eState);
				// remember the old top, since we're about to modify it to calculate the excluderect.
				iOldTop = rcClient.top;
				rcClient.top += SNAPPED_WINDOW_CAPTION_SIZE;
				dc.ExcludeClipRect(rcClient);
				rcClient.top = iOldTop;
				rcClient.bottom = rcClient.top + SNAPPED_WINDOW_CAPTION_SIZE;
				dc.FillSolidRect(rcClient, ::GetSysColor(COLOR_BTNFACE));
				dc.TextOut(rcClient.left + SNAPBAR_ICON_INTERNAL_OFFSET, 
					rcClient.top + SNAPBAR_ICON_INTERNAL_OFFSET, pParent->m_cWindowText);
				dc.SelectFont(hfOld);
				// now let's draw the buttons
				dc.DrawFrameControl(&pParent->m_rcCloseButtonRect, DFC_CAPTION, uiCloseState);
				this->DrawPushpinButton((CDCHandle)dc, pParent->m_bWindowPinned, 
					pParent->m_rcPushPinRect, pParent->m_bPinButtonPushed);
				// now we need to paint the gripper
				CRect rcGripper = this->GetGripperRect(pParent);
				dc.FillSolidRect(rcGripper, ::GetSysColor(COLOR_BTNFACE));
				// finally we need to invalidate the snap bar -- it needs to redraw itself with the right icon
				pParent->m_pSnapParent->Invalidate();
			}
			// figure out the actual rect of the gripper
			CRect			GetGripperRect(CSnapWindow* pParent)
			{
				CRect rcGripper;

				pParent->GetClientRect(rcGripper);
				switch(pParent->m_eState)
				{
				case eDockedTop:
					rcGripper.top = rcGripper.bottom - SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				case eDockedBottom:
					rcGripper.bottom = rcGripper.top + SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				case eDockedLeft:
					rcGripper.left = rcGripper.right - SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				case eDockedRight:
					rcGripper.right = rcGripper.left + SNAPPED_WINDOW_GRIPPER_SIZE;
					break;
				default:
					ATLASSERT(false);
					break;
				}
				return rcGripper;
			}
			virtual void	HandleFrameworkResize(IN CSnapWindow* pParent)
			{
				BOOL bHandled;
				pParent->m_bSizeSet = false; // we need to force a move, not a recalculation....
				this->OnSize(0, CPoint(0,0), pParent, bHandled);
			}
			// the window's being resized.
			void			HandleInternalResize(CSnapWindow* parent, CPoint pt)
			{
				CRect rc, rcOld;
				CSize sz = GetClientSize(parent);
				parent->GetClientRect(rc);
				rcOld.CopyRect(rc);
				//convert it to screen...
				::MapWindowPoints(parent->m_hWnd, NULL, (LPPOINT)((LPRECT)rc), sizeof(RECT)/sizeof(POINT));
				// convert the point to screen too
				parent->ClientToScreen(&pt);
				switch(parent->m_eState)
				{
				case eDockedLeft:
					rc.right = pt.x;
					break;
				case eDockedRight:
					rc.left = pt.x;
					break;
				case eDockedTop:
					rc.bottom = pt.y;
					break;
				case eDockedBottom:
					rc.top = pt.y;
					break;
				}
				parent->m_szSize.SetSize(
					IS_SNAPPED_VERT(parent->m_eState) ? 
						__min(__max(rc.Width(),SNAPPED_WINDOW_GAP_SIZE), sz.cx-SNAPPED_WINDOW_GAP_SIZE) : 
						parent->m_szSize.cx, 
					IS_SNAPPED_HORZ(parent->m_eState) ? 
						__min(__max(rc.Height(), SNAPPED_WINDOW_GAP_SIZE), sz.cy - SNAPPED_WINDOW_GAP_SIZE) : 
						parent->m_szSize.cy);
				// we have to do this again b/c we just gated the percentage...
				rc = this->GetWindowRect(parent, parent->m_eState, true);
				parent->m_bSizeSet = false;
				parent->MoveWindow(rc);
				// now let's trigger an NCSize
				parent->m_pParent->TriggerNCResize();
				// now let's resize the base elements of the window
				parent->GetClientRect(rc);
				// we need to adjust the client rect to make some room for the resizing bars...
				this->AdjustClientRect(rc, parent->m_eState);
				// ok let the baseclass handle all the silly drawing stuff
				this->DoOnSize(parent, rc);
				parent->SendMessage(WM_PAINT);
				parent->m_pParent->m_pT->SendMessage(WM_PAINT); // repaint the parent too.
				// let's intelligently repaint the opposite side windows
				parent->m_pParent->m_arrSnapBarWindows[eRight].ForceRepaintOfChildWindows();
				parent->m_pParent->m_arrSnapBarWindows[eLeft].ForceRepaintOfChildWindows();
				parent->m_pParent->m_arrSnapBarWindows[eTop].ForceRepaintOfChildWindows();
				parent->m_pParent->m_arrSnapBarWindows[eBottom].ForceRepaintOfChildWindows();
			}
			virtual void	OnNCLeftButtonUp(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				parent->m_bStopOnSizeRecursion = false; // just in case...

				if(parent->m_bWindowBeingSized)
				{
					::ReleaseCapture();
					parent->m_bWindowBeingSized = false;
					parent->m_bLeftButtonDown = false;
				}
				else
					CSnapWindowStateAuto::OnNCLeftButtonUp(ui, pt, parent, bHandled);
			}
			virtual void	OnNCLeftButtonDown(UINT ui, CPoint pt, CSnapWindow* parent, BOOL& bHandled)
			{
				CRect rcGripper = this->GetGripperRect(parent);
				if(rcGripper.PtInRect(pt))
				{
					parent->m_bLeftButtonDown = true;
					::SetCursor(LoadCursor(NULL, IS_SNAPPED_HORZ(parent->m_eState) ? IDC_SIZENS : IDC_SIZEWE));
				}
				else
					CSnapWindowStateAuto::OnNCLeftButtonDown(ui, pt, parent, bHandled);
			}


		};
		// let's give these two classes full access to their parent.
		friend class CSnapWindowStateFloating;
		friend class CSnapWindowStateAuto;
		friend class CSnapWindowStatePinned;
	public:
						CSnapWindow(eWinState eState, HWND hwndClient, 
									CSnapBarWindow* pSnapParent, CSnapFramework<T>* pParent) : m_bSizeSet(false), 
			m_pSnapParent(pSnapParent), m_bLeftButtonDown(false), m_pState(NULL), m_pParent(pParent), m_hwndClient(hwndClient),
			m_bStopOnSizeRecursion(false), m_bCloseButtonPushed(false), m_bHasCloseButton(true), m_bPinButtonPushed(false),
			m_bWindowPinned(false), m_bWindowBeingSized(false), m_eState(eNull)
		{ 
			// set the designated state.
			this->SetState(eState); 
		}
		void			SetState(eWinState eState)
		{
			eWinState eOldState = m_eState;
			// set the required state, and then do a stateswitch
			m_pState = (eState == eFloating) ? (CSnapWindowStateBase*)&this->m_cFloatState : 
			(m_bWindowPinned ? (CSnapWindowStateBase*)&this->m_cPinnedState : (CSnapWindowStateBase*)&this->m_cAutoState);
			this->m_eState = eState;
			m_pState->SwitchState(this, eState, eOldState);
		}
		// creates a window based on the current state.
		bool			CreateSnapWindow(IN HWND hWndClient, IN CSize szSize, IN bool bHasCloseCheckBox)
		{
			m_hwndClient = hWndClient;
			m_bHasCloseButton = bHasCloseCheckBox;
			::GetWindowText(hWndClient, m_cWindowText, 1024);
			this->m_szSize = szSize;
			return this->m_pState->Create(hWndClient, m_eState, bHasCloseCheckBox, szSize, this); // let the state handle this...
		}

		void			FrameworkResized()
		{
			this->m_pState->HandleFrameworkResize(this);
		}

		// paints the individual icon
		// note that the icon can go either on the snapbar (using the stored rect), or can be used for dragging
		// (in which case bUseStoredRect will be false, and we'll use a 0,0 as start point.)
		void			PaintSnapBarIcon(IN CDCHandle dc, IN COLORREF clrCaptionBack, IN COLORREF clrCaptionDown,
											IN CBrushHandle brushCaptionOutline, 
											IN bool bHorz, IN bool bUseStoredRect)
		{
			CRect rc;
			int iMaxChars;
			CSize sz;
			char* cWindowText = m_cWindowText; // yes, just a simple pointer assignment so far...
			CPoint pt;

			// is the mouse inside this window? it is if we're visible (otherwise we wouldn't be....), or if
			// we're pinned.
			bool bInside = ((this->GetStyle() & WS_VISIBLE) || this->m_bWindowPinned) && bUseStoredRect;

			// this is used when we're calculating a rect from scratch, i.e. when we're dragging.
			// if the user, for instance, wants this icon drawn vertically, and we normally draw it horizontally,
			// then we'll set this, and invert our stored rect's width and height in calculating the new rect.
			bool bNeedToInvert = (bHorz && (this->m_rcSnapBarCoords.Height() > this->m_rcSnapBarCoords.Width())) || 
				(!bHorz && (this->m_rcSnapBarCoords.Width() > this->m_rcSnapBarCoords.Height()));

			if(!bUseStoredRect)
				rc.SetRect(0, 0, bNeedToInvert ? this->m_rcSnapBarCoords.Height() : this->m_rcSnapBarCoords.Width(), 
								 bNeedToInvert ? this->m_rcSnapBarCoords.Width() : this->m_rcSnapBarCoords.Height());
			else
				rc.CopyRect(this->m_rcSnapBarCoords);

			dc.FillSolidRect(rc, bInside ? clrCaptionDown : clrCaptionBack);
			dc.FrameRect(rc, brushCaptionOutline);
			// we may need to modify the string a bit to wrap it correctly. This is what END_ELLIPSIS in DrawText would
			// do for us, but unfortunately we don't have it in TextOut, and DrawText doesn't draw vertical text.
			dc.DPtoLP(&rc);
			::GetTextExtentExPoint(dc, m_cWindowText, strlen(m_cWindowText), 
				bHorz ? rc.Width()-SNAPBAR_ICON_INTERNAL_OFFSET*2 : rc.Height() - SNAPBAR_ICON_INTERNAL_OFFSET*2, 
				&iMaxChars, NULL, &sz);
			
			iMaxChars = __max(iMaxChars, 0);

			if((size_t)iMaxChars < strlen(m_cWindowText)) // gate the new text if we have to.
			{
				cWindowText = new char[__max(iMaxChars + 1, 1)];
				memcpy(cWindowText, m_cWindowText, iMaxChars);
				cWindowText[iMaxChars] = 0;

				if(iMaxChars - 3 > 1)
				{
					cWindowText[iMaxChars-3] = 0;
					strcat(cWindowText, "...");
				}
				else
					cWindowText[0] = 0;
			}
			dc.LPtoDP(rc);
			dc.TextOut(bHorz ? rc.left + SNAPBAR_ICON_INTERNAL_OFFSET : rc.right - SNAPBAR_ICON_INTERNAL_OFFSET, 
						rc.top+SNAPBAR_ICON_INTERNAL_OFFSET, cWindowText);
			if(cWindowText != this->m_cWindowText) // simple pointer comparison
				delete [] cWindowText;
		}
		const CRect&	GetSnapBarIconRect() const { return m_rcSnapBarCoords; }
		eWinState		GetWinState() { return m_eState; }
		void			ComputeSnapBarRect(IN CDCHandle dc, IN bool bHorz, IN OUT CPoint& pt)
		{
			// ok this is fairly easy... just get the window text, find out how many pixels it'll take... that's it
			CSize sz;
			
			dc.GetTextExtent(m_cWindowText, strlen(m_cWindowText), &sz);

			// now let's calculate our rect
			this->m_rcSnapBarCoords.SetRect(pt.x, pt.y, 
				pt.x + (bHorz ? sz.cx : sz.cy), pt.y + (bHorz ? sz.cy : sz.cx));
			// now pad the rectangle w/ the offsets
			this->m_rcSnapBarCoords.InflateRect(SNAPBAR_ICON_INTERNAL_OFFSET, 
												SNAPBAR_ICON_INTERNAL_OFFSET,
												SNAPBAR_ICON_INTERNAL_OFFSET,
												SNAPBAR_ICON_INTERNAL_OFFSET);
			pt.x += bHorz ? this->m_rcSnapBarCoords.Width() : 0;
			pt.y += bHorz ? 0 : this->m_rcSnapBarCoords.Height();
		}
		// if the snapbar decided it can't fit all the windows, it'll start squeezing the rects. 
		void			SqueezeSnapBarRect(IN int iSqueezeAmount, IN bool bHorz, IN OUT CPoint& pt)
		{
			// first offset the rect to the new point
			this->m_rcSnapBarCoords.OffsetRect(bHorz ? pt.x - m_rcSnapBarCoords.left : 0, 
				bHorz ? 0 : pt.y - m_rcSnapBarCoords.top);
			// now squeeze it
			this->m_rcSnapBarCoords.right -= bHorz ? iSqueezeAmount : 0;
			this->m_rcSnapBarCoords.bottom -= bHorz ? 0 : iSqueezeAmount;
			if(bHorz)
				pt.x = this->m_rcSnapBarCoords.right;
			else
				pt.y = this->m_rcSnapBarCoords.bottom;
		}
		void			SetSnapBarParent(CSnapBarWindow* pParent) { this->m_pSnapParent = pParent; }
		int				GetRequiredRealEstate() { return this->m_pState->GetRequiredRealEstate(this); }
		bool			IsPinned() { return m_bWindowPinned; }
		BEGIN_MSG_MAP(CSnapWindow)
			MESSAGE_HANDLER(WM_CREATE, OnCreate)
			MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
			MESSAGE_HANDLER(WM_PAINT, OnPaint)
			MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNCLeftButtonDown)
			MESSAGE_HANDLER(WM_LBUTTONDOWN, OnNCLeftButtonDown)
			MESSAGE_HANDLER(WM_NCLBUTTONUP, OnNCLeftButtonUp)
			MESSAGE_HANDLER(WM_LBUTTONUP, OnNCLeftButtonUp)
			MESSAGE_HANDLER(WM_SIZE, OnSize)
			MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
			MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
			MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
			MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
		END_MSG_MAP()
	protected:
		// all these basically pass the message off to the current state to handle it. This is basically
		// the crux of the state design pattern.
		LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
		{
			return this->m_pState->OnCreate(const_cast<CSnapWindow*>(this), bHandled);
		}
		LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
		{
			return this->m_pState->OnEraseBkgnd(this, bHandled);
		}
		LRESULT	OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			if(!this->m_bStopOnSizeRecursion)
				this->m_pState->OnSize(wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), this, bHandled);
			return 0;

		}
		LRESULT	OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
		{
			CPaintDC dc(m_hWnd);
			this->m_pState->OnPaint((CDCHandle)dc, this);
			return 0;
		}
		LRESULT	OnNCLeftButtonDown(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			this->m_pState->OnNCLeftButtonDown(wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), this, bHandled);
			return 0;

		}
		LRESULT	OnNCLeftButtonUp(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			this->m_pState->OnNCLeftButtonUp(wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), this, bHandled);
			return 0;
		}
		LRESULT	OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
		{
			::DestroyWindow(m_hwndClient);
			::PostMessage(this->m_pParent->m_pT->m_hWnd, UWM_SNAP_WINDOW_DESTROYED, NULL, (LPARAM)this);
			return 0;
		}
		LRESULT	OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			this->m_pState->OnMouseMove(wParam, CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), this, bHandled);
			return 0;
		}
		LRESULT	OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
		{
			this->m_pState->GetMinMaxInfo(this, (MINMAXINFO*)lParam, bHandled);
			return 0;
		}

		// lotsa lotsa variables.... :)
		CSnapWindowStateBase*	m_pState;					// current state
		HWND					m_hwndClient;				// the client window.

		// notice I could've made these static... but these classes are pretty small (no member vars at all), and I figured
		// why clutter people up with basically global variables?

		CSnapWindowStateAuto	m_cAutoState;				// an instance of the auto state.
		CSnapWindowStateFloating m_cFloatState;				// an instance of the float state
		CSnapWindowStatePinned	m_cPinnedState;				// an instance of the pinned state

		CSnapFramework<T>*		m_pParent;					// the framework
		CSize					m_szSize;					// size of the window. depending on state, one of the dimensions
															// might be ignored. 
		bool					m_bSizeSet;					// is this window sized correctly?
		eWinState				m_eState;					// current window state
		CRect					m_rcSnapBarCoords;			// coordinates of this window on the snap bar (if used)
		CFont					m_fntCaption;				// the font used for the caption
		char					m_cWindowText[1024];		// just stores the window text so we don't need 
															// to retrieve it all the time
		CSnapBarWindow*			m_pSnapParent;				// the snapbar parent, if any
		bool					m_bLeftButtonDown;			// flag if left button's down
		bool					m_bStopOnSizeRecursion;		// flag to stop OnSize recursion
		CRect					m_rcLastParentClientRect;	// last client rect. (see explanation in CSnapWindowStateAuto::OnSize)
		CRect					m_rcCloseButtonRect;		// location of the close button
		CRect					m_rcPushPinRect;			// location of the pushpin button
		CRect					m_rcOpacitySliderRect;		// NOT IMPLEMENTED: location of the opacity slider rect
		bool					m_bHasCloseButton;			// does this window have a close button?
		bool					m_bCloseButtonPushed;		// is the button pushed?
		bool					m_bPinButtonPushed;			// is the pin button pushed?
		bool					m_bWindowPinned;			// is the window pinned?
		bool					m_bWindowBeingSized;		// is it being resized?
	};

	typedef std::list<CSnapWindow*> tSnapWindowList;
	// this is the horizontal/vertical window that'll draw all the buttons
	class CSnapBarWindow : public CWindowImpl<CSnapBarWindow>, public CMouseHover<CSnapBarWindow, true, true, false>
	{
		typedef CMouseHover<CSnapBarWindow, true, true, false> tSnapBarHover;
	public:
		CSnapBarWindow() : m_itCurWin(this->m_lstSnappedWindows.end()), m_bLButtonDown(false),
			m_bScheduleRepaint(false)
		{
		}
		~CSnapBarWindow()
		{
			tSnapWindowList::iterator i;
			// kill all our windows if any are left.
			for(i=this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
			{
				if(::IsWindow((*i)->m_hWnd))
					(*i)->DestroyWindow();
				delete (*i);
			}
		}
		// this really should be in the constructor, but since I have NO idea how to initialize
		// an array of classes when there's no default constructor (hey, C++ buffs, is it possible?),
		// I'm forced to use this rather kludgy way. Oh well, at least this class isn't exposed to the outside.
		void SetOptions(CSnapFramework<T>* pParent, eSide side)
		{
			this->m_pParent = pParent;
			this->m_bHorz = (side == eTop) || (side == eBottom);
			this->m_eSide = side;
			this->SetColors();
		}

		DECLARE_WND_CLASS(NULL)

		BEGIN_MSG_MAP(CSnapBarWindow)
			CHAIN_MSG_MAP(tSnapBarHover)
			MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
			MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
			MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
			MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
			MESSAGE_HANDLER(WM_SIZE, OnSize)
			MESSAGE_HANDLER(WM_PAINT, OnPaint)
			MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingsChange)
			MESSAGE_HANDLER(WM_MOUSEHOVER, OnMouseHover)
			MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
		END_MSG_MAP()

		void	AddWindowToBar(IN CSnapWindow* pWin)
		{
			BOOL bHandled;
			// stick this window into our list
			this->m_lstSnappedWindows.push_back(pWin);
			// tell the window that we're its parent now
			pWin->SetSnapBarParent(this);
			// schedule ourselves for a repaint (whenever the framework feels like checking it...)
			this->m_bScheduleRepaint = true;
			// let's trigger a forced resize too -- we need to recalculate the rects of the icons
			this->OnSize(0, 0, 0, bHandled);
		}

		bool	RemoveWindowFromBar(IN CSnapWindow* pWin, bool bDestroyWindow)
		{
			// let's find that window in our list
			tSnapWindowList::iterator i = std::find(this->m_lstSnappedWindows.begin(), 
										this->m_lstSnappedWindows.end(), pWin);
			BOOL bHandled;
			if(i == this->m_lstSnappedWindows.end())
				return false;
			// if we want the window destroyed, too....
			if(bDestroyWindow)
			{
				// ok now let's destroy this window
				pWin->DestroyWindow();
				delete pWin;
			}
			else // otherwise just reset its snapbar parent
				pWin->SetSnapBarParent(NULL);
			if(i == this->m_itCurWin) // oh crap, we were tracking this window....
			{
				// tell the parent to quit tracking
				this->m_pParent->ForceEndTracking();
				this->m_itCurWin = this->m_lstSnappedWindows.end();
			}
			this->m_lstSnappedWindows.erase(i);
			this->m_bScheduleRepaint = true;
			// if this window was pinned and not destroyed, we need to trigger an NCSize...
			if(pWin->IsPinned() && !bDestroyWindow)
			{
				this->m_pParent->TriggerNCResize();
				this->m_pParent->m_pT->Invalidate();
			}
			// let's trigger a forced resize too -- we need to recalculate the rects of the icons
			this->OnSize(0, 0, 0, bHandled);
			return true;
		}

		// this gets called by the framework when it thinks that the window should be hidden
		void	CursorLeftTrackingWindow()
		{
			ATLASSERT(this->m_itCurWin != this->m_lstSnappedWindows.end());
			// just a sanity check. I've seen this happen for some strange reason... let's make sure we don't crash 
			// if it does...
			if(this->m_itCurWin == this->m_lstSnappedWindows.end())
			{
				this->m_pParent->ForceEndTracking(); // let's tell the parent window to stop tracking this....
				return;
			}

			// we need to check if the cursor is in a valid location, actually.
			// let's just get the rect of the client window, and see if the cursor's in it.

			CPoint pt;
			CRect rc;
			::GetCursorPos(&pt);

			(*this->m_itCurWin)->ScreenToClient(&pt);
			(*this->m_itCurWin)->GetClientRect(rc);
			if(rc.PtInRect(pt))
				return; // don't stop the timer
			else
				(*this->m_itCurWin)->ClientToScreen(&pt); // translate back for the next test.

			// um.... let's check if the cursor isn't actually IN the exact right square on the snapbar...
			this->ScreenToClient(&pt);
			if((*this->m_itCurWin)->GetSnapBarIconRect().PtInRect(pt))
			{
				this->m_pParent->ForceEndTracking(); // let's tell the parent window to stop tracking this....
				return; // nothing to do... we'll restart the tracker when the mouse leaves the bar again
			}


			// ok. Now we can hide the window and stop tracking.
			(*this->m_itCurWin)->ShowWindow(SW_HIDE);
			this->m_itCurWin = this->m_lstSnappedWindows.end();
			this->m_pParent->ForceEndTracking(); // let's tell the parent window to stop tracking this....
			// invalidate ourselves, too... to redraw the paint icon
			// invalidate ourselves, since we'll need to redraw the rect
			this->Invalidate();
		}
		void	StartDragging(CPoint ptStartPoint, CSnapWindow* pWin)
		{
			// let the framework handle this...
			if(::DragDetect(this->m_hWnd, ptStartPoint) == 0)
				return; // nope. User didn't drag.

			this->m_pParent->HandleDragging(ptStartPoint, pWin);
		}
		int		GetNumSnappedWindows() { return this->m_lstSnappedWindows.size(); }
		bool	CheckRepaint()
		{ 
			if(this->m_bScheduleRepaint) 
			{ 
				m_bScheduleRepaint = false; 
				return true;
			} 
			return false; 
		}
		// this returns the width or height (depending on the side the snapbar's on) of the snapbar, plus
		// all of its pinned windows.
		int		GetRequiredArea()
		{
			int iResult = this->m_pParent->m_arrSidesActive[this->m_eSide] ? SNAPBAR_SIZE : 0;
			tSnapWindowList::iterator i;

			for(i=this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
				iResult += (*i)->GetRequiredRealEstate();

			return iResult;
		}
		//tell all our windows that framework resized.
		void	NotifyFrameworkResized()
		{
			for(tSnapWindowList::iterator i=this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
				(*i)->FrameworkResized();
		}

		// handle pinned state change
		void	WindowPinnedStateChange(CSnapWindow* pWin, bool bPinned)
		{
			if(bPinned)
			{
				if((this->m_itCurWin != this->m_lstSnappedWindows.end()) && (*this->m_itCurWin) == pWin)
					this->m_itCurWin = this->m_lstSnappedWindows.end();
				this->m_pParent->ForceEndTracking();
			}
			else
			{
				if(this->m_itCurWin == this->m_lstSnappedWindows.end())
				{
					this->m_itCurWin = std::find(this->m_lstSnappedWindows.begin(), this->m_lstSnappedWindows.end(), pWin);
					ATLASSERT(this->m_itCurWin != this->m_lstSnappedWindows.end());
					this->m_pParent->StartTrackingWindow(this->m_eSide);
				}
			}
		}
		// this returns only the area BETWEEN the snapbar (including the snapbar) and the window passed in
		int		GetStartOffsetForPinnedWindow(CSnapWindow* pWin)
		{
			tSnapWindowList::iterator i;
			int iResult = this->m_pParent->m_arrSidesActive[this->m_eSide] ? SNAPBAR_SIZE : 0;

			for(i=this->m_lstSnappedWindows.begin(); (i!=this->m_lstSnappedWindows.end()) && ((*i)!=pWin); i++)
				iResult += (*i)->GetRequiredRealEstate();
			return iResult;
		}
		void	ForceRepaintOfChildWindows()
		{
			tSnapWindowList::iterator i;
			for(i=this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
				(*i)->SendMessage(WM_PAINT);
		}
		void	StartTrackingWindow(CSnapWindow* pWin) // gets called by the window when it thinks it should be tracked.
		{
			tSnapWindowList::iterator i = std::find(this->m_lstSnappedWindows.begin(), this->m_lstSnappedWindows.end(), pWin);
			ATLASSERT( i != this->m_lstSnappedWindows.end());
			if(i == this->m_lstSnappedWindows.end()) // crash proof
				return; // don't do anything.

			ATLASSERT(this->m_itCurWin == this->m_lstSnappedWindows.end()); // shouldn't be one already being tracked.

			this->m_itCurWin = i;
			this->m_pParent->StartTrackingWindow(this->m_eSide);
		}
	protected:

		// just sets all the colors we'll be using.
		void	SetColors()
		{
			this->m_rgbBackground = ::GetSysColor(COLOR_BTNFACE);
			this->m_rgbCaptionOutline = ::GetSysColor(COLOR_BTNSHADOW);
			this->m_rgbCaptionBackground = ::GetSysColor(COLOR_INACTIVEBORDER);
			this->m_rgbCaptionPressedBackground = ::GetSysColor(COLOR_BTNSHADOW);
		}

		// reset our colors.
		LRESULT	OnSettingsChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
		{
			this->SetColors();
			this->Invalidate();
			return 0;
		}

		LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
		{ // standard anti-flicker.
			return 0;
		}
		LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
		{
			this->m_bLButtonDown = true;
			return 0;
		}
		LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
		{
			this->m_bLButtonDown = false;
			return 0;
		}

		LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
		{ // if the mouse left the snapbar, and we're supposed to be tracking something, tell the framework
			if(this->m_itCurWin != this->m_lstSnappedWindows.end()) // to start tracking
					this->m_pParent->StartTrackingWindow(this->m_eSide);
			return 0;
		}
		LRESULT OnMouseHover(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
		{ 
			tSnapWindowList::iterator i;
			CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 
			// see if the user hovered over a window... if so, let's show it.
			for(i=this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
			{
				if(((*i)->GetSnapBarIconRect().PtInRect(pt)) &&  // are we inside a particular rect?
					(!(*i)->IsPinned()) && // is this rect representing a pinned window? Don't mess with it then
					(i!=this->m_itCurWin)) // is this rect representing a currently tracking window? Why bother then?
				{
					// ok we found our window. let's make it visible.
					// first let's check if we already have a visible window...
					if(this->m_itCurWin != this->m_lstSnappedWindows.end())
						this->CursorLeftTrackingWindow();

					// let's see if this window's already selected....
					if(i != this->m_itCurWin)
					{
						(*i)->ShowWindow(SW_SHOW);
						(*i)->SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
						this->m_itCurWin = i;
					}
					m_fMouseOver = false;
					return 0;
				}
			}
			m_fMouseOver = false;
			return 0;
		}
		LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
		{
			CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 
			if(this->m_bLButtonDown) // oh oh.... we're drrrrrrragging!
			{
				tSnapWindowList::iterator i;
				for(i = this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
				{
					if((*i)->GetSnapBarIconRect().PtInRect(pt))
					{
						this->ClientToScreen(&pt);
						this->StartDragging(pt, (*i));
						return 0;
					}
				}
				// didn't find anything... oh well.
				this->m_bLButtonDown = false;
			}
			else if(this->m_pParent->m_bTrackingWindow) // if we're tracking...
				this->m_pParent->SnapBarMouseMove(pt, this->m_eSide);
			return 0;
		}
		LRESULT	OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			CRect rc;

			this->GetClientRect(rc);
			if(rc.IsRectEmpty())
				return 0; // nothing to do....


			// let's tell all our windows that there's been a resize
			tSnapWindowList::iterator i;
			CDCHandle dc = this->GetDC();

			HFONT hfOld = dc.SelectFont(this->m_bHorz ? this->m_pParent->m_fntHorizontal : this->m_pParent->m_fntVertical);
			// now for the hard part. We need to compute all the snapbar rects....
			// well, not really that hard, since every window computes its own rect
			CPoint ptStart;

			ptStart.SetPoint(rc.left+SNAPBAR_ICON_OFFSET, rc.top+SNAPBAR_ICON_OFFSET);

			for(i = this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
			{
				(*i)->ComputeSnapBarRect(dc, m_bHorz, ptStart);
				ptStart.Offset(m_bHorz ? SNAPBAR_ICON_SEPARATION : 0, m_bHorz ? 0 : SNAPBAR_ICON_SEPARATION);
			}

			// let's see if we've been too enthusiastic with allocation....
			if((m_bHorz && (ptStart.x > rc.right)) || (!m_bHorz && (ptStart.y > rc.bottom)))
			{
				// we must do a bit of squeezing...
				double dblSqueezeAmount = 
					(double)((m_bHorz ? ptStart.x : ptStart.y) - (m_bHorz ? rc.right : rc.bottom)) /
					(double)this->m_lstSnappedWindows.size();
				// now let's tell everyone to squeeze... together we stand, and all that...
				ptStart.SetPoint(rc.left+SNAPBAR_ICON_OFFSET, rc.top+SNAPBAR_ICON_OFFSET);

				for(i = this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
				{
					(*i)->SqueezeSnapBarRect((int)dblSqueezeAmount, m_bHorz, ptStart);
					ptStart.Offset(m_bHorz ? SNAPBAR_ICON_SEPARATION : 0, m_bHorz ? 0 : SNAPBAR_ICON_SEPARATION);
				}
			}
			// that's all there's to it
			dc.SelectFont(hfOld);
			::ReleaseDC(m_hWnd, dc);
			return 0;
		}
		LRESULT	OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		{
			tSnapWindowList::iterator i;
			CPaintDC dc(m_hWnd);
			CBrush brushOutline;
			brushOutline.CreateSolidBrush(this->m_rgbCaptionOutline);
			HFONT hfOld = dc.SelectFont(this->m_bHorz ? this->m_pParent->m_fntHorizontal : this->m_pParent->m_fntVertical);
			CRect rc;
			this->GetClientRect(rc);
			if(rc.IsRectEmpty())
				return 0; // don't do anything!
			dc.FillSolidRect(rc, this->m_rgbBackground);
			dc.SetBkMode(TRANSPARENT);
			// tell all the snap windows to paint their rects.
			for(i = this->m_lstSnappedWindows.begin(); i!=this->m_lstSnappedWindows.end(); i++)
				(*i)->PaintSnapBarIcon((CDCHandle)dc, this->m_rgbCaptionBackground, this->m_rgbCaptionPressedBackground,
					(CBrushHandle)brushOutline, this->m_bHorz, true);
			dc.SelectFont(hfOld);
			return 0;
		}
		
		CSnapFramework<T>*	m_pParent; // parent class
		bool				m_bHorz;   // are we drawing horizontally or vertically?
		tSnapWindowList		m_lstSnappedWindows;
		COLORREF			m_rgbBackground;
		COLORREF			m_rgbCaptionOutline;
		COLORREF			m_rgbCaptionBackground;
		COLORREF			m_rgbCaptionPressedBackground;
		tSnapWindowList::iterator m_itCurWin;
		eSide				m_eSide;
		bool				m_bLButtonDown;
		bool				m_bScheduleRepaint;
	};

	friend class CSnapWindow; // let this guy have access to our protected stuff.
	friend class CSnapWindow::CSnapWindowStateFloating;
	friend class CSnapWindow::CSnapWindowStateAuto;
	friend class CSnapWindow::CSnapWindowStatePinned;
	friend class CSnapBarWindow;

	// MAIN CLASS STUFF
public:

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
		MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
		MESSAGE_HANDLER(WM_NCCALCSIZE, OnNCCalcSize)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_TIMER, OnTimer)
		MESSAGE_HANDLER	(WM_SETTINGCHANGE, OnSettingsChange)
		MESSAGE_HANDLER(UWM_SNAP_WINDOW_DESTROYED, OnSnapWindowDestroyed)
	END_MSG_MAP()

	CSnapFramework() : m_bTrackingWindow(false), m_bTrackingTimerStarted(false), m_bFloaterActivationState(true),
		m_cParent(this)
	{
		::ZeroMemory(this->m_arrSidesActive, sizeof(this->m_arrSidesActive));
		// see notes on this function on why I had to do this rather silly thing.
		this->m_arrSnapBarWindows[eLeft].SetOptions(this, eLeft);
		this->m_arrSnapBarWindows[eTop].SetOptions(this, eTop);
		this->m_arrSnapBarWindows[eRight].SetOptions(this, eRight);
		this->m_arrSnapBarWindows[eBottom].SetOptions(this, eBottom);
		// finally get us to the CWindow somehow....
		m_pT = static_cast<T*>(this);
	}
	~CSnapFramework()
	{
	}

	// ok this dude attaches a new window with the specified options to the framework. 
	// Arguments:
	//				 hWnd: duh
	//				state: how you want the window attached (see enum above)
	//	           szSize : the dimensions of the window you want
	//						notice that if the window is docked top or bottom, the cx will be ignored,
	//						likewise, if the window is docked left or right, the cy will be ignored.
	//	bHasCloseCheckBox:  can this window be closed by the user?
	void		AttachWindow(IN HWND hWnd, IN eWinState state, IN CSize szSize, IN bool bHasCloseCheckBox)
	{
		// ok first thing we need to do is figure out what you people want:

		switch(state)
		{
		case eFloating:
			this->CreateFloatingWindow(hWnd, szSize, bHasCloseCheckBox);
			break;
		case eDockedTop: 
		case eDockedRight:
		case eDockedBottom:
		case eDockedLeft:
			this->CreateSnappedWindow(hWnd, szSize, state,  bHasCloseCheckBox);
			break;
		default:
			ATLASSERT(false);
			break;
		}
	}

	// notice I do NOT give a way to detach the window from the framework. In my project I don't need it. If you guys
	// need it just put in a request, it's quite easy to do.
protected:
	// Activation/Deactivation-related functions
	void		NotifyActivationEvent(IN bool bActivated)
	{
		// this will show/hide the floaters based on whether we've activated or deactivated
		if(this->m_bFloaterActivationState != bActivated) // i.e. if an event has occurred....
		{
			this->m_bFloaterActivationState = bActivated;
			tSnapWindowList::iterator i;
			// so just iterate through all the floaters, and perform the appropriate action on them
			for(i=this->m_lstFloatingWindows.begin(); i!=this->m_lstFloatingWindows.end(); i++)
				(*i)->ShowWindow(bActivated ? SW_SHOW : SW_HIDE);
		}
	}
	// dragging-related functions
	void		HandleDragging(IN CPoint ptStartPoint, IN CSnapWindow* pWin)
	{
		CBitmap bmp, bmpMask;
		CPoint ptH, ptV;
		CImageList lstNormal, lstRotated;
		CSize sz;
		CPoint ptOffset(0);

		if(this->m_bTrackingWindow) // if we're tracking we need to stop before we do anything....
			this->ForceEndTracking();

		sz = CSize(pWin->GetSnapBarIconRect().Width(), pWin->GetSnapBarIconRect().Height());
		if((sz.cx == 0) && (sz.cy == 0)) // ah crap, we haven't computed snapbar rects yet. darnit!
		{
			CDCHandle dc = this->m_pT->GetDC();
			CPoint pt(0);
			pWin->ComputeSnapBarRect(dc, true, pt);		
			sz = CSize(pWin->GetSnapBarIconRect().Width(), pWin->GetSnapBarIconRect().Height());
			ATLASSERT((sz.cx > 0) && (sz.cy > 0));
			::ReleaseDC(this->m_pT->m_hWnd, dc);
		}
		lstNormal.Create(__max(sz.cx, sz.cy), __min(sz.cy, sz.cx), ILC_COLOR24|ILC_MASK, 1, 1);
		lstRotated.Create(__min(sz.cx, sz.cy), __max(sz.cy, sz.cx), ILC_COLOR24|ILC_MASK, 1, 1);

		if(!this->CreateDragBitmaps(true, CSize(__max(sz.cx, sz.cy), __min(sz.cx, sz.cy)), pWin, bmp, bmpMask))
			return;

		lstNormal.Add(bmp, bmpMask);
		bmp.DeleteObject();
		bmpMask.DeleteObject();

		if(!this->CreateDragBitmaps(false, CSize(__min(sz.cx, sz.cy), __max(sz.cx, sz.cy)), pWin, bmp, bmpMask))
			return;

		lstRotated.Add(bmp, bmpMask);

		ptH = CPoint((int)((double)__max(pWin->GetSnapBarIconRect().Width(), pWin->GetSnapBarIconRect().Height())/2.0f),
			(int)((double)__min(pWin->GetSnapBarIconRect().Height(), pWin->GetSnapBarIconRect().Width())/2.0f));

		ptV = CPoint((int)((double)__min(pWin->GetSnapBarIconRect().Height(), pWin->GetSnapBarIconRect().Width())/2.0f),
			(int)((double)__max(pWin->GetSnapBarIconRect().Width(), pWin->GetSnapBarIconRect().Height())/2.0f));

		if(IS_SNAPPED(pWin->GetWinState())) // don't do this if we're a floating window....
		{
			lstNormal.BeginDrag(0,ptH);
			lstNormal.DragEnter(NULL, ptStartPoint);
			::SetCapture(this->m_pT->m_hWnd);
		}
		else
		{
			// let's figure out what our floating offset is
			CRect rc;
			pWin->GetWindowRect(rc);
			ptOffset.SetPoint(ptStartPoint.x - rc.left, ptStartPoint.y - rc.top);
			::SetCapture(pWin->m_hWnd);
		}
		this->DoDragging(pWin, lstNormal, lstRotated, ptH, ptV, ptOffset);
		// let's destroy our image lists....
		lstNormal.RemoveAll();
		lstNormal.Destroy();
		lstRotated.RemoveAll();
		lstRotated.Destroy();
	}
	void		DoDragging(CSnapWindow* pWin, CImageList& lstNormal, CImageList& lstRotated, 
							CPoint ptH, CPoint ptV, CPoint pointFloatOffset)
	{
		bool bDone = false, bHorz = true, bActivatedSnapBars = false, bSnapBarArray[4], bCurrent = true;
		eWinState state = eFloating, eCurState = eFloating;
		MSG msg;
		CPoint pt;

		::GetCursorPos(&pt); // just in case.
		// copy our current snap bar array into this one...
		memcpy(bSnapBarArray, this->m_arrSidesActive, sizeof(this->m_arrSidesActive));
		while(!bDone)
		{
			if(!::GetMessage(&msg, NULL, 0, 0))
			{
				::PostQuitMessage(msg.wParam);
				break;
			}
			switch(msg.message)
			{
			case WM_MOUSEMOVE:
				if(IS_SNAPPED(pWin->GetWinState()))
					this->HandleSnappedWindowMouseMove(lstNormal, lstRotated, bHorz, bCurrent, msg.lParam, 
						bActivatedSnapBars, bSnapBarArray, state, pt, ptV, ptH);
				else
				{
					CPoint ptMouse(msg.lParam);
					::MapWindowPoints(pWin->m_hWnd, this->m_pT->m_hWnd, &ptMouse, 1);
					this->HandleFloatingWindowMouseMove(lstNormal, lstRotated, eCurState, state, 
							bActivatedSnapBars, bSnapBarArray, ptV, ptH, ptMouse, pWin);
					// now actually reposition the window to reflect the changes
					if(IS_FLOATING(eCurState)) // don't bother if we're snapped in.
					{
						ptMouse -= pointFloatOffset;
						::SetWindowPos(pWin->m_hWnd, NULL, ptMouse.x, ptMouse.y, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
					}
				}
				break;
			case WM_LBUTTONUP:
				bDone = true;
				if(IS_SNAPPED(eCurState) || IS_SNAPPED(pWin->GetWinState()))
				{
					bCurrent ? lstNormal.DragLeave(NULL) : lstRotated.DragLeave(NULL);
					bCurrent ? lstNormal.EndDrag() : lstRotated.EndDrag();
				}
				::ReleaseCapture();				
				// ok now let's figure out what to do with this guy....
				this->HandleEndDrag(state, pWin, pt);
				break;
			default:
				::DispatchMessage(&msg);
				break;
			}
		}
		this->CheckSnapBars();
	}
	void		HandleSnappedWindowMouseMove(CImageList& lstNormal, CImageList& lstVertical, bool& bDesiredOrientation,
											bool& bCurrentOrientation, CPoint point, bool& bActivatedSnapBars,
											bool* bSnapBarArray, eWinState& state, CPoint& ptLastDragLocation,
											CPoint& ptDragV, CPoint& ptDragH)
	{
		ptLastDragLocation = this->GetDragPoint(point, state);
		bDesiredOrientation = IS_SNAPPED_HORZ(state);
		if(IS_FLOATING(state) && !bDesiredOrientation) // if we're not snapped, and are still vertical
			bDesiredOrientation = true; // we want to switch to horizontal.
		// let's check if we need to redo the client area
		//first we check if there are any snapbars we should DEactivate
		if(bActivatedSnapBars)
		{
			bCurrentOrientation ? lstNormal.DragLeave(NULL) : lstVertical.DragLeave(NULL);
			bActivatedSnapBars = this->CheckSnapBarDeactivation(bSnapBarArray, state);
			bCurrentOrientation ? lstNormal.DragEnter(NULL, ptLastDragLocation) : lstVertical.DragEnter(NULL, ptLastDragLocation);
		}
		// now let's check if there are any we should activate...
		if(IS_SNAPPED(state) && (this->m_arrSidesActive[state] == false))
		{
			// ok we have to temporarily "show" that snap bar
			bCurrentOrientation ? lstNormal.DragLeave(NULL) : lstVertical.DragLeave(NULL);
			this->ActivateSnapBar((eSide)state);
			bActivatedSnapBars = true;
			bCurrentOrientation ? lstNormal.DragEnter(NULL, ptLastDragLocation) : lstVertical.DragEnter(NULL, ptLastDragLocation);
		}
		if(bDesiredOrientation != bCurrentOrientation)
		{
			// terminate previous drag
			bCurrentOrientation ? lstNormal.DragLeave(NULL) : lstVertical.DragLeave(NULL);
			bCurrentOrientation ? lstNormal.EndDrag() : lstVertical.EndDrag();
			// start new drag
			bCurrentOrientation ? lstVertical.BeginDrag(0, ptDragV.x, ptDragV.y) : lstNormal.BeginDrag(0, ptDragH.x, ptDragH.y);
			bCurrentOrientation ? lstVertical.DragEnter(NULL, ptLastDragLocation) : lstNormal.DragEnter(NULL, ptLastDragLocation);
			bCurrentOrientation = bDesiredOrientation;
		}
		bCurrentOrientation ? lstNormal.DragMove(ptLastDragLocation) : lstVertical.DragMove(ptLastDragLocation);
	}


	void		HandleFloatingWindowMouseMove(  CImageList& lstNormal, CImageList& lstVertical, eWinState& eCurState,
												eWinState& eDesiredState, bool& bActivatedSnapBars, bool* bSnapBarArray,
												CPoint& ptDragV, CPoint& ptDragH, CPoint& ptMousePos, CSnapWindow* pWin)
	{
		// this is trying to figure out what actions we want to take at the end of the day. 
		// we'll have a bunch of if statements trying to figure out what to do, possibly affecting each other,
		// and then at the end of the day we'll do what's needed.
		bool bWantNormalOn = false, bWantNormalOff = false, bWantNormalEnded = false, bWantVerticalOn = false, 
			bWantVerticalOff = false, bWantVerticalEnded = false, bWantWindowShown = false, bWantWindowHidden = false,
			bWantActivateSnapBar = false, bWantNormalStarted = false, bWantVerticalStarted = false, bWantDeactivationChecked = true;
		CPoint pt = this->GetDragPoint(ptMousePos, eDesiredState);
		// ok first let's check if we're floating, and we weren't before
		if(IS_FLOATING(eDesiredState) && IS_SNAPPED(eCurState))
		{
			// ok we need to destroy our drag image
			IS_SNAPPED_HORZ(eCurState) ? (bWantNormalOff = bWantNormalEnded = true) : 
				(bWantVerticalOff = bWantVerticalEnded = true);
			bWantWindowShown = true;
		}
		// check if we need to activate any bars...
		if(IS_SNAPPED(eDesiredState) && (this->m_arrSidesActive[eDesiredState] == false))
		{
			// ok we have to temporarily "show" that snap bar
			IS_SNAPPED_HORZ(eCurState) ? (bWantNormalOff = true) : (bWantVerticalOff = true);
			bWantActivateSnapBar = bActivatedSnapBars = true;
			IS_SNAPPED_HORZ(eCurState) ? (bWantNormalOn = true) : (bWantVerticalOn = true);
		}
		// ok now let's check if we WERE floating, and are now snapped....
		if(IS_FLOATING(eCurState) && IS_SNAPPED(eDesiredState))
		{
			// and we need to hide the window...
			bWantWindowHidden = true;
			// this means we must create a drag image...
			IS_SNAPPED_HORZ(eDesiredState) ? (bWantNormalStarted = true) : (bWantVerticalStarted = true);
			IS_SNAPPED_HORZ(eDesiredState) ? (bWantNormalOn = true) : (bWantVerticalOn = true);
		}
		if(IS_SNAPPED(eDesiredState) && IS_SNAPPED(eCurState) && (eDesiredState != eCurState))
		{
			IS_SNAPPED_HORZ(eCurState) ? (bWantNormalOff = bWantNormalEnded = true) : 
										 (bWantVerticalOff = bWantVerticalEnded = true);
			IS_SNAPPED_HORZ(eDesiredState) ? (bWantNormalOn = bWantNormalStarted = true) : 
											 (bWantVerticalOn = bWantVerticalStarted = true);
		}
		bWantDeactivationChecked = bActivatedSnapBars;

		// ok now let's do all our actions..........
		// first let's do drag-ending routines
		if(bWantNormalOff)			lstNormal.DragLeave(NULL);
		if(bWantNormalEnded)		lstNormal.EndDrag();
		if(bWantVerticalOff)		lstVertical.DragLeave(NULL);
		if(bWantVerticalEnded)		lstVertical.EndDrag();
		// now let's do window showing/hiding, etc
		if(bWantWindowHidden)		
		{ 
			pWin->ShowWindow(SW_HIDE); 
			this->m_arrSnapBarWindows[eDesiredState].SendMessage(WM_PAINT, 0, 0); 
		}
		if(bWantActivateSnapBar)	this->ActivateSnapBar((eSide)eDesiredState);
		if(bWantDeactivationChecked) bActivatedSnapBars = this->CheckSnapBarDeactivation(bSnapBarArray, eDesiredState);
		// finally let's do drag-starting routines
		if(bWantNormalStarted)		lstNormal.BeginDrag(0, ptDragH);
		if(bWantVerticalStarted)	lstVertical.BeginDrag(0, ptDragV);
		if(bWantNormalOn)			lstNormal.DragEnter(NULL, pt);
		if(bWantVerticalOn)			lstVertical.DragEnter(NULL, pt);
		if(bWantWindowShown)		pWin->ShowWindow(SW_SHOW);
		if(IS_SNAPPED(eDesiredState)) // finally let's do the dragmove if we need to...
			IS_SNAPPED_HORZ(eDesiredState) ? lstNormal.DragMove(pt) : lstVertical.DragMove(pt);
		eCurState = eDesiredState;
		ptMousePos = pt;
	}

	void		HandleEndDrag(eWinState eDesiredState, CSnapWindow* pWin, CPoint ptScreenLast)
	{
		eWinState eOrigState = pWin->GetWinState();
		this->TransferWindow(pWin, eOrigState, eDesiredState);
	}
	void		CheckSnapBars()
	{
		bool bNeedResize = false;
		int iNumWnds;
		bool bRepaint[4];
		int j;
		memset(bRepaint, 0, sizeof(bRepaint));
		for(j=0;j<4;j++)
		{
			iNumWnds = this->m_arrSnapBarWindows[j].GetNumSnappedWindows();
			if((iNumWnds > 0) && (this->m_arrSidesActive[j] == false)) // we need to activate this one
			{
				this->m_arrSidesActive[j] = true;
				bRepaint[j] = true;
				bNeedResize = true;
			}
			else if((iNumWnds == 0) && (this->m_arrSidesActive[j] == true)) // need to deactivate this one
			{
				this->m_arrSidesActive[j] = false;
				this->m_arrSnapBarWindows[j].ShowWindow(SW_HIDE);
				bNeedResize = true;
			}
		}
		if(bNeedResize)
		{
			m_pT->SetWindowPos(NULL, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
			m_pT->SendMessage(WM_PAINT, NULL, NULL);
			for(j=0;j<4;j++)
			{
				if(bRepaint[j])
				{
					this->m_arrSnapBarWindows[j].ShowWindow(SW_SHOW);
					this->m_arrSnapBarWindows[j].SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
					this->m_arrSnapBarWindows[j].SendMessage(WM_PAINT, 0, 0);
				}
			}
		}
		// finally let's check to see if we need to repaint any of the snapbars
		for(j=0;j<4; j++)
			if(this->m_arrSnapBarWindows[j].CheckRepaint())
				this->m_arrSnapBarWindows[j].Invalidate();
				//this->m_arrSnapBarWindows[j].SendMessage(WM_PAINT, 0, 0);
	}
	bool		CheckSnapBarDeactivation(IN bool* arrTempBars, eWinState eCurrentState)
	{
		bool bResult = true;
		bool bNeedResize = false;

		for(int j=0;j<4;j++)
		{
			if((arrTempBars[j] != this->m_arrSidesActive[j]) && this->m_arrSidesActive[j])
			{
				// ok found them. let's see if it should remain activated
				if(eCurrentState != (eWinState)j) // i.e. if we're no longer there...
				{
					this->m_arrSidesActive[j] = false;
					this->m_arrSnapBarWindows[j].ShowWindow(SW_HIDE);
					bResult &= true;
					bNeedResize = true;
				}
				else // we must leave them...
					bResult = false;
			}
		}
		if(bNeedResize)
		{
			m_pT->SetWindowPos(NULL, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
			m_pT->SendMessage(WM_PAINT, NULL, NULL);
		}
		return !bResult; // return TRUE if there are any bars left. return FALSE if we got them all.
	}
	CPoint		GetDragPoint(IN CPoint ptClientMousePos, eWinState& state)
	{
		CPoint ptResult(ptClientMousePos);
		::MapWindowPoints(this->m_pT->m_hWnd, this->m_pT->GetParent(), &ptResult, 1);
		// this function basically checks if we should snap to any of the snap bars, and modifies the point accordingly
		state = eFloating;		
		for(eSide side = eTop; side <= eLeft; ((short&)side)++)
		{
			if(this->m_arrSnapSideRects[side].PtInRect(ptResult))
			{
				// wee we found one. Ok we need to modify the point to snap to the real rect...
				if((side == eTop) || (side == eBottom)) // i.e. we're horizontal
					ptResult.y = this->m_arrSnapBarRects[side].top + 
						(int)((double)this->m_arrSnapBarRects[side].Height()/2.0f);
				else
					ptResult.x = this->m_arrSnapBarRects[side].left + 
						(int)((double)this->m_arrSnapBarRects[side].Width()/2.0f);
				state = (eWinState)side;
				break;
			}
		}

		// map this guy into screen coordinates
		::ClientToScreen(this->m_pT->GetParent(), &ptResult);
		return ptResult;
	}
	bool		CreateDragBitmaps(IN bool bHorz, IN CSize sz, IN CSnapWindow* pWin, 
									OUT CBitmap& bitmap, OUT CBitmap& maskBitmap)
	{
		CDCHandle dc(this->m_pT->GetDC()); // let's get the CDC we'll use
		CDC memDC, maskDC;
		CBitmapHandle hBitmap;
		CBrush brsh;
		brsh.CreateSolidBrush(RGB(96,96,96));

		// let's create a memory device context
		if(memDC.CreateCompatibleDC(dc) == FALSE)
			goto CreateDragBitmaps_Failure;

		// now create an offscreen bitmap, and select it into the memory device context we just created
		if(bitmap.CreateCompatibleBitmap(dc, sz.cx, sz.cy) == FALSE)
			goto CreateDragBitmaps_Failure;

		hBitmap = memDC.SelectBitmap(bitmap);
		// now let's tell the window to draw itself into this bitmap
		memDC.SetTextColor(RGB(255,255,255));
		memDC.SelectFont(bHorz ? this->m_fntHorizontal : this->m_fntVertical);
		memDC.SetBkMode(TRANSPARENT);
		// paint at 0....
		pWin->PaintSnapBarIcon((CDCHandle)memDC, RGB(0,0,128), RGB(0,0,0), (CBrushHandle)brsh, bHorz, false); 
		// restore the old bitmap into the memdc
		memDC.SelectBitmap(hBitmap);
		// now let's create the mask DC
		if(maskDC.CreateCompatibleDC(memDC) == FALSE)
			goto CreateDragBitmaps_Failure;

		if(maskBitmap.CreateCompatibleBitmap(memDC, sz.cx, sz.cy) == FALSE)
			goto CreateDragBitmaps_Failure;

		hBitmap = maskDC.SelectBitmap(maskBitmap);
		maskDC.SetTextColor(RGB(255,255,255));
		maskDC.SelectFont(bHorz ? this->m_fntHorizontal : this->m_fntVertical);
		maskDC.SetBkMode(TRANSPARENT);
		pWin->PaintSnapBarIcon((CDCHandle)maskDC, RGB(0,0,128), RGB(0,0,0), (CBrushHandle) brsh, bHorz, false);
		maskDC.SelectBitmap(hBitmap);
		::ReleaseDC(m_pT->m_hWnd, dc);
		return true;
CreateDragBitmaps_Failure:
		::ReleaseDC(m_pT->m_hWnd, dc);
		return false;
	}

	// tracking-related functions
	void		ForceEndTracking()
	{
		if(this->m_bTrackingTimerStarted)
			this->m_pT->KillTimer(TRACKING_TIMER_ID);
		this->m_bTrackingTimerStarted = false;
		this->m_bTrackingWindow = false;
	}

	void		SnapBarMouseMove(CPoint pt, eSide side)
	{
		// what this means is user stuck their mouse onto a different snapbar... we ouggtha do the same thing
		// we do in mousemove
		ATLASSERT(this->m_bTrackingWindow);
		if(!this->m_bTrackingTimerStarted)
		{
			this->m_pT->SetTimer(TRACKING_TIMER_ID, 400);
			this->m_bTrackingTimerStarted = true;
			// let's translate this point to our coordinates before we store it...
			this->m_arrSnapBarWindows[side].ClientToScreen(&pt);
			this->m_pT->ScreenToClient(&pt);
			this->m_ptLastTrackPoint = pt;
		}
	}
	void		StartTrackingWindow(eSide eSideToTrack)
	{
		// let's check if we're already tracking, and if so cancel it...
		if(this->m_bTrackingWindow)
			this->ForceEndTracking();
		this->m_bTrackingWindow = true;
		this->m_eTrackingSide = eSideToTrack;
	}

	LRESULT		OnSnapWindowDestroyed(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		// let's try to find it in our list first, then we'll pass it on to the snapbars
		tSnapWindowList::iterator i;
		CSnapWindow* pWnd = (CSnapWindow*)lParam;

		for(i=this->m_lstFloatingWindows.begin(); i!=this->m_lstFloatingWindows.end(); i++)
		{
			if((*i) == pWnd)
			{
				this->m_lstFloatingWindows.erase(i); // can't use remove because then we won't know if it got removed
				// and doing a find first would result in two iterations of the list, which is just silly.
				delete pWnd;
				return 0;
			}
		}
		// ok I guess we didn't find it... let's pass it onto our bars...
		this->m_arrSnapBarWindows[pWnd->GetWinState()].RemoveWindowFromBar(pWnd, false);
		delete pWnd;
		this->CheckSnapBars();
		return 0;
	}
	void		CreateFloatingWindow(IN HWND hWnd, IN CSize szSize, IN bool bHasCloseCheckBox) throw(DWORD)
	{
		// ok first we need to create a new snapwindow
		CSnapWindow* pWin = new CSnapWindow(eFloating, hWnd, NULL, this);
		if(!pWin->CreateSnapWindow(hWnd, szSize, bHasCloseCheckBox))
		{
			ATLASSERT(false);
			throw(GetLastError());
		}
		this->m_lstFloatingWindows.push_back(pWin);
	}

	void		CreateSnappedWindow(IN HWND hWnd, IN CSize szSize, 
									IN eWinState state, IN bool bHasCloseCheckBox) throw(DWORD)
	{
		CSnapWindow* pWin = new CSnapWindow(state, hWnd, &this->m_arrSnapBarWindows[state], this);
		if(!pWin->CreateSnapWindow(hWnd, szSize, bHasCloseCheckBox))
		{
			ATLASSERT(false);
			throw(GetLastError());
		}
		this->m_arrSnapBarWindows[state].AddWindowToBar(pWin);
		this->CheckSnapBars();
	}
	// window transferring (transfer from one state to another)
	void		TransferWindow(CSnapWindow* pWin, eWinState eOrigState, eWinState eDesiredState)
	{
		// TODO put better error checking here..
		if(eOrigState == eDesiredState)
			return; // nothing to do

		if(eOrigState == eFloating)
		{
			tSnapWindowList::iterator i = std::find(this->m_lstFloatingWindows.begin(), 
													this->m_lstFloatingWindows.end(), pWin);
			if(i == this->m_lstFloatingWindows.end())
				return; // strange....

			// handle floating transitions completely differently... 
			if(eDesiredState == eNull)
			{
				pWin->DestroyWindow();
				delete pWin;
			}
			else
			{
				this->m_arrSnapBarWindows[eDesiredState].AddWindowToBar(pWin);
				pWin->SetState(eDesiredState); // we'll be no longer floating....
				// let's add it to the snap bar
			}
			this->m_lstFloatingWindows.erase(i);
			return;
		}

		if(eDesiredState == eNull) // ok they actually want to REMOVE the window....
			this->m_arrSnapBarWindows[eOrigState].RemoveWindowFromBar(pWin, true); // TODO check return value here!
		else
		{
			if(eDesiredState == eFloating) // they want to float the window
			{
				this->m_arrSnapBarWindows[eOrigState].RemoveWindowFromBar(pWin, false);
				pWin->SetState(eDesiredState); // switch to floating.
				this->m_lstFloatingWindows.push_back(pWin);
			}
			else
			{
				this->m_arrSnapBarWindows[eOrigState].RemoveWindowFromBar(pWin, false);
				this->m_arrSnapBarWindows[eDesiredState].AddWindowToBar(pWin);
				pWin->SetState(eDesiredState);
			}
		}
	}
	// MESSAGE HANDLERS
	LRESULT		OnTimer(UINT, WPARAM wParam, LPARAM, BOOL& bHandled)
	{
		if(wParam == TRACKING_TIMER_ID)
		{
			CPoint pt;

			::GetCursorPos(&pt);
			this->m_pT->ScreenToClient(&pt);
			if(pt == this->m_ptLastTrackPoint)
			{
				// ok we're done... CursorLeftTrackingWindow will take care of destroying the
				// timer if it wants to... there are some conditions under which it shouldn't.
				this->m_arrSnapBarWindows[this->m_eTrackingSide].CursorLeftTrackingWindow(); // that's all the doctor ordered.
			}
			else
				this->m_ptLastTrackPoint = pt;
			return 0;
		}
		bHandled = FALSE;
		return 0;
	}
	LRESULT		OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE; // give the other guys a chance to handle this.
		// let's get rid of the our windows
		this->m_arrSnapBarWindows[0].DestroyWindow();
		this->m_arrSnapBarWindows[1].DestroyWindow();
		this->m_arrSnapBarWindows[2].DestroyWindow();
		this->m_arrSnapBarWindows[3].DestroyWindow();

		tSnapWindowList::iterator i;

		for(i=this->m_lstFloatingWindows.begin(); i!=this->m_lstFloatingWindows.end(); i++)
		{
			(*i)->DestroyWindow();
			delete (*i);
		}
		// finally unsubclass our parent
		this->m_cParent.UnsubclassWindow();
		return 0;
	}
	LRESULT		OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE; // let's get our facts straight here...
		HWND hWnd, hOldWnd;
		T* pT = static_cast<T*>(this);

		// ok now we need to create our four snapbar windows so they're ready for us.
		this->m_arrSnapBarWindows[eLeft].Create(pT->GetParent(), T::rcDefault, NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
		this->m_arrSnapBarWindows[eRight].Create(pT->GetParent(), T::rcDefault, NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
		this->m_arrSnapBarWindows[eTop].Create(pT->GetParent(), T::rcDefault, NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
		this->m_arrSnapBarWindows[eBottom].Create(pT->GetParent(), T::rcDefault, NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
		this->SetFonts();
		// we need to walk the parent list to get the topmost parent
		hWnd = this->m_pT->GetParent();
		ATLASSERT(::IsWindow(hWnd) != FALSE);

		while(hWnd)
		{
			hOldWnd = hWnd;
			hWnd = ::GetParent(hWnd);
		}
		this->m_cParent.SubclassWindow(hOldWnd);
		return 0;
	}
	LRESULT		OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{	
		bHandled = FALSE;
		return 0;
	}

	LRESULT		OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CRect rcWin;
		// let's get the window rect of this window
		m_pT->GetWindowRect(rcWin);
		// now let's translate that rect to the client window's coordinate space
		// we have to do that instead of simply getting client window's clientrect because
		// the client rect might include stuff we don't want to obscure, like toolbars, etc.
		LPRECT lpRC = (LPRECT)rcWin;
		::MapWindowPoints(NULL, m_pT->GetParent(), (LPPOINT)lpRC, sizeof(RECT)/sizeof(POINT));

		// now let's calculate all the snapbar window sizes
		// yes, I know I could've just done SetRect, but then the code would've been
		// less readable.
		this->m_arrSnapBarRects[eTop].left = rcWin.left;
		this->m_arrSnapBarRects[eTop].right = rcWin.right;
		this->m_arrSnapBarRects[eTop].top = rcWin.top;
		this->m_arrSnapBarRects[eTop].bottom = rcWin.top + SNAPBAR_SIZE;

		this->m_arrSnapBarRects[eBottom].left = rcWin.left;
		this->m_arrSnapBarRects[eBottom].right = rcWin.right;
		this->m_arrSnapBarRects[eBottom].top = rcWin.bottom - SNAPBAR_SIZE;
		this->m_arrSnapBarRects[eBottom].bottom = rcWin.bottom;
		
		this->m_arrSnapBarRects[eLeft].left = rcWin.left;
		this->m_arrSnapBarRects[eLeft].right = rcWin.left + SNAPBAR_SIZE;
		this->m_arrSnapBarRects[eLeft].top = rcWin.top + (this->m_arrSidesActive[eTop] ? SNAPBAR_SIZE : 0);
		this->m_arrSnapBarRects[eLeft].bottom = rcWin.bottom - (this->m_arrSidesActive[eBottom] ? SNAPBAR_SIZE : 0);

		this->m_arrSnapBarRects[eRight].left = rcWin.right - SNAPBAR_SIZE;
		this->m_arrSnapBarRects[eRight].right = rcWin.right;
		this->m_arrSnapBarRects[eRight].top = rcWin.top + (this->m_arrSidesActive[eTop] ? SNAPBAR_SIZE : 0);
		this->m_arrSnapBarRects[eRight].bottom = rcWin.bottom - (this->m_arrSidesActive[eBottom] ? SNAPBAR_SIZE : 0);

		eSide side;
		// let's create our snap side rects, too
		for(side = eTop; side <= eLeft; ((short&)side)++)
		{
			this->m_arrSnapSideRects[side].CopyRect(this->m_arrSnapBarRects[side]);
			this->m_arrSnapSideRects[side].InflateRect(SNAP_DISTANCE, SNAP_DISTANCE, SNAP_DISTANCE, SNAP_DISTANCE);
			switch(side)
			{
			case eLeft:
			case eRight:
				if(this->m_arrSidesActive[eTop])
					this->m_arrSnapSideRects[side].top += SNAP_DISTANCE*2; // deflate it if there's a bar there
				if(this->m_arrSidesActive[eBottom])
					this->m_arrSnapSideRects[side].bottom -= SNAP_DISTANCE*2; // same thing.
				break;
			default:
				break;
			}
		}
		// let's move all the snapbar windows
		this->m_arrSnapBarWindows[eLeft].MoveWindow(this->m_arrSnapBarRects[eLeft].left,
			this->m_arrSnapBarRects[eLeft].top, this->m_arrSnapBarRects[eLeft].Width(), 
			this->m_arrSnapBarRects[eLeft].Height());

		this->m_arrSnapBarWindows[eTop].MoveWindow(this->m_arrSnapBarRects[eTop].left,
			this->m_arrSnapBarRects[eTop].top, this->m_arrSnapBarRects[eTop].Width(), 
			this->m_arrSnapBarRects[eTop].Height());

		this->m_arrSnapBarWindows[eRight].MoveWindow(this->m_arrSnapBarRects[eRight].left,
			this->m_arrSnapBarRects[eRight].top, this->m_arrSnapBarRects[eRight].Width(), 
			this->m_arrSnapBarRects[eRight].Height());

		this->m_arrSnapBarWindows[eBottom].MoveWindow(this->m_arrSnapBarRects[eBottom].left,
			this->m_arrSnapBarRects[eBottom].top, this->m_arrSnapBarRects[eBottom].Width(), 
			this->m_arrSnapBarRects[eBottom].Height());

		// now we have to explicitly tell all the bars that the framework got resized
		// the reason for this awkwardness (as opposed to, say, doing the framework resize
		// from within the snapbar's OnSize method, is that sometimes this onsize gets called
		// without the size of the parent changing. (for instance, when a window gets pinned,
		// the client size changes, the parent size doesn't.) Since OnSize doesn't even get called
		// when a MoveWindow doesn't change window's size, we have to do this explicitly
		for(side = eTop; side <= eLeft; ((short&)side)++)
			this->m_arrSnapBarWindows[side].NotifyFrameworkResized();
		// finally let's notify our floaters that we've resized
		tSnapWindowList::iterator i;

		for(i = this->m_lstFloatingWindows.begin(); i!=this->m_lstFloatingWindows.end(); i++)
			(*i)->FrameworkResized();

		bHandled = FALSE;
		return 0;

	}
	LRESULT		OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;
		if(this->m_bTrackingWindow) // ok we were tracking something... the fact that we've gotten this message means
		{ // the user moved the mouse outside the window... we should set a timer
			// only start the timer if we're outside the window's client rect
			CPoint pt(lParam);
			CRect rc;
			this->m_pT->GetClientRect(rc);
			if(!this->m_bTrackingTimerStarted && rc.PtInRect(pt))
			{
				this->m_pT->SetTimer(TRACKING_TIMER_ID, 400);
				this->m_bTrackingTimerStarted = true;
				this->m_ptLastTrackPoint = CPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
			}
		}
		return 0;
	}

	LRESULT		OnLButtonDown(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;
		return 0;

	}
	LRESULT		OnNCCalcSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		BOOL bValidRect = (BOOL)wParam;
		bHandled = FALSE; // let the default handler do all the frame adjusting crap.
		if(bValidRect)
		{
			NCCALCSIZE_PARAMS* calcSize = (NCCALCSIZE_PARAMS*)lParam;
			if((calcSize->rgrc[0].left == calcSize->rgrc[0].right) || (calcSize->rgrc[0].top == calcSize->rgrc[0].bottom))
				return 0; // don't do anything
			calcSize->rgrc[0].left += this->m_arrSidesActive[eLeft] ? 
				this->m_arrSnapBarWindows[eLeft].GetRequiredArea()-2 : 0;
			calcSize->rgrc[0].right -= this->m_arrSidesActive[eRight] ? 
				this->m_arrSnapBarWindows[eRight].GetRequiredArea()-2 : 0;
			calcSize->rgrc[0].bottom -= this->m_arrSidesActive[eBottom] ? 
				this->m_arrSnapBarWindows[eBottom].GetRequiredArea()-2 : 0;
			calcSize->rgrc[0].top += this->m_arrSidesActive[eTop] ? 
				this->m_arrSnapBarWindows[eTop].GetRequiredArea()-2 : 0;
		}
		return 0;
	}
	LRESULT		OnLButtonDblClk(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;
		return 0;

	}
	LRESULT		OnSettingsChange(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		this->SetFonts();
		return 0;
	}
	// utility functions.
	void		SetFonts()
	{
		// Caption fonts
		NONCLIENTMETRICS ncm = { 0 };
		ncm.cbSize = sizeof(ncm);
		::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
		if( !m_fntHorizontal.IsNull() )
			m_fntHorizontal.DeleteObject();
		// Don't use bold font
		ncm.lfSmCaptionFont.lfWeight = FW_NORMAL;
		this->m_fntHorizontal.CreateFontIndirect(&ncm.lfSmCaptionFont); 
		ncm.lfSmCaptionFont.lfEscapement = ncm.lfSmCaptionFont.lfOrientation  = 2700;
		if(!m_fntVertical.IsNull())
			m_fntVertical.DeleteObject();
		m_fntVertical.CreateFontIndirect(&ncm.lfSmCaptionFont); //Small caption font
	}
	void		ActivateSnapBar(eSide side)
	{
			this->m_arrSidesActive[side] = true;
			this->TriggerNCResize();
			this->m_arrSnapBarWindows[side].ShowWindow(SW_SHOW);
			this->m_arrSnapBarWindows[side].SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
			// make SURE the snapbar repaints itself. this is VERY important, especially in dragging.
			this->m_arrSnapBarWindows[side].SendMessage(WM_PAINT, NULL, NULL);
			m_pT->Invalidate();
	}
	void		TriggerNCResize()
	{ m_pT->SetWindowPos(NULL, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE); }

	CRect				m_arrSnapBarRects[4]; // these are the actual coordinates of the snap bars
	CRect				m_arrSnapSideRects[4]; // these are the same coordinates, except inflated by a certain amount to
												// aid with snap-to operations.
	bool				m_arrSidesActive[4]; // array of active sides
	CSnapBarWindow		m_arrSnapBarWindows[4]; // array of snap bar windows (hidden)
	tSnapWindowList		m_lstFloatingWindows;
	T*					m_pT;
	eSide				m_eTrackingSide;
	CPoint				m_ptLastTrackPoint;
	bool				m_bTrackingTimerStarted;
	bool				m_bTrackingWindow;
	CFont				m_fntHorizontal;
	CFont				m_fntVertical;
	bool				m_bFloaterActivationState;
	CParentWindow		m_cParent;
};

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions