Click here to Skip to main content
15,879,613 members
Articles / Desktop Programming / MFC

DirectUI Window as in XP system folders

Rate me:
Please Sign up or sign in to vote.
4.45/5 (43 votes)
1 Aug 2004CPOL6 min read 262.2K   7.6K   170  
A control to show a list of possible tasks just as in XP.
// WndJobs.cpp: Implementierungsdatei
//

#include "stdafx.h"
#include "todowin.h"
#include "WndJobs.h"
#include "memdc.h"

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

/////////////////////////////////////////////////////////////////////////////
// CJobGroup

CJobGroup::CJobGroup(CString strName, CImageList* pList)
{
	m_strName    = strName;
	m_nHeight    = 0;
	m_pImageList = pList;
	CalcHeight();
}

CJobGroup::~CJobGroup()
{
	RemoveAllItems();
}

COLORREF CJobGroup::MakeXPColor(COLORREF cl, double factor)
{
	if(factor>0.0&&factor<=1.0){
		BYTE red,green,blue,lightred,lightgreen,lightblue;
		red = GetRValue(cl);
		green = GetGValue(cl);
		blue = GetBValue(cl);
		lightred = (BYTE)((factor*(255-red)) + red);
		lightgreen = (BYTE)((factor*(255-green)) + green);
		lightblue = (BYTE)((factor*(255-blue)) + blue);
		cl = RGB(lightred,lightgreen,lightblue);
	}
	return(cl);
}

void CJobGroup::OnDraw(CDC* pDC, CRect rcItem)
{
	const int nHeaderRadius = 5;

	CPen pnBorder;
	CBrush brHeader;
	CBrush brItemArea;
	COLORREF clBorder   = GetSysColor(COLOR_HIGHLIGHT);
	COLORREF clText     = GetSysColor(COLOR_HIGHLIGHTTEXT);
	COLORREF clItemArea = MakeXPColor(clBorder, 0.85); // Seems to be ok
	pnBorder.CreatePen(PS_SOLID, 1, clBorder);
	brHeader.CreateSolidBrush(clBorder);
	brItemArea.CreateSolidBrush(clItemArea);

	// Draw Header
	LOGFONT lf;
	CFont::FromHandle((HFONT) GetStockObject(ANSI_VAR_FONT))->GetLogFont(&lf);
	CFont fnHeader;
	lf.lfWeight = FW_BOLD;
	fnHeader.CreateFontIndirect(&lf);
	pDC->SelectObject(&fnHeader);

	// Calculate all area sizes
	CRect rcHeader;
	CRect rcPieLeft;
	CRect rcPieRight;
	CRect rcHeaderTextArea;
	rcHeader.SetRect(rcItem.left, rcItem.top, rcItem.right, rcItem.top + GetSystemMetrics(SM_CYCAPTION)); // Whole top area
	rcPieLeft.SetRect(rcItem.left, rcItem.top, rcItem.left+nHeaderRadius+nHeaderRadius, rcItem.top+nHeaderRadius+nHeaderRadius); // Upper-left edge
	rcPieRight.SetRect(rcItem.right-nHeaderRadius-nHeaderRadius, rcItem.top, rcItem.right, rcItem.top+nHeaderRadius+nHeaderRadius);// Upper-right edge
	rcHeaderTextArea.SetRect(rcHeader.left+nHeaderRadius, rcHeader.top, rcHeader.right-nHeaderRadius, rcHeader.bottom); // area between
	
	// Draw the "title bar"
	pDC->SelectObject(&pnBorder);
	pDC->SelectObject(&brHeader);
	pDC->Pie(rcPieLeft,  CPoint(rcPieLeft.left+nHeaderRadius, rcPieLeft.top), CPoint(rcPieLeft.left, rcPieLeft.top+nHeaderRadius));
	pDC->Pie(rcPieRight, CPoint(rcPieRight.left+nHeaderRadius, rcPieRight.top+nHeaderRadius), CPoint(rcPieRight.left+nHeaderRadius, rcPieRight.top));
	pDC->FillSolidRect(rcHeaderTextArea, clBorder);
	pDC->FillSolidRect(CRect(rcHeader.left, rcHeader.top+nHeaderRadius, rcHeader.left+nHeaderRadius, rcHeader.bottom), clBorder);
	pDC->FillSolidRect(CRect(rcHeader.right-nHeaderRadius, rcHeader.top+nHeaderRadius, rcHeader.right, rcHeader.bottom), clBorder);
	pDC->SetTextColor(clText);
	pDC->SetBkColor(clBorder);

	// Make the text field a little smaller
	rcHeaderTextArea.left += nHeaderRadius+nHeaderRadius;
	pDC->DrawText(m_strName, rcHeaderTextArea, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS);

	// Now draw the lighter area for all items
	pDC->SelectObject(&brItemArea);
	pDC->Rectangle(rcItem.left, rcHeaderTextArea.bottom, rcItem.right, rcItem.bottom);

	// Draw Items
	CRect rcItems;
	rcItems.SetRect(rcItem.left+1, rcHeaderTextArea.bottom, rcItem.right-1, rcHeaderTextArea.bottom);
	m_pImageList->SetBkColor(clItemArea);
	for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
	{
		rcItems.top     = rcItems.bottom;
		rcItems.bottom += MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8); // add 12,5% space between the items
		m_lstItems.GetNext(pos)->OnDraw(pDC, rcItems, m_pImageList);
	}
}

