Click here to Skip to main content
15,895,772 members
Articles / Desktop Programming / WTL

Dev-studio like EditListBox Control

Rate me:
Please Sign up or sign in to vote.
4.73/5 (21 votes)
7 Nov 20049 min read 64.6K   2.3K   48  
An article on EditListBox Control.
#if !defined(__BUTTONEDIT_H_)
#define __BUTTONEDIT_H_

#pragma once

#include "messagehook.h"

#define BEM_BASE					0x0210
#define BEM_BROWSE					BEM_BASE + 0

namespace codeproject
{

template<class TEdit = CEdit>
class CButtonEditImplCCtrlT : public CWindowImpl<CButtonEditImplCCtrlT, TEdit>
{
public:
	typedef CButtonEditImplCCtrlT						thisClass;
	typedef CWindowImpl<CButtonEditImplCCtrlT, TEdit>	baseClass;

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_NCHITTEST,	OnNcHitTest)
		MESSAGE_HANDLER(WM_SIZE,		OnSize)
		MESSAGE_HANDLER(EM_SETMARGINS,	OnSetMargins)
		MESSAGE_HANDLER(EM_SETRECT,		OnSetRect)
		MESSAGE_HANDLER(EM_SETRECTNP,	OnSetRectNP)
		COMMAND_CODE_HANDLER(BN_CLICKED, OnButtonNotifyClicked)
	END_MSG_MAP()

protected:
	CButton m_wndInButton;
	RECT m_rcButton;
	BOOL m_bHideButton;
	UINT m_nButtonWidht;

	class XButtonFocusManagerHook : public CMessageHook<XButtonFocusManagerHook>
	{
	public:
		XButtonFocusManagerHook() : CMessageHook<XButtonFocusManagerHook>(TRUE)
		{
		}

	protected:
		virtual BOOL ProcessWindowMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
		{
			BOOL bHandled = FALSE;

			switch(uMsg)
			{
			case WM_SETFOCUS:
			case WM_KILLFOCUS:
				bHandled = TRUE;

				lResult = DefWindowProc(uMsg, wParam, lParam);

				if(::GetParent(GetHwnd()) != (HWND)wParam)
					::SendMessage(::GetParent(GetHwnd()), uMsg, wParam, lParam);
				break;
			}

			return bHandled;
		}
	};

public:
	// c'tor
	CButtonEditImplCCtrlT() : m_bHideButton(FALSE), m_nButtonWidht(3 * ::GetSystemMetrics(SM_CXVSCROLL) / 2)
	{
	}

	// d'tor
	virtual ~CButtonEditImplCCtrlT()
	{
	}

	// properties
	void HideButton(BOOL bHide)
	{
		if(!IsWindow())
		{
			m_bHideButton = bHide;
			return;
		}

		if(bHide)
		{
			if(!m_bHideButton)
			{
				// remove the space for the button
				UINT nDefRMargin = HIWORD((DWORD)DefWindowProc(EM_GETMARGINS, 0, 0L));
				DefWindowProc(EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, nDefRMargin - m_nButtonWidht));
			}
		}
		else
		{
			if(m_bHideButton)
			{
				// reserve the space for the button
				UINT nDefRMargin = HIWORD((DWORD)DefWindowProc(EM_GETMARGINS, 0, 0L));
				DefWindowProc(EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, nDefRMargin + m_nButtonWidht));
			}
			
