Click here to Skip to main content
15,896,201 members
Articles / Desktop Programming / MFC

ProjectZip Remoded 1.6 - A '3-in-1' tools package for zipping VC6 and .NET workspace and project files

Rate me:
Please Sign up or sign in to vote.
4.87/5 (40 votes)
31 Mar 2004CC (ASA 2.5)5 min read 166.9K   3.7K   52  
Zip your VC6 and .NET workspace and project files using a Standalone Executable, an Explorer shell extension or an updated VC6 Addin
// RCCtrlParser.cpp: implementation of the CRCCtrlParser class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "RCCtrlParser.h"
#include "winstyles.h"

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

/////////////////////////////////////////////////////////////////////////////

enum RCTYPE
{
	AUTO3STATE,
	AUTOCHECKBOX,
	AUTORADIOBUTTON,
	CHECKBOX,
	COMBOBOX,
	CONTROL,
	CTEXT,
	DEFPUSHBUTTON,
	EDITTEXT,
	GROUPBOX,
	ICON,
	LISTBOX,
	LTEXT,
	PUSHBOX,
	PUSHBUTTON,
	RADIOBUTTON,
	RTEXT,
	SCROLLBAR,
	STATE3, 
};

struct RCCTRLTYPE
{
	LPCTSTR szType;
	RCTYPE nType;
	LPCTSTR szClass;
	DWORD dwDefStyle;
};

#define MAKERCTYPE(type) #type, (RCTYPE)type 

static RCCTRLTYPE RCCTRLTYPES[] = 
{
	{ MAKERCTYPE(AUTO3STATE), "button", BS_AUTO3STATE | WS_TABSTOP },
	{ MAKERCTYPE(AUTOCHECKBOX), "button", BS_AUTOCHECKBOX | WS_TABSTOP },
	{ MAKERCTYPE(AUTORADIOBUTTON), "button", BS_AUTORADIOBUTTON | WS_TABSTOP },
	{ MAKERCTYPE(CHECKBOX), "button", BS_CHECKBOX | WS_TABSTOP },
	{ MAKERCTYPE(COMBOBOX), "combobox", CBS_SIMPLE | WS_TABSTOP },
	{ MAKERCTYPE(CONTROL), NULL, 0 },
	{ MAKERCTYPE(CTEXT), "static", SS_CENTER | WS_GROUP }, 
	{ MAKERCTYPE(DEFPUSHBUTTON), "button", BS_DEFPUSHBUTTON | WS_TABSTOP },
	{ MAKERCTYPE(EDITTEXT), "edit", ES_LEFT | WS_BORDER | WS_TABSTOP },
	{ MAKERCTYPE(GROUPBOX), "button", BS_GROUPBOX },
	{ MAKERCTYPE(ICON), "static", SS_ICON },
	{ MAKERCTYPE(LISTBOX), "listbox", LBS_NOTIFY | WS_BORDER },
	{ MAKERCTYPE(LTEXT), "static", SS_LEFT | WS_GROUP },
	{ MAKERCTYPE(PUSHBOX), "button", /*BS_PUSHBOX | */WS_TABSTOP },
	{ MAKERCTYPE(PUSHBUTTON), "button", BS_PUSHBUTTON | WS_TABSTOP },
	{ MAKERCTYPE(RADIOBUTTON), "button", BS_RADIOBUTTON | WS_TABSTOP },
	{ MAKERCTYPE(RTEXT), "static", SS_RIGHT | WS_GROUP },
	{ MAKERCTYPE(SCROLLBAR), "scrollbar", SBS_HORZ },
	{ MAKERCTYPE(STATE3), "button", BS_3STATE | WS_TABSTOP }, 
};

const int NUMRCTYPES = sizeof(RCCTRLTYPES) / sizeof(RCCTRLTYPE);

/////////////////////////////////////////////////////////////////////////////

// list of classes requiring WS_EX_CLIENTEDGE by default