int CJobGroup::AddItem(CJobItem *pItem)
{
	m_lstItems.AddTail(pItem);
	CalcHeight();
	return m_lstItems.GetCount()-1;
}

void CJobGroup::RemoveAllItems()
{
	while (m_lstItems.GetCount()) delete m_lstItems.RemoveHead();
	CalcHeight();
}

void CJobGroup::CalcHeight()
{
	m_nHeight = GetSystemMetrics(SM_CYCAPTION);
	m_nHeight += (m_lstItems.GetCount()*MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8)); // 12.5% Space
}

CJobItem* CJobGroup::HitTest(CPoint point)
{
	for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
	{
		CJobItem* pItem = m_lstItems.GetNext(pos);
		if (pItem->HitTest(point)) return pItem;
	}

	return NULL;
}

void CJobGroup::ParseToolbar(CToolBar *pToolbar)
{
	for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
	{
		CJobItem* pItem = m_lstItems.GetNext(pos);
		pItem->ParseToolbar(pToolbar);
	}
}

/////////////////////////////////////////////////////////////////////////////
// CJobItem

CJobItem::CJobItem(CString strName, UINT uiCommand, int nIconIndex)
{
	m_rcItem.SetRectEmpty();
	m_strName    = strName;
	m_nIconIndex = nIconIndex;
	m_uiCommand  = uiCommand;
	m_uiItemState= 0;
}

void CJobItem::OnDraw(CDC* pDC, CRect rcItem, CImageList* pImageList)
{
	const nBorderX     = 10;
	const nBorderIconX = 4;
	const nIconSize    = 16;

	rcItem.left  += nBorderX;
	rcItem.right -= nBorderX;

	// When there's an icon place the text more to the right
	if (m_nIconIndex != -1)
		rcItem.left += nIconSize+nBorderIconX;
	
	m_rcItem = rcItem;

	// Item selected? Then draw it brighter
	if (m_uiItemState & ODS_SELECTED)
		pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHT));
	else
		pDC->SetTextColor(CJobGroup::MakeXPColor(GetSysColor(COLOR_HIGHLIGHT), 0.15));

	pDC->SelectStockObject(ANSI_VAR_FONT);
	pDC->SetBkMode(TRANSPARENT);
	pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS);
	pDC->DrawText(m_strName, m_rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS|DT_CALCRECT);

	// Item selected? Then underline it (doing it this way I don't need to create an underlined font)
	if (m_uiItemState & ODS_SELECTED)
	{
		pDC->MoveTo(m_rcItem.left, m_rcItem.bottom);
		pDC->LineTo(m_rcItem.right, m_rcItem.bottom);
	}

	if (m_nIconIndex != -1)
	{
		// Finally draw the icon
		m_rcItem.left -= (nBorderIconX+nIconSize);
		if (m_uiItemState & ODS_SELECTED)
			pImageList->Draw(pDC, m_nIconIndex, CPoint(m_rcItem.left, rcItem.top+((rcItem.Height()-nIconSize)/2)), ILD_SELECTED);
		else
			pImageList->Draw(pDC, m_nIconIndex, CPoint(m_rcItem.left, rcItem.top+((rcItem.Height()-nIconSize)/2)), ILD_NORMAL);
	}
	
}