			if(!m_wndInButton.IsWindow())
			{
				m_wndInButton.Create(m_hWnd, &rcDefault, NULL, WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY);
				ATLASSERT(m_wndInButton.IsWindow());
				m_wndInButton.SetFont((HFONT)::SendMessage(GetParent(), WM_GETFONT, 0, 0L));
				m_wndInButton.SetWindowText(_T("..."));

				XButtonFocusManagerHook *pHook = new XButtonFocusManagerHook;
				pHook->Hook(m_wndInButton);
			}
		}
		
		m_bHideButton = bHide;
		
		UpdateButton();
	}

	// operations
	HWND Create(HWND hwndParent, RECT& rcPos, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
	{
		ATLASSERT(NULL == m_hWnd);

		CEdit wndEditTemp;
		HWND hWnd = wndEditTemp.Create(hwndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam);
		wndEditTemp.Detach();

		//	HWND hWnd = baseClass::Create(hwndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam);
		
		if(hWnd)
			SubclassWindow(hWnd);

		return hWnd;
	}

	BOOL SubclassWindow(HWND hWnd)
	{
		if(!m_bHideButton)
		{
			// reserve the space for the button
			UINT nDefRMargin = HIWORD((DWORD)::SendMessage(hWnd, EM_GETMARGINS, 0, 0L));
			::SendMessage(hWnd, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, nDefRMargin + m_nButtonWidht));
		}

		BOOL bRet = baseClass::SubclassWindow(hWnd);

		LONG lStyle = GetStyle();
		SetWindowLong(GWL_STYLE, lStyle | WS_CLIPCHILDREN);

		if(!m_bHideButton && !m_wndInButton.IsWindow())
		{
			const DWORD dwStyle = WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY;
			m_wndInButton.Create(m_hWnd, &rcDefault, NULL, m_bHideButton ? dwStyle : dwStyle | WS_VISIBLE);
			ATLASSERT(m_wndInButton.IsWindow());
			m_wndInButton.SetFont((HFONT)::SendMessage(GetParent(), WM_GETFONT, 0, 0L));
			m_wndInButton.SetWindowText(_T("..."));

			XButtonFocusManagerHook *pHook = new XButtonFocusManagerHook;
			pHook->Hook(m_wndInButton);
		}

		UpdateButton();

		return bRet;
	}

	HWND UnsubclassWindow(BOOL bForce = FALSE)
	{
		if(m_wndInButton.IsWindow())
			m_wndInButton.DestroyWindow();

		HWND hWnd = baseClass::UnsubclassWindow(bForce);

		if(!m_bHideButton)
		{
			// remove the space for the button
			UINT nDefRMargin = HIWORD((DWORD)::SendMessage(hWnd, EM_GETMARGINS, 0, 0L));
			::SendMessage(hWnd, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, nDefRMargin - m_nButtonWidht));
			
			::InvalidateRect(hWnd, &m_rcButton, TRUE);
		}

		return hWnd;
	}

	// Implementations
