Click here to Skip to main content
15,886,725 members
Articles / Desktop Programming / MFC

SkinControls 1.1 - A journey in automating the skinning of Windows controls

Rate me:
Please Sign up or sign in to vote.
4.97/5 (80 votes)
11 Oct 200314 min read 359.1K   19.8K   273  
A self-contained, user-extensible, application-wide skinning architecture for Windows controls.
// SkinGlobals.cpp: implementation of the CSkinGlobals class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SkinGlobals.h"
#include "skinbase.h"
#include "..\shared\SysColors.h"

#ifndef NO_SKIN_INI
#include "Skininifile.h"
#endif

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

// statics
CPen CSkinGlobals::s_pen;
CBrush CSkinGlobals::s_brush;
CFont CSkinGlobals::s_font;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSkinGlobals::CSkinGlobals() : m_nFontPointSize(0), m_nFontHeight(0)
{
}

CSkinGlobals::~CSkinGlobals()
{
	ResetFonts();
	ResetColors();
	ResetCtrls();
}

void CSkinGlobals::ResetFonts()
{
	m_fontMarlett.DeleteObject();
	m_fontMenu.DeleteObject();
	m_fontTip.DeleteObject();
	m_fontMessage.DeleteObject();
	m_fontCaption.DeleteObject();
}

void CSkinGlobals::ResetCtrls()
{
	// delete control bitmap info
	for (int nCtl = 0; nCtl < SKCB_LAST; nCtl++)
	{
		CSkinBitmap* pSkb = NULL;
		
		if (m_mapCtrlBitmaps.Lookup(nCtl, pSkb))
		{
			delete pSkb;
			m_mapCtrlBitmaps.RemoveKey(nCtl);
		}
	}
}

void CSkinGlobals::ResetColors()
{
	CBrush* pBrush = NULL;
	COLORREF color = 0;
	POSITION pos = m_mapBrushes.GetStartPosition();
	
	while (pos)
	{
		m_mapBrushes.GetNextAssoc(pos, color, pBrush);
		
		if (pBrush)
			pBrush->DeleteObject();
		
		delete pBrush;
	}
	
	CPen* pPen = NULL;
	pos = m_mapPens.GetStartPosition();
	
	while (pos)
	{
		m_mapPens.GetNextAssoc(pos, color, pPen);
		
		if (pPen)
			pPen->DeleteObject();
		
		delete pPen;
	}
	
	m_mapBrushes.RemoveAll();
	m_mapPens.RemoveAll();
	m_mapColors.RemoveAll();
}

#ifndef NO_SKIN_INI
BOOL CSkinGlobals::LoadSkin(const CSkinIniGlobalsFile* pIniFile)
{
	if (!pIniFile)
		return FALSE;
	
	// font
	ResetFonts();

	CSkinFont font;
	pIniFile->GetGlobalFont(font);

	// find the first acceptable font
	CString sFont;
	int nPointSize = 0, nHeight = 0;

	for (int nFont = 0; nFont < font.aFonts.GetSize(); nFont++)
	{
		sFont = font.aFonts[nFont].sFaceName;

		if (sFont.IsEmpty())
			continue;

		if (CSkinBase::FontIsPresent(sFont))
		{
			nPointSize = font.aFonts[nFont].nPointSize;
			nHeight = font.aFonts[nFont].nHeight;
			break;
		}
		else
		{
			// if there is a font file path try adding before trying again
			if (!font.aFonts[nFont].sFilePath.IsEmpty())
			{
				if (AddFontResource(font.aFonts[nFont].sFilePath))
				{
					// verify
					if (CSkinBase::FontIsPresent(sFont))
					{
						nPointSize = font.aFonts[nFont].nPointSize;
						nHeight = font.aFonts[nFont].nHeight;
						break;
					}
				}
			}
			// else
			sFont.Empty();
		}
	}

	SetFont(sFont, nPointSize);

	// colors
	ResetColors();
	
	int nColor = COLOR_LAST;
	
	while (nColor--)
	{
		COLORREF color = pIniFile->GetColor(nColor);
		SetColor(nColor, color);
	}

	// controls
	ResetCtrls();

	pIniFile->GetControlBitmapInfo(m_mapCtrlBitmaps);

	return TRUE;
}

