Click here to Skip to main content
15,885,216 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 263K   7.6K   170  
A control to show a list of possible tasks just as in XP.
// WndDirectUI.cpp: Implementierungsdatei
//

#include "stdafx.h"
#include "todowin.h"

#ifdef USE_THEMES
#include "VisualStylesXP.h"
#endif
#include "WndDirectUI.h"
#include "memdc.h"
#include "Inplaceedit2.h"

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

/////////////////////////////////////////////////////////////////////////////
// CDirectUIGroup

/////////////////////////////////////////////////////////////////////////////
// 
// Credits...
// 
// Zebrex          : On fast MouseMove underlined item stays underlined; TODO
// John A. Johnson : DT_NOPREFIX was deleted in all the code
// Andreas Kapust  : ::OnMousemove bugfix was added to handle the scrollbar correctly
// Zebrex          : HitTest failed on collapsed group
// Ewan Ward       : How to convert the CString to TCHAR for DrawThemeText()
// 


#define ADVANCED_UI_CLASSNAME _T("WndDirectUI")

#ifdef USE_THEMES
CFont CDirectUIItem::m_fntThemedText;
#endif

HCURSOR CDirectUIItem::m_hCursorHand = 0;
HCURSOR CDirectUIItemStatic::m_hCursorArrow = 0;
HCURSOR CDirectUIItemEdit::m_hCursorBeam = 0;

CDirectUIGroup::CDirectUIGroup(CString strName, CImageList* pList, int* pnStyle)
{
	m_strName    = strName;
	m_nHeight    = 0;
	m_pImageList = pList;
	m_pnStyle    = pnStyle;
	m_uiItemState= 0;
	m_rcExpandCollapseBtn.SetRectEmpty();
	m_bIsCollapsed = FALSE;
	m_bCalcHeight  = TRUE;

//	CalcHeight(NULL);
}

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

COLORREF CDirectUIGroup::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);
}

BOOL CDirectUIGroup::EnableItem(UINT uiCommand, BOOL bEnable)
{
	BOOL bRepaint;
	bRepaint = FALSE;

	for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
	{
		CDirectUIItem* pItem = m_lstItems.GetNext(pos);
		if (pItem->m_uiCommand == uiCommand)
			bRepaint |= pItem->EnableItem(bEnable);
	}

	return m_bIsCollapsed ? FALSE : bRepaint;
}

void CDirectUIGroup::OnDraw(CDC* pDC, CRect rcItem, HTHEME hTheme, int nOffsetY, CWnd* pParent)
{
	if (m_bCalcHeight)
	{
		InternalCalcHeight(pDC, hTheme);
	}

	const int nHeaderRadius = 5;

	switch (*m_pnStyle)
	{
	case CWndDirectUI::styleOffice:
		{
			CPen*  pnOld;
			CFont* fnOld;
			// Draw Header
			LOGFONT lf;
			CFont::FromHandle((HFONT) GetStockObject(ANSI_VAR_FONT))->GetLogFont(&lf);
			CFont fnHeader;
			lf.lfWeight = FW_BOLD;
			fnHeader.CreateFontIndirect(&lf);
			fnOld = pDC->SelectObject(&fnHeader);

			CRect rcHeader;
			rcHeader.SetRect(rcItem.left, rcItem.top, rcItem.right, rcItem.top + GetSystemMetrics(SM_CYCAPTION)); // Whole top area
			pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
			pDC->SetBkColor(GetSysColor(COLOR_WINDOW));
			pDC->DrawText(m_strName, rcHeader, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
			
			CPen pnSeparator;
			pnSeparator.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DFACE));
			pnOld = pDC->SelectObject(&pnSeparator);
			pDC->MoveTo(rcHeader.left, rcHeader.bottom);
			pDC->LineTo(rcHeader.BottomRight());

			// Draw Items
			CRect rcItems;
			rcItems.SetRect(rcItem.left+1, rcHeader.bottom, rcItem.right-1, rcHeader.bottom);
			if (m_pImageList) m_pImageList->SetBkColor(GetSysColor(COLOR_WINDOW));
			pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHT));
			CPen pnUnderline;
			pnUnderline.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_HIGHLIGHT));
			pDC->SelectObject(&pnUnderline);

			// Draw Items
			if (!m_bIsCollapsed)
			{
				for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
				{
					rcItems.top     = rcItems.bottom;
					rcItems.bottom  = rcItems.top + m_lstItems.GetAt(pos)->GetItemHeight(pDC, *m_pnStyle, NULL);
					m_lstItems.GetNext(pos)->OnDraw(pDC, rcItems, m_pImageList, *m_pnStyle, NULL, nOffsetY, pParent);
				}
			}
			pDC->SelectObject(fnOld);
			pDC->SelectObject(pnOld);
		}
		break;
	case CWndDirectUI::styleXPclassic:
		{
			CPen*   pnOld;
			CFont*  fnOld;
			CBrush* brOld;

			CPen   pnBorder;
			CPen   pnMarker;
			CBrush brHeader;
			CBrush brItemArea;
			COLORREF clBorder   = GetSysColor(COLOR_3DFACE);
			COLORREF clText     = GetSysColor(COLOR_BTNTEXT);
			COLORREF clItemArea = GetSysColor(COLOR_WINDOW);
			pnBorder.CreatePen(PS_SOLID, 1, clBorder);
			pnMarker.CreatePen(PS_SOLID, 1, clText);
			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);
			fnOld= pDC->SelectObject(&fnHeader);

			// Draw Header
			CRect rcHeader;
			CRect rcHeaderTextArea;
			rcHeader.SetRect(rcItem.left, rcItem.top, rcItem.right, rcItem.top + GetSystemMetrics(SM_CYCAPTION)); // Whole top area
			pDC->SetTextColor(clText);
			pDC->SetBkColor(clBorder);
			rcHeaderTextArea.SetRect(rcHeader.left+nHeaderRadius, rcHeader.top, rcHeader.right-nHeaderRadius, rcHeader.bottom); // area between
			brOld = pDC->SelectObject(&brHeader);
			pDC->FillSolidRect(rcHeader, clBorder);
			pDC->DrawText(m_strName, rcHeaderTextArea, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);

			// Draw the Expand/Collapse button
			const int nBtnSpace = 2;
			int nBtnSize;
			nBtnSize = GetSystemMetrics(SM_CYCAPTION)-nBtnSpace-nBtnSpace;
			m_rcExpandCollapseBtn.SetRect(rcItem.right-nHeaderRadius-nBtnSize, rcItem.top +nBtnSpace, rcItem.right-nHeaderRadius, rcItem.top+nBtnSpace+nBtnSize);
			if (m_uiItemState & ODS_SELECTED) pDC->Draw3dRect(m_rcExpandCollapseBtn, GetSysColor(COLOR_3DHILIGHT), GetSysColor(COLOR_3DSHADOW));
			pnOld = pDC->SelectObject(&pnMarker);
			CPoint ptCenter = m_rcExpandCollapseBtn.CenterPoint();
			CPoint ptLeft   = ptCenter +CPoint(-4, m_bIsCollapsed ? -4:4);
			CPoint ptRight  = ptCenter +CPoint( 4, m_bIsCollapsed ? -4:4);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptLeft);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptRight);
			ptCenter.Offset(0, m_bIsCollapsed ?3:-3);
			ptLeft.Offset(0, m_bIsCollapsed ?3:-3);
			ptRight.Offset(0, m_bIsCollapsed ?3:-3);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptLeft);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptRight);
			m_rcExpandCollapseBtn.left = rcHeader.left;

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

			// Draw Items
			pDC->SelectObject(&pnMarker);
			if (!m_bIsCollapsed)
			{
				CRect rcItems;
				rcItems.SetRect(rcItem.left+1, rcHeaderTextArea.bottom, rcItem.right-1, rcHeaderTextArea.bottom);
				if (m_pImageList) m_pImageList->SetBkColor(clItemArea);
				for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
				{
					rcItems.top     = rcItems.bottom;
					rcItems.bottom  = rcItems.top + m_lstItems.GetAt(pos)->GetItemHeight(pDC, *m_pnStyle, NULL);
					m_lstItems.GetNext(pos)->OnDraw(pDC, rcItems, m_pImageList, *m_pnStyle, NULL, nOffsetY, pParent);
				}
			}
			pDC->SelectObject(fnOld);
			pDC->SelectObject(pnOld);
			pDC->SelectObject(brOld);
		}
		break;