protected:
	LRESULT OnButtonNotifyClicked(WORD wNotifyCode, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		wNotifyCode;
		ATLASSERT(BN_CLICKED == wNotifyCode);
		
		::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);

		return 0;
	}

	LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &/*bHandled*/)
	{
		ATLASSERT(WM_NCHITTEST == uMsg);
		LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);

		if(!m_bHideButton && HTCLIENT == lRes)
		{
			POINT ptHit = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
			ScreenToClient(&ptHit);

			if(::PtInRect(&m_rcButton, ptHit))
				return HTTRANSPARENT;
		}

		return lRes;
	}

	LRESULT OnSize(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL &bHandled)
	{
		uMsg;
		ATLASSERT(WM_SIZE == uMsg);

		UpdateButton();

		bHandled = FALSE;

		return 0;
	}

	LRESULT OnSetMargins(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &/*bHandled*/)
	{
		ATLASSERT(EM_SETMARGINS == uMsg);

		if(EC_LEFTMARGIN | wParam)
		{
			UINT nLMargin = LOWORD(lParam), nRMargin = LOWORD(lParam);
			if(m_nButtonWidht > nRMargin)
			{
				ATLTRACE(_T("\nCButtonEditImplCCtrlT::OnSetMargins(): the right margin is changed to %d in order to reserve the space for button.\n\n"), m_nButtonWidht);
				nRMargin = m_nButtonWidht;
			}

			lParam = (LPARAM)MAKELONG(nLMargin, nRMargin);
		}

		return DefWindowProc(uMsg, wParam, lParam);
	}

	LRESULT OnSetRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &/*bHandled*/)
	{
		ATLASSERT(EM_SETRECT == uMsg);

		LPRECT prcFmt = (LPRECT)lParam;
		if(m_nButtonWidht > (UINT)prcFmt->left)
		{
			ATLTRACE(_T("\nCButtonEditImplCCtrlT::OnSetRect(): the right of formatting rectangle is changed to %d in order to reserve the space for drawing icon.\n\n"), m_nButtonWidht);
			prcFmt->right -= m_nButtonWidht;
		}

		return DefWindowProc(uMsg, wParam, lParam);
	}

	LRESULT OnSetRectNP(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &/*bHandled*/)
	{
		ATLASSERT(EM_SETRECTNP == uMsg);

		LPRECT prcFmt = (LPRECT)lParam;
		if(m_nButtonWidht > (UINT)prcFmt->left)
		{
			ATLTRACE(_T("\nCButtonEditImplCCtrlT::OnSetRectNP(): the right of formatting rectangle is changed to %d in order to reserve the space for drawing icon.\n\n"), m_nButtonWidht);
			prcFmt->right -= m_nButtonWidht;
		}

		return DefWindowProc(uMsg, wParam, lParam);
	}

	void UpdateButton()
	{
		ATLASSERT(IsWindow());

		if(m_wndInButton.IsWindow())
		{
			if(m_bHideButton)
			{
				m_wndInButton.ShowWindow(SW_HIDE);
				InvalidateRect(&m_rcButton, TRUE);
			}
			else
			{
				RECT rcWindow;
				GetWindowRect(&rcWindow);

				RECT rcButton;
				GetClientRect(&rcButton);
				rcButton.left = rcButton.right - m_nButtonWidht;

				int cyBorder = ((rcWindow.bottom - rcWindow.top) - (rcButton.bottom - rcButton.top)) / 2;
				int cyEdge = ::GetSystemMetrics(SM_CYEDGE);
				if(cyEdge - 1 < cyBorder)
					::InflateRect(&rcButton, cyEdge - 1, cyEdge - 1);

				m_rcButton = rcButton;

				m_wndInButton.SetWindowPos(HWND_TOP, &rcButton, /*SWP_NOZORDER*/SWP_NOACTIVATE);
				m_wndInButton.ShowWindow(SW_SHOW);
			}

			m_wndInButton.EnableWindow(IsWindowEnabled());
		}
	}
};	// template<class TEdit = CEdit> class CButtonEditImplCCtrlT : public CWindowImpl<CButtonEditImplCCtrlT, TEdit>

template<class TEdit = CEdit>
class CButtonEditImplNcCtrlT : public CWindowImpl<CButtonEditImplNcCtrlT, TEdit>
{
public:
	typedef CButtonEditImplNcCtrlT						thisClass;
	typedef CWindowImpl<CButtonEditImplNcCtrlT, TEdit>	baseClass;
	
	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
		MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
		MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
		COMMAND_CODE_HANDLER(BN_CLICKED, OnButtonNotifyClicked)
	END_MSG_MAP()
		
protected:
	typedef struct tagButtonInfo
	{
		RECT rect;				// Button rectangle
		UINT nWidth;			// the width of the button
		BOOL bHide;				// hide or show the button
	} ButtonInfo;
	
	ButtonInfo m_buttonInfo;
	
	CButton m_wndInButton;
	
public:
	// c'tor
	CButtonEditImplNcCtrlT()
	{
		::memset(&m_buttonInfo, 0, sizeof(ButtonInfo));
		m_buttonInfo.nWidth = ::GetSystemMetrics(SM_CXVSCROLL);
	}
	
	BOOL HideButton(BOOL bHide)
	{
		if(!IsWindow())
			return FALSE;
		
		if(m_buttonInfo.bHide != bHide)
		{
			m_buttonInfo.bHide = bHide;
			
			//	Doing this forces our Edit control to pay attention to our change in it's client size
			SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		}
		
		return TRUE;
	}
	