#endif

void CSkinGlobals::Reset()
{
	ResetFonts();
	ResetColors();
	ResetCtrls();
}

COLORREF CSkinGlobals::GetColor(int nColor)
{
	COLORREF color = (COLORREF)-1;
	
	if (m_mapColors.Lookup(nColor, color) && color >= 0)
	{
		return color;
	}

	// else
	color = ::GetSysColor((nColor != COLOR_PARENTBKGND) ? nColor : COLOR_3DFACE);
	
	// else save 0 for transparency
	if (color == 0)
		color = 1;
	
	SetColor(nColor, color);
	
	return color;
}

CBrush* CSkinGlobals::GetColorBrush(int nColor)
{
	COLORREF color = GetColor(nColor);
	
	if (color == ::GetSysColor(nColor))
		return CBrush::FromHandle(::GetSysColorBrush(nColor));
	
	// else
	return GetColorBrush(color);
}

CBrush* CSkinGlobals::GetColorBrush(COLORREF color)
{
	CBrush* pBrush = NULL;
	
	if (m_mapBrushes.Lookup(color, pBrush) && pBrush)
		return pBrush;
	
	pBrush = new CBrush;
	
	if (pBrush->CreateSolidBrush(color))
	{
		m_mapBrushes[color] = pBrush;
		return pBrush;
	}
	
	return NULL;
}

CPen* CSkinGlobals::GetColorPen(int nColor, int nWidth)
{
	COLORREF color = GetColor(nColor);
	
	return GetColorPen(GetColor(nColor), nWidth);
}

CPen* CSkinGlobals::GetColorPen(COLORREF color, int nWidth)
{
	CPen* pPen = NULL;
	
	m_mapPens.Lookup(color, pPen);
	
	// check width matches
	if (pPen)
	{
		LOGPEN lp;
		
		if (pPen->GetLogPen(&lp) && (lp.lopnWidth.x == nWidth))
			return pPen;
		
		// else
		pPen->DeleteObject();
		delete pPen;
	}
	
	pPen = new CPen;
	
	if (pPen->CreatePen(PS_SOLID, nWidth, color))
	{
		m_mapPens[color] = pPen;
		return pPen;
	}
	
	return NULL;
}

void CSkinGlobals::SetColor(int nColor, COLORREF color)
{
	COLORREF crPrev = (COLORREF)-1;
	
	// save 0 for transparency
	if (color == 0)
		color = 1;
	
#ifdef _DEBUG
	CString sColor = GetColorName(nColor);
#endif
	
	// delete any brush or pen associated with the old color
	if (m_mapColors.Lookup(nColor, crPrev) && crPrev >= 0 && crPrev != color)
	{
		CBrush* pBrush = NULL;
		
		if (m_mapBrushes.Lookup(color, pBrush) && pBrush)
		{
			m_mapBrushes.RemoveKey(color);
			pBrush->DeleteObject();
			delete pBrush;
		}
		
		CPen* pPen = NULL;
		
		if (m_mapPens.Lookup(color, pPen) && pPen)
		{
			m_mapPens.RemoveKey(color);
			pPen->DeleteObject();
			delete pPen;
		}
	}
	
	if (color == -1)
		m_mapColors.RemoveKey(nColor);
	else
		m_mapColors[nColor] = color;
}

CFont* CSkinGlobals::GetFont(int nFont)
{
	switch (nFont)
	{
	case SBFONT_MARLETT:
		return GetMarlett();

	case SBFONT_TIP:
		return GetTipFont();

	case SBFONT_MENU:
		return GetMenuFont();

	case SBFONT_MESSAGE:
		return GetMessageFont();

	case SBFONT_CAPTION:
		return GetCaptionFont();
	}

	ASSERT (0);
	return NULL;
}