#ifdef USE_THEMES
	case CWndDirectUI::styleThemed:
		if (g_xpStyle.IsAppThemed() & (hTheme != 0))
		{
			CRect rcHeader, rcHeaderTextArea;
			rcHeader.SetRect(rcItem.left, rcItem.top, rcItem.right, rcItem.top + GetSystemMetrics(SM_CYCAPTION)); // Whole top area
			if ((*m_pnStyle == CWndDirectUI::styleThemed) & g_xpStyle.IsAppThemed()) 
				rcHeader.bottom = rcHeader.top + MulDiv(rcHeader.Height(), 9, 8);

			g_xpStyle.DrawThemeBackground(hTheme, pDC->m_hDC, EBP_NORMALGROUPHEAD, 0, &rcHeader, 0);
			g_xpStyle.GetThemeBackgroundContentRect(hTheme, pDC->m_hDC, EBP_NORMALGROUPHEAD, 0, rcHeader,  &rcHeaderTextArea);

			int nTextLen = strlen(m_strName); 
			int mlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, NULL, 0); 
			WCHAR* output = new WCHAR[mlen];
			if(output)
			{
				MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, output, mlen);
				g_xpStyle.DrawThemeText(hTheme, pDC->m_hDC, EBP_NORMALGROUPHEAD, 0, output, -1, DT_LEFT|DT_VCENTER | DT_SINGLELINE|DT_END_ELLIPSIS, 0, &rcHeaderTextArea);
				delete output;
			}

			const int nBtnSpace = 2;
			int nBtnSize;
			nBtnSize = GetSystemMetrics(SM_CYCAPTION)-nBtnSpace-nBtnSpace;
			m_rcExpandCollapseBtn.SetRect(rcItem.right-nHeaderRadius-nBtnSize, rcItem.top +nBtnSpace, rcItem.right-nHeaderRadius, rcItem.top+nBtnSpace+nBtnSize);
			g_xpStyle.DrawThemeBackground(hTheme, pDC->m_hDC, m_bIsCollapsed ? EBP_NORMALGROUPEXPAND:EBP_NORMALGROUPCOLLAPSE, (m_uiItemState & ODS_SELECTED) ? EBNGC_HOT: EBNGC_NORMAL, &m_rcExpandCollapseBtn, 0);
			m_rcExpandCollapseBtn.left = rcHeader.left;
			if (!m_bIsCollapsed)
				g_xpStyle.DrawThemeBackground(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0, CRect(rcItem.left, rcHeader.bottom-1, rcItem.right, rcItem.bottom), 0);

			CPen*   pnOld;
			CPen   pnUnderline;
			pnUnderline.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_HIGHLIGHTTEXT));
			pnOld = pDC->SelectObject(&pnUnderline);

			if (!m_bIsCollapsed)
			{
				CRect rcItems;
				g_xpStyle.GetThemeBackgroundContentRect(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0, rcHeader,  &rcItems);
				rcItems.top = rcHeader.bottom;
				rcItems.bottom = rcHeader.bottom;
				for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
				{
					rcItems.top     = rcItems.bottom;
					rcItems.bottom  = rcItems.top + m_lstItems.GetAt(pos)->GetItemHeight(pDC, *m_pnStyle, NULL);
					m_lstItems.GetNext(pos)->OnDraw(pDC, rcItems, m_pImageList, *m_pnStyle, hTheme, nOffsetY, pParent);
				}
			}
			pDC->SelectObject(pnOld);

			break;
		}
