Click here to Skip to main content
15,893,588 members
Articles / Desktop Programming / MFC

VssReporter 2.1 - A Visual SourceSafe reporting tool for build administrators

Rate me:
Please Sign up or sign in to vote.
4.88/5 (100 votes)
25 Mar 200610 min read 626.9K   8.9K   162  
A support tool to allow those performing builds to independently determine exactly what source files have been changed and by whom
// enedit.cpp : implementation file
//

#include "stdafx.h"
#include "enedit.h"
#include "themed.h"
#include "dlgunits.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CEnEdit

CEnEdit::CEnEdit(BOOL bComboStyle) : 
					CMaskEdit("", TRUE),
					m_bComboStyle(bComboStyle),
					m_bFirstShow(TRUE), 
					m_nButtonDown(-1),
					m_nBottomBorder(0),
					m_nTopBorder(0)
{
}

CEnEdit::~CEnEdit()
{
}


BEGIN_MESSAGE_MAP(CEnEdit, CMaskEdit)
	//{{AFX_MSG_MAP(CEnEdit)
	ON_WM_NCCALCSIZE()
	ON_WM_NCLBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_SIZE()
	ON_WM_NCPAINT()
	ON_WM_NCHITTEST()
	//}}AFX_MSG_MAP
	ON_REGISTERED_MESSAGE(WM_HTHOTCHANGE, OnHotChange)
	ON_WM_ENABLE()
	ON_MESSAGE(EM_SETREADONLY, OnSetReadOnly)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEnEdit message handlers

BOOL CEnEdit::AddButton(UINT nID, LPCTSTR szCaption, LPCTSTR szTip, int nWidth, LPCTSTR szFont)
{
	return InsertButton(m_aButtons.GetSize(), nID, szCaption, szTip, nWidth, szFont, FALSE);
}

BOOL CEnEdit::AddButton(UINT nID, UINT nChar, LPCTSTR szTip, int nWidth, LPCTSTR szFont)
{
	// Valik - cast to TCHAR is necessary or the compiler complains under VC 7.1
	return InsertButton(m_aButtons.GetSize(), nID, CString(static_cast<TCHAR>(nChar)), szTip, nWidth, szFont, TRUE);
}

BOOL CEnEdit::InsertButton(int nPos, UINT nID, LPCTSTR szCaption, LPCTSTR szTip, int nWidth, LPCTSTR szFont)
{
	return InsertButton(nPos, nID, szCaption, szTip, nWidth, szFont, FALSE);
}

BOOL CEnEdit::InsertButton(int nPos, UINT nID, UINT nChar, LPCTSTR szTip, int nWidth, LPCTSTR szFont)
{
	// Valik - cast to TCHAR is necessary or the compiler complains under VC 7.1
	return InsertButton(nPos, nID, CString(static_cast<TCHAR>(nChar)), szTip, nWidth, szFont, TRUE);
}

BOOL CEnEdit::InsertButton(int nPos, UINT nID, LPCTSTR szCaption, LPCTSTR szTip, int nWidth, LPCTSTR szFont, BOOL bSymbolFont)
{
	if (nWidth < CALC_BTNWIDTH || !nID)
		return FALSE;

	nPos = max(nPos, 0);
	nPos = min(nPos, m_aButtons.GetSize());

	EDITBTN eb;

	eb.nID = nID;
	eb.sCaption = szCaption;
	eb.sTip = szTip;
	eb.nWidth = nWidth;
	eb.hFont = NULL;
	eb.bChecked = FALSE;
	eb.bEnabled = TRUE;
	eb.bDropMenu = FALSE;

	if (szFont)
	{
		LOGFONT lf;
		HFONT hDef = (HFONT)GetStockObject(DEFAULT_GUI_FONT);

		if (GetObject(hDef, sizeof(lf), &lf))
		{
			lstrcpy(lf.lfFaceName, szFont);

			if (bSymbolFont)
				lf.lfCharSet = SYMBOL_CHARSET;
			
			eb.hFont = CreateFontIndirect(&lf);

			if (!eb.hFont)
				return FALSE;
		}
	}

	m_aButtons.InsertAt(nPos, eb);

	// add rect to hot tracker
	m_hotTrack.AddRect(CRect(0, 0, 0, 0));

	if (GetSafeHwnd())
	{
		if (!eb.sTip.IsEmpty())
			m_tooltip.AddTool(this, eb.sTip, CRect(0, 0, 10, 10), nID);

		RecalcBtnRects();

		// force WM_NCCALCSIZE
		if (!m_bFirstShow)
			SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); 
	}

	return TRUE;
}