CFont* CSkinGlobals::GetMarlett()
{
	if (!m_fontMarlett.GetSafeHandle())
	{
		VERIFY(m_fontMarlett.CreateFont(GetSystemMetrics(SM_CYMENUSIZE) - 6, 0, 0, 0,
			FW_THIN, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett")));
	}
	
	return &m_fontMarlett;
}

CFont* CSkinGlobals::GetMenuFont()
{
	if (!m_fontMenu.GetSafeHandle())
	{
		NONCLIENTMETRICS ncm;
		ZeroMemory(&ncm,sizeof(ncm));
		ncm.cbSize = sizeof(ncm);
	
		// Get the system font
		SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, (PVOID)&ncm, FALSE);
	
		VERIFY(CreateFont(&m_fontMenu, &ncm.lfMenuFont));
	}

	return &m_fontMenu;
}

CFont* CSkinGlobals::GetTipFont()
{
	if (!m_fontTip.GetSafeHandle())
	{
		NONCLIENTMETRICS ncm;
		ZeroMemory(&ncm,sizeof(ncm));
		ncm.cbSize = sizeof(ncm);
	
		// Get the system font
		SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, (PVOID)&ncm, FALSE);
	
		VERIFY(CreateFont(&m_fontTip, &ncm.lfStatusFont));
	}

	return &m_fontTip;
}

CFont* CSkinGlobals::GetMessageFont()
{
	if (!m_fontMessage.GetSafeHandle())
	{
		NONCLIENTMETRICS ncm;
		ZeroMemory(&ncm,sizeof(ncm));
		ncm.cbSize = sizeof(ncm);
	
		// Get the system font
		SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, (PVOID)&ncm, FALSE);
	
		VERIFY(CreateFont(&m_fontMessage, &ncm.lfMessageFont));
	}

	return &m_fontMessage;
}

CFont* CSkinGlobals::GetCaptionFont()
{
	if (!m_fontCaption.GetSafeHandle())
	{
		NONCLIENTMETRICS ncm;
		ZeroMemory(&ncm,sizeof(ncm));
		ncm.cbSize = sizeof(ncm);
	
		// Get the system font
		SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, (PVOID)&ncm, FALSE);
	
		VERIFY(CreateFont(&m_fontCaption, &ncm.lfCaptionFont));
	}

	return &m_fontCaption;
}