static LPCTSTR szCtrlsWantingClientEdge[] = 
{
	"Edit",
	"ComboBox",
	"ComboLBox",
	"ListBox",
	"msctls_hotkey32",
	"SysListView32",
	"SysTreeView32",
	"Richedit",
	"SysDateTimePick32",
	"SysIPAddress32",
	"SysPager32",
	"ComboBoxEx32",
//	"Button", 
//	"Static",
//	"Scrollbar",
//	"toolbarwindow32",
//	"msctls_updown32",
//	"msctls_progress32",
//	"msctls_trackbar32",
//	"SysTabControl32",
//	"SysAnimate32",
//	"SysMonthCal32",
//	"msctls_statusbar32",
//	"ReBarWindow32",
//	"SysHeader32",
//	"tooltips_class32",
};

/////////////////////////////////////////////////////////////////////////////

// see ParseRCStyles() for what this is about
#define WS_NOTVISIBLE WS_POPUP

/////////////////////////////////////////////////////////////////////////////

CRCCtrlParser::CRCCtrlParser(LPCTSTR szRCControls)
{
	if (szRCControls)
		ParseRCControls(szRCControls);
}

CRCCtrlParser::~CRCCtrlParser()
{

}

int CRCCtrlParser::ParseRCControls(const CString& sRCControls)
{
	m_lstControls.RemoveAll();

	// parse the text control by control
	int nStart = 0, nEnd = -1, nCount = 0;

	do
	{
		nEnd = FindNextRCControl(sRCControls, nStart ? nStart + 1 : 0);
		CString sRCCtrl = nEnd != -1 ? sRCControls.Mid(nStart, nEnd - nStart - 1) : sRCControls.Mid(nStart);

		sRCCtrl.TrimLeft();
		sRCCtrl.TrimRight();

		AddRCControl(sRCCtrl);

		nStart = nEnd;
	}
	while (nEnd != -1);

	return m_lstControls.GetCount();
}

int CRCCtrlParser::GetRCControls(CRTCtrlList& lstControls) const
{
	lstControls.AddTail((CRTCtrlList*)&m_lstControls);

	return m_lstControls.GetCount();
}

BOOL CRCCtrlParser::AddRCControl(const CString& sRCCtrl)
{
	CString sCaption;
	UINT uID = 0;
	CString sClass;
	DWORD dwStyle = 0;
	POINT pos = { 0, 0 };
	SIZE size = { 0, 0 };
	DWORD dwExStyle = 0;
	UINT nIconID;

	int nType = ParseRCControl(sRCCtrl, sCaption, uID, sClass, dwStyle, pos, size, dwExStyle, nIconID);
	
	if (nType == -1)
		return FALSE;

	RTCONTROL rtc(NULL, sClass, sCaption, dwStyle, dwExStyle, CRect(pos, size), uID, TRUE);
	rtc.m_nIconID = nIconID;

	return (NULL != m_lstControls.AddTail(rtc));
}

int CRCCtrlParser::FindNextRCControl(const CString& sRCControls, int nStart)
{
	int nType = NUMRCTYPES, nFirstType = -1, nFirstFind = -1;

	while (nType--)
	{
		int nFind = sRCControls.Find(RCCTRLTYPES[nType].szType, nStart);

		if (nFind != -1 && (nFind < nFirstFind || nFirstType == -1) && (nFind == 0 || isspace(sRCControls[nFind - 1])))
		{
			nFirstType = RCCTRLTYPES[nType].nType;
			nFirstFind = nFind;
		}
	}

	return nFirstFind;
}