#endif
	case CWndDirectUI::styleXP:
	default:
		{
			CPen*   pnOld;
			CFont*  fnOld;
			CBrush* brOld;

			CPen   pnMarker;
			CPen   pnBorder;
			CPen   pnBtn;
			CBrush brHeader;
			CBrush brItemArea;
			COLORREF clBorder   = GetSysColor(COLOR_HIGHLIGHT);
			COLORREF clText     = GetSysColor(COLOR_HIGHLIGHTTEXT);
			COLORREF clItemArea = MakeXPColor(clBorder, 0.85); // Seems to be ok
			pnMarker.CreatePen(PS_SOLID, 1, clText);
			pnBtn   .CreatePen(PS_SOLID, 1, clItemArea);
			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);
			fnOld = 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"
			pnOld = pDC->SelectObject(&pnBorder);
			brOld = 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);
			if (m_uiItemState & ODS_SELECTED) 
				pDC->SetTextColor(clItemArea);
			else
				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_END_ELLIPSIS);

			// Draw the Expand/Collapse button
			const int nBtnSpace = 4;
			int nBtnSize;
			nBtnSize = GetSystemMetrics(SM_CYCAPTION)-nBtnSpace-nBtnSpace;
			m_rcExpandCollapseBtn.SetRect(rcItem.right-nHeaderRadius-nBtnSize, rcItem.top +nBtnSpace, rcItem.right-nHeaderRadius, rcItem.top+nBtnSpace+nBtnSize);
			pDC->SelectStockObject(NULL_BRUSH);
			pDC->SelectObject(&pnBtn);
			pDC->Ellipse(m_rcExpandCollapseBtn);
			if (m_uiItemState & ODS_SELECTED) 
				pDC->SelectObject(&pnBtn);
			else
				pDC->SelectObject(&pnMarker);
			CPoint ptCenter = m_rcExpandCollapseBtn.CenterPoint();
			CPoint ptLeft   = ptCenter +CPoint(-4, m_bIsCollapsed ? -4:4);
			CPoint ptRight  = ptCenter +CPoint( 4, m_bIsCollapsed ? -4:4);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptLeft);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptRight);
			ptCenter.Offset(0, m_bIsCollapsed ?3:-3);
			ptLeft.Offset(0, m_bIsCollapsed ?3:-3);
			ptRight.Offset(0, m_bIsCollapsed ?3:-3);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptLeft);
			pDC->MoveTo(ptCenter);
			pDC->LineTo(ptRight);
			m_rcExpandCollapseBtn.left = rcHeader.left;

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

			// Draw Items
			if (!m_bIsCollapsed)
			{
				CRect rcItems;
				rcItems.SetRect(rcItem.left+1, rcHeaderTextArea.bottom, rcItem.right-1, rcHeaderTextArea.bottom);
				if (m_pImageList) m_pImageList->SetBkColor(clItemArea);
				for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
				{
					rcItems.top     = rcItems.bottom;
					rcItems.bottom  = rcItems.top + m_lstItems.GetAt(pos)->GetItemHeight(pDC, *m_pnStyle, NULL);
					m_lstItems.GetNext(pos)->OnDraw(pDC, rcItems, m_pImageList, *m_pnStyle, NULL, nOffsetY, pParent);
				}
			}

			pDC->SelectObject(fnOld);
			pDC->SelectObject(pnOld);
			pDC->SelectObject(brOld);
		}
	}
}

int CDirectUIGroup::AddItem(CDirectUIItem *pItem)
{
	m_lstItems.AddTail(pItem);
	HTHEME hTheme;
	hTheme = 0;
	CalcHeight();
	return m_lstItems.GetCount()-1;
}

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

void CDirectUIGroup::CalcHeight()
{
	m_bCalcHeight = TRUE;
}

BOOL CDirectUIGroup::HitTestButton(CPoint point)
{
	if (*m_pnStyle == CWndDirectUI::styleOffice) return FALSE; // Not supported for Office style
	return (PtInRect(m_rcExpandCollapseBtn, point)) ? TRUE:FALSE;
}

CDirectUIItem* CDirectUIGroup::HitTest(CPoint point)
{
	if (m_bIsCollapsed) return FALSE;

	for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
	{
		CDirectUIItem* pItem = m_lstItems.GetNext(pos);
		if (pItem->HitTest(point)) return pItem;

	}

	return NULL;
}

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

BOOL CDirectUIGroup::Expand(BOOL bExpand)
{
	if (m_bIsCollapsed != bExpand) return FALSE;

	m_bIsCollapsed = !bExpand;
	CalcHeight();
	return TRUE;
}

void CDirectUIGroup::ToggleExpand()
{	
	m_bIsCollapsed = !m_bIsCollapsed;
	CalcHeight();
}

void CDirectUIGroup::InternalCalcHeight(CDC *pDC, HTHEME hTheme)
{
	if (!m_bCalcHeight) return;
	m_nHeight = GetSystemMetrics(SM_CYCAPTION);

#ifdef USE_THEMES
	// Make the title bar a little larger
	if ((*m_pnStyle == CWndDirectUI::styleThemed) & g_xpStyle.IsAppThemed()) 
		m_nHeight = MulDiv(m_nHeight, 9, 8);
#endif

	if (!m_bIsCollapsed)
		for (POSITION pos = m_lstItems.GetHeadPosition(); pos;)
		{
#ifdef USE_THEMES
			if ((*m_pnStyle == CWndDirectUI::styleThemed) & g_xpStyle.IsAppThemed()) 
				m_nHeight += m_lstItems.GetNext(pos)->GetItemHeight(pDC, *m_pnStyle, hTheme);
			else
#endif
				m_nHeight += m_lstItems.GetNext(pos)->GetItemHeight(pDC, *m_pnStyle, NULL);
		}
		m_bCalcHeight = FALSE;
}