BOOL CSkinGlobals::CreateFont(CFont* pFont, LOGFONT* pLF)
{
	ASSERT (pFont && pLF);

	if (!(pFont && pLF))
		return FALSE;
	
	// override ANSI_CHARSET because some fonts do not show with it
	if (pLF->lfCharSet == ANSI_CHARSET)
		pLF->lfCharSet = DEFAULT_CHARSET;

	pLF->lfQuality = ANTIALIASED_QUALITY;
	pLF->lfOutPrecision = OUT_TT_PRECIS;

	if (!m_sFontName.IsEmpty())
		lstrcpy(pLF->lfFaceName, m_sFontName);

	if (m_nFontPointSize > 0)
	{
		HDC hDC = GetDC(NULL);
		pLF->lfHeight = -MulDiv(m_nFontPointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
		ReleaseDC(NULL, hDC);
	}
	else if (m_nFontHeight > 0)
		pLF->lfHeight = -m_nFontHeight;

	return pFont->CreateFont(pLF->lfHeight,0,0,0,pLF->lfWeight,FALSE, FALSE, FALSE,DEFAULT_CHARSET ,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH, pLF->lfFaceName);
}

BOOL CSkinGlobals::SetFont(LPCTSTR szFaceName, int nPointSize, int nHeight)
{
	nPointSize = abs(nPointSize);

	if (nPointSize && (nPointSize < 6 || nPointSize > 24))
		return FALSE;

	if (m_sFontName.CompareNoCase(szFaceName) != 0 || m_nFontPointSize != nPointSize)
	{
		if (!CString(szFaceName).IsEmpty() && !CSkinBase::FontIsPresent(szFaceName))
			return FALSE;

		m_sFontName = szFaceName;
		m_nFontPointSize = nPointSize;
		m_nFontHeight = nHeight;

		// delete existing fonts
		ResetFonts();

		return TRUE;
	}

	return FALSE;
}

// static versions
CBrush* CSkinGlobals::GetColorBrushSt(int nColor)
{
	return GetColorBrushSt(::GetSysColor(nColor));
}

CBrush* CSkinGlobals::GetColorBrushSt(COLORREF color)
{
	s_brush.DeleteObject();
	s_brush.CreateSolidBrush(color);

	return &s_brush;
}

CPen* CSkinGlobals::GetColorPenSt(int nColor, int nWidth)
{
	return GetColorPenSt(::GetSysColor(nColor));
}

CPen* CSkinGlobals::GetColorPenSt(COLORREF color, int nWidth)
{
	s_pen.DeleteObject();
	s_pen.CreatePen(PS_SOLID, nWidth, color);

	return &s_pen;
}

CFont* CSkinGlobals::GetFontSt(int nFont)
{
	s_font.DeleteObject();

	if (nFont == SBFONT_MARLETT)
	{
		VERIFY(s_font.CreateFont(GetSystemMetrics(SM_CYMENUSIZE) - 6, 0, 0, 0,
				FW_THIN, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett")));

		return &s_font;
	}

	return CFont::FromHandle((HFONT)::GetStockObject(ANSI_VAR_FONT));
}

COLORREF CSkinGlobals::GetColorSt(int nColor)
{
	if (nColor == COLOR_PARENTBKGND)
		nColor = COLOR_3DFACE;

	return ::GetSysColor(nColor);
}

CBitmap* CSkinGlobals::GetControlBitmap(int nItem, int nState, COLORREF* pMask, int nAltItem)
{
	ASSERT (nState == IM_HOT || nState == IM_COLD || nState == IM_DOWN || nState == IM_DISABLED);

	CSkinBitmap* pSkb = NULL;

	if ((m_mapCtrlBitmaps.Lookup(nItem, pSkb) && pSkb) ||
		(nAltItem != -1 && m_mapCtrlBitmaps.Lookup(nAltItem, pSkb) && pSkb))
	{
		if (pSkb->IsValid(IM_HOT))
		{
			if (pMask)
				*pMask = pSkb->crMask;

			return pSkb->Image(nState);
		}
	}

	// else
	return NULL;
}

BOOL CSkinGlobals::SetControlBitmap(int nItem, CBitmap bitmap[IM_LAST], COLORREF crMask)
{
	ASSERT (bitmap[IM_HOT].GetSafeHandle());

	if (!bitmap[IM_HOT].GetSafeHandle())
		return FALSE;

	// delete existing bitmaps first
	CSkinBitmap* pSkb = NULL;
	
	if (m_mapCtrlBitmaps.Lookup(nItem, pSkb) && pSkb)
	{
		m_mapCtrlBitmaps.RemoveKey(nItem);
		delete pSkb;
	}
	
	// assign new bitmaps to item
	m_mapCtrlBitmaps[nItem] = new CSkinBitmap(bitmap, crMask);
	
	return TRUE;
}

BOOL CSkinGlobals::SetControlBitmap(int nItem, UINT uIDBitmap[IM_LAST], COLORREF crMask)
{
	CBitmap bitmap[IM_LAST];

	int nBM = IM_LAST;

	while (nBM--)
	{
		if (uIDBitmap[nBM])
			bitmap[nBM].LoadBitmap(uIDBitmap[nBM]);
	}

	return SetControlBitmap(nItem, bitmap, crMask);
}

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