int CRCCtrlParser::ParseRCControl(const CString& sRCCtrl, CString& sCaption, UINT& uID, CString& sClass, 
								  DWORD& dwStyle, POINT& pos, SIZE& size, DWORD& dwExStyle, UINT& nIconID)
{
	int nIndex = GetRCControlTypeIndex(sRCCtrl);

	if (nIndex == -1)
		return -1;

	CString sID, sStyle, sX, sY, sCx, sCy, sExStyle, sIconID;

	switch (RCCTRLTYPES[nIndex].nType)
	{
	// 'CONTROL' definitions always take the same form:

	// caption, id, class, style, x, y, cx, cy, exstyle
	case CONTROL:
		{
			CString* pItems[] = { &sCaption, &sID, &sClass, &sStyle, &sX, &sY, &sCx, &sCy, &sExStyle };
			ExtractRCItems(sRCCtrl, RCCTRLTYPES[nIndex].szType, pItems, sizeof(pItems) / sizeof(CString*));
		}
		break;

	// older window control definitions have 3 forms:

	// 1. caption, id, x, y, cx, cy, style, exstyle
	case AUTO3STATE:
	case AUTOCHECKBOX:
	case AUTORADIOBUTTON:
	case CHECKBOX:
	case CTEXT:
	case DEFPUSHBUTTON:
	case GROUPBOX:
	case LTEXT:
	case PUSHBOX:
	case PUSHBUTTON:
	case RADIOBUTTON:
	case RTEXT:
	case STATE3:
		{
			CString* pItems[] = { &sCaption, &sID, &sX, &sY, &sCx, &sCy, &sStyle, &sExStyle };
			ExtractRCItems(sRCCtrl, RCCTRLTYPES[nIndex].szType, pItems, sizeof(pItems) / sizeof(CString*));
		}
		break;
		
	// 2. id, x, y, cx, cy, style, exstyle
	case SCROLLBAR:
	case COMBOBOX:
	case EDITTEXT:
	case LISTBOX:
		{
			CString* pItems[] = { &sID, &sX, &sY, &sCx, &sCy, &sStyle, &sExStyle };
			ExtractRCItems(sRCCtrl, RCCTRLTYPES[nIndex].szType, pItems, sizeof(pItems) / sizeof(CString*));
		}
		break;

	// 3. iconid, id, x, y, cx, cy
	case ICON:
		{
			CString* pItems[] = { &sIconID, &sID, &sX, &sY, &sCx, &sCy };
			ExtractRCItems(sRCCtrl, RCCTRLTYPES[nIndex].szType, pItems, sizeof(pItems) / sizeof(CString*));
		}
		break;
	
	default:
		nIndex = -1;
	}

	// global settings
	if (nIndex != -1)
	{
		// class names for predefined classes
		if (sClass.IsEmpty())
			sClass = RCCTRLTYPES[nIndex].szClass;

		// styles
		dwStyle = ParseRCStyles(sClass, sStyle);
		dwExStyle = ParseRCExStyles(sClass, sExStyle);

		// handle invisibility
		BOOL bVisible = !(dwStyle & WS_NOTVISIBLE);
		dwStyle &= ~WS_NOTVISIBLE;

		// default styles if no styles specified
		if (!dwStyle)
			dwStyle = RCCTRLTYPES[nIndex].dwDefStyle;

		dwStyle |= WS_CHILD | (bVisible ? WS_VISIBLE : 0); // always

		// add client-edge depending on class name
		if (CtrlWantsClientEdge(sClass))
			dwExStyle |= WS_EX_CLIENTEDGE;

		uID = atoi(sID);
		pos.x = atoi(sX);
		pos.y = atoi(sY);
		size.cx = atoi(sCx);
		size.cy = atoi(sCy);
		nIconID = atoi(sIconID);
	}

	return nIndex;
}

void CRCCtrlParser::ExtractRCItems(const CString& sRCCtrl, LPCTSTR szType, CString* pItems[], int nMaxItems)
{
	CString sTemp(sRCCtrl);

	sTemp = sTemp.Mid(lstrlen(szType));
	sTemp.TrimLeft();

	int nItem = 0;

	while (nItem < nMaxItems)
	{
		// if the first char is a double quote then we need to ignore 
		// commas till we find the end quote
		int nStart = 0;

		if (sTemp[0] == '\"')
			nStart = sTemp.Find('\"', 1);

		int nFind = sTemp.Find(',', nStart);

		if (nFind == -1)
		{
			*pItems[nItem] = sTemp;
			pItems[nItem]->Replace("\"", "");

			break;
		}

		// else
		*pItems[nItem] = sTemp.Left(nFind);
		pItems[nItem]->Replace("\"", "");

		sTemp = sTemp.Mid(nFind + 1);
		sTemp.TrimLeft();

		nItem++;
	}
}