/////////////////////////////////////////////////////////////////////////////
// CDirectUIItem

int     CDirectUIItem::GetItemHeight(CDC* pDC, int nStyle, HTHEME hTheme) 
{
	if (m_nHeight == -1)
	{
		m_nHeight = MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8);
	}
	return m_nHeight;
};

CDirectUIItem::CDirectUIItem(CString strName, UINT uiCommand, int nIconIndex)
{
	m_rcItem.SetRectEmpty();
	m_strName    = strName;
	m_nIconIndex = nIconIndex;
	m_uiCommand  = uiCommand;
	m_uiItemState= 0;
	m_nHeight    = -1;
	if (!m_hCursorHand) m_hCursorHand = AfxGetApp()->LoadCursor(IDC_MYHAND);;
}

BOOL CDirectUIItem::EnableItem(BOOL bEnable)
{
	BOOL bIsEnabled;
	bIsEnabled = ! (m_uiItemState & ODS_DISABLED);
	if (bEnable == bIsEnabled) return FALSE;

	if (!bEnable) m_uiItemState |= ODS_DISABLED;
	if (bEnable)  m_uiItemState &= !ODS_DISABLED;
	return TRUE;
}

void CDirectUIItem::OnDraw(CDC* pDC, CRect rcItem, CImageList* pImageList, int nStyle, HTHEME hTheme, int nOffsetY, CWnd* pParent)
{
	const nBorderX     = 10;
	const nBorderIconX = 4;
	const nIconSize    = 16;

	switch (nStyle)
	{
	case CWndDirectUI::styleOffice:
		{
			CGdiObject*  fnOld;

			m_rcItem = rcItem;

			rcItem.left += nIconSize+nBorderIconX;
			fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
			pDC->SetTextColor((m_uiItemState & ODS_DISABLED) ? GetSysColor(COLOR_GRAYTEXT) : CDirectUIGroup::MakeXPColor(GetSysColor(COLOR_HIGHLIGHT), 0.15));
			pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
			pDC->DrawText(m_strName, m_rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS|DT_CALCRECT);
			m_rcItem.OffsetRect(nIconSize+nBorderIconX, 0);

			if ((m_nIconIndex != -1) && (pImageList))
				pImageList->Draw(pDC, m_nIconIndex, CPoint(m_rcItem.left-nIconSize-nBorderIconX, rcItem.top+((rcItem.Height()-nIconSize)/2)), ILD_NORMAL);

			if ((m_uiItemState & ODS_SELECTED) & !(m_uiItemState & ODS_DISABLED))
			{
				pDC->MoveTo(m_rcItem.left, m_rcItem.bottom);
				pDC->LineTo(m_rcItem.right, m_rcItem.bottom);
			}

			pDC->SelectObject(fnOld);
		} 
		break;
#ifdef USE_THEMES
	case CWndDirectUI::styleThemed:
		if (g_xpStyle.IsAppThemed() & (hTheme != 0))
		{
			// When there's an icon place the text more to the right
			if (m_nIconIndex != -1)
				rcItem.left += nIconSize+nBorderIconX;
			
			m_rcItem = rcItem;

			int nTextLen = strlen(m_strName); 
			int mlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, NULL, 0); 
			WCHAR* output = new WCHAR[mlen];
			if(output)
			{
				CRect rc;
				MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, output, mlen);
				if (m_uiItemState & ODS_DISABLED)
				{
					CFont* fntOld = SetThemeFont(pDC, hTheme);
					pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
					pDC->SetBkMode(TRANSPARENT);
					pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
					g_xpStyle.GetThemeTextExtent(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0, output, -1, DT_LEFT|DT_VCENTER | DT_SINGLELINE|DT_END_ELLIPSIS, rcItem, m_rcItem);
					pDC->SelectObject(fntOld);
				}
				else
				{
					g_xpStyle.DrawThemeText(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0 , output, -1, DT_LEFT|DT_VCENTER | DT_SINGLELINE|DT_END_ELLIPSIS, DTT_GRAYED, &rcItem);
					g_xpStyle.GetThemeTextExtent(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0, output, -1, DT_LEFT|DT_VCENTER | DT_SINGLELINE|DT_END_ELLIPSIS, rcItem, m_rcItem);
				}
				delete output;
			}

			// 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) && (pImageList))
			{
				// 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|ILD_TRANSPARENT);
				else
					pImageList->Draw(pDC, m_nIconIndex, CPoint(m_rcItem.left, rcItem.top+((rcItem.Height()-nIconSize)/2)), ILD_NORMAL|ILD_TRANSPARENT);
			}

			break;
		}
#endif
	case CWndDirectUI::styleXP:
	default:
		{
			CGdiObject*  fnOld;

			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(CDirectUIGroup::MakeXPColor(GetSysColor(COLOR_HIGHLIGHT), 0.15));
			if (m_uiItemState & ODS_DISABLED) pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));

			fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
			pDC->SetBkMode(TRANSPARENT);
			pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
			pDC->DrawText(m_strName, m_rcItem, DT_LEFT|DT_SINGLELINE|DT_VCENTER|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) && (pImageList))
			{
				// 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);
			}

			pDC->SelectObject(fnOld);
		}
	}
}

BOOL CDirectUIItem::HitTest(CPoint point)
{
	if (m_uiItemState & ODS_DISABLED) return FALSE;
	return (PtInRect(m_rcItem, point)) ? TRUE:FALSE;
}

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


BOOL CDirectUIItem::MouseItemIn(CPoint point, CPoint ptOffset)
{
	m_uiItemState |= ODS_SELECTED;
	return TRUE;
}

BOOL CDirectUIItem::MouseItemOut(CPoint point, CPoint ptOffset)
{
	m_uiItemState &= ~ODS_SELECTED;
	return TRUE;
}

