// AddBuddy.cpp: --------------------------------------------------------------

#include "stdafx.h"
#include "AddBuddy.h"


static LPCTSTR g_szOldProc = _T("BuddyOldProc");
static LPCTSTR g_szData = _T("BuddyData");

class CData
{
public:
	UINT m_uBuddyWidth;
	UINT m_uBuddyHeight;
	HWND m_hwndBuddy;
	UINT m_uStyle;
	short m_bVisible;
};

static LRESULT CALLBACK SubClassedProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	WNDPROC WndProc = reinterpret_cast<WNDPROC>(::GetProp(hwnd, g_szOldProc));
	CData *pData = reinterpret_cast<CData*>(::GetProp(hwnd, g_szData));

	ASSERT(WndProc);

	switch(message)
	{
		case WM_NCDESTROY:
		{
			SetWindowLong(hwnd, GWL_WNDPROC, (long)WndProc);
			RemoveProp(hwnd, g_szOldProc);
			RemoveProp(hwnd, g_szData);

			delete pData;	
			break;
		}
		case WM_NCHITTEST:
		{
			LRESULT lr = CallWindowProc(WndProc, hwnd, message, wParam, lParam);
			if(lr == HTNOWHERE)
			{
				lr = HTTRANSPARENT;
			}
			return lr;
		}
		case WM_NCCALCSIZE:
		{
			LRESULT lr = CallWindowProc(WndProc, hwnd, message, wParam, lParam);
			LPNCCALCSIZE_PARAMS lpnccs = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
			
			switch(pData->m_uStyle)
			{
				case BDS_LEFT:
					lpnccs->rgrc[0].left += pData->m_uBuddyWidth;
					break;
				case BDS_RIGHT:
					lpnccs->rgrc[0].right -= pData->m_uBuddyWidth;
					break;
				case BDS_TOP:
					lpnccs->rgrc[0].top += pData->m_uBuddyHeight;
					break;
				case BDS_BOTTOM:
					lpnccs->rgrc[0].bottom -= pData->m_uBuddyHeight;
					break;
				default:
					break;
			}

			return lr;
		}
		case WM_SIZE:
		{
			CRect rc;
			::GetClientRect(hwnd, rc);
			
			switch(pData->m_uStyle)
			{
				case BDS_LEFT:
					rc.right = rc.left;
					rc.left = rc.left - pData->m_uBuddyWidth;
					break;
				case BDS_RIGHT:
					rc.left = rc.right;
					rc.right = rc.left + pData->m_uBuddyWidth;
					break;
				case BDS_TOP:
					rc.bottom = rc.top;
					rc.top = rc.top - pData->m_uBuddyHeight;
					break;
				case BDS_BOTTOM:
					rc.top = rc.bottom;
					rc.bottom = rc.bottom + pData->m_uBuddyHeight;
					break;
			}
	
			::MapWindowPoints(hwnd, GetParent(hwnd), (LPPOINT)&rc, 2);
			::SetWindowPos(pData->m_hwndBuddy, NULL, rc.left, rc.top, 
					rc.Width(), rc.Height(), SWP_NOZORDER);
			break;
		}
		case WM_SHOWWINDOW:
		{
			if(wParam != pData->m_bVisible)
			{
				pData->m_bVisible = (short)wParam;
				::ShowWindow(pData->m_hwndBuddy, wParam ? SW_SHOW : SW_HIDE);
			}
			break;
		}
	}

	return CallWindowProc(WndProc, hwnd, message, wParam, lParam);
}

BOOL AddBuddy(HWND hwndTarget, HWND hwndBuddy, UINT uStyle)
{
	if(uStyle == BDS_LEFT || uStyle == BDS_RIGHT || uStyle == BDS_TOP || uStyle == BDS_BOTTOM)
	{
		if(::IsWindow(hwndTarget) && ::IsWindow(hwndBuddy))
		{
			FARPROC lpfnWndProc = reinterpret_cast<FARPROC>(SetWindowLong(hwndTarget, GWL_WNDPROC, (long)SubClassedProc));
			ASSERT(lpfnWndProc != NULL);
			VERIFY(::SetProp(hwndTarget, g_szOldProc, reinterpret_cast<HANDLE>(lpfnWndProc)));
			
			CData *pData = new CData;
			pData->m_uStyle = uStyle;

			CRect rcBuddy;
			::GetWindowRect(hwndBuddy, rcBuddy);

			pData->m_uBuddyWidth = rcBuddy.Width();
			pData->m_uBuddyHeight = rcBuddy.Height();
			pData->m_hwndBuddy = hwndBuddy;
			pData->m_bVisible = -1;

			VERIFY(::SetProp(hwndTarget, g_szData, reinterpret_cast<HANDLE>(pData)));

			::SetWindowPos(hwndTarget, NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
			
			return TRUE;
		}
		else
		{
			SetLastErrorEx(ERROR_INVALID_WINDOW_HANDLE, SLE_ERROR);
		}
	}
	else
	{
		SetLastErrorEx(ERROR_INVALID_DATA, SLE_ERROR);
	}

	return FALSE;
}
//-----------------------------------------------------------------------------
// (p) 2003 by FoRcHa  ... (based on Gipsysoft's BuddyButton)