int CRCCtrlParser::GetRCControlTypeIndex(const CString& sRCCtrl)
{
	int nType = NUMRCTYPES;

	while (nType--)
	{
		if (0 == sRCCtrl.Find(RCCTRLTYPES[nType].szType))
			return nType;
	}

	return -1;
}

DWORD CRCCtrlParser::ParseRCStyles(LPCTSTR szClass, const CString& sStyle)
{
	CStringArray aStyles;
	DWORD dwStyle = 0;

	if (ExtractRCStyles(sStyle, aStyles))
	{
		int nStyle = aStyles.GetSize();

		while (nStyle--)
		{
			// note: invisible controls are handled via 'NOT WS_VISIBLE'
			// so we must handle this explicitly via a hack
			// we can reuse WS_POPUP because this will _never_ appear for 
			// a child control (i hope)
			if (CString(aStyles[nStyle]).CompareNoCase("NOT WS_VISIBLE") == 0)
				dwStyle |= WS_NOTVISIBLE;
			else
			{
				dwStyle |= LookupWndStyle(aStyles[nStyle]);
				dwStyle |= LookupCtrlStyle(aStyles[nStyle]);
			}
		}
	}

	return dwStyle;
}

DWORD CRCCtrlParser::ParseRCExStyles(LPCTSTR szClass, const CString& sExStyle)
{
	CStringArray aExStyles;
	DWORD dwExStyle = 0;

	if (ExtractRCStyles(sExStyle, aExStyles))
	{
		int nExStyle = aExStyles.GetSize();

		while (nExStyle--)
		{
			dwExStyle |= LookupWndExStyle(aExStyles[nExStyle]);
			dwExStyle |= LookupCtrlStyle(aExStyles[nExStyle]);
		}
	}

	return dwExStyle;
}

int CRCCtrlParser::ExtractRCStyles(const CString& sStyles, CStringArray& aStyles)
{
	// styles are delimetered by '|'
	CString sTemp(sStyles);
	sTemp.TrimLeft();

	do
	{
		int nFind = sTemp.Find('|');

		if (nFind == -1)
		{
			sTemp.TrimRight();

			if (sTemp.GetLength())
				aStyles.Add(sTemp);
			break;
		}
		else
		{
			CString sStyle = sTemp.Left(nFind);
			sStyle.TrimRight();

			sTemp = sTemp.Mid(nFind + 1);
			sTemp.TrimLeft();

			if (sStyle.GetLength())
				aStyles.Add(sStyle);
		}

	}
	while (TRUE);

	return aStyles.GetSize();
}

BOOL CRCCtrlParser::CtrlWantsClientEdge(LPCTSTR szClass)
{
	int nClass = sizeof(szCtrlsWantingClientEdge) / sizeof(LPCTSTR);
	CString sClass(szClass);

	while (nClass--)
	{
		if (sClass.CompareNoCase(szCtrlsWantingClientEdge[nClass]) == 0)
			return TRUE;
	}

	return FALSE; // no match
}

DWORD CRCCtrlParser::GetDefaultStyles(LPCTSTR szRCType)
{
	int nType = NUMRCTYPES;

	while (nType--)
	{
		if (stricmp(RCCTRLTYPES[nType].szType, szRCType) == 0)
			return RCCTRLTYPES[nType].dwDefStyle;
	}

	return 0;
}

BOOL CRCCtrlParser::GetClassName(LPCTSTR szRCType, CString& sClass)
{
	int nType = NUMRCTYPES;

	while (nType--)
	{
		if (stricmp(RCCTRLTYPES[nType].szType, szRCType) == 0)
		{
			sClass = RCCTRLTYPES[nType].szClass;
			return TRUE;
		}
	}

	return FALSE;
}

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