BOOL CDirectUIItem::MouseItemLDown(CPoint point, CPoint ptOffset)
{
	return FALSE;
}

BOOL CDirectUIItem::MouseItemLUp(CPoint point, CPoint ptOffset)
{
	AfxGetMainWnd()->PostMessage(WM_COMMAND, m_uiCommand, 0);
	return FALSE;
}



/////////////////////////////////////////////////////////////////////////////
// CWndDirectUI

BEGIN_MESSAGE_MAP(CWndDirectUI, CWnd)
	//{{AFX_MSG_MAP(CWndDirectUI)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_WM_VSCROLL()
	//}}AFX_MSG_MAP
#ifdef USE_THEMES
	ON_MESSAGE(WM_THEMECHANGED, OnThemeChanged)
#endif
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Behandlungsroutinen f�r Nachrichten CWndDirectUI 

CWndDirectUI::CWndDirectUI() 
{
	m_pLastHitItem  = NULL;
	m_pLastHitGroup = NULL;
	m_hHand         = AfxGetApp()->LoadCursor(IDC_MYHAND);
	VERIFY(m_hHand);  // If asserts, add this cursor to your resource
	m_nStyle        = styleThemed;
	m_lstImages.Create(1, 1, ILC_COLOR,1, 1);
	RegisterWindowClass();
}

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


int CWndDirectUI::GetOffsetY()
{
	if (::GetWindowLong(m_hWnd, GWL_STYLE) & WS_VSCROLL) 
	{
		return GetScrollPos(SB_VERT);
	}
	else
		return 0;
}

int CWndDirectUI::GetOffsetX()
{
	return 0;
}

void CWndDirectUI::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();

	BOOL bHasScrollbar;
	BOOL bNeedScrollbar;
	int  nOffsetY;

	bHasScrollbar  = ::GetWindowLong(m_hWnd, GWL_STYLE) & WS_VSCROLL ? TRUE:FALSE;
	if (bHasScrollbar) 
	{
		rc.right -= GetSystemMetrics(SM_CXHTHUMB);
		nOffsetY = GetOffsetY();
	}
	else
		nOffsetY = 0;

	rc.bottom +=nOffsetY; 
#if TRUE
	CMemDC MemDC(&dc, &rc);
	MemDC.SaveDC();
#else
	#define MemDC dc
#endif


	dc.SetWindowOrg(0, nOffsetY);

	HTHEME hTheme;
	hTheme = 0;

	switch (m_nStyle)
	{
	case styleXPclassic:
	case styleOffice:
		MemDC.FillSolidRect(rc, GetSysColor(COLOR_WINDOW));
		break;
#ifdef USE_THEMES
	case styleThemed:
		if (g_xpStyle.IsAppThemed())
		{
			hTheme = g_xpStyle.OpenThemeData(GetSafeHwnd(), L"EXPLORERBAR");
			g_xpStyle.DrawThemeBackground(hTheme, MemDC.m_hDC, EBP_HEADERBACKGROUND, 0, &rc, 0);
			break;
		}
#endif
	case styleXP:
	default:
		MemDC.FillSolidRect(rc, CDirectUIGroup::MakeXPColor(GetSysColor(COLOR_HIGHLIGHT), 0.4));
	}

	rcItem.SetRect(nSpaceX, 0, rc.right - nSpaceX, 0);
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
	{
		CDirectUIGroup* pGroup = m_lstGroups.GetNext(pos);
		pGroup->InternalCalcHeight(&MemDC, hTheme);
		rcItem.top    = rcItem.bottom + nSpaceY;
		rcItem.bottom = rcItem.top + pGroup->m_nHeight;
		pGroup->OnDraw(&MemDC, rcItem, hTheme, nOffsetY, this);
	}

#ifdef USE_THEMES
	if (hTheme)
	{
		g_xpStyle.CloseThemeData(hTheme);
	}
#endif

	MemDC.RestoreDC(-1);

	// Show Scrollbar if needed
	bNeedScrollbar = ((rc.Height()-nOffsetY) < (rcItem.bottom+nSpaceY)) ? TRUE:FALSE;
	if (bNeedScrollbar != bHasScrollbar)
	{
		if (bNeedScrollbar) 
			ShowScrollBar(SB_VERT, TRUE);
		else
			ShowScrollBar(SB_VERT, FALSE);
		Invalidate();
	}
	
	if (bNeedScrollbar)
	{
		// Set Scrollbar range
		SCROLLINFO si;
		si.cbSize = sizeof(si);
		si.fMask  = SIF_PAGE|SIF_RANGE;
		si.nMin   = 0;
		si.nMax   = rcItem.bottom+nSpaceY;
		si.nPage  = rc.Height()-nOffsetY;
		SetScrollInfo(SB_VERT, &si, TRUE);
	}
}

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

void CWndDirectUI::RemoveAll()
{
	m_pLastHitItem = NULL;
	m_pLastHitGroup = NULL;
	while (m_lstGroups.GetCount()) delete m_lstGroups.RemoveHead();
}

int CWndDirectUI::AddGroup(CString strName)
{
	CDirectUIGroup* pGroup = new CDirectUIGroup(strName, &m_lstImages, &m_nStyle);
	m_lstGroups.AddTail(pGroup);

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

BOOL CWndDirectUI::RemoveGroup(int nGroup)
{
	CDirectUIGroup* 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& CWndDirectUI::GetGroupName(int nGroup)
{
	static CString strNull;
	CDirectUIGroup* pGroup = GetGroupByNumber(nGroup);
	if (!pGroup) return strNull;

	return pGroup->m_strName;
}

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

	pGroup->m_strName = strName;
	InvalidateIfPossible();

	return TRUE;
}

BOOL CWndDirectUI::ExpandGroup(int nGroup, BOOL bExpand)
{
	CDirectUIGroup* pGroup = GetGroupByNumber(nGroup);
	if (!pGroup) return FALSE;

	if (pGroup->Expand(bExpand)) InvalidateIfPossible();

	return TRUE;
}

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

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

		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 CDirectUIItem(strName, info.wID));
			j++;
		}

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

	InvalidateIfPossible();
	return TRUE;
}