	HWND Create(HWND hwndParent, RECT& rcPos, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
	{
		CEdit wndEditTemp;
		HWND hWnd = wndEditTemp.Create(hwndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam);
		wndEditTemp.Detach();

		//	HWND hWnd = baseClass::Create(hwndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam);
		
		if(hWnd)
			SubclassWindow(hWnd);

		return hWnd;
	}
	
	BOOL SubclassWindow(HWND hWnd)
	{
		BOOL bRet = baseClass::SubclassWindow(hWnd);
		
		if(!m_wndInButton.IsWindow())
		{
			m_wndInButton.Create(m_hWnd, &rcDefault, NULL, WS_POPUP | BS_PUSHBUTTON | BS_NOTIFY);
			ATLASSERT(m_wndInButton.IsWindow());
			m_wndInButton.SetFont((HFONT)::SendMessage(GetParent(), WM_GETFONT, 0, 0L));
			m_wndInButton.SetWindowText(_T("..."));
		}
		
		//	Doing this forces our Edit control to pay attention to our change in it's client size
		SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		
		return bRet;
	}
	
	HWND UnsubclassWindow(BOOL bForce = FALSE)
	{
		if(m_wndInButton.IsWindow())
			m_wndInButton.Detach();
		
		HWND hWnd = baseClass::UnsubclassWindow(bForce);
		
		//	Doing this forces our Edit control to pay attention to our change in it's client size
		::SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		
		return hWnd;
	}
	BOOL SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags)
	{
		if(!m_buttonInfo.bHide)
			cy -= m_buttonInfo.nWidth;
		
		return baseClass::SetWindowPos(hWndInsertAfter, x, y, cx, cy, nFlags);
	}
	
	BOOL SetWindowPos(HWND hWndInsertAfter, LPCRECT lpRect, UINT nFlags)
	{
		if(!m_buttonInfo.bHide)
		{
			RECT rcNew;
			::CopyRect(&rcNew, lpRect);
			rcNew.right -= m_buttonInfo.nWidth;
			
			return baseClass::SetWindowPos(hWndInsertAfter, &rcNew, nFlags);
		}
		
		return baseClass::SetWindowPos(hWndInsertAfter, lpRect, nFlags);
	}
	
protected:
	LRESULT OnButtonNotifyClicked(WORD wNotifyCode, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		wNotifyCode;
		ATLASSERT(BN_CLICKED == wNotifyCode);
		
		::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);

		return 0;
	}

	LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCCALCSIZE == uMsg);
		
		LPNCCALCSIZE_PARAMS lpnccs = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
		
		LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);
		
		if(!m_buttonInfo.bHide)
		{
			// now we can allocate additional space by deflating the
			// rectangle even further. Our button will go on the right-hand side,
			// and will be the same width as a scrollbar button
			lpnccs->rgrc[0].right -= m_buttonInfo.nWidth + ::GetSystemMetrics(SM_CXEDGE);
		}
		
		return lRes;
	}
	
	LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCPAINT == uMsg);
		
		if(!m_buttonInfo.bHide)
		{
			RECT rcWindow;
			GetWindowRect(&rcWindow);

			RECT rcClient;
			GetClientRect(&rcClient);

			CRgn rgn;
			if(1 == wParam)
				rgn.CreateRectRgnIndirect(&rcWindow);
			else
				rgn.Attach((HRGN)wParam);
			
			RECT rcButton;
			::CopyRect(&rcButton, &rcWindow);
			rcButton.left = rcButton.right - m_buttonInfo.nWidth - /*2 * */::GetSystemMetrics(SM_CXEDGE);
			
			int cyBorder = (rcWindow.bottom - rcWindow.top) - (rcClient.bottom - rcClient.top);
			int cyEdge = ::GetSystemMetrics(SM_CYEDGE);
			if(cyEdge - 1 < cyBorder)
				::InflateRect(&rcButton, -cyEdge+ 1, -cyEdge + 1);
			
			::CopyRect(&m_buttonInfo.rect, &rcButton);
			
			rcButton.left -= ::GetSystemMetrics(SM_CXEDGE);
			CRgn rgnButton;
			rgnButton.CreateRectRgnIndirect(&rcButton);
			
			::CombineRgn(rgn, rgn, rgnButton, RGN_XOR);
			
			DefWindowProc(uMsg, (WPARAM)(HRGN)rgn, lParam);
			
			if(1 != wParam)
				rgn.Detach();
			
			if(m_wndInButton.IsWindow())
				m_wndInButton.SetWindowPos(m_hWnd, &m_buttonInfo.rect, SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
			
			// An application returns zero if it processes this message. 
			return 0;
		}
		
		return DefWindowProc(uMsg, wParam, lParam);
	}
	
	LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCHITTEST == uMsg);
		LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);
		
		if(HTNOWHERE != lRes || m_buttonInfo.bHide)
			return lRes;
		
		// retrieve the mouse coordinates
		POINT ptHit = { (short)LOWORD(lParam), (short)HIWORD(lParam) };
		
		if(::PtInRect(&m_buttonInfo.rect, ptHit))
			return HTTRANSPARENT;
		
		return HTNOWHERE;
	}
};	// template<class TEdit = CEdit> Class CButtonEditImplNcCtrlT : public CWindowImpl<CButtonEditImplNcCtrlT, TEdit>