BOOL CJobItem::HitTest(CPoint point)
{
	return (PtInRect(m_rcItem, point)) ? TRUE:FALSE;
}

void CJobItem::ParseToolbar(CToolBar *pToolbar)
{
	ASSERT(pToolbar);
	int nBitmap = pToolbar->SendMessage(TB_GETBITMAP, m_uiCommand, 0);
	if (nBitmap > 0) 
		m_nIconIndex = nBitmap; 
	else 
		m_nIconIndex = -1;
}


/////////////////////////////////////////////////////////////////////////////
// CWndJobs

BEGIN_MESSAGE_MAP(CWndJobs, CWnd)
	//{{AFX_MSG_MAP(CWndJobs)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Behandlungsroutinen f�r Nachrichten CWndJobs 

CWndJobs::CWndJobs() 
{
	m_pLastHit = NULL;
	m_hHand    = AfxGetApp()->LoadCursor(IDC_HAND);
}

CWndJobs::~CWndJobs() 
{
	RemoveAll();
}

void CWndJobs::OnPaint() 
{
	const int nSpaceX = 10;
	const int nSpaceY = 10;

	CPaintDC dc(this); // device context for painting

	CRect rc;
	CRect rcItem;
	GetWindowRect(&rc);
	rc -= rc.TopLeft();

	CMemDC MemDC(&dc, &rc);

	MemDC.FillSolidRect(rc, CJobGroup::MakeXPColor(GetSysColor(COLOR_HIGHLIGHT), 0.4));
	rcItem.SetRect(nSpaceX, 0, rc.right - nSpaceX, 0);
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
	{
		CJobGroup* pGroup = m_lstGroups.GetNext(pos);
		rcItem.top = rcItem.bottom + nSpaceY;
		rcItem.bottom = rcItem.top + pGroup->m_nHeight;
		pGroup->OnDraw(&MemDC, rcItem);
	}
}

BOOL CWndJobs::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;
}

void CWndJobs::RemoveAll()
{
	while (m_lstGroups.GetCount()) delete m_lstGroups.RemoveHead();
}

int CWndJobs::AddGroup(CString strName)
{
	CJobGroup* pGroup = new CJobGroup(strName, &m_lstImages);
	m_lstGroups.AddTail(pGroup);

	InvalidateIfPossible();
	
	return m_lstGroups.GetCount()-1;
}

BOOL CWndJobs::RemoveGroup(int nGroup)
{
	CJobGroup* pGroup = GetGroupByNumber(nGroup);
	if (!pGroup) return FALSE;

	POSITION pos = m_lstGroups.Find(pGroup);
	ASSERT(pos);
	if (!pos) return FALSE;

	m_lstGroups.RemoveAt(pos);
	delete pGroup;

	InvalidateIfPossible();
	return TRUE;
}

CString& CWndJobs::GetGroupName(int nGroup)
{
	static CString strNull;
	CJobGroup* pGroup = GetGroupByNumber(nGroup);
	if (!pGroup) return strNull;

	return pGroup->m_strName;
}

BOOL CWndJobs::ChangeGroupName(int nGroup, CString strName)
{
	CJobGroup* pGroup = GetGroupByNumber(nGroup);
	if (!pGroup) return FALSE;

	pGroup->m_strName = strName;
	InvalidateIfPossible();

	return TRUE;
}

BOOL CWndJobs::ExpandGroup(int nGroup, BOOL bExpand)
{
	// TODO
	ASSERT(FALSE);
	return FALSE;
}

BOOL CWndJobs::InitFromMenu(UINT id)
{
	// Main funtion! Use a 2D Menu
	RemoveAll();
	CMenu mnuJobs;
	MENUITEMINFO info;

	if (!mnuJobs.LoadMenu(id)) return FALSE;
	
	int i = 0;
	int j = 0;
	while (mnuJobs.GetSubMenu(i))
	{
		CMenu* pMenu = mnuJobs.GetSubMenu(i);
		CString strName;
		mnuJobs.GetMenuString(i, strName, MF_BYPOSITION);
		CJobGroup* pGroup = new CJobGroup(strName, &m_lstImages);

		info.cbSize = sizeof (MENUITEMINFO);
		info.fMask = MIIM_ID;

		j = 0;
		while (pMenu->GetMenuString(j, strName, MF_BYPOSITION))
		{
			pMenu->GetMenuItemInfo(j, &info, TRUE);
			pGroup->AddItem(new CJobItem(strName, info.wID));
			j++;
		}

		m_lstGroups.AddTail(pGroup);
		i++;
	}

	InvalidateIfPossible();
	return TRUE;
}