void CEnEdit::SetBorders(int nTop, int nBottom)
{
	nTop = max(nTop, 0);
	nBottom = max(nBottom, 0);

	if (m_nTopBorder != nTop || m_nBottomBorder != nBottom)
	{
		m_nBottomBorder = nBottom;
		m_nTopBorder = nTop;

		// force WM_NCCALCSIZE
		if (GetSafeHwnd())
			SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); 
	}
}

void CEnEdit::PreSubclassWindow() 
{
	CMaskEdit::PreSubclassWindow();

	// create tooltip
	if (!m_tooltip.GetSafeHwnd() && m_tooltip.Create(this))
	{
		// add any existing buttons
		int nBtn = m_aButtons.GetSize();

		while (nBtn--)
		{
			const EDITBTN& eb = m_aButtons[nBtn];
			m_tooltip.AddTool(this, eb.sTip, CRect(), eb.nID);
		}
	}

	RecalcBtnRects();

	// hot tracking
	if (CThemed().AreControlsThemed())
		m_hotTrack.Initialize(this);
}

BOOL CEnEdit::PreTranslateMessage(MSG* pMsg) 
{
	m_tooltip.RelayEvent(pMsg);
	
	return CMaskEdit::PreTranslateMessage(pMsg);
}

void CEnEdit::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) 
{
	if (!(GetStyle() & ES_MULTILINE))
	{
		if (bCalcValidRects)
		{
			m_bFirstShow = FALSE; // in case we get here before OnNcPaint()
		
			int nBtn = m_aButtons.GetSize();

			while (nBtn--)
				lpncsp->rgrc[0].right -= GetButtonWidth(nBtn);

			lpncsp->rgrc[0].top += m_nTopBorder;
			lpncsp->rgrc[0].bottom -= m_nBottomBorder;
		}
	}
	
	CMaskEdit::OnNcCalcSize(bCalcValidRects, lpncsp);
}

void CEnEdit::OnNcLButtonDown(UINT nHitTest, CPoint point) 
{
	CMaskEdit::OnNcLButtonDown(nHitTest, point);

	if (IsWindowEnabled())
	{
		int nBtn = ButtonHitTest(point);

		if (nBtn >= 0)
		{
			if (m_aButtons[nBtn].bEnabled)
			{
				SetCapture();
				m_nButtonDown = nBtn;
				
				SendMessage(WM_NCPAINT);
			}
		}
		else
			SetFocus();
	}
}

void CEnEdit::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CMaskEdit::OnLButtonUp(nFlags, point);

	if (m_nButtonDown == -1)
		return;

	ClientToScreen(&point);
	int nBtnDown = m_nButtonDown;
	int nBtnUp = ButtonHitTest(point);

	// update UI
	ReleaseCapture();
	m_nButtonDown = -1;
	
	SendMessage(WM_NCPAINT);

	// process
	if (nBtnDown == nBtnUp)
	{
		// call derived class first
		OnBtnClick(m_aButtons[nBtnUp].nID);

		// then parent
		GetParent()->SendMessage(WM_EE_BTNCLICK, GetDlgCtrlID(), m_aButtons[nBtnUp].nID);
	}
	
	SendMessage(WM_NCPAINT);
}

void CEnEdit::OnMouseMove(UINT nFlags, CPoint point) 
{
	CMaskEdit::OnMouseMove(nFlags, point);

	if (m_nButtonDown != -1)
	{
		ClientToScreen(&point);

		if (ButtonHitTest(point) != m_nButtonDown)
		{
			ReleaseCapture();
			m_nButtonDown = -1;
			SendMessage(WM_NCPAINT);
		}
	}
}

void CEnEdit::OnSize(UINT nType, int cx, int cy) 
{
	CMaskEdit::OnSize(nType, cx, cy);
	
	// update tool rects
	RecalcBtnRects();
}

void CEnEdit::RecalcBtnRects()
{
	int nBtn = m_aButtons.GetSize();
	
	while (nBtn--)
	{
		CRect rBtn = GetButtonRectByIndex(nBtn);
		ScreenToClient(rBtn);

		m_tooltip.SetToolRect(this, m_aButtons[nBtn].nID, rBtn);

		// update hottracker too
		m_hotTrack.UpdateRect(nBtn, rBtn);
	}
}

CRect CEnEdit::GetButtonRectByIndex(int nBtn) const
{
	int nOffset = 0;

	for (int nIndex = 0; nIndex < nBtn; nIndex++)
		nOffset += GetButtonWidth(nIndex);

	CRect rButton;
	GetClientRect(rButton);

	rButton.left = rButton.right + nOffset;
	rButton.right += nOffset + GetButtonWidth(nBtn);
	rButton.top -= m_nTopBorder;
	rButton.bottom += m_nBottomBorder;

	ClientToScreen(rButton);

	return rButton;
}

void CEnEdit::OnNcPaint() 
{
	if (m_bFirstShow)
	{
		m_bFirstShow = FALSE;
		SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); 
	}

	Default();

	// draw the icon and browse button
	CWindowDC dc(this);

	CRect rWindow;
	GetWindowRect(rWindow);

	NcPaint(&dc, rWindow);
}

void CEnEdit::NcPaint(CDC* pDC, const CRect& rWindow)
{
	CPoint ptCursor(::GetMessagePos());
	int nBtn = m_aButtons.GetSize();
	
	while (nBtn--)
		DrawButton(pDC, rWindow, nBtn, ptCursor);
}

int CEnEdit::ButtonHitTest(CPoint ptScreen) const
{
	int nBtn = m_aButtons.GetSize();
	
	while (nBtn--)
	{
		if (GetButtonRectByIndex(nBtn).PtInRect(ptScreen))
			return nBtn;
	}

	return -1;
}

int CEnEdit::ButtonHitTest(UINT nID) const
{
	int nBtn = m_aButtons.GetSize();
	
	while (nBtn--)
	{
		if (m_aButtons[nBtn].nID == nID)
			return nBtn;
	}

	return -1;
}

BOOL CEnEdit::EnableButton(UINT nID, BOOL bEnable)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn < 0)
		return FALSE;

	if (m_aButtons[nBtn].bEnabled != bEnable)
	{
		m_aButtons[nBtn].bEnabled = bEnable;
		SendMessage(WM_NCPAINT);
	}

	return TRUE;
}

BOOL CEnEdit::SetDropMenuButton(UINT nID, BOOL bDropMenu)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn < 0)
		return FALSE;

	if (m_aButtons[nBtn].bDropMenu != bDropMenu)
	{
		m_aButtons[nBtn].bDropMenu = bDropMenu;
	
		if (GetSafeHwnd())
			SendMessage(WM_NCPAINT);
	}

	return TRUE;
}

BOOL CEnEdit::CheckButton(UINT nID, BOOL bChecked)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn < 0)
		return FALSE;

	if (m_aButtons[nBtn].bChecked != bChecked)
	{
		m_aButtons[nBtn].bChecked = bChecked;
		SendMessage(WM_NCPAINT);
	}

	return TRUE;
}

BOOL CEnEdit::SetButtonTip(UINT nID, LPCTSTR szTip)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn < 0)
		return FALSE;

	if (m_aButtons[nBtn].sTip.Compare(szTip) != 0)
	{
		m_aButtons[nBtn].sTip = szTip;

		if (GetSafeHwnd())
		{
			m_tooltip.UpdateTipText(szTip, this, m_aButtons[nBtn].nID);
			SendMessage(WM_NCPAINT);
		}
	}

	return TRUE;
}

BOOL CEnEdit::SetButtonCaption(UINT nID, LPCTSTR szCaption)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn < 0)
		return FALSE;

	if (m_aButtons[nBtn].sCaption.Compare(szCaption) != 0)
	{
		m_aButtons[nBtn].sCaption = szCaption;

		if (GetSafeHwnd())
			SendMessage(WM_NCPAINT);
	}

	return TRUE;
}

CRect CEnEdit::GetButtonRect(UINT nID)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn >= 0)
		return GetButtonRectByIndex(nBtn);

	return CRect(0, 0, 0, 0);
}

void CEnEdit::DrawButton(CDC* pDC, const CRect& rWindow, int nBtn, const CPoint& ptCursor) const
{
	const EDITBTN& eb = m_aButtons[nBtn];

	CRect rBtn = GetButtonRectByIndex(nBtn);

	if (rBtn.IsRectEmpty())
		return;

	BOOL bThemed = CThemed().AreControlsThemed();
	BOOL bHot = rBtn.PtInRect(ptCursor);
	BOOL bEnabled = (IsWindowEnabled() && eb.bEnabled);
	BOOL bDown = (m_nButtonDown == nBtn || eb.bChecked);

	rBtn.OffsetRect(-rWindow.TopLeft());

	// nasty business here because the API function DrawThemeEdge() is not theme aware!
	// and drawing a themed combostyle button will also draw the arrow which we don't want
	if (!m_bComboStyle || bThemed)	// draw as button type (for now)
	{
		UINT nFlags = DFCS_ADJUSTRECT | DFCS_BUTTONPUSH;
		
		// note: we do not take account of ES_READONLY as the effect of this
		// is not deterministic at this level so we assume derived classes or 
		// parents have handled it
		if (!bEnabled)
			nFlags |= DFCS_INACTIVE;
		
		else if (bDown)
			nFlags |= DFCS_PUSHED;
		
		else if (bHot)
			nFlags |= DFCS_HOT;
		
		// clip the drawing rect to prevent window getting the parent bkgnd color wrong
		CRect rClip(rBtn);

		if (bThemed)
			rBtn.InflateRect(1, 1);
		
		// for now
		CThemed::DrawFrameControl(this, pDC, rBtn, DFC_BUTTON, nFlags, rClip);
	}
	else // unthemed combo style
	{
		if (bEnabled && bDown)
			pDC->DrawEdge(rBtn, BDR_RAISEDOUTER, BF_ADJUST | BF_RECT | BF_MIDDLE | BF_FLAT);
		else
		{
			pDC->DrawEdge(rBtn, BDR_RAISEDOUTER, BF_ADJUST | BF_RECT | BF_MIDDLE);
			pDC->DrawEdge(rBtn, BDR_RAISEDINNER, BF_ADJUST | BF_RECT | BF_MIDDLE);
		}
	}

	// drop menu arrow
	if (eb.bDropMenu)
	{
		CRect rArrow(rBtn);
		
		if (bDown)
			rArrow.OffsetRect(1, 1);

		// draw menu arrow
		int nMidY = rArrow.CenterPoint().y;
		const int MENUSIZE = 4;

		// if the button has text then place on the RHS
		// else place centrally
		int nLHS = eb.sCaption.IsEmpty() ? rArrow.CenterPoint().x : rArrow.right - 8;

		POINT ptMenu[3] = 
		{
			{ nLHS, nMidY - MENUSIZE },
			{ nLHS, nMidY + MENUSIZE },
			{ nLHS + MENUSIZE, nMidY }
		};

		pDC->SelectStockObject(NULL_PEN);
		pDC->SelectStockObject(bEnabled ? BLACK_BRUSH : GRAY_BRUSH);
		pDC->Polygon(ptMenu, 3);
	}
	
	CString sCaption(eb.sCaption);

	if (!sCaption.IsEmpty())
	{
		// draw custom caption
		if (m_nButtonDown == nBtn || eb.bChecked)
			rBtn.OffsetRect(1, 1);

		CFont* pOld = NULL;
		
		if (eb.hFont)
			pOld = pDC->SelectObject(CFont::FromHandle(eb.hFont));
		else
		{
			CFont* pFont = GetFont();
			pOld = (CFont*)pDC->SelectObject(pFont);
		}
		
		pDC->SetTextAlign(TA_CENTER | TA_TOP);
		pDC->SetBkMode(TRANSPARENT);
		
		BOOL bEnabled = IsWindowEnabled() && eb.bEnabled;

		int CHAR_HEIGHT = pDC->GetTextExtent("A").cy;

		int nVOffset = (rBtn.Height() - CHAR_HEIGHT + 1) / 2 - 1;
		int nHOffset = (rBtn.Width() + 1) / 2;

		CPoint ptText(rBtn.left + nHOffset, rBtn.top + nVOffset);

		if (bEnabled)
		{
			pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
			pDC->ExtTextOut(ptText.x, ptText.y, 0, rBtn, sCaption, NULL);
		}
		else
		{
			// draw embossed: dark over pale
			ptText.Offset(1, 1);
			pDC->SetTextColor(GetSysColor(COLOR_3DHIGHLIGHT));
			pDC->ExtTextOut(ptText.x, ptText.y, 0, rBtn, sCaption, NULL);

			ptText.Offset(-1, -1);
			pDC->SetTextColor(GetSysColor(COLOR_3DSHADOW));
			pDC->ExtTextOut(ptText.x, ptText.y, 0, rBtn, sCaption, NULL);
		}

		// cleanup
		if (pOld)
			pDC->SelectObject(pOld);
	}
}