template<class TEdit = CEdit>
class CButtonEditImplNcDrawT : public CWindowImpl<CButtonEditImplNcDrawT, TEdit>
{
public:
	typedef CButtonEditImplNcDrawT						thisClass;
	typedef CWindowImpl<CButtonEditImplNcDrawT, TEdit>	baseClass;

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
		MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
		MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
		MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDownDblClk)
		MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDownDblClk)
		MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
	END_MSG_MAP()

protected:
	typedef struct tagButtonInfo
	{
		RECT rect;				// Button rectangle
		UINT uCmd;				// Command to send when clicked (WM_COMMAND)
		BOOL bPressed;			// Is the button pressed in or out?
		BOOL bPressedMouseOver;	// Is the button press and the mouse cursor over the button? to emulates the way a normal button in Windows works
		UINT nWidth;			// the width of the button
		BOOL bHide;				// hide or show the button
	} ButtonInfo;

	ButtonInfo m_buttonInfo;

public:
	// c'tor
	CButtonEditImplNcDrawT()
	{
		::memset(&m_buttonInfo, 0, sizeof(ButtonInfo));
		m_buttonInfo.nWidth = ::GetSystemMetrics(SM_CXVSCROLL);
		m_buttonInfo.uCmd = BEM_BROWSE;
	}

	// d'tor
	virtual ~CButtonEditImplNcDrawT()
	{
	}

	BOOL HideButton(BOOL bHide)
	{
		if(!IsWindow())
			return FALSE;

		if(m_buttonInfo.bHide != bHide)
		{
			m_buttonInfo.bHide = bHide;
			
			//	Doing this forces our Edit control to pay attention to our change in it's client size
			SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		}

		return TRUE;
	}

	HWND Create(HWND hwndParent, RECT& rcPos, LPCTSTR szWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
	{
		HWND hWnd = baseClass::Create(hwndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam);
		
		//	Doing this forces our Edit control to pay attention to our change in it's client size
		SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		
		return hWnd;
	}

	BOOL SubclassWindow(HWND hWnd)
	{
		BOOL bRet = baseClass::SubclassWindow(hWnd);

		//	Doing this forces our Edit control to pay attention to our change in it's client size
		SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);

		return bRet;
	}

	HWND UnsubclassWindow(BOOL bForce = FALSE)
	{
		HWND hWnd = baseClass::UnsubclassWindow(bForce);

		//	Doing this forces our Edit control to pay attention to our change in it's client size
		::SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);

		return hWnd;
	}
	BOOL SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags)
	{
		cy -= m_buttonInfo.nWidth;
		return baseClass::SetWindowPos(hWndInsertAfter, x, y, cx, cy, nFlags);
	}

	BOOL SetWindowPos(HWND hWndInsertAfter, LPCRECT lpRect, UINT nFlags)
	{
		RECT rcNew;
		::CopyRect(&rcNew, lpRect);
		rcNew.right -= m_buttonInfo.nWidth;
		return baseClass::SetWindowPos(hWndInsertAfter, &rcNew, nFlags);
	}