BOOL CWndDirectUI::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	if (m_pLastHitItem != NULL)
	{
		SetCursor(m_pLastHitItem->GetCursor());
		return TRUE;
	}
	else
	if (((m_pLastHitGroup != NULL)) & (nHitTest == HTCLIENT))
	{
		SetCursor(m_hHand);
		return TRUE;
	}
	else
		return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

void CWndDirectUI::OnLButtonDown(UINT nFlags, CPoint point) 
{
	//CWnd::OnLButtonDown(nFlags, point);
	if (m_pLastHitGroup)
	{
		m_pLastHitGroup->ToggleExpand();
		Invalidate();
	}

	if (m_pLastHitItem)
		if (m_pLastHitItem->MouseItemLDown(point, CPoint(GetOffsetX(), GetOffsetY()))) InvalidateIfPossible();
}

void CWndDirectUI::OnMouseMove(UINT nFlags, CPoint point) 
{
	// Do the hoover thing
	CDirectUIItem*  pItem;
	CDirectUIGroup* pCurrentGroup;
	CDirectUIGroup* pGroup;
	pItem  = NULL;
	pGroup = NULL;

	if (::GetWindowLong(m_hWnd, GWL_STYLE) & WS_VSCROLL)
		point.y += GetScrollPos(SB_VERT);

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

	if (pGroup != m_pLastHitGroup)
	{
		if (m_pLastHitGroup) m_pLastHitGroup->m_uiItemState &= ~ODS_SELECTED;
		m_pLastHitGroup = pGroup;
		if (m_pLastHitGroup) m_pLastHitGroup->m_uiItemState |= ODS_SELECTED;
		Invalidate();
	}
	else if (pItem  != m_pLastHitItem)	
	{
		// Draw Item
		BOOL bRedraw = FALSE;
		if (m_pLastHitItem) bRedraw |= m_pLastHitItem->MouseItemOut(point, CPoint(GetOffsetX(), GetOffsetY()));
		m_pLastHitItem = pItem;
		if (m_pLastHitItem) bRedraw |= m_pLastHitItem->MouseItemIn(point, CPoint(GetOffsetX(), GetOffsetY()));
		if (bRedraw) 
		{
			InvalidateIfPossible();
		}
	}

	//CWnd::OnMouseMove(nFlags, point);
}

void CWndDirectUI::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (m_pLastHitItem)
		if (m_pLastHitItem->MouseItemLUp(point, CPoint(GetOffsetX(), GetOffsetY()))) InvalidateIfPossible();
	
	//CWnd::OnLButtonUp(nFlags, point);
}

CDirectUIGroup* CWndDirectUI::GetGroupByNumber(int nPos)
{
	if (nPos < 0) return NULL;

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

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

BOOL CWndDirectUI::SetToolbarImages(UINT uiToolbar, UINT uiAlternateImages, int nFlagColor, COLORREF clBackground, int nWidth)
{
	// 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.DeleteImageList();
	m_lstImages.Detach();

	if (uiAlternateImages == -1)
	{
		if (!m_lstImages.Create(CImageList::FromHandle((HIMAGELIST)tbToolbar.SendMessage(TB_GETIMAGELIST, 0, 0)))) return FALSE;
	}
	else
	{
		// Credits to Dany Cantin's "Very simple true color toolbar (It's true!)" where I
		// got the code for loading the hi-color toolbars

		CBitmap		cBitmap;
		BITMAP		bmBitmap;
		if (!cBitmap.Attach(LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(uiAlternateImages),
									  IMAGE_BITMAP, 0, 0,
									  LR_DEFAULTSIZE|LR_CREATEDIBSECTION)) ||
			!cBitmap.GetBitmap(&bmBitmap))
			return FALSE;
		CSize		cSize(bmBitmap.bmWidth, bmBitmap.bmHeight); 

		if (!m_lstImages.Create(nWidth, cSize.cy, nFlagColor|ILC_MASK, cSize.cx/nWidth, 0))
			return FALSE;

		if (m_lstImages.Add(&cBitmap, clBackground) == -1)
			return FALSE;
	}
	
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
		m_lstGroups.GetNext(pos)->ParseToolbar(&tbToolbar);

	InvalidateIfPossible();
	return TRUE;
}

BOOL CWndDirectUI::AddItem(int nGroup, CDirectUIItem* pItem)
{
	CDirectUIGroup* pGroup = GetGroupByNumber(nGroup);
	if (!pGroup) return FALSE;
	pGroup->AddItem(pItem);
	InvalidateIfPossible();
	return TRUE;
}

BOOL CWndDirectUI::AddItemCommand(int nGroup, CString strItem, UINT uiCommand)
{
	CDirectUIItem* pItem = new CDirectUIItem(strItem, uiCommand);
	if (AddItem(nGroup, pItem)) return TRUE;
	else
	{
		delete pItem;
		return FALSE;
	}
}

BOOL CWndDirectUI::SetStyle(int nStyle)
{
	m_nStyle = nStyle;
	
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
	{
		CDirectUIGroup* pGroup;
		pGroup = m_lstGroups.GetNext(pos);
		if (nStyle == styleOffice) 
			pGroup->Expand(TRUE); // Includes the CalcHeight(), so we don't have to do it again.
		else
			pGroup->CalcHeight();
	}

	InvalidateIfPossible();
	return TRUE;
}

int CWndDirectUI::GetStyle()
{
	return m_nStyle;
}


void CWndDirectUI::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	switch (nSBCode)
	{
	case SB_BOTTOM:
		nPos = GetScrollLimit(SB_VERT);
		break;
	case SB_ENDSCROLL:   
		return;
	case SB_LINEDOWN:
		nPos = GetScrollPos(SB_VERT) +1;
		break;
	case SB_LINEUP:
		nPos = GetScrollPos(SB_VERT) -1;
		break;
	case SB_PAGEDOWN:
		nPos = GetScrollPos(SB_VERT) + MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8);
		break;
	case SB_PAGEUP:
		nPos = GetScrollPos(SB_VERT) - MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8);
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		break;
	case SB_TOP:
		nPos = 0;
		break;
	}
	Invalidate();
	SetScrollPos(SB_VERT, nPos, TRUE);
}

BOOL CWndDirectUI::Create(const RECT &rect, CWnd *pParentWnd, UINT nID)
{
	return CWnd::Create(NULL, ADVANCED_UI_CLASSNAME, WS_CHILD|WS_VISIBLE, rect, pParentWnd, nID);
}

#ifdef USE_THEMES
LRESULT CWndDirectUI::OnThemeChanged(WPARAM wParam, LPARAM lParam)
{
	InvalidateIfPossible();
	return 0;
}
#endif

void CWndDirectUI::EnableItem(UINT uiCommand, BOOL bEnable)
{
	BOOL bRepaint;
	bRepaint = FALSE;
	for (POSITION pos = m_lstGroups.GetHeadPosition(); pos;)
	{
		bRepaint |= (m_lstGroups.GetNext(pos))->EnableItem(uiCommand, bEnable);
	}
	if (bRepaint) InvalidateIfPossible();
}


int CWndDirectUI::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    return 0;
}

BOOL CWndDirectUI::RegisterWindowClass()
{
    WNDCLASS wndcls;
    HINSTANCE hInst = AfxGetInstanceHandle();

    if (!(::GetClassInfo(hInst, ADVANCED_UI_CLASSNAME, &wndcls)))
    {
        // otherwise we need to register a new class
        wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        wndcls.lpfnWndProc      = ::DefWindowProc;
        wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
        wndcls.hInstance        = hInst;
        wndcls.hIcon            = NULL;
        wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
        wndcls.lpszMenuName     = NULL;
        wndcls.lpszClassName    = ADVANCED_UI_CLASSNAME;

        if (!AfxRegisterClass(&wndcls))
        {
            AfxThrowResourceException();
            return FALSE;
        }
    }

    return TRUE;
}

#ifdef USE_THEMES
CFont* CDirectUIItem::SetThemeFont(CDC *pDC, HTHEME hTheme)
{
	if (m_fntThemedText.m_hObject == 0)
	{
		struct _FONT_STRUCT
		{
			LOGFONT lfFont;
			BYTE    bufer[255];
		} fontbuffer;

		ZeroMemory(&fontbuffer, sizeof(fontbuffer));
		if (S_OK != g_xpStyle.GetThemeFont(hTheme, pDC->m_hDC,EBP_NORMALGROUPBACKGROUND,0,TMT_FONT, &fontbuffer.lfFont))
		{
			CFont::FromHandle((HFONT) GetStockObject(ANSI_VAR_FONT))->GetLogFont(&fontbuffer.lfFont);
		}
		m_fntThemedText.CreateFontIndirect(&fontbuffer.lfFont);
	}
	return pDC->SelectObject(&m_fntThemedText);
}
#endif


/////////////////////////////////////////////////////////////////////////////
// CDirectUIItemStatic

void CDirectUIItemStatic::OnDraw(CDC* pDC, CRect rcItem, CImageList* pImageList, int nStyle, HTHEME hTheme, int nOffsetY, CWnd* pParent)
{
	const nBorderX     = 10;
	const nBorderIconX = 4;
	const nIconSize    = 16;

	rcItem.top += STATIC_DELTA_Y;

	switch (nStyle)
	{
	case CWndDirectUI::styleOffice:
		{
			CGdiObject*  fnOld;

			m_rcItem = rcItem;

			fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
			pDC->SetTextColor((m_uiItemState & ODS_DISABLED) ? GetSysColor(COLOR_GRAYTEXT) : CDirectUIGroup::MakeXPColor(GetSysColor(COLOR_HIGHLIGHT), 0.15));
			pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_END_ELLIPSIS);
			pDC->DrawText(m_strName, m_rcItem, DT_LEFT|DT_END_ELLIPSIS|DT_CALCRECT);
			m_rcItem.OffsetRect(nIconSize+nBorderIconX, 0);

			pDC->SelectObject(fnOld);
		} 
		break;
#ifdef USE_THEMES
	case CWndDirectUI::styleThemed:
		if (g_xpStyle.IsAppThemed() & (hTheme != 0))
		{
			// When there's an icon place the text more to the right
			m_rcItem = rcItem;

			int nTextLen = strlen(m_strName); 
			int mlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, NULL, 0); 
			WCHAR* output = new WCHAR[mlen];
			if(output)
			{
				CRect rc;
				MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, output, mlen);
				{
					g_xpStyle.DrawThemeText(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0 , output, -1, DT_LEFT|DT_END_ELLIPSIS, 0, &rcItem);
					g_xpStyle.GetThemeTextExtent(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0, output, -1, DT_LEFT|DT_END_ELLIPSIS, rcItem, m_rcItem);
				}
				delete output;
			}
			break;
		}
#endif
	case CWndDirectUI::styleXP:
	default:
		{
			CGdiObject*  fnOld;

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

			m_rcItem = rcItem;

			pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHT));

			fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
			pDC->SetBkMode(TRANSPARENT);
			pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_END_ELLIPSIS);
			pDC->DrawText(m_strName, m_rcItem, DT_LEFT|DT_END_ELLIPSIS|DT_CALCRECT);

			pDC->SelectObject(fnOld);
		}
	}
}