UINT CEnEdit::OnNcHitTest(CPoint point) 
{
	int nBtn = ButtonHitTest(point);

	if (nBtn >= 0)
		return HTBORDER;

	return CMaskEdit::OnNcHitTest(point);
}

LRESULT CEnEdit::OnHotChange(WPARAM wp, LPARAM lp)
{
	LRESULT lr = Default();

	// wp has prev hot rect index
	// lp has new hot rect index
	ASSERT (((int)wp != -1 || (int)lp != -1) && (int)wp != (int)lp);

	CWindowDC dc(this);
	CPoint ptCursor(::GetMessagePos());

	CRect rWindow;
	GetWindowRect(rWindow);
//	int nBtn = m_aButtons.GetSize();

	if ((int)wp != -1 && m_aButtons[wp].bEnabled)
		DrawButton(&dc, rWindow, wp, ptCursor);

	if ((int)lp != -1 && m_aButtons[lp].bEnabled)
		DrawButton(&dc, rWindow, lp, ptCursor);

	return lr;
}

BOOL CEnEdit::SetButtonWidth(UINT nID, int nWidth)
{
	int nBtn = ButtonHitTest(nID);

	if (nBtn < 0)
		return FALSE;

	if (m_aButtons[nBtn].nWidth != nWidth)
	{
		m_aButtons[nBtn].nWidth = nWidth;

		if (GetSafeHwnd())
		{
			RecalcBtnRects();
			SendMessage(WM_NCPAINT);
		}
	}

	return TRUE;
}

int CEnEdit::GetButtonWidth(int nBtn) const
{
	if (nBtn < 0 || nBtn >= m_aButtons.GetSize() || !GetSafeHwnd())
		return 0;

	const EDITBTN& eb = m_aButtons[nBtn];

	if (eb.nWidth > 0)
		return eb.nWidth;
	
	// else calc
	CWindowDC dc(GetParent());
	CFont* pFont = CFont::FromHandle(eb.hFont ? eb.hFont : (HFONT)GetStockObject(DEFAULT_GUI_FONT));
	CFont* pOldFont = dc.SelectObject(pFont);

	int nWidth = dc.GetTextExtent(eb.sCaption).cx + 6;

	// cleanup
	dc.SelectObject(pOldFont);

	return nWidth;
}

BOOL CEnEdit::SetButtonWidthDLU(UINT nID, int nDLU)
{
	ASSERT (GetSafeHwnd());

	return SetButtonWidth(nID, CDlgUnits(*GetTopLevelParent()).ToPixelsX(nDLU));
}

LRESULT CEnEdit::OnSetReadOnly(WPARAM wp, LPARAM /*lp*/)
{
	LRESULT lr = Default();

	OnSetReadOnly((BOOL)wp);
	SendMessage(WM_NCPAINT);

	return lr;
}

void CEnEdit::OnEnable(BOOL bEnable) 
{
	CEdit::OnEnable(bEnable);
	
	SendMessage(WM_NCPAINT);	
}



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
Software Developer Maptek
Australia Australia
.dan.g. is a naturalised Australian and has been developing commercial windows software since 1998.

Comments and Discussions