protected:
	LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCCALCSIZE == uMsg);

		LPNCCALCSIZE_PARAMS lpnccs = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);

		LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);
		
		if(!m_buttonInfo.bHide)
		{
			// now we can allocate additional space by deflating the
			// rectangle even further. Our button will go on the right-hand side,
			// and will be the same width as a scrollbar button
			lpnccs->rgrc[0].right -= m_buttonInfo.nWidth;
		}

		return lRes;
	}

	LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCPAINT == uMsg);
		
		if(!m_buttonInfo.bHide)
		{
			// Get the SCREEN coordinates of this window.
			RECT rcWindow;
			GetWindowRect(&rcWindow);
			
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// Update layouts
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// calculate button rectangles
			RECT rcButton;
			::CopyRect(&rcButton, &rcWindow);
			::OffsetRect(&rcButton, -rcWindow.left, -rcWindow.top);
			if(WS_BORDER & GetStyle())
				::InflateRect(&rcButton, -::GetSystemMetrics(SM_CXEDGE), -::GetSystemMetrics(SM_CYEDGE));
			rcButton.left = rcButton.right - m_buttonInfo.nWidth - ::GetSystemMetrics(SM_CXEDGE);
			::CopyRect(&m_buttonInfo.rect, &rcButton);
			
			LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);
			
			// Draw Button
			DrawButton();
			
			return lRes;
		}
		else
			return DefWindowProc(uMsg, wParam, lParam);
	}


	void DrawButton(HDC hdcWindow = NULL)
	{
		if(m_buttonInfo.bHide)
			return;

		CDCHandle dcWindow;
		if(hdcWindow)
			dcWindow.Attach(hdcWindow);
		else
			dcWindow.Attach(GetWindowDC());
		
		//	int nSaveDC = dcWindow.SaveDC();
		
		CFontHandle oldFont = dcWindow.SelectFont(GetFont());
		dcWindow.SetBkMode(TRANSPARENT);

		dcWindow.IntersectClipRect(&m_buttonInfo.rect);
		
		// erase background
		CBrush brBtnFace;
		brBtnFace.CreateSysColorBrush(COLOR_BTNFACE);
		dcWindow.FillRect(&m_buttonInfo.rect, brBtnFace);
		
		if(IsWindowEnabled())
		{
			if(m_buttonInfo.bPressedMouseOver)
			{
				RECT rcPressed;
				::CopyRect(&rcPressed, &m_buttonInfo.rect);
				::OffsetRect(&rcPressed, 1, 1);
				dcWindow.DrawText(_T("..."), 3, &rcPressed, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
				
				dcWindow.DrawEdge(&m_buttonInfo.rect, EDGE_RAISED, BF_RECT | BF_FLAT);
			}
			else
			{
				dcWindow.DrawText(_T("..."), 3, &m_buttonInfo.rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
				
				dcWindow.DrawEdge(&m_buttonInfo.rect, EDGE_RAISED, BF_RECT);
			}
		}
		else
		{
			dcWindow.SetTextColor(::GetSysColor(COLOR_BTNHIGHLIGHT));
			::OffsetRect(&m_buttonInfo.rect, 1, 1);
			dcWindow.DrawText(_T("..."), 3, &m_buttonInfo.rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

			dcWindow.SetTextColor(::GetSysColor(COLOR_BTNSHADOW));
			::OffsetRect(&m_buttonInfo.rect, -1, -1);
			dcWindow.DrawText(_T("..."), 3, &m_buttonInfo.rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

			dcWindow.DrawEdge(&m_buttonInfo.rect, EDGE_RAISED, BF_RECT);
		}
		
		dcWindow.SelectFont(oldFont);

		//	dcWindow.RestoreDC(nSaveDC);
		
		if(hdcWindow)
			dcWindow.Detach();
		else
			ReleaseDC(dcWindow.Detach());
	}

	LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCHITTEST == uMsg);
		LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);

		if(HTNOWHERE != lRes || m_buttonInfo.bHide)
			return lRes;

		// retrieve the mouse coordinates
		POINT ptHit = { (short)LOWORD(lParam), (short)HIWORD(lParam) };
		
		RECT rcWindow;
		GetWindowRect(&rcWindow);
		
		RECT rcButton;
		::CopyRect(&rcButton, &m_buttonInfo.rect);
		::OffsetRect(&rcButton, rcWindow.left, rcWindow.top);
		
		if(::PtInRect(&rcButton, ptHit))
			return HTBORDER;
		
		return HTNOWHERE;
	}

	LRESULT OnNcLButtonDownDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_NCLBUTTONDOWN == uMsg || WM_NCLBUTTONDBLCLK == uMsg);

		if(!m_buttonInfo.bHide)
		{
			// retrieve the mouse coordinates
			POINT ptHit = { (short)LOWORD(lParam), (short)HIWORD(lParam) };
			
			RECT rcWindow;
			GetWindowRect(&rcWindow);
			
			// to see if button has been clicked
			//get screen coordinates of each button
			RECT rcButton;
			::CopyRect(&rcButton, &m_buttonInfo.rect);
			::OffsetRect(&rcButton, rcWindow.left, rcWindow.top);
			
			//if clicked in a custom button
			if(::PtInRect(&rcButton, ptHit))
			{
				m_buttonInfo.bPressed = TRUE;
				m_buttonInfo.bPressedMouseOver = TRUE;
				
				DrawButton();
				
				SetCapture();
				
				// If an application processes this message, it should return zero.
				return 0;
			}
		}
		
		return DefWindowProc(uMsg, wParam, lParam);
	}
	
	LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_LBUTTONUP == uMsg);

		if(!m_buttonInfo.bPressed || m_buttonInfo.bHide)
			return DefWindowProc(uMsg, wParam, lParam);
		
		ReleaseCapture();
		
		RECT rcWindow;
		GetWindowRect(&rcWindow);
		
		RECT rcButton;
		::CopyRect(&rcButton, &m_buttonInfo.rect);
		::OffsetRect(&rcButton, rcWindow.left, rcWindow.top);
		
		// retrieve the mouse coordinates
		POINT ptHit = { (short)LOWORD(lParam), (short)HIWORD(lParam) };
		ClientToScreen(&ptHit);
		
		// if clicked in a button
		if(::PtInRect(&rcButton, ptHit))
		{
			SendMessage(WM_COMMAND, (WPARAM)m_buttonInfo.uCmd, MAKELPARAM(ptHit.x, ptHit.y));
		}
	
		m_buttonInfo.bPressed = FALSE;
		m_buttonInfo.bPressedMouseOver = FALSE;
		
		DrawButton();
		
		// If an application processes this message, it should return zero.
		return 0;
	}
	
	LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		uMsg;
		ATLASSERT(WM_MOUSEMOVE == uMsg);

		if(!m_buttonInfo.bPressed || m_buttonInfo.bHide)
			return DefWindowProc(uMsg, wParam, lParam);
		
		RECT rcWindow;
		GetWindowRect(&rcWindow);
		
		RECT rcButton;
		::CopyRect(&rcButton, &m_buttonInfo.rect);
		::OffsetRect(&rcButton, rcWindow.left, rcWindow.top);
		
		// retrieve the mouse coordinates
		POINT ptHit = { (short)LOWORD(lParam), (short)HIWORD(lParam) };
		ClientToScreen(&ptHit);
		
		BOOL bPressedMouseOver = ::PtInRect(&rcButton, ptHit);
		
		if(bPressedMouseOver != m_buttonInfo.bPressedMouseOver)
		{
			m_buttonInfo.bPressedMouseOver ^= 1;

			DrawButton();
		}
		
		// If an application processes this message, it should return zero.
		return 0;
	}
};	// template<class TEdit = CEdit> class CButtonEditImplNcDrawT : public CWindowImpl<CButtonEditImplNcDrawT, TEdit>

}	// namespace codeproject

#endif	// #if !defined(__BUTTONEDIT_H_)

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
Other
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions