Click here to Skip to main content
15,891,687 members
Articles / Desktop Programming / MFC

EasyFtp 1.3.2 (for Applications)

Rate me:
Please Sign up or sign in to vote.
4.91/5 (28 votes)
21 Mar 2004CC (ASA 2.5)7 min read 150.1K   3.1K   119  
A 'drop-in' FTP solution for applications providing a full GUI, extended commandline options and no resource files. Use standalone or compiled into your own app.
// 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)
{
}

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, BOOL bSymbolFont)
{
	return InsertButton(m_aButtons.GetSize(), nID, szCaption, szTip, nWidth, szFont, bSymbolFont);
}

BOOL CEnEdit::InsertButton(int nPos, UINT nID, LPCTSTR szCaption, LPCTSTR szTip, int nWidth, LPCTSTR szFont, BOOL bSymbolFont)
{
	if (nWidth <= 0 || !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;

	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::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);

	CMaskEdit::PreSubclassWindow();
}

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

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

		while (nBtn--)
			lpncsp->rgrc[0].right -= m_aButtons[nBtn].nWidth;
	}
	
	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 += m_aButtons[nIndex].nWidth;

	CRect rButton;
	GetClientRect(rButton);

	rButton.left = rButton.right + nOffset;
	rButton.right += nOffset + m_aButtons[nBtn].nWidth;

	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::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);

	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 (m_nButtonDown == nBtn || eb.bChecked)
			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 && (m_nButtonDown == nBtn || eb.bChecked))
			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);
		}
	}
	
	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
			pDC->SelectStockObject(DEFAULT_GUI_FONT);

		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 (wp != -1 && lp != -1)
		int a = 5;

	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;
}

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

	return SetButtonWidth(nID, CDlgUnits(*GetParent()).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, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License


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