int     CDirectUIItemStatic::GetItemHeight(CDC* pDC, int nStyle, HTHEME hTheme)
{
	ASSERT(pDC);

	if (m_nHeight == -1)
	{
		switch (nStyle)
		{
#ifdef USE_THEMES
		case CWndDirectUI::styleThemed:
			if (g_xpStyle.IsAppThemed() & (hTheme != 0))
			{
				int nTextLen = strlen(m_strName); 
				int mlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, NULL, 0); 
				WCHAR* output = new WCHAR[mlen];
				if(output)
				{
					CRect rc;
					CRect rc2;
					rc.SetRect(0, 0, 1000, 1000);
					MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, output, mlen);
					g_xpStyle.GetThemeTextExtent(hTheme, pDC->m_hDC, EBP_NORMALGROUPBACKGROUND, 0, output, -1, DT_LEFT|DT_END_ELLIPSIS, rc, rc2);
					m_nHeight = rc2.Height()+STATIC_DELTA_Y+STATIC_DELTA_Y;
				}
				delete output;
				break;
			}
#endif
		default:
			{
				m_nHeight = MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8);

				CGdiObject*  fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
				CRect rc;
				rc.SetRect(0, 0, 1000, 1000);
				pDC->DrawText(m_strName, rc, DT_LEFT|DT_END_ELLIPSIS|DT_CALCRECT);
				pDC->SelectObject(fnOld);
				m_nHeight = rc.Height()+STATIC_DELTA_Y+STATIC_DELTA_Y;
			}
		}
	}
	return m_nHeight;
}



/////////////////////////////////////////////////////////////////////////////
// CDirectUIItemEdit
void    CDirectUIItemEdit::OnDraw(CDC* pDC, CRect rcItem, CImageList* pImageList, int nStyle, HTHEME hTheme, int nOffsetY, CWnd* pParent)
{
	const nBorderX     = 10;
	// Draw like the edit control is displayed...
	rcItem.left+= nBorderX;
	rcItem.right-= nBorderX;
	m_rcItem = rcItem;

	switch (nStyle)
	{
	case CWndDirectUI::styleOffice:
		{
			pDC->Draw3dRect(m_rcItem, GetSysColor(COLOR_WINDOWFRAME), GetSysColor(COLOR_WINDOWFRAME));
			m_rcItem.DeflateRect(1, 1, 1, 1);
			pDC->FillSolidRect(m_rcItem, GetSysColor(COLOR_WINDOW));
			m_rcItem.DeflateRect(1, 1, 1, 1);
			pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
			CGdiObject* fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
			pDC->SetBkMode(TRANSPARENT);
			pDC->DrawText(m_strName, m_rcItem, DT_LEFT|DT_SINGLELINE);
			pDC->SelectObject(fnOld);
		} 
		break;
#ifdef USE_THEMES
	case CWndDirectUI::styleThemed:
		if (g_xpStyle.IsAppThemed())
		{
			HTHEME hTheme2 = g_xpStyle.OpenThemeData(pParent->GetSafeHwnd(), L"EDIT");
			if (hTheme2)
			{
				int nTextLen = strlen(m_strName); 
				int mlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, NULL, 0); 
				WCHAR* output = new WCHAR[mlen];
				if(output)
				{
					CRect rc;
					MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_strName, nTextLen + 1, output, mlen);
					{
						g_xpStyle.DrawThemeBackground(hTheme2, pDC->m_hDC, EP_EDITTEXT, 0 , &rcItem, &rcItem);
						rcItem.DeflateRect(1, 1, 1, 1);
						g_xpStyle.DrawThemeText(hTheme2, pDC->m_hDC, EP_EDITTEXT, 0 , output, -1, DT_LEFT|DT_END_ELLIPSIS, 0, &rcItem);
					}
					delete output;
				}
				g_xpStyle.CloseThemeData(hTheme2);
				break;
			}
		}
#endif
	case CWndDirectUI::styleXP:
	default:
		{
			pDC->Draw3dRect(rcItem, GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT));
			rcItem.DeflateRect(1, 1, 1, 1);
			pDC->FillSolidRect(rcItem, CDirectUIGroup::MakeXPColor(GetSysColor(COLOR_3DFACE), 0.5));
			rcItem.DeflateRect(1, 1, 1, 1);
			pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
			CGdiObject* fnOld = pDC->SelectStockObject(ANSI_VAR_FONT);
			pDC->SetBkMode(TRANSPARENT);
			pDC->DrawText(m_strName, rcItem, DT_LEFT|DT_SINGLELINE);
			pDC->SelectObject(fnOld);
			rcItem.InflateRect(2, 2, 2, 2);
		}
	}
	m_rcItem = rcItem;
}

int     CDirectUIItemEdit::GetItemHeight(CDC* pDC, int nStyle, HTHEME hTheme)
{
	if (m_nHeight == -1)
	{
		m_nHeight = MulDiv(GetSystemMetrics(SM_CYMENU), 9, 8);
	}
	return m_nHeight;
}

BOOL CDirectUIItemEdit::MouseItemIn(CPoint point, CPoint ptOffset)
{
	if (!m_pEdit)
	{
		m_pEdit =  new CInPlaceEdit2(GetText(), this);
		CRect rc(m_rcItem);
		rc.OffsetRect(-ptOffset.x, -ptOffset.y);
		m_pEdit->Create(WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL, rc, m_pParent, 4001);
	}
	return FALSE;
}

BOOL CDirectUIItemEdit::MouseItemOut(CPoint point, CPoint ptOffset)
{
	if (m_pEdit)
		m_pEdit->DestroyWindow();

	return FALSE;
}

CDirectUIItemEdit::CDirectUIItemEdit(CString strText, CWnd* pParent) : CDirectUIItem(strText, -1, -1) 
{	
	if (!m_hCursorBeam) 
		m_hCursorBeam = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);

	m_pParent = pParent;
	m_pEdit = NULL;

};

CDirectUIItemEdit::~CDirectUIItemEdit()
{
	if (m_pEdit)
		m_pEdit->DestroyWindow();
}

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