BOOL CWndJobs::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	if (m_pLastHit != NULL)
	{
		SetCursor(m_hHand);
		return TRUE;
	}
	else
		return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

void CWndJobs::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// In future: draw focus rect...
	CWnd::OnLButtonDown(nFlags, point);
}

void CWndJobs::OnMouseMove(UINT nFlags, CPoint point) 
{
	// Do the hoover thing
	CJobItem*  pItem;
	CJobGroup* pGroup;
	pItem = NULL;

	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
	{
		pGroup = m_lstGroups.GetNext(pos);
		pItem   = pGroup->HitTest(point);
		if (pItem != NULL) break;
	}

	if (pItem != m_pLastHit)	
	{
		// Draw Item
		if (m_pLastHit) m_pLastHit->m_uiItemState &= ~ODS_SELECTED;
		m_pLastHit = pItem;
		if (m_pLastHit) m_pLastHit->m_uiItemState |= ODS_SELECTED;
		Invalidate();
	}

	CWnd::OnMouseMove(nFlags, point);
}

void CWndJobs::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (m_pLastHit)
		AfxGetMainWnd()->PostMessage(WM_COMMAND, m_pLastHit->m_uiCommand, 0);
	
	CWnd::OnLButtonUp(nFlags, point);
}

CJobGroup* CWndJobs::GetGroupByNumber(int nPos)
{
	if (nPos < 0) return NULL;

	CJobGroup* pGroup;
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
	{
		pGroup = m_lstGroups.GetNext(pos);
		if (nPos == 0) return pGroup;
		nPos--;
	}
	return NULL;
}

void CWndJobs::InvalidateIfPossible()
{
	if (IsWindow(m_hWnd)) Invalidate();
}

BOOL CWndJobs::SetToolbarImages(UINT uiToolbar)
{
	// Use this function after inserting all items and groups

	// Create an invisible toolbar and let it do all the work
	CToolBar tbToolbar;
	if (!tbToolbar.Create(this, WS_CHILD, 42)) return FALSE;
	if (!tbToolbar.LoadToolBar(uiToolbar)) return FALSE;

	m_lstImages.Detach();
	if (!m_lstImages.Create(CImageList::FromHandle((HIMAGELIST)tbToolbar.SendMessage(TB_GETIMAGELIST, 0, 0)))) return FALSE;
	
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
		m_lstGroups.GetNext(pos)->ParseToolbar(&tbToolbar);

	InvalidateIfPossible();
	return TRUE;
}

BOOL CWndJobs::AddItem(int nGroup, CString strItem, UINT uiCommand)
{
	CJobGroup* pJob = GetGroupByNumber(nGroup);
	if (!pJob) return FALSE;
	pJob->AddItem(new CJobItem(strItem, uiCommand));
	InvalidateIfPossible();
	return TRUE;
}

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 Code Project Open License (CPOL)


Written By
Web Developer
Germany Germany
Ok, a few words about me:

I started programming on the C64 by the "trial and error" method. Years later my parents got their first PC (Atari PC4, AT 8 MHz) where I started with Turbo Pascal. The next steps led to Turbo Pascal for Windows, Visual C++ 1.52c, and finally Visual C++ 6.

I had several chances to code larger projects, e.g.
* stand-alone disc copy station software (Win 3.1) with automatic reboot, disc encryption, ...
* government-used strategic decision system
* MLM marketing tool (www.upline.de)
* maintenance for show planning system and other TV software (www.hse24.de)

A lot of code, tipps and help came from this site for my later projects. Thanks again to all who helped me, even if they don't know it. I'm trying to share code (which is worth sharing) so others can get the help I got (and still need) for everyday problems.

Well, I think that's enough.

Comments and Discussions