Ver código fonte

新增UI类:BtnST相关类。

Jeff 6 anos atrás
pai
commit
3e0fbbe215

+ 3069 - 0
source/hook/skinui/BCMenu.cpp

@@ -0,0 +1,3069 @@
+#include "stdafx.h"        // Standard windows header file
+#include "BCMenu.h"        // BCMenu class declaration
+#include <afxpriv.h>       //SK: makes A2W and other spiffy AFX macros work
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define BCMENU_GAP 1
+#ifndef OBM_CHECK
+#define OBM_CHECK 32760 // from winuser.h
+#endif
+
+#if _MFC_VER <0x400
+#error This code does not work on Versions of MFC prior to 4.0
+#endif
+
+static CPINFO CPInfo;
+// how the menu's are drawn in win9x/NT/2000
+UINT BCMenu::original_drawmode = BCMENU_DRAWMODE_ORIGINAL;
+BOOL BCMenu::original_select_disabled = TRUE;
+// how the menu's are drawn in winXP
+UINT BCMenu::xp_drawmode = BCMENU_DRAWMODE_XP;
+BOOL BCMenu::xp_select_disabled = FALSE;
+BOOL BCMenu::xp_draw_3D_bitmaps = TRUE;
+
+CImageList BCMenu::m_AllImages;
+CArray<int, int&> BCMenu::m_AllImagesID;
+
+enum Win32Type {
+	Win32s,
+	WinNT3,
+	Win95,
+	Win98,
+	WinME,
+	WinNT4,
+	Win2000,
+	WinXP
+};
+
+
+Win32Type IsShellType()
+{
+	Win32Type  ShellType;
+	DWORD winVer;
+	OSVERSIONINFO *osvi;
+
+	winVer = GetVersion();
+	if (winVer < 0x80000000) {/*NT */
+		ShellType = WinNT3;
+		osvi = (OSVERSIONINFO *)malloc(sizeof(OSVERSIONINFO));
+		if (osvi != NULL) {
+			memset(osvi, 0, sizeof(OSVERSIONINFO));
+			osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+			GetVersionEx(osvi);
+			if (osvi->dwMajorVersion == 4L)ShellType = WinNT4;
+			else if (osvi->dwMajorVersion == 5L && osvi->dwMinorVersion == 0L)ShellType = Win2000;
+			else if (osvi->dwMajorVersion == 5L && osvi->dwMinorVersion == 1L)ShellType = WinXP;
+			free(osvi);
+		}
+	}
+	else if (LOBYTE(LOWORD(winVer)) < 4)
+		ShellType = Win32s;
+	else {
+		ShellType = Win95;
+		osvi = (OSVERSIONINFO *)malloc(sizeof(OSVERSIONINFO));
+		if (osvi != NULL) {
+			memset(osvi, 0, sizeof(OSVERSIONINFO));
+			osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+			GetVersionEx(osvi);
+			if (osvi->dwMajorVersion == 4L && osvi->dwMinorVersion == 10L)ShellType = Win98;
+			else if (osvi->dwMajorVersion == 4L && osvi->dwMinorVersion == 90L)ShellType = WinME;
+			free(osvi);
+		}
+	}
+	return ShellType;
+}
+
+static Win32Type g_Shell = IsShellType();
+
+void BCMenuData::SetAnsiString(LPCSTR szAnsiString)
+{
+	USES_CONVERSION;
+	SetWideString(A2W(szAnsiString));  //SK:  see MFC Tech Note 059
+}
+
+CString BCMenuData::GetString(void)//returns the menu text in ANSI or UNICODE
+//depending on the MFC-Version we are using
+{
+	CString strText;
+	if (m_szMenuText)
+	{
+#ifdef UNICODE
+		strText = m_szMenuText;
+#else
+		USES_CONVERSION;
+		strText = W2A(m_szMenuText);     //SK:  see MFC Tech Note 059
+#endif    
+	}
+	return strText;
+}
+
+CTypedPtrArray<CPtrArray, HMENU> BCMenu::m_AllSubMenus;  // Stores list of all sub-menus
+
+IMPLEMENT_DYNAMIC(BCMenu, CMenu)
+
+/*
+===============================================================================
+BCMenu::BCMenu()
+BCMenu::~BCMenu()
+-----------------
+
+Constructor and Destructor.
+
+===============================================================================
+*/
+
+BCMenu::BCMenu()
+{
+	m_bDynIcons = FALSE;     // O.S. - no dynamic icons by default
+	disable_old_style = FALSE;
+	m_iconX = 16;            // Icon sizes default to 16 x 16
+	m_iconY = 15;            // ...
+	m_selectcheck = -1;
+	m_unselectcheck = -1;
+	checkmaps = NULL;
+	checkmapsshare = FALSE;
+	// set the color used for the transparent background in all bitmaps
+	m_bitmapBackground = RGB(192, 192, 192); //gray
+	m_bitmapBackgroundFlag = FALSE;
+	GetCPInfo(CP_ACP, &CPInfo);
+	m_loadmenu = FALSE;
+}
+
+
+BCMenu::~BCMenu()
+{
+	DestroyMenu();
+}
+
+BOOL BCMenu::IsNewShell()
+{
+	return (g_Shell >= Win95);
+}
+
+BOOL BCMenu::IsWinXPLuna()
+{
+	if (g_Shell == WinXP) {
+		if (IsWindowsClassicTheme())return(FALSE);
+		else return(TRUE);
+	}
+	return(FALSE);
+}
+
+BOOL BCMenu::IsLunaMenuStyle()
+{
+	if (IsWinXPLuna()) {
+		if (xp_drawmode == BCMENU_DRAWMODE_XP)return(TRUE);
+	}
+	else {
+		if (original_drawmode == BCMENU_DRAWMODE_XP)return(TRUE);
+	}
+	return(FALSE);
+}
+
+BCMenuData::~BCMenuData()
+{
+	if (bitmap)
+		delete(bitmap);
+
+	delete[] m_szMenuText; //Need not check for NULL because ANSI X3J16 allows "delete NULL"
+}
+
+
+void BCMenuData::SetWideString(const wchar_t *szWideString)
+{
+	delete[] m_szMenuText;//Need not check for NULL because ANSI X3J16 allows "delete NULL"
+
+	if (szWideString)
+	{
+		m_szMenuText = new wchar_t[sizeof(wchar_t)*(wcslen(szWideString) + 1)];
+		if (m_szMenuText)
+			wcscpy(m_szMenuText, szWideString);
+	}
+	else
+		m_szMenuText = NULL;//set to NULL so we need not bother about dangling non-NULL Ptrs
+}
+
+BOOL BCMenu::IsMenu(CMenu *submenu)
+{
+	int m;
+	int numSubMenus = m_AllSubMenus.GetUpperBound();
+	for (m = 0; m <= numSubMenus; ++m) {
+		if (submenu->m_hMenu == m_AllSubMenus[m])return(TRUE);
+	}
+	return(FALSE);
+}
+
+BOOL BCMenu::IsMenu(HMENU submenu)
+{
+	int m;
+	int numSubMenus = m_AllSubMenus.GetUpperBound();
+	for (m = 0; m <= numSubMenus; ++m) {
+		if (submenu == m_AllSubMenus[m])return(TRUE);
+	}
+	return(FALSE);
+}
+
+BOOL BCMenu::DestroyMenu()
+{
+	// Destroy Sub menus:
+	int m, n;
+	int numAllSubMenus = m_AllSubMenus.GetUpperBound();
+	for (n = numAllSubMenus; n >= 0; n--) {
+		if (m_AllSubMenus[n] == this->m_hMenu)m_AllSubMenus.RemoveAt(n);
+	}
+	int numSubMenus = m_SubMenus.GetUpperBound();
+	for (m = numSubMenus; m >= 0; m--) {
+		numAllSubMenus = m_AllSubMenus.GetUpperBound();
+		for (n = numAllSubMenus; n >= 0; n--) {
+			if (m_AllSubMenus[n] == m_SubMenus[m])m_AllSubMenus.RemoveAt(n);
+		}
+		CMenu *ptr = FromHandle(m_SubMenus[m]);
+		BOOL flag = ptr->IsKindOf(RUNTIME_CLASS(BCMenu));
+		if (flag)delete((BCMenu *)ptr);
+	}
+	m_SubMenus.RemoveAll();
+	// Destroy menu data
+	int numItems = m_MenuList.GetUpperBound();
+	for (m = 0; m <= numItems; m++)delete(m_MenuList[m]);
+	m_MenuList.RemoveAll();
+	if (checkmaps && !checkmapsshare) {
+		delete checkmaps;
+		checkmaps = NULL;
+	}
+	// Call base-class implementation last:
+	return(CMenu::DestroyMenu());
+};
+
+int BCMenu::GetMenuDrawMode(void)
+{
+	if (IsWinXPLuna())return(xp_drawmode);
+	return(original_drawmode);
+}
+
+BOOL BCMenu::GetSelectDisableMode(void)
+{
+	if (IsLunaMenuStyle())return(xp_select_disabled);
+	return(original_select_disabled);
+}
+
+
+/*
+==========================================================================
+void BCMenu::DrawItem(LPDRAWITEMSTRUCT)
+---------------------------------------
+
+Called by the framework when a particular item needs to be drawn.  We
+overide this to draw the menu item in a custom-fashion, including icons
+and the 3D rectangle bar.
+==========================================================================
+*/
+
+void BCMenu::DrawItem(LPDRAWITEMSTRUCT lpDIS)
+{
+	ASSERT(lpDIS != NULL);
+	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+	if (pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)DrawItem_Win9xNT2000(lpDIS);
+	else {
+		if (IsWinXPLuna()) {
+			if (xp_drawmode == BCMENU_DRAWMODE_XP) DrawItem_WinXP(lpDIS);
+			else DrawItem_Win9xNT2000(lpDIS);
+		}
+		else {
+			if (original_drawmode == BCMENU_DRAWMODE_XP) DrawItem_WinXP(lpDIS);
+			else DrawItem_Win9xNT2000(lpDIS);
+		}
+	}
+}
+
+void BCMenu::DrawItem_Win9xNT2000(LPDRAWITEMSTRUCT lpDIS)
+{
+	ASSERT(lpDIS != NULL);
+	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+	CRect rect;
+	UINT state = (((BCMenuData*)(lpDIS->itemData))->nFlags);
+	CBrush m_brBackground;
+	COLORREF m_clrBack;
+
+	if (IsWinXPLuna())m_clrBack = GetSysColor(COLOR_3DFACE);
+	else m_clrBack = GetSysColor(COLOR_MENU);
+
+	m_brBackground.CreateSolidBrush(m_clrBack);
+
+	// remove the selected bit if it's grayed out
+	if (lpDIS->itemState & ODS_GRAYED && !original_select_disabled) {
+		if (lpDIS->itemState & ODS_SELECTED)lpDIS->itemState = lpDIS->itemState & ~ODS_SELECTED;
+	}
+
+	if (state & MF_SEPARATOR) {
+		rect.CopyRect(&lpDIS->rcItem);
+		pDC->FillRect(rect, &m_brBackground);
+		rect.top += (rect.Height() >> 1);
+		pDC->DrawEdge(&rect, EDGE_ETCHED, BF_TOP);
+	}
+	else {
+		CRect rect2;
+		BOOL standardflag = FALSE, selectedflag = FALSE, disableflag = FALSE;
+		BOOL checkflag = FALSE;
+		COLORREF crText = GetSysColor(COLOR_MENUTEXT);
+		CBrush m_brSelect;
+		CPen m_penBack;
+		int x0, y0, dy;
+		int nIconNormal = -1, xoffset = -1, global_offset = -1;
+		CImageList *bitmap = NULL;
+
+		// set some colors
+		m_penBack.CreatePen(PS_SOLID, 0, m_clrBack);
+		m_brSelect.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
+
+		// draw the colored rectangle portion
+
+		rect.CopyRect(&lpDIS->rcItem);
+		rect2 = rect;
+
+		// draw the up/down/focused/disabled state
+
+		UINT state = lpDIS->itemState;
+		CString strText;
+
+		if (lpDIS->itemData != NULL) {
+			nIconNormal = (((BCMenuData*)(lpDIS->itemData))->menuIconNormal);
+			xoffset = (((BCMenuData*)(lpDIS->itemData))->xoffset);
+			global_offset = (((BCMenuData*)(lpDIS->itemData))->global_offset);
+			bitmap = (((BCMenuData*)(lpDIS->itemData))->bitmap);
+			strText = ((BCMenuData*)(lpDIS->itemData))->GetString();
+
+			if (nIconNormal < 0 && global_offset >= 0) {
+				xoffset = global_offset;
+				nIconNormal = 0;
+				bitmap = &m_AllImages;
+			}
+
+			if (state&ODS_CHECKED && nIconNormal < 0) {
+				if (state&ODS_SELECTED && m_selectcheck > 0)checkflag = TRUE;
+				else if (m_unselectcheck > 0) checkflag = TRUE;
+			}
+			else if (nIconNormal != -1) {
+				standardflag = TRUE;
+				if (state&ODS_SELECTED && !(state&ODS_GRAYED))selectedflag = TRUE;
+				else if (state&ODS_GRAYED) disableflag = TRUE;
+			}
+		}
+		else {
+			strText.Empty();
+		}
+
+		if (state&ODS_SELECTED) { // draw the down edges
+
+			CPen *pOldPen = pDC->SelectObject(&m_penBack);
+
+			// You need only Text highlight and thats what you get
+
+			if (checkflag || standardflag || selectedflag || disableflag || state & ODS_CHECKED)
+				rect2.SetRect(rect.left + m_iconX + 4 + BCMENU_GAP, rect.top, rect.right, rect.bottom);
+			pDC->FillRect(rect2, &m_brSelect);
+
+			pDC->SelectObject(pOldPen);
+			crText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+		}
+		else {
+			CPen *pOldPen = pDC->SelectObject(&m_penBack);
+			pDC->FillRect(rect, &m_brBackground);
+			pDC->SelectObject(pOldPen);
+
+			// draw the up edges	
+			pDC->Draw3dRect(rect, m_clrBack, m_clrBack);
+		}
+
+		// draw the text if there is any
+		//We have to paint the text only if the image is nonexistant
+
+		dy = (rect.Height() - 4 - m_iconY) / 2;
+		dy = dy < 0 ? 0 : dy;
+
+		if (checkflag || standardflag || selectedflag || disableflag) {
+			rect2.SetRect(rect.left + 1, rect.top + 1 + dy, rect.left + m_iconX + 3,
+				rect.top + m_iconY + 3 + dy);
+			pDC->Draw3dRect(rect2, m_clrBack, m_clrBack);
+			if (checkflag && checkmaps) {
+				pDC->FillRect(rect2, &m_brBackground);
+				rect2.SetRect(rect.left, rect.top + dy, rect.left + m_iconX + 4,
+					rect.top + m_iconY + 4 + dy);
+
+				pDC->Draw3dRect(rect2, m_clrBack, m_clrBack);
+				CPoint ptImage(rect.left + 2, rect.top + 2 + dy);
+
+				if (state&ODS_SELECTED)checkmaps->Draw(pDC, 1, ptImage, ILD_TRANSPARENT);
+				else checkmaps->Draw(pDC, 0, ptImage, ILD_TRANSPARENT);
+			}
+			else if (disableflag) {
+				if (!selectedflag) {
+					CBitmap bitmapstandard;
+					GetBitmapFromImageList(pDC, bitmap, xoffset, bitmapstandard);
+					rect2.SetRect(rect.left, rect.top + dy, rect.left + m_iconX + 4,
+						rect.top + m_iconY + 4 + dy);
+					pDC->Draw3dRect(rect2, m_clrBack, m_clrBack);
+					if (disable_old_style)
+						DitherBlt(lpDIS->hDC, rect.left + 2, rect.top + 2 + dy, m_iconX, m_iconY,
+						(HBITMAP)(bitmapstandard), 0, 0, m_clrBack);
+					else
+						DitherBlt2(pDC, rect.left + 2, rect.top + 2 + dy, m_iconX, m_iconY,
+							bitmapstandard, 0, 0, m_clrBack);
+					bitmapstandard.DeleteObject();
+				}
+			}
+			else if (selectedflag) {
+				pDC->FillRect(rect2, &m_brBackground);
+				rect2.SetRect(rect.left, rect.top + dy, rect.left + m_iconX + 4,
+					rect.top + m_iconY + 4 + dy);
+				if (IsNewShell()) {
+					if (state&ODS_CHECKED)
+						pDC->Draw3dRect(rect2, GetSysColor(COLOR_3DSHADOW),
+							GetSysColor(COLOR_3DHILIGHT));
+					else
+						pDC->Draw3dRect(rect2, GetSysColor(COLOR_3DHILIGHT),
+							GetSysColor(COLOR_3DSHADOW));
+				}
+				CPoint ptImage(rect.left + 2, rect.top + 2 + dy);
+				if (bitmap)bitmap->Draw(pDC, xoffset, ptImage, ILD_TRANSPARENT);
+			}
+			else {
+				if (state&ODS_CHECKED) {
+					CBrush brush;
+					COLORREF col = m_clrBack;
+					col = LightenColor(col, 0.6);
+					brush.CreateSolidBrush(col);
+					pDC->FillRect(rect2, &brush);
+					brush.DeleteObject();
+					rect2.SetRect(rect.left, rect.top + dy, rect.left + m_iconX + 4,
+						rect.top + m_iconY + 4 + dy);
+					if (IsNewShell())
+						pDC->Draw3dRect(rect2, GetSysColor(COLOR_3DSHADOW),
+							GetSysColor(COLOR_3DHILIGHT));
+				}
+				else {
+					pDC->FillRect(rect2, &m_brBackground);
+					rect2.SetRect(rect.left, rect.top + dy, rect.left + m_iconX + 4,
+						rect.top + m_iconY + 4 + dy);
+					pDC->Draw3dRect(rect2, m_clrBack, m_clrBack);
+				}
+				CPoint ptImage(rect.left + 2, rect.top + 2 + dy);
+				if (bitmap)bitmap->Draw(pDC, xoffset, ptImage, ILD_TRANSPARENT);
+			}
+		}
+		if (nIconNormal < 0 && state&ODS_CHECKED && !checkflag) {
+			rect2.SetRect(rect.left + 1, rect.top + 2 + dy, rect.left + m_iconX + 1,
+				rect.top + m_iconY + 2 + dy);
+			CMenuItemInfo info;
+			info.fMask = MIIM_CHECKMARKS;
+			::GetMenuItemInfo((HMENU)lpDIS->hwndItem, lpDIS->itemID,
+				MF_BYCOMMAND, &info);
+			if (state&ODS_CHECKED || info.hbmpUnchecked) {
+				Draw3DCheckmark(pDC, rect2, state&ODS_SELECTED,
+					state&ODS_CHECKED ? info.hbmpChecked :
+					info.hbmpUnchecked);
+			}
+		}
+
+		//This is needed always so that we can have the space for check marks
+
+		x0 = rect.left; y0 = rect.top;
+		rect.left = rect.left + m_iconX + 8 + BCMENU_GAP;
+
+		if (!strText.IsEmpty()) {
+
+			CRect rectt(rect.left, rect.top - 1, rect.right, rect.bottom - 1);
+
+			//   Find tabs
+
+			CString leftStr, rightStr;
+			leftStr.Empty(); rightStr.Empty();
+			int tablocr = strText.ReverseFind(_T('\t'));
+			if (tablocr != -1) {
+				rightStr = strText.Mid(tablocr + 1);
+				leftStr = strText.Left(strText.Find(_T('\t')));
+				rectt.right -= m_iconX;
+			}
+			else leftStr = strText;
+
+			int iOldMode = pDC->GetBkMode();
+			pDC->SetBkMode(TRANSPARENT);
+
+			// Draw the text in the correct colour:
+
+			UINT nFormat = DT_LEFT | DT_SINGLELINE | DT_VCENTER;
+			UINT nFormatr = DT_RIGHT | DT_SINGLELINE | DT_VCENTER;
+			if (!(lpDIS->itemState & ODS_GRAYED)) {
+				pDC->SetTextColor(crText);
+				pDC->DrawText(leftStr, rectt, nFormat);
+				if (tablocr != -1) pDC->DrawText(rightStr, rectt, nFormatr);
+			}
+			else {
+
+				// Draw the disabled text
+				if (!(state & ODS_SELECTED)) {
+					RECT offset = *rectt;
+					offset.left += 1;
+					offset.right += 1;
+					offset.top += 1;
+					offset.bottom += 1;
+					pDC->SetTextColor(GetSysColor(COLOR_BTNHILIGHT));
+					pDC->DrawText(leftStr, &offset, nFormat);
+					if (tablocr != -1) pDC->DrawText(rightStr, &offset, nFormatr);
+					pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
+					pDC->DrawText(leftStr, rectt, nFormat);
+					if (tablocr != -1) pDC->DrawText(rightStr, rectt, nFormatr);
+				}
+				else {
+					// And the standard Grey text:
+					pDC->SetTextColor(m_clrBack);
+					pDC->DrawText(leftStr, rectt, nFormat);
+					if (tablocr != -1) pDC->DrawText(rightStr, rectt, nFormatr);
+				}
+			}
+			pDC->SetBkMode(iOldMode);
+		}
+
+		m_penBack.DeleteObject();
+		m_brSelect.DeleteObject();
+	}
+	m_brBackground.DeleteObject();
+}
+
+COLORREF BCMenu::LightenColor(COLORREF col, double factor)
+{
+	if (factor > 0.0&&factor <= 1.0) {
+		BYTE red, green, blue, lightred, lightgreen, lightblue;
+		red = GetRValue(col);
+		green = GetGValue(col);
+		blue = GetBValue(col);
+		lightred = (BYTE)((factor*(255 - red)) + red);
+		lightgreen = (BYTE)((factor*(255 - green)) + green);
+		lightblue = (BYTE)((factor*(255 - blue)) + blue);
+		col = RGB(lightred, lightgreen, lightblue);
+	}
+	return(col);
+}
+
+COLORREF BCMenu::DarkenColor(COLORREF col, double factor)
+{
+	if (factor > 0.0&&factor <= 1.0) {
+		BYTE red, green, blue, lightred, lightgreen, lightblue;
+		red = GetRValue(col);
+		green = GetGValue(col);
+		blue = GetBValue(col);
+		lightred = (BYTE)(red - (factor*red));
+		lightgreen = (BYTE)(green - (factor*green));
+		lightblue = (BYTE)(blue - (factor*blue));
+		col = RGB(lightred, lightgreen, lightblue);
+	}
+	return(col);
+}
+
+
+void BCMenu::DrawItem_WinXP(LPDRAWITEMSTRUCT lpDIS)
+{
+	ASSERT(lpDIS != NULL);
+	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
+#ifdef BCMENU_USE_MEMDC
+	BCMenuMemDC *pMemDC = NULL;
+#endif
+	CRect rect, rect2;
+	UINT state = (((BCMenuData*)(lpDIS->itemData))->nFlags);
+	COLORREF m_newclrBack = GetSysColor(COLOR_3DFACE);
+	COLORREF m_clrBack = GetSysColor(COLOR_WINDOW);
+	CFont m_fontMenu, *pFont = NULL;
+	LOGFONT m_lf;
+	if (!IsWinXPLuna())m_newclrBack = LightenColor(m_newclrBack, 0.25);
+	CBrush m_newbrBackground, m_brBackground;
+	m_brBackground.CreateSolidBrush(m_clrBack);
+	m_newbrBackground.CreateSolidBrush(m_newclrBack);
+	int BCMENU_PAD = 4;
+	if (xp_draw_3D_bitmaps)BCMENU_PAD = 7;
+	int barwidth = m_iconX + BCMENU_PAD;
+
+	// remove the selected bit if it's grayed out
+	if (lpDIS->itemState & ODS_GRAYED && !xp_select_disabled) {
+		if (lpDIS->itemState & ODS_SELECTED)lpDIS->itemState = lpDIS->itemState & ~ODS_SELECTED;
+#ifdef BCMENU_USE_MEMDC
+		pMemDC = new BCMenuMemDC(pDC, &lpDIS->rcItem);
+		pDC = pMemDC;
+		ZeroMemory((PVOID)&m_lf, sizeof(LOGFONT));
+		NONCLIENTMETRICS nm;
+		nm.cbSize = sizeof(NONCLIENTMETRICS);
+		VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, 0));
+		m_lf = nm.lfMenuFont;
+		m_fontMenu.CreateFontIndirect(&m_lf);
+		pFont = pDC->SelectObject(&m_fontMenu);
+#endif
+
+	}
+
+	if (state & MF_SEPARATOR) {
+		rect.CopyRect(&lpDIS->rcItem);
+		pDC->FillRect(rect, &m_brBackground);
+		rect2.SetRect(rect.left, rect.top, rect.left + barwidth, rect.bottom);
+		rect.top += rect.Height() >> 1;
+		rect.left = rect2.right + BCMENU_PAD;
+		pDC->DrawEdge(&rect, EDGE_ETCHED, BF_TOP);
+		pDC->FillRect(rect2, &m_newbrBackground);
+		pDC->Draw3dRect(rect2, m_newclrBack, m_newclrBack);
+	}
+	else {
+		BOOL standardflag = FALSE, selectedflag = FALSE, disableflag = FALSE;
+		BOOL checkflag = FALSE;
+		COLORREF crText = GetSysColor(COLOR_MENUTEXT);
+		COLORREF crSelect = GetSysColor(COLOR_HIGHLIGHT);
+		COLORREF crSelectFill;
+		if (!IsWinXPLuna())crSelectFill = LightenColor(crSelect, 0.85);
+		else crSelectFill = LightenColor(crSelect, 0.7);
+		CBrush m_brSelect;
+		CPen m_penBack;
+		int x0, y0, dx, dy;
+		int nIconNormal = -1, xoffset = -1, global_offset = -1;
+		int faded_offset = 1, shadow_offset = 2;
+		CImageList *bitmap = NULL;
+		BOOL CanDraw3D = FALSE;
+
+		// set some colors
+		m_penBack.CreatePen(PS_SOLID, 0, m_clrBack);
+		m_brSelect.CreateSolidBrush(crSelectFill);
+
+		// draw the colored rectangle portion
+
+		rect.CopyRect(&lpDIS->rcItem);
+		rect2 = rect;
+
+		// draw the up/down/focused/disabled state
+
+		UINT state = lpDIS->itemState;
+		CString strText;
+
+		if (lpDIS->itemData != NULL) {
+			nIconNormal = (((BCMenuData*)(lpDIS->itemData))->menuIconNormal);
+			xoffset = (((BCMenuData*)(lpDIS->itemData))->xoffset);
+			bitmap = (((BCMenuData*)(lpDIS->itemData))->bitmap);
+			strText = ((BCMenuData*)(lpDIS->itemData))->GetString();
+			global_offset = (((BCMenuData*)(lpDIS->itemData))->global_offset);
+
+			if (xoffset == 0 && xp_draw_3D_bitmaps&&bitmap&&bitmap->GetImageCount() > 2)CanDraw3D = TRUE;
+
+			if (nIconNormal < 0 && xoffset < 0 && global_offset >= 0) {
+				xoffset = global_offset;
+				nIconNormal = 0;
+				bitmap = &m_AllImages;
+				if (xp_draw_3D_bitmaps&&CanDraw3DImageList(global_offset)) {
+					CanDraw3D = TRUE;
+					faded_offset = global_offset + 1;
+					shadow_offset = global_offset + 2;
+				}
+			}
+
+
+			if (state&ODS_CHECKED && nIconNormal < 0) {
+				if (state&ODS_SELECTED && m_selectcheck > 0)checkflag = TRUE;
+				else if (m_unselectcheck > 0) checkflag = TRUE;
+			}
+			else if (nIconNormal != -1) {
+				standardflag = TRUE;
+				if (state&ODS_SELECTED && !(state&ODS_GRAYED))selectedflag = TRUE;
+				else if (state&ODS_GRAYED) disableflag = TRUE;
+			}
+		}
+		else {
+			strText.Empty();
+		}
+
+		if (state&ODS_SELECTED) { // draw the down edges
+
+			CPen *pOldPen = pDC->SelectObject(&m_penBack);
+
+			pDC->FillRect(rect, &m_brSelect);
+			pDC->Draw3dRect(rect, crSelect, crSelect);
+
+			pDC->SelectObject(pOldPen);
+		}
+		else {
+			rect2.SetRect(rect.left, rect.top, rect.left + barwidth, rect.bottom);
+			CPen *pOldPen = pDC->SelectObject(&m_penBack);
+			pDC->FillRect(rect, &m_brBackground);
+			pDC->FillRect(rect2, &m_newbrBackground);
+			pDC->SelectObject(pOldPen);
+
+			// draw the up edges
+
+			pDC->Draw3dRect(rect, m_clrBack, m_clrBack);
+			pDC->Draw3dRect(rect2, m_newclrBack, m_newclrBack);
+		}
+
+		// draw the text if there is any
+		//We have to paint the text only if the image is nonexistant
+
+		dy = (int)(0.5 + (rect.Height() - m_iconY) / 2.0);
+		dy = dy < 0 ? 0 : dy;
+		dx = (int)(0.5 + (barwidth - m_iconX) / 2.0);
+		dx = dx < 0 ? 0 : dx;
+
+		if (checkflag || standardflag || selectedflag || disableflag) {
+			rect2.SetRect(rect.left + 1, rect.top + 1, rect.left + barwidth - 1,
+				rect.bottom - 1);
+			if (checkflag && checkmaps) {
+				pDC->FillRect(rect2, &m_newbrBackground);
+				CPoint ptImage(rect.left + dx, rect.top + dy);
+				if (state&ODS_SELECTED)checkmaps->Draw(pDC, 1, ptImage, ILD_TRANSPARENT);
+				else checkmaps->Draw(pDC, 0, ptImage, ILD_TRANSPARENT);
+			}
+			else if (disableflag) {
+				if (!selectedflag) {
+					CBitmap bitmapstandard;
+					GetBitmapFromImageList(pDC, bitmap, xoffset, bitmapstandard);
+					COLORREF transparentcol = m_newclrBack;
+					if (state&ODS_SELECTED)transparentcol = crSelectFill;
+					if (disable_old_style)
+						DitherBlt(lpDIS->hDC, rect.left + dx, rect.top + dy, m_iconX, m_iconY,
+						(HBITMAP)(bitmapstandard), 0, 0, transparentcol);
+					else
+						DitherBlt2(pDC, rect.left + dx, rect.top + dy, m_iconX, m_iconY,
+							bitmapstandard, 0, 0, transparentcol);
+					if (state&ODS_SELECTED)pDC->Draw3dRect(rect, crSelect, crSelect);
+					bitmapstandard.DeleteObject();
+				}
+			}
+			else if (selectedflag) {
+				pDC->FillRect(rect2, &m_brSelect);
+				CPoint ptImage(rect.left + dx, rect.top + dy);
+				if (state&ODS_CHECKED) {
+					pDC->Draw3dRect(rect2, crSelect, crSelect);
+					ptImage.x -= 1; ptImage.y -= 1;
+				}
+				if (bitmap) {
+					if (CanDraw3D && !(state&ODS_CHECKED)) {
+						CPoint ptImage1(ptImage.x + 1, ptImage.y + 1);
+						CPoint ptImage2(ptImage.x - 1, ptImage.y - 1);
+						bitmap->Draw(pDC, shadow_offset, ptImage1, ILD_TRANSPARENT);
+						bitmap->Draw(pDC, xoffset, ptImage2, ILD_TRANSPARENT);
+					}
+					else bitmap->Draw(pDC, xoffset, ptImage, ILD_TRANSPARENT);
+				}
+			}
+			else {
+				if (state&ODS_CHECKED) {
+					CBrush brushin;
+					brushin.CreateSolidBrush(LightenColor(crSelect, 0.85));
+					pDC->FillRect(rect2, &brushin);
+					brushin.DeleteObject();
+					pDC->Draw3dRect(rect2, crSelect, crSelect);
+					CPoint ptImage(rect.left + dx - 1, rect.top + dy - 1);
+					if (bitmap)bitmap->Draw(pDC, xoffset, ptImage, ILD_TRANSPARENT);
+				}
+				else {
+					pDC->FillRect(rect2, &m_newbrBackground);
+					pDC->Draw3dRect(rect2, m_newclrBack, m_newclrBack);
+					CPoint ptImage(rect.left + dx, rect.top + dy);
+					if (bitmap) {
+						if (CanDraw3D)
+							bitmap->Draw(pDC, faded_offset, ptImage, ILD_TRANSPARENT);
+						else
+							bitmap->Draw(pDC, xoffset, ptImage, ILD_TRANSPARENT);
+					}
+				}
+			}
+		}
+		if (nIconNormal < 0 && state&ODS_CHECKED && !checkflag) {
+			dy = (int)(0.5 + (rect.Height() - 16) / 2.0);
+			dy = dy < 0 ? 0 : dy;
+			dx = (int)(0.5 + (barwidth - 16) / 2.0);
+			dx = dx < 0 ? 0 : dx;
+			CMenuItemInfo info;
+			info.fMask = MIIM_CHECKMARKS;
+			::GetMenuItemInfo((HMENU)lpDIS->hwndItem, lpDIS->itemID,
+				MF_BYCOMMAND, &info);
+			if (state&ODS_CHECKED || info.hbmpUnchecked) {
+				rect2.SetRect(rect.left + dx, rect.top + dy, rect.left + dx + 16, rect.top + dy + 16);
+				DrawXPCheckmark(pDC, rect2, state&ODS_CHECKED ? info.hbmpChecked :
+					info.hbmpUnchecked, crSelect);
+			}
+		}
+
+		//This is needed always so that we can have the space for check marks
+
+		x0 = rect.left; y0 = rect.top;
+		rect.left = rect.left + barwidth + 8;
+
+		if (!strText.IsEmpty()) {
+
+			CRect rectt(rect.left, rect.top - 1, rect.right, rect.bottom - 1);
+
+			//   Find tabs
+
+			CString leftStr, rightStr;
+			leftStr.Empty(); rightStr.Empty();
+			int tablocr = strText.ReverseFind(_T('\t'));
+			if (tablocr != -1) {
+				rightStr = strText.Mid(tablocr + 1);
+				leftStr = strText.Left(strText.Find(_T('\t')));
+				rectt.right -= m_iconX;
+			}
+			else leftStr = strText;
+
+			int iOldMode = pDC->GetBkMode();
+			pDC->SetBkMode(TRANSPARENT);
+
+			// Draw the text in the correct colour:
+
+			UINT nFormat = DT_LEFT | DT_SINGLELINE | DT_VCENTER;
+			UINT nFormatr = DT_RIGHT | DT_SINGLELINE | DT_VCENTER;
+			if (!(lpDIS->itemState & ODS_GRAYED)) {
+				pDC->SetTextColor(crText);
+				pDC->DrawText(leftStr, rectt, nFormat);
+				if (tablocr != -1) pDC->DrawText(rightStr, rectt, nFormatr);
+			}
+			else {
+				RECT offset = *rectt;
+				offset.left += 1;
+				offset.right += 1;
+				offset.top += 1;
+				offset.bottom += 1;
+				if (!IsWinXPLuna()) {
+					COLORREF graycol = GetSysColor(COLOR_GRAYTEXT);
+					if (!(state&ODS_SELECTED))graycol = LightenColor(graycol, 0.4);
+					pDC->SetTextColor(graycol);
+				}
+				else pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
+				pDC->DrawText(leftStr, rectt, nFormat);
+				if (tablocr != -1) pDC->DrawText(rightStr, rectt, nFormatr);
+			}
+			pDC->SetBkMode(iOldMode);
+		}
+
+		m_penBack.DeleteObject();
+		m_brSelect.DeleteObject();
+	}
+	m_brBackground.DeleteObject();
+	m_newbrBackground.DeleteObject();
+#ifdef BCMENU_USE_MEMDC
+	if (pFont)pDC->SelectObject(pFont); //set it to the old font
+	m_fontMenu.DeleteObject();
+	if (pMemDC)delete pMemDC;
+#endif
+}
+
+BOOL BCMenu::GetBitmapFromImageList(CDC* pDC, CImageList *imglist, int nIndex, CBitmap &bmp)
+{
+	HICON hIcon = imglist->ExtractIcon(nIndex);
+	CDC dc;
+	dc.CreateCompatibleDC(pDC);
+	bmp.CreateCompatibleBitmap(pDC, m_iconX, m_iconY);
+	CBitmap* pOldBmp = dc.SelectObject(&bmp);
+	CBrush brush;
+	COLORREF m_newclrBack;
+	m_newclrBack = GetSysColor(COLOR_3DFACE);
+	brush.CreateSolidBrush(m_newclrBack);
+	::DrawIconEx(
+		dc.GetSafeHdc(),
+		0,
+		0,
+		hIcon,
+		m_iconX,
+		m_iconY,
+		0,
+		(HBRUSH)brush,
+		DI_NORMAL
+	);
+	dc.SelectObject(pOldBmp);
+	dc.DeleteDC();
+	// the icon is not longer needed
+	::DestroyIcon(hIcon);
+	return(TRUE);
+}
+
+/*
+==========================================================================
+void BCMenu::MeasureItem(LPMEASUREITEMSTRUCT)
+---------------------------------------------
+
+Called by the framework when it wants to know what the width and height
+of our item will be.  To accomplish this we provide the width of the
+icon plus the width of the menu text, and then the height of the icon.
+
+==========================================================================
+*/
+
+void BCMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
+{
+	UINT state = (((BCMenuData*)(lpMIS->itemData))->nFlags);
+	int BCMENU_PAD = 4;
+	if (IsLunaMenuStyle() && xp_draw_3D_bitmaps)BCMENU_PAD = 7;
+	if (state & MF_SEPARATOR) {
+		lpMIS->itemWidth = 0;
+		int temp = GetSystemMetrics(SM_CYMENU) >> 1;
+		lpMIS->itemHeight = temp > (m_iconY + BCMENU_PAD) / 2 ? temp : (m_iconY + BCMENU_PAD) / 2;
+	}
+	else {
+		CFont m_fontMenu;
+		LOGFONT m_lf;
+		ZeroMemory((PVOID)&m_lf, sizeof(LOGFONT));
+		NONCLIENTMETRICS nm;
+		nm.cbSize = sizeof(NONCLIENTMETRICS);
+		VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
+			nm.cbSize, &nm, 0));
+		m_lf = nm.lfMenuFont;
+		m_fontMenu.CreateFontIndirect(&m_lf);
+
+		// Obtain the width of the text:
+		CWnd *pWnd = AfxGetMainWnd();            // Get main window
+		if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+		CDC *pDC = pWnd->GetDC();              // Get device context
+		CFont* pFont = NULL;    // Select menu font in...
+
+		if (IsNewShell())
+			pFont = pDC->SelectObject(&m_fontMenu);// Select menu font in...
+
+		//Get pointer to text SK
+		const wchar_t *lpstrText = ((BCMenuData*)(lpMIS->itemData))->GetWideString();//SK: we use const to prevent misuse
+
+		SIZE size;
+		size.cx = size.cy = 0;
+
+		if (Win32s != g_Shell)
+			VERIFY(::GetTextExtentPoint32W(pDC->m_hDC, lpstrText,
+				wcslen(lpstrText), &size)); //SK should also work on 95
+#ifndef UNICODE //can't be UNICODE for Win32s
+		else {//it's Win32suckx
+			RECT rect;
+			rect.left = rect.top = 0;
+			size.cy = DrawText(pDC->m_hDC, (LPCTSTR)lpstrText,
+				wcslen(lpstrText), &rect,
+				DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT);
+			//+3 makes at least three pixels space to the menu border
+			size.cx = rect.right - rect.left + 3;
+			size.cx += 3 * (size.cx / wcslen(lpstrText));
+		}
+#endif    
+
+		CSize t = CSize(size);
+		if (IsNewShell())
+			pDC->SelectObject(pFont);  // Select old font in
+		pWnd->ReleaseDC(pDC);  // Release the DC
+
+		// Set width and height:
+
+		if (IsLunaMenuStyle())lpMIS->itemWidth = m_iconX + BCMENU_PAD + 8 + t.cx;
+		else lpMIS->itemWidth = m_iconX + t.cx + m_iconX + BCMENU_GAP;
+		int temp = GetSystemMetrics(SM_CYMENU);
+		lpMIS->itemHeight = temp > m_iconY + BCMENU_PAD ? temp : m_iconY + BCMENU_PAD;
+		m_fontMenu.DeleteObject();
+	}
+}
+
+void BCMenu::SetIconSize(int width, int height)
+{
+	m_iconX = width;
+	m_iconY = height;
+}
+
+BOOL BCMenu::AppendODMenuA(LPCSTR lpstrText, UINT nFlags, UINT nID,
+	int nIconNormal)
+{
+	USES_CONVERSION;
+	return AppendODMenuW(A2W(lpstrText), nFlags, nID, nIconNormal);//SK: See MFC Tech Note 059
+}
+
+
+BOOL BCMenu::AppendODMenuW(wchar_t *lpstrText, UINT nFlags, UINT nID,
+	int nIconNormal)
+{
+	// Add the MF_OWNERDRAW flag if not specified:
+	if (!nID) {
+		if (nFlags&MF_BYPOSITION)nFlags = MF_SEPARATOR | MF_OWNERDRAW | MF_BYPOSITION;
+		else nFlags = MF_SEPARATOR | MF_OWNERDRAW;
+	}
+	else if (!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	if (nFlags & MF_POPUP) {
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.Add((HMENU)nID);
+	}
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.Add(mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	mdata->menuIconNormal = -1;
+	mdata->xoffset = -1;
+
+	if (nIconNormal >= 0) {
+		CImageList bitmap;
+		int xoffset = 0;
+		LoadFromToolBar(nID, nIconNormal, xoffset);
+		if (mdata->bitmap) {
+			mdata->bitmap->DeleteImageList();
+			mdata->bitmap = NULL;
+		}
+		bitmap.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+		if (AddBitmapToImageList(&bitmap, nIconNormal)) {
+			mdata->global_offset = AddToGlobalImageList(&bitmap, xoffset, nID);
+		}
+	}
+	else mdata->global_offset = GlobalImageListOffset(nID);
+
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	BOOL returnflag = CMenu::AppendMenu(nFlags, nID, (LPCTSTR)mdata);
+	if (m_loadmenu)RemoveTopLevelOwnerDraw();
+	return(returnflag);
+}
+
+BOOL BCMenu::AppendODMenuA(LPCSTR lpstrText, UINT nFlags, UINT nID,
+	CImageList *il, int xoffset)
+{
+	USES_CONVERSION;
+	return AppendODMenuW(A2W(lpstrText), nFlags, nID, il, xoffset);
+}
+
+BOOL BCMenu::AppendODMenuW(wchar_t *lpstrText, UINT nFlags, UINT nID,
+	CImageList *il, int xoffset)
+{
+	// Add the MF_OWNERDRAW flag if not specified:
+	if (!nID) {
+		if (nFlags&MF_BYPOSITION)nFlags = MF_SEPARATOR | MF_OWNERDRAW | MF_BYPOSITION;
+		else nFlags = MF_SEPARATOR | MF_OWNERDRAW;
+	}
+	else if (!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	if (nFlags & MF_POPUP) {
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.Add((HMENU)nID);
+	}
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.Add(mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	if (il) {
+		mdata->menuIconNormal = 0;
+		mdata->xoffset = 0;
+		if (mdata->bitmap)mdata->bitmap->DeleteImageList();
+		else mdata->bitmap = new(CImageList);
+		ImageListDuplicate(il, xoffset, mdata->bitmap);
+	}
+	else {
+		mdata->menuIconNormal = -1;
+		mdata->xoffset = -1;
+	}
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	return(CMenu::AppendMenu(nFlags, nID, (LPCTSTR)mdata));
+}
+
+BOOL BCMenu::InsertODMenuA(UINT nPosition, LPCSTR lpstrText, UINT nFlags, UINT nID,
+	int nIconNormal)
+{
+	USES_CONVERSION;
+	return InsertODMenuW(nPosition, A2W(lpstrText), nFlags, nID, nIconNormal);
+}
+
+
+BOOL BCMenu::InsertODMenuW(UINT nPosition, wchar_t *lpstrText, UINT nFlags, UINT nID,
+	int nIconNormal)
+{
+	if (!(nFlags & MF_BYPOSITION)) {
+		int iPosition = 0;
+		BCMenu* pMenu = FindMenuOption(nPosition, iPosition);
+		if (pMenu) {
+			return(pMenu->InsertODMenuW(iPosition, lpstrText, nFlags | MF_BYPOSITION, nID, nIconNormal));
+		}
+		else return(FALSE);
+	}
+
+	if (!nID)nFlags = MF_SEPARATOR | MF_OWNERDRAW | MF_BYPOSITION;
+	else if (!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	int menustart = 0;
+
+	if (nFlags & MF_POPUP) {
+		if (m_loadmenu) {
+			menustart = GetMenuStart();
+			if (nPosition < (UINT)menustart)menustart = 0;
+		}
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.InsertAt(nPosition, (HMENU)nID);
+	}
+
+	//Stephane Clog suggested adding this, believe it or not it's in the help 
+	if (nPosition == (UINT)-1)nPosition = GetMenuItemCount();
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.InsertAt(nPosition - menustart, mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	mdata->menuIconNormal = nIconNormal;
+	mdata->xoffset = -1;
+	if (nIconNormal >= 0) {
+		CImageList bitmap;
+		int xoffset = 0;
+		LoadFromToolBar(nID, nIconNormal, xoffset);
+		if (mdata->bitmap) {
+			mdata->bitmap->DeleteImageList();
+			mdata->bitmap = NULL;
+		}
+		bitmap.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+		if (AddBitmapToImageList(&bitmap, nIconNormal)) {
+			mdata->global_offset = AddToGlobalImageList(&bitmap, xoffset, nID);
+		}
+	}
+	else mdata->global_offset = GlobalImageListOffset(nID);
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	BOOL returnflag = CMenu::InsertMenu(nPosition, nFlags, nID, (LPCTSTR)mdata);
+	if (m_loadmenu)RemoveTopLevelOwnerDraw();
+	return(returnflag);
+}
+
+BOOL BCMenu::InsertODMenuA(UINT nPosition, LPCSTR lpstrText, UINT nFlags, UINT nID,
+	CImageList *il, int xoffset)
+{
+	USES_CONVERSION;
+	return InsertODMenuW(nPosition, A2W(lpstrText), nFlags, nID, il, xoffset);
+}
+
+BOOL BCMenu::InsertODMenuW(UINT nPosition, wchar_t *lpstrText, UINT nFlags, UINT nID,
+	CImageList *il, int xoffset)
+{
+	if (!(nFlags & MF_BYPOSITION)) {
+		int iPosition = 0;
+		BCMenu* pMenu = FindMenuOption(nPosition, iPosition);
+		if (pMenu) {
+			return(pMenu->InsertODMenuW(iPosition, lpstrText, nFlags | MF_BYPOSITION, nID, il, xoffset));
+		}
+		else return(FALSE);
+	}
+
+	if (!nID)nFlags = MF_SEPARATOR | MF_OWNERDRAW | MF_BYPOSITION;
+	else if (!(nFlags & MF_OWNERDRAW))nFlags |= MF_OWNERDRAW;
+
+	if (nFlags & MF_POPUP) {
+		m_AllSubMenus.Add((HMENU)nID);
+		m_SubMenus.InsertAt(nPosition, (HMENU)nID);
+	}
+
+	//Stephane Clog suggested adding this, believe it or not it's in the help 
+	if (nPosition == (UINT)-1)nPosition = GetMenuItemCount();
+
+	BCMenuData *mdata = new BCMenuData;
+	m_MenuList.InsertAt(nPosition, mdata);
+	mdata->SetWideString(lpstrText);    //SK: modified for dynamic allocation
+
+	mdata->menuIconNormal = -1;
+	mdata->xoffset = -1;
+
+	if (il) {
+		if (mdata->bitmap) {
+			mdata->bitmap->DeleteImageList();
+			mdata->bitmap = NULL;
+		}
+		mdata->global_offset = AddToGlobalImageList(il, xoffset, nID);
+	}
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+	return(CMenu::InsertMenu(nPosition, nFlags, nID, (LPCTSTR)mdata));
+}
+
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText, UINT nID, int nIconNormal)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText), nID, nIconNormal);//SK: see MFC Tech Note 059
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText, UINT nID, int nIconNormal)
+{
+	int nLoc;
+	BCMenuData *mdata;
+	CArray<BCMenu*, BCMenu*>bcsubs;
+	CArray<int, int&>bclocs;
+
+	// Find the old BCMenuData structure:
+	BCMenu *psubmenu = FindMenuOption(nID, nLoc);
+	do {
+		if (psubmenu && nLoc >= 0)mdata = psubmenu->m_MenuList[nLoc];
+		else {
+			// Create a new BCMenuData structure:
+			mdata = new BCMenuData;
+			m_MenuList.Add(mdata);
+		}
+
+		ASSERT(mdata);
+		if (lpstrText)
+			mdata->SetWideString(lpstrText);  //SK: modified for dynamic allocation
+		mdata->menuIconNormal = -1;
+		mdata->xoffset = -1;
+		if (nIconNormal >= 0) {
+			CImageList bitmap;
+			int xoffset = 0;
+			LoadFromToolBar(nID, nIconNormal, xoffset);
+			if (mdata->bitmap) {
+				mdata->bitmap->DeleteImageList();
+				mdata->bitmap = NULL;
+			}
+			bitmap.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+			if (AddBitmapToImageList(&bitmap, nIconNormal)) {
+				mdata->global_offset = AddToGlobalImageList(&bitmap, xoffset, nID);
+			}
+		}
+		else mdata->global_offset = GlobalImageListOffset(nID);
+		mdata->nFlags &= ~(MF_BYPOSITION);
+		mdata->nFlags |= MF_OWNERDRAW;
+		mdata->nID = nID;
+		bcsubs.Add(psubmenu);
+		bclocs.Add(nLoc);
+		if (psubmenu && nLoc >= 0)psubmenu = FindAnotherMenuOption(nID, nLoc, bcsubs, bclocs);
+		else psubmenu = NULL;
+	} while (psubmenu);
+	return (CMenu::ModifyMenu(nID, mdata->nFlags, nID, (LPCTSTR)mdata));
+}
+
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText, UINT nID, CImageList *il, int xoffset)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText), nID, il, xoffset);
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText, UINT nID, CImageList *il, int xoffset)
+{
+	int nLoc;
+	BCMenuData *mdata;
+	CArray<BCMenu*, BCMenu*>bcsubs;
+	CArray<int, int&>bclocs;
+
+	// Find the old BCMenuData structure:
+	BCMenu *psubmenu = FindMenuOption(nID, nLoc);
+	do {
+		if (psubmenu && nLoc >= 0)mdata = psubmenu->m_MenuList[nLoc];
+		else {
+			// Create a new BCMenuData structure:
+			mdata = new BCMenuData;
+			m_MenuList.Add(mdata);
+		}
+
+		ASSERT(mdata);
+		if (lpstrText)
+			mdata->SetWideString(lpstrText);  //SK: modified for dynamic allocation
+		mdata->menuIconNormal = -1;
+		mdata->xoffset = -1;
+		if (il) {
+			if (mdata->bitmap) {
+				mdata->bitmap->DeleteImageList();
+				mdata->bitmap = NULL;
+			}
+			mdata->global_offset = AddToGlobalImageList(il, xoffset, nID);
+		}
+		mdata->nFlags &= ~(MF_BYPOSITION);
+		mdata->nFlags |= MF_OWNERDRAW;
+		mdata->nID = nID;
+		bcsubs.Add(psubmenu);
+		bclocs.Add(nLoc);
+		if (psubmenu && nLoc >= 0)psubmenu = FindAnotherMenuOption(nID, nLoc, bcsubs, bclocs);
+		else psubmenu = NULL;
+	} while (psubmenu);
+	return (CMenu::ModifyMenu(nID, mdata->nFlags, nID, (LPCTSTR)mdata));
+}
+
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText, UINT nID, CBitmap *bmp)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText), nID, bmp);
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText, UINT nID, CBitmap *bmp)
+{
+	if (bmp) {
+		CImageList temp;
+		temp.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+		if (m_bitmapBackgroundFlag)temp.Add(bmp, m_bitmapBackground);
+		else temp.Add(bmp, GetSysColor(COLOR_3DFACE));
+		return ModifyODMenuW(lpstrText, nID, &temp, 0);
+	}
+	return ModifyODMenuW(lpstrText, nID, NULL, 0);
+}
+
+// courtesy of Warren Stevens
+BOOL BCMenu::ModifyODMenuA(const char * lpstrText, UINT nID, COLORREF fill, COLORREF border, int hatchstyle, CSize *pSize)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText), nID, fill, border, hatchstyle, pSize);
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText, UINT nID, COLORREF fill, COLORREF border, int hatchstyle, CSize *pSize)
+{
+	CWnd *pWnd = AfxGetMainWnd();            // Get main window
+	CDC *pDC = pWnd->GetDC();              // Get device context
+	SIZE sz;
+	if (!pSize) {
+		sz.cx = m_iconX;
+		sz.cy = m_iconY;
+	}
+	else {
+		sz.cx = pSize->cx;
+		sz.cy = pSize->cy;
+	}
+	CSize bitmap_size(sz);
+	CSize icon_size(m_iconX, m_iconY);
+	CBitmap bmp;
+	ColorBitmap(pDC, bmp, bitmap_size, icon_size, fill, border, hatchstyle);
+	pWnd->ReleaseDC(pDC);
+	return ModifyODMenuW(lpstrText, nID, &bmp);
+}
+
+
+BOOL BCMenu::ModifyODMenuA(const char *lpstrText, const char *OptionText,
+	int nIconNormal)
+{
+	USES_CONVERSION;
+	return ModifyODMenuW(A2W(lpstrText), A2W(OptionText), nIconNormal);//SK: see MFC  Tech Note 059
+}
+
+BOOL BCMenu::ModifyODMenuW(wchar_t *lpstrText, wchar_t *OptionText,
+	int nIconNormal)
+{
+	BCMenuData *mdata;
+
+	// Find the old BCMenuData structure:
+	mdata = FindMenuOption(OptionText);
+	if (mdata) {
+		if (lpstrText)
+			mdata->SetWideString(lpstrText);//SK: modified for dynamic allocation
+		mdata->menuIconNormal = nIconNormal;
+		mdata->xoffset = -1;
+		if (nIconNormal >= 0) {
+			mdata->xoffset = 0;
+			if (mdata->bitmap)mdata->bitmap->DeleteImageList();
+			else mdata->bitmap = new(CImageList);
+			mdata->bitmap->Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+			if (!AddBitmapToImageList(mdata->bitmap, nIconNormal)) {
+				mdata->bitmap->DeleteImageList();
+				delete mdata->bitmap;
+				mdata->bitmap = NULL;
+				mdata->menuIconNormal = nIconNormal = -1;
+				mdata->xoffset = -1;
+			}
+		}
+		return(TRUE);
+	}
+	return(FALSE);
+}
+
+BCMenuData *BCMenu::NewODMenu(UINT pos, UINT nFlags, UINT nID, CString string)
+{
+	BCMenuData *mdata;
+
+	mdata = new BCMenuData;
+	mdata->menuIconNormal = -1;
+	mdata->xoffset = -1;
+#ifdef UNICODE
+	mdata->SetWideString((LPCTSTR)string);//SK: modified for dynamic allocation
+#else
+	mdata->SetAnsiString(string);
+#endif
+	mdata->nFlags = nFlags;
+	mdata->nID = nID;
+
+	//	if(nFlags & MF_POPUP)m_AllSubMenus.Add((HMENU)nID);
+
+	if (nFlags&MF_OWNERDRAW) {
+		ASSERT(!(nFlags&MF_STRING));
+		ModifyMenu(pos, nFlags, nID, (LPCTSTR)mdata);
+	}
+	else if (nFlags&MF_STRING) {
+		ASSERT(!(nFlags&MF_OWNERDRAW));
+		ModifyMenu(pos, nFlags, nID, mdata->GetString());
+	}
+	else {
+		ASSERT(nFlags&MF_SEPARATOR);
+		ModifyMenu(pos, nFlags, nID);
+	}
+
+	return(mdata);
+};
+
+BOOL BCMenu::LoadToolbars(const UINT *arID, int n)
+{
+	ASSERT(arID);
+	BOOL returnflag = TRUE;
+	for (int i = 0; i < n; ++i) {
+		if (!LoadToolbar(arID[i]))returnflag = FALSE;
+	}
+	return(returnflag);
+}
+
+BOOL BCMenu::LoadToolbar(UINT nToolBar)
+{
+	UINT nID, nStyle;
+	BOOL returnflag = FALSE;
+	BCMenuToolBar bar;
+	int xoffset = -1, xset;
+	int iconx, icony;
+
+	CWnd* pWnd = AfxGetMainWnd();
+	if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
+	bar.Create(pWnd);
+	if (bar.LoadToolBar(nToolBar)) {
+		bar.GetIconSize(iconx, icony);
+		if (iconx > m_iconX)m_iconX = iconx;
+		if (icony > m_iconY)m_iconY = icony;
+		CImageList imglist;
+		imglist.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+		if (AddBitmapToImageList(&imglist, nToolBar)) {
+			returnflag = TRUE;
+			for (int i = 0; i < bar.GetCount(); ++i) {
+				nID = bar.GetItemID(i);
+				if (nID && GetMenuState(nID, MF_BYCOMMAND)
+					!= 0xFFFFFFFF) {
+					xoffset = bar.CommandToIndex(nID);
+					if (xoffset >= 0) {
+						bar.GetButtonInfo(xoffset, nID, nStyle, xset);
+						if (xset > 0)xoffset = xset;
+					}
+					ModifyODMenu(NULL, nID, &imglist, xoffset);
+				}
+			}
+		}
+	}
+	return(returnflag);
+}
+
+BOOL BCMenu::LoadFromToolBar(UINT nID, UINT nToolBar, int& xoffset)
+{
+	int xset, offset;
+	UINT nStyle;
+	BOOL returnflag = FALSE;
+	CToolBar bar;
+
+	CWnd* pWnd = AfxGetMainWnd();
+	if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
+	bar.Create(pWnd);
+	if (bar.LoadToolBar(nToolBar)) {
+		offset = bar.CommandToIndex(nID);
+		if (offset >= 0) {
+			bar.GetButtonInfo(offset, nID, nStyle, xset);
+			if (xset > 0)xoffset = xset;
+			returnflag = TRUE;
+		}
+	}
+	return(returnflag);
+}
+
+// O.S.
+BCMenuData *BCMenu::FindMenuItem(UINT nID)
+{
+	BCMenuData *pData = NULL;
+	int i;
+
+	for (i = 0; i <= m_MenuList.GetUpperBound(); i++) {
+		if (m_MenuList[i]->nID == nID) {
+			pData = m_MenuList[i];
+			break;
+		}
+	}
+	if (!pData) {
+		int loc;
+		BCMenu *pMenu = FindMenuOption(nID, loc);
+		ASSERT(pMenu != this);
+		if (loc >= 0) {
+			return pMenu->FindMenuItem(nID);
+		}
+	}
+	return pData;
+}
+
+
+BCMenu *BCMenu::FindAnotherMenuOption(int nId, int& nLoc, CArray<BCMenu*, BCMenu*>&bcsubs,
+	CArray<int, int&>&bclocs)
+{
+	int i, numsubs, j;
+	BCMenu *psubmenu, *pgoodmenu;
+	BOOL foundflag;
+
+	for (i = 0; i < (int)(GetMenuItemCount()); ++i) {
+#ifdef _CPPRTTI 
+		psubmenu = dynamic_cast<BCMenu *>(GetSubMenu(i));
+#else
+		psubmenu = (BCMenu *)GetSubMenu(i);
+#endif
+		if (psubmenu) {
+			pgoodmenu = psubmenu->FindAnotherMenuOption(nId, nLoc, bcsubs, bclocs);
+			if (pgoodmenu)return(pgoodmenu);
+		}
+		else if (nId == (int)GetMenuItemID(i)) {
+			numsubs = bcsubs.GetSize();
+			foundflag = TRUE;
+			for (j = 0; j < numsubs; ++j) {
+				if (bcsubs[j] == this&&bclocs[j] == i) {
+					foundflag = FALSE;
+					break;
+				}
+			}
+			if (foundflag) {
+				nLoc = i;
+				return(this);
+			}
+		}
+	}
+	nLoc = -1;
+	return(NULL);
+}
+
+BCMenu *BCMenu::FindMenuOption(int nId, int& nLoc)
+{
+	int i;
+	BCMenu *psubmenu, *pgoodmenu;
+
+	for (i = 0; i < (int)(GetMenuItemCount()); ++i) {
+#ifdef _CPPRTTI 
+		psubmenu = dynamic_cast<BCMenu *>(GetSubMenu(i));
+#else
+		psubmenu = (BCMenu *)GetSubMenu(i);
+#endif
+		if (psubmenu) {
+			pgoodmenu = psubmenu->FindMenuOption(nId, nLoc);
+			if (pgoodmenu)return(pgoodmenu);
+		}
+		else if (nId == (int)GetMenuItemID(i)) {
+			nLoc = i;
+			return(this);
+		}
+	}
+	nLoc = -1;
+	return(NULL);
+}
+
+BCMenuData *BCMenu::FindMenuOption(wchar_t *lpstrText)
+{
+	int i, j;
+	BCMenu *psubmenu;
+	BCMenuData *pmenulist;
+
+	for (i = 0; i < (int)(GetMenuItemCount()); ++i) {
+#ifdef _CPPRTTI 
+		psubmenu = dynamic_cast<BCMenu *>(GetSubMenu(i));
+#else
+		psubmenu = (BCMenu *)GetSubMenu(i);
+#endif
+		if (psubmenu) {
+			pmenulist = psubmenu->FindMenuOption(lpstrText);
+			if (pmenulist)return(pmenulist);
+		}
+		else {
+			const wchar_t *szWide;//SK: we use const to prevent misuse of this Ptr
+			for (j = 0; j <= m_MenuList.GetUpperBound(); ++j) {
+				szWide = m_MenuList[j]->GetWideString();
+				if (szWide && !wcscmp(lpstrText, szWide))//SK: modified for dynamic allocation
+					return(m_MenuList[j]);
+			}
+		}
+	}
+	return(NULL);
+}
+
+
+BOOL BCMenu::LoadMenu(int nResource)
+{
+	return(BCMenu::LoadMenu(MAKEINTRESOURCE(nResource)));
+};
+
+BOOL BCMenu::LoadMenu(LPCTSTR lpszResourceName)
+{
+	ASSERT_VALID(this);
+	ASSERT(lpszResourceName != NULL);
+
+	// Find the Menu Resource:
+	HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_MENU);
+	HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_MENU);
+	if (hRsrc == NULL) {
+		hInst = NULL;
+		hRsrc = ::FindResource(hInst, lpszResourceName, RT_MENU);
+	}
+	if (hRsrc == NULL)return FALSE;
+
+	// Load the Menu Resource:
+
+	HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
+	if (hGlobal == NULL)return FALSE;
+
+	// first destroy the menu if we're trying to loadmenu again
+	DestroyMenu();
+
+	// Attempt to create us as a menu...
+	if (!CMenu::CreateMenu())return FALSE;
+
+	// Get Item template Header, and calculate offset of MENUITEMTEMPLATES
+
+	MENUITEMTEMPLATEHEADER *pTpHdr =
+		(MENUITEMTEMPLATEHEADER*)LockResource(hGlobal);
+	BYTE* pTp = (BYTE*)pTpHdr +
+		(sizeof(MENUITEMTEMPLATEHEADER) + pTpHdr->offset);
+
+
+	// Variables needed during processing of Menu Item Templates:
+
+	int j = 0;
+	WORD    dwFlags = 0;              // Flags of the Menu Item
+	WORD    dwID = 0;              // ID of the Menu Item
+	UINT    uFlags;                  // Actual Flags.
+	wchar_t *szCaption = NULL;
+	int      nLen = 0;                // Length of caption
+	CTypedPtrArray<CPtrArray, BCMenu*>  m_Stack;    // Popup menu stack
+	CArray<BOOL, BOOL>  m_StackEnd;    // Popup menu stack
+	m_Stack.Add(this);                  // Add it to this...
+	m_StackEnd.Add(FALSE);
+
+	do {
+		// Obtain Flags and (if necessary), the ID...
+		memcpy(&dwFlags, pTp, sizeof(WORD)); pTp += sizeof(WORD);// Obtain Flags
+		if (!(dwFlags & MF_POPUP)) {
+			memcpy(&dwID, pTp, sizeof(WORD)); // Obtain ID
+			pTp += sizeof(WORD);
+		}
+		else dwID = 0;
+
+		uFlags = (UINT)dwFlags; // Remove MF_END from the flags that will
+		if (uFlags & MF_END) // be passed to the Append(OD)Menu functions.
+			uFlags -= MF_END;
+
+		// Obtain Caption (and length)
+
+		nLen = 0;
+		szCaption = new wchar_t[wcslen((wchar_t *)pTp) + 1];
+		wcscpy(szCaption, (wchar_t *)pTp);
+		pTp = &pTp[(wcslen((wchar_t *)pTp) + 1) * sizeof(wchar_t)];//modified SK
+
+		// Handle popup menus first....
+
+		//WideCharToMultiByte
+		if (dwFlags & MF_POPUP) {
+			if (dwFlags & MF_END)m_StackEnd.SetAt(m_Stack.GetUpperBound(), TRUE);
+			BCMenu* pSubMenu = new BCMenu;
+			pSubMenu->m_unselectcheck = m_unselectcheck;
+			pSubMenu->m_selectcheck = m_selectcheck;
+			pSubMenu->checkmaps = checkmaps;
+			pSubMenu->checkmapsshare = TRUE;
+			pSubMenu->CreatePopupMenu();
+
+			// Append it to the top of the stack:
+
+			m_Stack[m_Stack.GetUpperBound()]->AppendODMenuW(szCaption, uFlags,
+				(UINT)pSubMenu->m_hMenu, -1);
+			m_Stack.Add(pSubMenu);
+			m_StackEnd.Add(FALSE);
+		}
+		else {
+			m_Stack[m_Stack.GetUpperBound()]->AppendODMenuW(szCaption, uFlags,
+				dwID, -1);
+			if (dwFlags & MF_END)m_StackEnd.SetAt(m_Stack.GetUpperBound(), TRUE);
+			j = m_Stack.GetUpperBound();
+			while (j >= 0 && m_StackEnd.GetAt(j)) {
+				m_Stack[m_Stack.GetUpperBound()]->InsertSpaces();
+				m_Stack.RemoveAt(j);
+				m_StackEnd.RemoveAt(j);
+				--j;
+			}
+		}
+
+		delete[] szCaption;
+	} while (m_Stack.GetUpperBound() != -1);
+
+	for (int i = 0; i < (int)GetMenuItemCount(); ++i) {
+		CString str = m_MenuList[i]->GetString();
+		if (GetSubMenu(i)) {
+			m_MenuList[i]->nFlags = MF_POPUP | MF_BYPOSITION;
+			ModifyMenu(i, MF_POPUP | MF_BYPOSITION,
+				(UINT)GetSubMenu(i)->m_hMenu, str);
+		}
+		else {
+			m_MenuList[i]->nFlags = MF_STRING | MF_BYPOSITION;
+			ModifyMenu(i, MF_STRING | MF_BYPOSITION, m_MenuList[i]->nID, str);
+		}
+	}
+
+	m_loadmenu = TRUE;
+
+	return(TRUE);
+}
+
+int BCMenu::GetMenuStart(void)
+{
+	if (!m_loadmenu)return(0);
+
+	CString name, str;
+	int menuloc = -1, listloc = -1, menustart = 0, i = 0, j = 0;
+	int nummenulist = m_MenuList.GetSize();
+	int nummenu = (int)GetMenuItemCount();
+
+	while (i < nummenu&&menuloc == -1) {
+		GetMenuString(i, name, MF_BYPOSITION);
+		if (name.GetLength() > 0) {
+			for (j = 0; j < nummenulist; ++j) {
+				str = m_MenuList[j]->GetString();
+				if (name == str) {
+					menuloc = i;
+					listloc = j;
+					break;
+				}
+			}
+		}
+		++i;
+	}
+	if (menuloc >= 0 && listloc >= 0 && menuloc >= listloc)menustart = menuloc - listloc;
+	return(menustart);
+}
+
+void BCMenu::RemoveTopLevelOwnerDraw(void)
+{
+	CString str;
+	int i = 0, j = 0;
+	int nummenulist = m_MenuList.GetSize(), menustart;
+
+	menustart = GetMenuStart();
+	for (i = menustart, j = 0; i < (int)GetMenuItemCount(); ++i, ++j) {
+		if (j < nummenulist) {
+			str = m_MenuList[j]->GetString();
+			if (GetSubMenu(i)) {
+				m_MenuList[j]->nFlags = MF_POPUP | MF_BYPOSITION;
+				ModifyMenu(i, MF_POPUP | MF_BYPOSITION,
+					(UINT)GetSubMenu(i)->m_hMenu, str);
+			}
+		}
+	}
+
+}
+
+void BCMenu::InsertSpaces(void)
+{
+	int i, j, numitems, maxlength;
+	CString string, newstring;
+	CSize t;
+	CFont m_fontMenu;
+	LOGFONT m_lf;
+
+	ZeroMemory((PVOID)&m_lf, sizeof(LOGFONT));
+	NONCLIENTMETRICS nm;
+	nm.cbSize = sizeof(NONCLIENTMETRICS);
+	VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, 0));
+	m_lf = nm.lfMenuFont;
+	m_fontMenu.CreateFontIndirect(&m_lf);
+
+	CWnd *pWnd = AfxGetMainWnd();
+	if (pWnd == NULL)pWnd = CWnd::GetDesktopWindow();
+	CDC *pDC = pWnd->GetDC();
+	CFont* pFont = pDC->SelectObject(&m_fontMenu);
+
+	numitems = GetMenuItemCount();
+	maxlength = -1;
+	for (i = 0; i < numitems; ++i) {
+		string = m_MenuList[i]->GetString();
+		j = string.Find((char)9);
+		newstring.Empty();
+		if (j != -1)newstring = string.Left(j);
+		else newstring = string;
+		newstring += _T(" ");//SK: modified for Unicode correctness. 
+		LPCTSTR lpstrText = (LPCTSTR)newstring;
+		t = pDC->GetTextExtent(lpstrText, _tcslen(lpstrText));
+		if (t.cx > maxlength)maxlength = t.cx;
+	}
+	for (i = 0; i < numitems; ++i) {
+		string = m_MenuList[i]->GetString();
+		j = string.Find((char)9);
+		if (j != -1) {
+			newstring.Empty();
+			newstring = string.Left(j);
+			LPCTSTR lpstrText = (LPCTSTR)(newstring);
+			t = pDC->GetTextExtent(lpstrText, _tcslen(lpstrText));
+			while (t.cx < maxlength) {
+				newstring += _T(' ');//SK: modified for Unicode correctness
+				LPCTSTR lpstrText = (LPCTSTR)(newstring);
+				t = pDC->GetTextExtent(lpstrText, _tcslen(lpstrText));
+			}
+			newstring += string.Mid(j);
+#ifdef UNICODE      
+			m_MenuList[i]->SetWideString(newstring);//SK: modified for dynamic allocation
+#else
+			m_MenuList[i]->SetAnsiString(newstring);
+#endif
+		}
+	}
+	pDC->SelectObject(pFont);              // Select old font in
+	pWnd->ReleaseDC(pDC);       // Release the DC
+	m_fontMenu.DeleteObject();
+}
+
+void BCMenu::LoadCheckmarkBitmap(int unselect, int select)
+{
+	if (unselect > 0 && select > 0) {
+		m_selectcheck = select;
+		m_unselectcheck = unselect;
+		if (checkmaps)checkmaps->DeleteImageList();
+		else checkmaps = new(CImageList);
+		checkmaps->Create(m_iconX, m_iconY, ILC_MASK, 2, 1);
+		BOOL flag1 = AddBitmapToImageList(checkmaps, unselect);
+		BOOL flag2 = AddBitmapToImageList(checkmaps, select);
+		if (!flag1 || !flag2) {
+			checkmaps->DeleteImageList();
+			delete checkmaps;
+			checkmaps = NULL;
+		}
+	}
+}
+
+//--------------------------------------------------------------------------
+//[18.06.99 rj]
+BOOL BCMenu::GetMenuText(UINT id, CString& string, UINT nFlags/*= MF_BYPOSITION*/)
+{
+	BOOL returnflag = FALSE;
+
+	if (MF_BYPOSITION&nFlags) {
+		UINT numMenuItems = m_MenuList.GetUpperBound();
+		if (id <= numMenuItems) {
+			string = m_MenuList[id]->GetString();
+			returnflag = TRUE;
+		}
+	}
+	else {
+		int uiLoc;
+		BCMenu* pMenu = FindMenuOption(id, uiLoc);
+		if (NULL != pMenu) returnflag = pMenu->GetMenuText(uiLoc, string);
+	}
+	return(returnflag);
+}
+
+
+void BCMenu::DrawRadioDot(CDC *pDC, int x, int y, COLORREF color)
+{
+	CRect rcDot(x, y, x + 6, y + 6);
+	CBrush brush;
+	CPen pen;
+	brush.CreateSolidBrush(color);
+	pen.CreatePen(PS_SOLID, 0, color);
+	CBrush *pOldBrush = pDC->SelectObject(&brush);
+	CPen *pOldPen = pDC->SelectObject(&pen);
+	pDC->Ellipse(&rcDot);
+	pDC->SelectObject(pOldBrush);
+	pDC->SelectObject(pOldPen);
+	pen.DeleteObject();
+	brush.DeleteObject();
+}
+
+void BCMenu::DrawCheckMark(CDC* pDC, int x, int y, COLORREF color)
+{
+	CPen m_penBack;
+	m_penBack.CreatePen(PS_SOLID, 0, color);
+	CPen *pOldPen = pDC->SelectObject(&m_penBack);
+	pDC->MoveTo(x, y + 2);
+	pDC->LineTo(x, y + 5);
+
+	pDC->MoveTo(x + 1, y + 3);
+	pDC->LineTo(x + 1, y + 6);
+
+	pDC->MoveTo(x + 2, y + 4);
+	pDC->LineTo(x + 2, y + 7);
+
+	pDC->MoveTo(x + 3, y + 3);
+	pDC->LineTo(x + 3, y + 6);
+
+	pDC->MoveTo(x + 4, y + 2);
+	pDC->LineTo(x + 4, y + 5);
+
+	pDC->MoveTo(x + 5, y + 1);
+	pDC->LineTo(x + 5, y + 4);
+
+	pDC->MoveTo(x + 6, y);
+	pDC->LineTo(x + 6, y + 3);
+
+	pDC->SelectObject(pOldPen);
+	m_penBack.DeleteObject();
+}
+
+BCMenuData *BCMenu::FindMenuList(UINT nID)
+{
+	for (int i = 0; i <= m_MenuList.GetUpperBound(); ++i) {
+		if (m_MenuList[i]->nID == nID && !m_MenuList[i]->syncflag) {
+			m_MenuList[i]->syncflag = 1;
+			return(m_MenuList[i]);
+		}
+	}
+	return(NULL);
+}
+
+void BCMenu::InitializeMenuList(int value)
+{
+	for (int i = 0; i <= m_MenuList.GetUpperBound(); ++i)
+		m_MenuList[i]->syncflag = value;
+}
+
+void BCMenu::DeleteMenuList(void)
+{
+	for (int i = 0; i <= m_MenuList.GetUpperBound(); ++i) {
+		if (!m_MenuList[i]->syncflag) {
+			delete m_MenuList[i];
+		}
+	}
+}
+
+void BCMenu::SynchronizeMenu(void)
+{
+	CTypedPtrArray<CPtrArray, BCMenuData*> temp;
+	BCMenuData *mdata;
+	CString string;
+	UINT submenu, nID = 0, state, j;
+
+	InitializeMenuList(0);
+	for (j = 0; j < GetMenuItemCount(); ++j) {
+		mdata = NULL;
+		state = GetMenuState(j, MF_BYPOSITION);
+		if (state&MF_POPUP) {
+			submenu = (UINT)GetSubMenu(j)->m_hMenu;
+			mdata = FindMenuList(submenu);
+			GetMenuString(j, string, MF_BYPOSITION);
+			if (!mdata)mdata = NewODMenu(j,
+				(state & 0xFF) | MF_BYPOSITION | MF_POPUP | MF_OWNERDRAW, submenu, string);
+			else if (string.GetLength() > 0)
+#ifdef UNICODE
+				mdata->SetWideString(string);  //SK: modified for dynamic allocation
+#else
+				mdata->SetAnsiString(string);
+#endif
+		}
+		else if (state&MF_SEPARATOR) {
+			mdata = FindMenuList(0);
+			if (!mdata)mdata = NewODMenu(j,
+				state | MF_BYPOSITION | MF_SEPARATOR | MF_OWNERDRAW, 0, _T(""));//SK: modified for Unicode correctness
+			else ModifyMenu(j, mdata->nFlags, nID, (LPCTSTR)mdata);
+		}
+		else {
+			nID = GetMenuItemID(j);
+			mdata = FindMenuList(nID);
+			GetMenuString(j, string, MF_BYPOSITION);
+			if (!mdata)mdata = NewODMenu(j, state | MF_BYPOSITION | MF_OWNERDRAW,
+				nID, string);
+			else {
+				mdata->nFlags = state | MF_BYPOSITION | MF_OWNERDRAW;
+				if (string.GetLength() > 0)
+#ifdef UNICODE
+					mdata->SetWideString(string);//SK: modified for dynamic allocation
+#else
+					mdata->SetAnsiString(string);
+#endif
+
+				ModifyMenu(j, mdata->nFlags, nID, (LPCTSTR)mdata);
+			}
+		}
+		if (mdata)temp.Add(mdata);
+	}
+	DeleteMenuList();
+	m_MenuList.RemoveAll();
+	m_MenuList.Append(temp);
+	temp.RemoveAll();
+}
+
+void BCMenu::UpdateMenu(CMenu *pmenu)
+{
+#ifdef _CPPRTTI 
+	BCMenu *psubmenu = dynamic_cast<BCMenu *>(pmenu);
+#else
+	BCMenu *psubmenu = (BCMenu *)pmenu;
+#endif
+	if (psubmenu)psubmenu->SynchronizeMenu();
+}
+
+LRESULT BCMenu::FindKeyboardShortcut(UINT nChar, UINT nFlags,
+	CMenu *pMenu)
+{
+#ifdef _CPPRTTI 
+	BCMenu *pBCMenu = dynamic_cast<BCMenu *>(pMenu);
+#else
+	BCMenu *pBCMenu = (BCMenu *)pMenu;
+#endif
+	if (pBCMenu && nFlags&MF_POPUP) {
+		CString key(_T('&'), 2);//SK: modified for Unicode correctness
+		key.SetAt(1, (TCHAR)nChar);
+		key.MakeLower();
+		CString menutext;
+		int menusize = (int)pBCMenu->GetMenuItemCount();
+		if (menusize != (pBCMenu->m_MenuList.GetUpperBound() + 1))
+			pBCMenu->SynchronizeMenu();
+		for (int i = 0; i < menusize; ++i) {
+			if (pBCMenu->GetMenuText(i, menutext)) {
+				menutext.MakeLower();
+				if (menutext.Find(key) >= 0)return(MAKELRESULT(i, 2));
+			}
+		}
+	}
+	return(0);
+}
+
+void BCMenu::DitherBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth,
+	int nHeight, HBITMAP hbm, int nXSrc, int nYSrc,
+	COLORREF bgcolor)
+{
+	ASSERT(hdcDest && hbm);
+	ASSERT(nWidth > 0 && nHeight > 0);
+
+	// Create a generic DC for all BitBlts
+	HDC hDC = CreateCompatibleDC(hdcDest);
+	ASSERT(hDC);
+
+	if (hDC)
+	{
+		// Create a DC for the monochrome DIB section
+		HDC bwDC = CreateCompatibleDC(hDC);
+		ASSERT(bwDC);
+
+		if (bwDC)
+		{
+			// Create the monochrome DIB section with a black and white palette
+			struct {
+				BITMAPINFOHEADER bmiHeader;
+				RGBQUAD      bmiColors[2];
+			} RGBBWBITMAPINFO = {
+
+				{    // a BITMAPINFOHEADER
+					sizeof(BITMAPINFOHEADER),  // biSize 
+						nWidth,         // biWidth; 
+						nHeight,        // biHeight; 
+						1,            // biPlanes; 
+						1,            // biBitCount 
+						BI_RGB,         // biCompression; 
+						0,            // biSizeImage; 
+						0,            // biXPelsPerMeter; 
+						0,            // biYPelsPerMeter; 
+						0,            // biClrUsed; 
+						0            // biClrImportant; 
+				},
+				{
+					{ 0x00, 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF, 0x00 }
+					}
+			};
+			VOID *pbitsBW;
+			HBITMAP hbmBW = CreateDIBSection(bwDC,
+				(LPBITMAPINFO)&RGBBWBITMAPINFO, DIB_RGB_COLORS, &pbitsBW, NULL, 0);
+			ASSERT(hbmBW);
+
+			if (hbmBW)
+			{
+				// Attach the monochrome DIB section and the bitmap to the DCs
+				HBITMAP olddib = (HBITMAP)SelectObject(bwDC, hbmBW);
+				HBITMAP hdcolddib = (HBITMAP)SelectObject(hDC, hbm);
+
+				// BitBlt the bitmap into the monochrome DIB section
+				BitBlt(bwDC, 0, 0, nWidth, nHeight, hDC, nXSrc, nYSrc, SRCCOPY);
+
+				// Paint the destination rectangle in gray
+				FillRect(hdcDest, CRect(nXDest, nYDest, nXDest + nWidth, nYDest +
+					nHeight), GetSysColorBrush(bgcolor));
+				//SK: looks better on the old shell
+				// BitBlt the black bits in the monochrome bitmap into COLOR_3DHILIGHT
+				// bits in the destination DC
+				// The magic ROP comes from the Charles Petzold's book
+				HBRUSH hb = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
+				HBRUSH oldBrush = (HBRUSH)SelectObject(hdcDest, hb);
+				BitBlt(hdcDest, nXDest + 1, nYDest + 1, nWidth, nHeight, bwDC, 0, 0, 0xB8074A);
+
+				// BitBlt the black bits in the monochrome bitmap into COLOR_3DSHADOW
+				// bits in the destination DC
+				hb = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
+				DeleteObject(SelectObject(hdcDest, hb));
+				BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, bwDC, 0, 0, 0xB8074A);
+				DeleteObject(SelectObject(hdcDest, oldBrush));
+				VERIFY(DeleteObject(SelectObject(bwDC, olddib)));
+				SelectObject(hDC, hdcolddib);
+			}
+
+			VERIFY(DeleteDC(bwDC));
+		}
+
+		VERIFY(DeleteDC(hDC));
+	}
+}
+
+void BCMenu::GetFadedBitmap(CBitmap &bmp)
+{
+	CDC ddc;
+	COLORREF bgcol, col;
+	BITMAP BitMap;
+
+	bmp.GetBitmap(&BitMap);
+	ddc.CreateCompatibleDC(NULL);
+	CBitmap * pddcOldBmp = ddc.SelectObject(&bmp);
+
+	// use this to get the background color, takes into account color shifting
+	CDC ddc2;
+	CBrush brush;
+	CBitmap bmp2;
+	ddc2.CreateCompatibleDC(NULL);
+	bmp2.CreateCompatibleBitmap(&ddc, BitMap.bmWidth, BitMap.bmHeight);
+	col = GetSysColor(COLOR_3DFACE);
+	brush.CreateSolidBrush(col);
+	CBitmap * pddcOldBmp2 = ddc2.SelectObject(&bmp2);
+	CRect rect(0, 0, BitMap.bmWidth, BitMap.bmHeight);
+	ddc2.FillRect(rect, &brush);
+	bgcol = ddc2.GetPixel(1, 1);
+	brush.DeleteObject();
+	ddc2.SelectObject(pddcOldBmp2);
+
+	for (int i = 0; i < BitMap.bmWidth; ++i) {
+		for (int j = 0; j < BitMap.bmHeight; ++j) {
+			col = ddc.GetPixel(i, j);
+			if (col != bgcol)ddc.SetPixel(i, j, LightenColor(col, 0.3));
+		}
+	}
+	ddc.SelectObject(pddcOldBmp);
+}
+
+void BCMenu::GetShadowBitmap(CBitmap &bmp)
+{
+	CDC ddc;
+	COLORREF bgcol, col, shadowcol = GetSysColor(COLOR_BTNSHADOW);
+	BITMAP BitMap;
+
+	if (!IsWinXPLuna())shadowcol = LightenColor(shadowcol, 0.49);
+	bmp.GetBitmap(&BitMap);
+	ddc.CreateCompatibleDC(NULL);
+	CBitmap * pddcOldBmp = ddc.SelectObject(&bmp);
+
+	// use this to get the background color, takes into account color shifting
+	CDC ddc2;
+	CBrush brush;
+	CBitmap bmp2;
+	ddc2.CreateCompatibleDC(NULL);
+	bmp2.CreateCompatibleBitmap(&ddc, BitMap.bmWidth, BitMap.bmHeight);
+	col = GetSysColor(COLOR_3DFACE);
+	brush.CreateSolidBrush(col);
+	CBitmap * pddcOldBmp2 = ddc2.SelectObject(&bmp2);
+	CRect rect(0, 0, BitMap.bmWidth, BitMap.bmHeight);
+	ddc2.FillRect(rect, &brush);
+	bgcol = ddc2.GetPixel(1, 1);
+	brush.DeleteObject();
+	ddc2.SelectObject(pddcOldBmp2);
+
+	for (int i = 0; i < BitMap.bmWidth; ++i) {
+		for (int j = 0; j < BitMap.bmHeight; ++j) {
+			col = ddc.GetPixel(i, j);
+			if (col != bgcol)ddc.SetPixel(i, j, shadowcol);
+		}
+	}
+	ddc.SelectObject(pddcOldBmp);
+}
+
+
+BOOL BCMenu::AddBitmapToImageList(CImageList *bmplist, UINT nResourceID)
+{
+	BOOL bReturn = FALSE;
+
+	// O.S.
+	if (m_bDynIcons) {
+		bmplist->Add((HICON)nResourceID);
+		bReturn = TRUE;
+	}
+	else {
+		HBITMAP hbmp = LoadSysColorBitmap(nResourceID);
+		if (hbmp) {
+			CBitmap bmp;
+			bmp.Attach(hbmp);
+			if (m_bitmapBackgroundFlag) {
+				if (bmplist->Add(&bmp, m_bitmapBackground) >= 0)bReturn = TRUE;
+			}
+			else {
+				if (bmplist->Add(&bmp, GetSysColor(COLOR_3DFACE)) >= 0)bReturn = TRUE;
+			}
+			bmp.Detach();
+			DeleteObject(hbmp);
+		}
+		else {
+			CBitmap mybmp;
+			if (mybmp.LoadBitmap(nResourceID)) {
+				if (m_bitmapBackgroundFlag) {
+					if (bmplist->Add(&mybmp, m_bitmapBackground) >= 0)bReturn = TRUE;
+				}
+				else {
+					if (bmplist->Add(&mybmp, GetSysColor(COLOR_3DFACE)) >= 0)bReturn = TRUE;
+				}
+			}
+		}
+	}
+	if (bReturn&&IsLunaMenuStyle() && xp_draw_3D_bitmaps) {
+		CWnd *pWnd = AfxGetMainWnd();            // Get main window
+		if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+		CDC *pDC = pWnd->GetDC();              // Get device context
+		CBitmap bmp, bmp2;
+		GetBitmapFromImageList(pDC, bmplist, 0, bmp);
+		GetFadedBitmap(bmp);
+		bmplist->Add(&bmp, GetSysColor(COLOR_3DFACE));
+		GetBitmapFromImageList(pDC, bmplist, 0, bmp2);
+		GetShadowBitmap(bmp2);
+		bmplist->Add(&bmp2, GetSysColor(COLOR_3DFACE));
+		pWnd->ReleaseDC(pDC);  // Release the DC
+	}
+	return(bReturn);
+}
+
+void BCMenu::SetBitmapBackground(COLORREF color)
+{
+	m_bitmapBackground = color;
+	m_bitmapBackgroundFlag = TRUE;
+}
+
+void BCMenu::UnSetBitmapBackground(void)
+{
+	m_bitmapBackgroundFlag = FALSE;
+}
+
+// Given a toolbar, append all the options from it to this menu
+// Passed a ptr to the toolbar object and the toolbar ID
+// Author : Robert Edward Caldecott
+void BCMenu::AddFromToolBar(CToolBar* pToolBar, int nResourceID)
+{
+	for (int i = 0; i < pToolBar->GetCount(); i++) {
+		UINT nID = pToolBar->GetItemID(i);
+		// See if this toolbar option
+		// appears as a command on this
+		// menu or is a separator
+		if (nID == 0 || GetMenuState(nID, MF_BYCOMMAND) == 0xFFFFFFFF)
+			continue; // Item doesn't exist
+		UINT nStyle;
+		int nImage;
+		// Get the toolbar button info
+		pToolBar->GetButtonInfo(i, nID, nStyle, nImage);
+		// OK, we have the command ID of the toolbar
+		// option, and the tollbar bitmap offset
+		int nLoc;
+		BCMenuData* pData;
+		BCMenu *pSubMenu = FindMenuOption(nID, nLoc);
+		if (pSubMenu && nLoc >= 0)pData = pSubMenu->m_MenuList[nLoc];
+		else {
+			// Create a new BCMenuData structure
+			pData = new BCMenuData;
+			m_MenuList.Add(pData);
+		}
+		// Set some default structure members
+		pData->menuIconNormal = nResourceID;
+		pData->nID = nID;
+		pData->nFlags = MF_BYCOMMAND | MF_OWNERDRAW;
+		pData->xoffset = nImage;
+		if (pData->bitmap)pData->bitmap->DeleteImageList();
+		else pData->bitmap = new CImageList;
+		pData->bitmap->Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+
+		if (!AddBitmapToImageList(pData->bitmap, nResourceID)) {
+			pData->bitmap->DeleteImageList();
+			delete pData->bitmap;
+			pData->bitmap = NULL;
+			pData->menuIconNormal = -1;
+			pData->xoffset = -1;
+		}
+
+		// Modify our menu
+		ModifyMenu(nID, pData->nFlags, nID, (LPCTSTR)pData);
+	}
+}
+
+BOOL BCMenu::Draw3DCheckmark(CDC *dc, const CRect& rc,
+	BOOL bSelected, HBITMAP hbmCheck)
+{
+	CRect rcDest = rc;
+	CBrush brush;
+	COLORREF col;
+	if (IsWinXPLuna())col = GetSysColor(COLOR_3DFACE);
+	else col = GetSysColor(COLOR_MENU);
+	if (!bSelected)col = LightenColor(col, 0.6);
+	brush.CreateSolidBrush(col);
+	dc->FillRect(rcDest, &brush);
+	brush.DeleteObject();
+	if (IsNewShell()) //SK: looks better on the old shell
+		dc->DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT);
+	if (!hbmCheck)DrawCheckMark(dc, rc.left + 4, rc.top + 4, GetSysColor(COLOR_MENUTEXT));
+	else DrawRadioDot(dc, rc.left + 5, rc.top + 4, GetSysColor(COLOR_MENUTEXT));
+	return TRUE;
+}
+
+BOOL BCMenu::DrawXPCheckmark(CDC *dc, const CRect& rc, HBITMAP hbmCheck, COLORREF &colorout)
+{
+	CBrush brushin;
+	brushin.CreateSolidBrush(LightenColor(colorout, 0.85));
+	dc->FillRect(rc, &brushin);
+	brushin.DeleteObject();
+	dc->Draw3dRect(rc, colorout, colorout);
+	if (!hbmCheck)DrawCheckMark(dc, rc.left + 5, rc.top + 4, GetSysColor(COLOR_MENUTEXT));
+	else DrawRadioDot(dc, rc.left + 5, rc.top + 4, GetSysColor(COLOR_MENUTEXT));
+	return TRUE;
+}
+
+void BCMenu::DitherBlt2(CDC *drawdc, int nXDest, int nYDest, int nWidth,
+	int nHeight, CBitmap &bmp, int nXSrc, int nYSrc,
+	COLORREF bgcolor)
+{
+	// create a monochrome memory DC
+	CDC ddc;
+	ddc.CreateCompatibleDC(0);
+	CBitmap bwbmp;
+	bwbmp.CreateCompatibleBitmap(&ddc, nWidth, nHeight);
+	CBitmap * pddcOldBmp = ddc.SelectObject(&bwbmp);
+
+	CDC dc;
+	dc.CreateCompatibleDC(0);
+	CBitmap * pdcOldBmp = dc.SelectObject(&bmp);
+
+	// build a mask
+	ddc.PatBlt(0, 0, nWidth, nHeight, WHITENESS);
+	dc.SetBkColor(GetSysColor(COLOR_BTNFACE));
+	ddc.BitBlt(0, 0, nWidth, nHeight, &dc, nXSrc, nYSrc, SRCCOPY);
+	dc.SetBkColor(GetSysColor(COLOR_BTNHILIGHT));
+	ddc.BitBlt(0, 0, nWidth, nHeight, &dc, nXSrc, nYSrc, SRCPAINT);
+
+	// Copy the image from the toolbar into the memory DC
+	// and draw it (grayed) back into the toolbar.
+	dc.FillSolidRect(0, 0, nWidth, nHeight, bgcolor);
+	//SK: Looks better on the old shell
+	dc.SetBkColor(RGB(0, 0, 0));
+	dc.SetTextColor(RGB(255, 255, 255));
+	CBrush brShadow, brHilight;
+	brHilight.CreateSolidBrush(GetSysColor(COLOR_BTNHILIGHT));
+	brShadow.CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
+	CBrush * pOldBrush = dc.SelectObject(&brHilight);
+	dc.BitBlt(0, 0, nWidth, nHeight, &ddc, 0, 0, 0x00E20746L);
+	drawdc->BitBlt(nXDest + 1, nYDest + 1, nWidth, nHeight, &dc, 0, 0, SRCCOPY);
+	dc.BitBlt(1, 1, nWidth, nHeight, &ddc, 0, 0, 0x00E20746L);
+	dc.SelectObject(&brShadow);
+	dc.BitBlt(0, 0, nWidth, nHeight, &ddc, 0, 0, 0x00E20746L);
+	drawdc->BitBlt(nXDest, nYDest, nWidth, nHeight, &dc, 0, 0, SRCCOPY);
+	// reset DCs
+	ddc.SelectObject(pddcOldBmp);
+	ddc.DeleteDC();
+	dc.SelectObject(pOldBrush);
+	dc.SelectObject(pdcOldBmp);
+	dc.DeleteDC();
+
+	brShadow.DeleteObject();
+	brHilight.DeleteObject();
+	bwbmp.DeleteObject();
+}
+
+void BCMenu::SetDisableOldStyle(void)
+{
+	disable_old_style = TRUE;
+}
+
+void BCMenu::UnSetDisableOldStyle(void)
+{
+	disable_old_style = FALSE;
+}
+
+BOOL BCMenu::GetDisableOldStyle(void)
+{
+	return(disable_old_style);
+}
+
+
+WORD BCMenu::NumBitmapColors(LPBITMAPINFOHEADER lpBitmap)
+{
+	if (lpBitmap->biClrUsed != 0)
+		return (WORD)lpBitmap->biClrUsed;
+
+	switch (lpBitmap->biBitCount) {
+	case 1:
+		return 2;
+	case 4:
+		return 16;
+	case 8:
+		return 256;
+	default:
+		return 0;
+	}
+	return 0;
+}
+
+HBITMAP BCMenu::LoadSysColorBitmap(int nResourceId)
+{
+	HINSTANCE hInst =
+		AfxFindResourceHandle(MAKEINTRESOURCE(nResourceId), RT_BITMAP);
+	HRSRC hRsrc =
+		::FindResource(hInst, MAKEINTRESOURCE(nResourceId), RT_BITMAP);
+	if (hRsrc == NULL) {
+		hInst = NULL;
+		hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nResourceId), RT_BITMAP);
+	}
+	if (hRsrc == NULL)return NULL;
+
+	// determine how many colors in the bitmap
+	HGLOBAL hglb;
+	if ((hglb = LoadResource(hInst, hRsrc)) == NULL)
+		return NULL;
+	LPBITMAPINFOHEADER lpBitmap = (LPBITMAPINFOHEADER)LockResource(hglb);
+	if (lpBitmap == NULL)return NULL;
+	WORD numcol = NumBitmapColors(lpBitmap);
+	::FreeResource(hglb);
+
+	if (numcol != 16)return(NULL);
+
+	return AfxLoadSysColorBitmap(hInst, hRsrc, FALSE);
+}
+
+BOOL BCMenu::RemoveMenu(UINT uiId, UINT nFlags)
+{
+	if (MF_BYPOSITION&nFlags) {
+		UINT uint = GetMenuState(uiId, MF_BYPOSITION);
+		if (uint&MF_SEPARATOR && !(uint&MF_POPUP)) {
+			delete m_MenuList.GetAt(uiId);
+			m_MenuList.RemoveAt(uiId);
+		}
+		else {
+			BCMenu* pSubMenu = (BCMenu*)GetSubMenu(uiId);
+			if (NULL == pSubMenu) {
+				UINT uiCommandId = GetMenuItemID(uiId);
+				for (int i = 0; i < m_MenuList.GetSize(); i++) {
+					if (m_MenuList[i]->nID == uiCommandId) {
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+			}
+			else {
+				int numSubMenus = m_SubMenus.GetUpperBound();
+				for (int m = numSubMenus; m >= 0; m--) {
+					if (m_SubMenus[m] == pSubMenu->m_hMenu) {
+						int numAllSubMenus = m_AllSubMenus.GetUpperBound();
+						for (int n = numAllSubMenus; n >= 0; n--) {
+							if (m_AllSubMenus[n] == m_SubMenus[m])m_AllSubMenus.RemoveAt(n);
+						}
+						m_SubMenus.RemoveAt(m);
+					}
+				}
+				int num = pSubMenu->GetMenuItemCount();
+				for (int i = num - 1; i >= 0; --i)pSubMenu->RemoveMenu(i, MF_BYPOSITION);
+				for (int i = m_MenuList.GetUpperBound(); i >= 0; i--) {
+					if (m_MenuList[i]->nID == (UINT)pSubMenu->m_hMenu) {
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+				delete pSubMenu;
+			}
+		}
+	}
+	else {
+		int iPosition = 0;
+		BCMenu* pMenu = FindMenuOption(uiId, iPosition);
+		// bug fix RIA 14th September 2000 
+		// failed to return correct value on call to remove menu as the item was 
+		// removed twice. The second time its not found 
+		// so a value of 0 was being returned 
+		if (pMenu) return pMenu->RemoveMenu(iPosition, MF_BYPOSITION); // added return 
+	}
+	return CMenu::RemoveMenu(uiId, nFlags);
+}
+
+BOOL BCMenu::DeleteMenu(UINT uiId, UINT nFlags)
+{
+	if (MF_BYPOSITION&nFlags) {
+		UINT uint = GetMenuState(uiId, MF_BYPOSITION);
+		if (uint&MF_SEPARATOR && !(uint&MF_POPUP)) {
+			// make sure it's a separator
+			int menulistsize = m_MenuList.GetSize();
+			if (uiId < (UINT)menulistsize) {
+				CString str = m_MenuList[uiId]->GetString();
+				if (str == "") {
+					delete m_MenuList.GetAt(uiId);
+					m_MenuList.RemoveAt(uiId);
+				}
+			}
+		}
+		else {
+			BCMenu* pSubMenu = (BCMenu*)GetSubMenu(uiId);
+			if (NULL == pSubMenu) {
+				UINT uiCommandId = GetMenuItemID(uiId);
+				for (int i = 0; i < m_MenuList.GetSize(); i++) {
+					if (m_MenuList[i]->nID == uiCommandId) {
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+			}
+			else {
+				int numSubMenus = m_SubMenus.GetUpperBound();
+				for (int m = numSubMenus; m >= 0; m--) {
+					if (m_SubMenus[m] == pSubMenu->m_hMenu) {
+						int numAllSubMenus = m_AllSubMenus.GetUpperBound();
+						for (int n = numAllSubMenus; n >= 0; n--) {
+							if (m_AllSubMenus[n] == m_SubMenus[m])m_AllSubMenus.RemoveAt(n);
+						}
+						m_SubMenus.RemoveAt(m);
+					}
+				}
+				int num = pSubMenu->GetMenuItemCount();
+				for (int i = num - 1; i >= 0; --i)pSubMenu->DeleteMenu(i, MF_BYPOSITION);
+				for (int i = m_MenuList.GetUpperBound(); i >= 0; i--) {
+					if (m_MenuList[i]->nID == (UINT)pSubMenu->m_hMenu) {
+						delete m_MenuList.GetAt(i);
+						m_MenuList.RemoveAt(i);
+						break;
+					}
+				}
+				delete pSubMenu;
+			}
+		}
+	}
+	else {
+		int iPosition = 0;
+		BCMenu* pMenu = FindMenuOption(uiId, iPosition);
+		if (pMenu)return pMenu->DeleteMenu(iPosition, MF_BYPOSITION);
+	}
+
+	return(CMenu::DeleteMenu(uiId, nFlags));
+}
+
+
+BOOL BCMenu::AppendMenuA(UINT nFlags, UINT nIDNewItem, const char *lpszNewItem, int nIconNormal)
+{
+	USES_CONVERSION;
+	return AppendMenuW(nFlags, nIDNewItem, A2W(lpszNewItem), nIconNormal);
+}
+
+BOOL BCMenu::AppendMenuW(UINT nFlags, UINT nIDNewItem, wchar_t *lpszNewItem, int nIconNormal)
+{
+	return AppendODMenuW(lpszNewItem, nFlags, nIDNewItem, nIconNormal);
+}
+
+BOOL BCMenu::AppendMenuA(UINT nFlags, UINT nIDNewItem, const char *lpszNewItem, CImageList *il, int xoffset)
+{
+	USES_CONVERSION;
+	return AppendMenuW(nFlags, nIDNewItem, A2W(lpszNewItem), il, xoffset);
+}
+
+BOOL BCMenu::AppendMenuW(UINT nFlags, UINT nIDNewItem, wchar_t *lpszNewItem, CImageList *il, int xoffset)
+{
+	return AppendODMenuW(lpszNewItem, nFlags, nIDNewItem, il, xoffset);
+}
+
+BOOL BCMenu::AppendMenuA(UINT nFlags, UINT nIDNewItem, const char *lpszNewItem, CBitmap *bmp)
+{
+	USES_CONVERSION;
+	return AppendMenuW(nFlags, nIDNewItem, A2W(lpszNewItem), bmp);
+}
+
+BOOL BCMenu::AppendMenuW(UINT nFlags, UINT nIDNewItem, wchar_t *lpszNewItem, CBitmap *bmp)
+{
+	if (bmp) {
+		CImageList temp;
+		temp.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+		if (m_bitmapBackgroundFlag)temp.Add(bmp, m_bitmapBackground);
+		else temp.Add(bmp, GetSysColor(COLOR_3DFACE));
+		return AppendODMenuW(lpszNewItem, nFlags, nIDNewItem, &temp, 0);
+	}
+	return AppendODMenuW(lpszNewItem, nFlags, nIDNewItem, NULL, 0);
+}
+
+BOOL BCMenu::InsertMenuA(UINT nPosition, UINT nFlags, UINT nIDNewItem, const char *lpszNewItem, int nIconNormal)
+{
+	USES_CONVERSION;
+	return InsertMenuW(nPosition, nFlags, nIDNewItem, A2W(lpszNewItem), nIconNormal);
+}
+
+BOOL BCMenu::InsertMenuW(UINT nPosition, UINT nFlags, UINT nIDNewItem, wchar_t *lpszNewItem, int nIconNormal)
+{
+	return InsertODMenuW(nPosition, lpszNewItem, nFlags, nIDNewItem, nIconNormal);
+}
+
+BOOL BCMenu::InsertMenuA(UINT nPosition, UINT nFlags, UINT nIDNewItem, const char *lpszNewItem, CImageList *il, int xoffset)
+{
+	USES_CONVERSION;
+	return InsertMenuW(nPosition, nFlags, nIDNewItem, A2W(lpszNewItem), il, xoffset);
+}
+
+BOOL BCMenu::InsertMenuW(UINT nPosition, UINT nFlags, UINT nIDNewItem, wchar_t *lpszNewItem, CImageList *il, int xoffset)
+{
+	return InsertODMenuW(nPosition, lpszNewItem, nFlags, nIDNewItem, il, xoffset);
+}
+
+BOOL BCMenu::InsertMenuA(UINT nPosition, UINT nFlags, UINT nIDNewItem, const char *lpszNewItem, CBitmap *bmp)
+{
+	USES_CONVERSION;
+	return InsertMenuW(nPosition, nFlags, nIDNewItem, A2W(lpszNewItem), bmp);
+}
+
+BOOL BCMenu::InsertMenuW(UINT nPosition, UINT nFlags, UINT nIDNewItem, wchar_t *lpszNewItem, CBitmap *bmp)
+{
+	if (bmp) {
+		CImageList temp;
+		temp.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+		if (m_bitmapBackgroundFlag)temp.Add(bmp, m_bitmapBackground);
+		else temp.Add(bmp, GetSysColor(COLOR_3DFACE));
+		return InsertODMenuW(nPosition, lpszNewItem, nFlags, nIDNewItem, &temp, 0);
+	}
+	return InsertODMenuW(nPosition, lpszNewItem, nFlags, nIDNewItem, NULL, 0);
+}
+
+//--------------------------------------------------------------------------
+//[21.06.99 rj]
+BCMenu* BCMenu::AppendODPopupMenuW(wchar_t *lpstrText)
+{
+	BCMenu* pSubMenu = new BCMenu;
+	pSubMenu->m_unselectcheck = m_unselectcheck;
+	pSubMenu->m_selectcheck = m_selectcheck;
+	pSubMenu->checkmaps = checkmaps;
+	pSubMenu->checkmapsshare = TRUE;
+	pSubMenu->CreatePopupMenu();
+	AppendODMenuW(lpstrText, MF_POPUP, (UINT)pSubMenu->m_hMenu, -1);
+	return pSubMenu;
+}
+
+//--------------------------------------------------------------------------
+//[21.06.99 rj]
+BCMenu* BCMenu::AppendODPopupMenuA(LPCSTR lpstrText)
+{
+	USES_CONVERSION;
+	return AppendODPopupMenuW(A2W(lpstrText));
+}
+
+BOOL BCMenu::ImageListDuplicate(CImageList *il, int xoffset, CImageList *newlist)
+{
+	if (il == NULL || newlist == NULL || xoffset < 0) return FALSE;
+	HICON hIcon = il->ExtractIcon(xoffset);
+	int cx, cy;
+	ImageList_GetIconSize(il->m_hImageList, &cx, &cy);
+	newlist->Create(cx, cy, ILC_COLORDDB | ILC_MASK, 1, 1);
+	newlist->Add(hIcon);
+	::DestroyIcon(hIcon);
+	if (IsLunaMenuStyle() && xp_draw_3D_bitmaps) {
+		CWnd *pWnd = AfxGetMainWnd();            // Get main window
+		if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+		CDC *pDC = pWnd->GetDC();              // Get device context
+		CBitmap bmp, bmp2;
+		GetBitmapFromImageList(pDC, newlist, 0, bmp);
+		GetFadedBitmap(bmp);
+		newlist->Add(&bmp, GetSysColor(COLOR_3DFACE));
+		GetBitmapFromImageList(pDC, newlist, 0, bmp2);
+		GetShadowBitmap(bmp2);
+		newlist->Add(&bmp2, GetSysColor(COLOR_3DFACE));
+		pWnd->ReleaseDC(pDC);  // Release the DC
+	}
+	return TRUE;
+}
+
+// 2001-07-12, Damir Valiulin:
+//          Added GetSubMenu (LPCTSTR lpszSubMenuName) function
+//
+
+CMenu* BCMenu::GetSubMenu(int nPos)
+{
+	return CMenu::GetSubMenu(nPos);
+}
+
+CMenu* BCMenu::GetSubMenu(LPCTSTR lpszSubMenuName)
+{
+	int num = GetMenuItemCount();
+	CString name;
+
+	for (int i = 0; i < num; i++)
+	{
+		GetMenuString(i, name, MF_BYPOSITION);
+		if (name.Compare(lpszSubMenuName) == 0)
+		{
+			return CMenu::GetSubMenu(i);
+		}
+	}
+
+	return NULL;
+}
+
+BCMenu* BCMenu::GetSubBCMenu(char* pText)
+{
+	USES_CONVERSION;
+	return GetSubBCMenu(A2W(pText));
+}
+
+BCMenu* BCMenu::GetSubBCMenu(wchar_t* lpszSubMenuName)
+{
+	BCMenuData *mdata;
+	mdata = FindMenuOption(lpszSubMenuName);
+	if (mdata) {
+		HMENU bchmenu = (HMENU)mdata->nID;
+		CMenu *ptr = FromHandle(bchmenu);
+		BOOL flag = ptr->IsKindOf(RUNTIME_CLASS(BCMenu));
+		if (flag)return((BCMenu *)ptr);
+	}
+	return NULL;
+}
+
+
+// Tongzhe Cui, Functions to remove a popup menu based on its name. Seperators
+// before and after the popup menu can also be removed if they exist.
+int BCMenu::GetMenuPosition(char* pText)
+{
+	USES_CONVERSION;
+	return GetMenuPosition(A2W(pText));
+}
+
+int BCMenu::GetMenuPosition(wchar_t* pText)
+{
+	int i, j;
+	BCMenu* psubmenu;
+	for (i = 0; i < (int)(GetMenuItemCount()); ++i)
+	{
+		psubmenu = (BCMenu *)GetSubMenu(i);
+		if (!psubmenu)
+		{
+			const wchar_t *szWide;//SK: we use const to prevent misuse of this Ptr
+			for (j = 0; j <= m_MenuList.GetUpperBound(); ++j)
+			{
+				szWide = m_MenuList[j]->GetWideString();
+				if (szWide && !wcscmp(pText, szWide))//SK: modified for dynamic allocation
+					return j;
+			}
+		}
+	}
+	// means no found;
+	return -1;
+}
+
+int BCMenu::RemoveMenu(char* pText, BC_Seperator sPos)
+{
+	USES_CONVERSION;
+	return RemoveMenu(A2W(pText), sPos);
+}
+
+int BCMenu::RemoveMenu(wchar_t* pText, BC_Seperator sPos)
+{
+	int nPos = GetMenuPosition(pText);
+	if (nPos != -1)
+	{
+		RemoveMenu(nPos, MF_BYPOSITION);
+	}
+	if (sPos == BCMENU_HEAD)
+	{
+		ASSERT(nPos - 1 >= 0);
+		RemoveMenu(nPos - 1, MF_BYPOSITION);
+	}
+	else if (sPos == BCMENU_TAIL)
+	{
+		RemoveMenu(nPos - 1, MF_BYPOSITION);
+	}
+	else if (sPos == BCMENU_BOTH)
+	{
+		// remove the end first;
+		RemoveMenu(nPos - 1, MF_BYPOSITION);
+		// remove the head;
+		ASSERT(nPos - 1 >= 0);
+		RemoveMenu(nPos - 1, MF_BYPOSITION);
+	}
+	return nPos;
+}
+
+int BCMenu::DeleteMenu(char* pText, BC_Seperator sPos)
+{
+	USES_CONVERSION;
+	return DeleteMenu(A2W(pText), sPos);
+}
+
+int BCMenu::DeleteMenu(wchar_t* pText, BC_Seperator sPos)
+{
+	int nPos = GetMenuPosition(pText);
+	if (nPos != -1)
+	{
+		DeleteMenu(nPos, MF_BYPOSITION);
+	}
+	if (sPos == BCMENU_HEAD)
+	{
+		ASSERT(nPos - 1 >= 0);
+		DeleteMenu(nPos - 1, MF_BYPOSITION);
+	}
+	else if (sPos == BCMENU_TAIL)
+	{
+		DeleteMenu(nPos - 1, MF_BYPOSITION);
+	}
+	else if (sPos == BCMENU_BOTH)
+	{
+		// remove the end first;
+		DeleteMenu(nPos - 1, MF_BYPOSITION);
+		// remove the head;
+		ASSERT(nPos - 1 >= 0);
+		DeleteMenu(nPos - 1, MF_BYPOSITION);
+	}
+	return nPos;
+}
+
+// Larry Antram
+BOOL BCMenu::SetMenuText(UINT id, CString string, UINT nFlags/*= MF_BYPOSITION*/)
+{
+	BOOL returnflag = FALSE;
+
+	if (MF_BYPOSITION&nFlags)
+	{
+		UINT numMenuItems = m_MenuList.GetUpperBound();
+		if (id <= numMenuItems) {
+#ifdef UNICODE
+			m_MenuList[id]->SetWideString((LPCTSTR)string);
+#else
+			m_MenuList[id]->SetAnsiString(string);
+#endif
+			returnflag = TRUE;
+		}
+	}
+	else {
+		int uiLoc;
+		BCMenu* pMenu = FindMenuOption(id, uiLoc);
+		if (NULL != pMenu) returnflag = pMenu->SetMenuText(uiLoc, string);
+	}
+	return(returnflag);
+}
+
+// courtesy of Warren Stevens
+void BCMenu::ColorBitmap(CDC* pDC, CBitmap& bmp, CSize bitmap_size, CSize icon_size, COLORREF fill, COLORREF border, int hatchstyle)
+{
+	CDC bmpdc;
+	COLORREF m_newclrBack;
+	int x1 = 0, y1 = 0, x2 = bitmap_size.cx, y2 = bitmap_size.cy;
+
+	if (IsWinXPLuna())m_newclrBack = GetSysColor(COLOR_3DFACE);
+	else m_newclrBack = GetSysColor(COLOR_MENU);
+
+	bmpdc.CreateCompatibleDC(pDC);
+
+	bmp.CreateCompatibleBitmap(pDC, icon_size.cx, icon_size.cy);
+	CBitmap* pOldBitmap = bmpdc.SelectObject(&bmp);
+
+	if (bitmap_size != icon_size) {
+		CBrush background_brush;
+		background_brush.CreateSolidBrush(m_newclrBack);
+		CRect rect(0, 0, icon_size.cx, icon_size.cy);
+		bmpdc.FillRect(rect, &background_brush);
+		x1 = (icon_size.cx - bitmap_size.cx) / 2;
+		y1 = (icon_size.cy - bitmap_size.cy) / 2;
+		x2 = x1 + bitmap_size.cx;
+		y2 = y1 + bitmap_size.cy;
+		background_brush.DeleteObject();
+	}
+
+	CPen border_pen(PS_SOLID, 1, border);
+	CBrush fill_brush;
+	if (hatchstyle != -1) { fill_brush.CreateHatchBrush(hatchstyle, fill); }
+	else { fill_brush.CreateSolidBrush(fill); }
+
+	CPen*    pOldPen = bmpdc.SelectObject(&border_pen);
+	CBrush*  pOldBrush = bmpdc.SelectObject(&fill_brush);
+
+	bmpdc.Rectangle(x1, y1, x2, y2);
+
+	if (NULL != pOldBrush) { bmpdc.SelectObject(pOldBrush); }
+	if (NULL != pOldPen) { bmpdc.SelectObject(pOldPen); }
+	if (NULL != pOldBitmap) { bmpdc.SelectObject(pOldBitmap); }
+}
+
+BOOL BCMenu::IsWindowsClassicTheme(void)
+{
+	TCHAR Buf[_MAX_PATH + 10];
+	HKEY hKey;
+	DWORD size, type;
+	long lRetCode;
+	static BOOL XPTheme_returnflag = FALSE;
+	static BOOL XPTheme_checkflag = FALSE;
+
+	if (XPTheme_checkflag)return(XPTheme_returnflag);
+
+	XPTheme_checkflag = TRUE;
+	lRetCode = RegOpenKeyEx(HKEY_CURRENT_USER,
+		_T("Software\\Microsoft\\Plus!\\Themes\\Current"),
+		0, KEY_READ, &hKey);
+	if (lRetCode == ERROR_SUCCESS) {
+		size = _MAX_PATH; type = REG_SZ;
+		lRetCode = ::RegQueryValueEx(hKey, NULL, NULL, &type,
+			(unsigned char *)Buf, &size);
+		if (lRetCode == ERROR_SUCCESS) {
+			TCHAR szClassicTheme[] = _T("Windows Classic.theme");
+			int len = lstrlen(Buf);
+			if (len >= lstrlen(szClassicTheme)) {
+				if (!lstrcmpi(&Buf[len - lstrlen(szClassicTheme)], szClassicTheme)) {
+					XPTheme_returnflag = TRUE;
+				}
+			}
+		}
+		RegCloseKey(hKey);
+	}
+	return(XPTheme_returnflag);
+}
+
+int BCMenu::GlobalImageListOffset(int nID)
+{
+	int numcurrent = m_AllImagesID.GetSize();
+	int existsloc = -1;
+	for (int i = 0; i < numcurrent; ++i) {
+		if (m_AllImagesID[i] == nID) {
+			existsloc = i;
+			break;
+		}
+	}
+	return existsloc;
+}
+
+BOOL BCMenu::CanDraw3DImageList(int offset)
+{
+	BOOL retflag = FALSE;
+	int numcurrent = m_AllImagesID.GetSize();
+	if (offset + 1 < numcurrent&&offset + 2 < numcurrent) {
+		int nID = m_AllImagesID[offset];
+		if (m_AllImagesID[offset + 1] == nID && m_AllImagesID[offset + 2] == nID)retflag = TRUE;
+	}
+	return(retflag);
+}
+
+int BCMenu::AddToGlobalImageList(CImageList *il, int xoffset, int nID)
+{
+	int loc = -1;
+	HIMAGELIST hImageList = m_AllImages.m_hImageList;
+	if (!hImageList) {
+		m_AllImages.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+	}
+	HICON hIcon = il->ExtractIcon(xoffset);
+	if (hIcon) {
+		CBitmap bmp, bmp2;
+		if (IsLunaMenuStyle() && xp_draw_3D_bitmaps) {
+			CWnd *pWnd = AfxGetMainWnd();            // Get main window
+			if (pWnd == NULL) pWnd = CWnd::GetDesktopWindow();
+			CDC *pDC = pWnd->GetDC();              // Get device context
+			GetBitmapFromImageList(pDC, il, xoffset, bmp);
+			GetFadedBitmap(bmp);
+			GetBitmapFromImageList(pDC, il, xoffset, bmp2);
+			GetShadowBitmap(bmp2);
+			pWnd->ReleaseDC(pDC);  // Release the DC
+		}
+		int numcurrent = m_AllImagesID.GetSize();
+		int existsloc = -1;
+		for (int i = 0; i < numcurrent; ++i) {
+			if (m_AllImagesID[i] == nID) {
+				existsloc = i;
+				break;
+			}
+		}
+		if (existsloc >= 0) {
+			m_AllImages.Replace(existsloc, hIcon);
+			loc = existsloc;
+			if (IsLunaMenuStyle() && xp_draw_3D_bitmaps) {
+				if (existsloc + 1 < numcurrent&&m_AllImagesID[existsloc + 1] == nID) {
+					if (existsloc + 2 < numcurrent&&m_AllImagesID[existsloc + 2] == nID) {
+						CImageList il2;
+						il2.Create(m_iconX, m_iconY, ILC_COLORDDB | ILC_MASK, 1, 1);
+						il2.Add(&bmp, GetSysColor(COLOR_3DFACE));
+						HICON hIcon2 = il2.ExtractIcon(0);
+						m_AllImages.Replace(existsloc + 1, hIcon2);
+						il2.Add(&bmp2, GetSysColor(COLOR_3DFACE));
+						HICON hIcon3 = il2.ExtractIcon(1);
+						m_AllImages.Replace(existsloc + 2, hIcon3);
+						::DestroyIcon(hIcon2);
+						::DestroyIcon(hIcon3);
+					}
+				}
+			}
+		}
+		else {
+			m_AllImages.Add(hIcon);
+			m_AllImagesID.Add(nID);
+			loc = numcurrent;
+			if (IsLunaMenuStyle() && xp_draw_3D_bitmaps) {
+				m_AllImages.Add(&bmp, GetSysColor(COLOR_3DFACE));
+				m_AllImages.Add(&bmp2, GetSysColor(COLOR_3DFACE));
+				m_AllImagesID.Add(nID);
+				m_AllImagesID.Add(nID);
+			}
+		}
+		::DestroyIcon(hIcon);
+	}
+	return(loc);
+}
+
+//*************************************************************************
+
+/////////////////////////////////////////////////////////////////////////////
+// Toolbar for loading images
+/////////////////////////////////////////////////////////////////////////////
+
+struct CToolBarData
+{
+	WORD wVersion;
+	WORD wWidth;
+	WORD wHeight;
+	WORD wItemCount;
+	WORD* items() {
+		return (WORD*)(this + 1);
+	}
+};
+
+BOOL BCMenuToolBar::LoadToolBar(LPCTSTR lpszResourceName)
+{
+	ASSERT_VALID(this);
+	ASSERT(lpszResourceName != NULL);
+
+	HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR);
+	HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR);
+	if (hRsrc == NULL) {
+		hInst = NULL;
+		hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR);
+	}
+	if (hRsrc == NULL)
+		return FALSE;
+
+	HGLOBAL hGlobal = ::LoadResource(hInst, hRsrc);
+	if (hGlobal == NULL)
+		return FALSE;
+
+	CToolBarData* pData = (CToolBarData*)::LockResource(hGlobal);
+	if (pData == NULL)
+		return FALSE;
+	ASSERT(pData->wVersion == 1);
+
+	UINT* pItems = new UINT[pData->wItemCount];
+	for (int i = 0; i < pData->wItemCount; i++)
+		pItems[i] = pData->items()[i];
+	BOOL bResult = SetButtons(pItems, pData->wItemCount);
+	delete[] pItems;
+
+	if (bResult) {
+		CSize sizeImage(pData->wWidth, pData->wHeight);
+		CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7);
+		SetSizes(sizeButton, sizeImage);
+		m_iconX = pData->wWidth;
+		m_iconY = pData->wHeight;
+		bResult = LoadBitmap(lpszResourceName);
+	}
+
+	::UnlockResource(hGlobal);
+	::FreeResource(hGlobal);
+	return bResult;
+}
+
+BOOL BCMenuToolBar::LoadBitmap(LPCTSTR lpszResourceName)
+{
+	ASSERT_VALID(this);
+	ASSERT(lpszResourceName != NULL);
+
+	HINSTANCE hInstImageWell = AfxFindResourceHandle(lpszResourceName, RT_BITMAP);
+	HRSRC hRsrcImageWell = ::FindResource(hInstImageWell, lpszResourceName, RT_BITMAP);
+	if (hRsrcImageWell == NULL) {
+		hInstImageWell = NULL;
+		hRsrcImageWell = ::FindResource(hInstImageWell, lpszResourceName, RT_BITMAP);
+	}
+	if (hRsrcImageWell == NULL)
+		return FALSE;
+
+	HBITMAP hbmImageWell;
+	hbmImageWell = AfxLoadSysColorBitmap(hInstImageWell, hRsrcImageWell);
+
+	if (!AddReplaceBitmap(hbmImageWell))
+		return FALSE;
+
+	m_hInstImageWell = hInstImageWell;
+	m_hRsrcImageWell = hRsrcImageWell;
+	return TRUE;
+}

+ 366 - 0
source/hook/skinui/BCMenu.h

@@ -0,0 +1,366 @@
+#ifndef BCMenu_H
+#define BCMenu_H
+
+#include <afxtempl.h>
+
+// BCMenuData class. Fill this class structure to define a single menu item:
+class BCMenuData
+{
+	wchar_t *m_szMenuText;
+public:
+	BCMenuData () {menuIconNormal=-1;xoffset=-1;bitmap=NULL;pContext=NULL;
+	nFlags=0;nID=0;syncflag=0;m_szMenuText=NULL;global_offset=-1;threeD=FALSE;};
+	void SetAnsiString(LPCSTR szAnsiString);
+	void SetWideString(const wchar_t *szWideString);
+	const wchar_t *GetWideString(void) {return m_szMenuText;};
+	~BCMenuData ();
+	CString GetString(void);//returns the menu text in ANSI or UNICODE
+	int xoffset,global_offset;
+	BOOL threeD;
+	int menuIconNormal;
+	UINT nFlags,nID,syncflag;
+	CImageList *bitmap;
+	void *pContext; // used to attach user data
+};
+
+//struct CMenuItemInfo : public MENUITEMINFO {
+struct CMenuItemInfo : public 
+	//MENUITEMINFO 
+#ifndef UNICODE   //SK: this fixes warning C4097: typedef-name 'MENUITEMINFO' used as synonym for class-name 'tagMENUITEMINFOA'
+	tagMENUITEMINFOA
+#else
+	tagMENUITEMINFOW
+#endif
+{
+	CMenuItemInfo()
+	{
+		memset(this, 0, sizeof(MENUITEMINFO));
+		cbSize = sizeof(MENUITEMINFO);
+	}
+};
+
+// how the menu's are drawn, either original or XP style
+typedef enum {BCMENU_DRAWMODE_ORIGINAL,BCMENU_DRAWMODE_XP} BC_MenuDrawMode;
+
+// how seperators are handled when removing a menu (Tongzhe Cui)
+typedef enum {BCMENU_NONE, BCMENU_HEAD, BCMENU_TAIL, BCMENU_BOTH} BC_Seperator;
+
+// defines for unicode support
+#ifndef UNICODE
+#define AppendMenu AppendMenuA
+#define InsertMenu InsertMenuA
+#define InsertODMenu InsertODMenuA
+#define AppendODMenu AppendODMenuA
+#define AppendODPopupMenu AppendODPopupMenuA
+#define ModifyODMenu ModifyODMenuA
+#else
+#define AppendMenu AppendMenuW
+#define InsertMenu InsertMenuW
+#define InsertODMenu InsertODMenuW
+#define AppendODMenu AppendODMenuW
+#define ModifyODMenu ModifyODMenuW
+#define AppendODPopupMenu AppendODPopupMenuW
+#endif
+
+
+class BCMenu : public CMenu
+{
+	DECLARE_DYNAMIC( BCMenu )
+public:
+	BCMenu(); 
+	virtual ~BCMenu();
+
+	// Functions for loading and applying bitmaps to menus (see example application)
+	virtual BOOL LoadMenu(LPCTSTR lpszResourceName);
+	virtual BOOL LoadMenu(int nResource);
+	BOOL LoadToolbar(UINT nToolBar);
+	BOOL LoadToolbars(const UINT *arID,int n);
+	void AddFromToolBar(CToolBar* pToolBar, int nResourceID);
+	BOOL LoadFromToolBar(UINT nID,UINT nToolBar,int& xoffset);
+	BOOL AddBitmapToImageList(CImageList *list,UINT nResourceID);
+	static HBITMAP LoadSysColorBitmap(int nResourceId);
+	void LoadCheckmarkBitmap(int unselect,int select); // custom check mark bitmaps
+
+	// functions for appending a menu option, use the AppendMenu call (see above define)
+	BOOL AppendMenuA(UINT nFlags,UINT nIDNewItem=0,const char *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList *il,int xoffset);
+	BOOL AppendMenuA(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap *bmp);
+	BOOL AppendMenuW(UINT nFlags,UINT nIDNewItem=0,wchar_t *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CImageList *il,int xoffset);
+	BOOL AppendMenuW(UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CBitmap *bmp);
+	BOOL AppendODMenuA(LPCSTR lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1);  
+	BOOL AppendODMenuW(wchar_t *lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1);  
+	BOOL AppendODMenuA(LPCSTR lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+	BOOL AppendODMenuW(wchar_t *lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+
+	// for appending a popup menu (see example application)
+	BCMenu* AppendODPopupMenuA(LPCSTR lpstrText);
+	BCMenu* AppendODPopupMenuW(wchar_t *lpstrText);
+
+	// functions for inserting a menu option, use the InsertMenu call (see above define)
+	BOOL InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,const char *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList *il,int xoffset);
+	BOOL InsertMenuA(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap *bmp);
+	BOOL InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,wchar_t *lpszNewItem=NULL,int nIconNormal=-1);
+	BOOL InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CImageList *il,int xoffset);
+	BOOL InsertMenuW(UINT nPosition,UINT nFlags,UINT nIDNewItem,wchar_t *lpszNewItem,CBitmap *bmp);
+	BOOL InsertODMenuA(UINT nPosition,LPCSTR lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1); 
+	BOOL InsertODMenuW(UINT nPosition,wchar_t *lpstrText,UINT nFlags = MF_OWNERDRAW,UINT nID = 0,int nIconNormal = -1);  
+	BOOL InsertODMenuA(UINT nPosition,LPCSTR lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+	BOOL InsertODMenuW(UINT nPosition,wchar_t *lpstrText,UINT nFlags,UINT nID,CImageList *il,int xoffset);
+
+	// functions for modifying a menu option, use the ModifyODMenu call (see above define)
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID=0,int nIconNormal=-1);
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID,CImageList *il,int xoffset);
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID,CBitmap *bmp);
+	BOOL ModifyODMenuA(const char *lpstrText,const char *OptionText,int nIconNormal);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID=0,int nIconNormal=-1);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID,CImageList *il,int xoffset);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID,CBitmap *bmp);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,wchar_t *OptionText,int nIconNormal);
+	// use this method for adding a solid/hatched colored square beside a menu option
+	// courtesy of Warren Stevens
+	BOOL ModifyODMenuA(const char *lpstrText,UINT nID,COLORREF fill,COLORREF border,int hatchstyle=-1,CSize *pSize=NULL);
+	BOOL ModifyODMenuW(wchar_t *lpstrText,UINT nID,COLORREF fill,COLORREF border,int hatchstyle=-1,CSize *pSize=NULL);
+
+	// for deleting and removing menu options
+	BOOL	RemoveMenu(UINT uiId,UINT nFlags);
+	BOOL	DeleteMenu(UINT uiId,UINT nFlags);
+	// sPos means Seperator's position, since we have no way to find the seperator's position in the menu
+	// we have to specify them when we call the RemoveMenu to make sure the unused seperators are removed;
+	// sPos  = None no seperator removal;
+	//       = Head  seperator in front of this menu item;
+	//       = Tail  seperator right after this menu item;
+	//       = Both  seperators at both ends;
+	// remove the menu item based on their text, return -1 if not found, otherwise return the menu position;
+	int RemoveMenu(char* pText, BC_Seperator sPos=BCMENU_NONE);
+	int RemoveMenu(wchar_t* pText, BC_Seperator sPos=BCMENU_NONE);
+	int DeleteMenu(char* pText, BC_Seperator sPos=BCMENU_NONE);
+	int DeleteMenu(wchar_t* pText, BC_Seperator sPos=BCMENU_NONE);
+
+	// Destoying
+	virtual BOOL DestroyMenu();
+
+	// function for retrieving and setting a menu options text (use this function
+	// because it is ownerdrawn)
+	BOOL GetMenuText(UINT id,CString &string,UINT nFlags = MF_BYPOSITION);
+	BOOL SetMenuText(UINT id,CString string, UINT nFlags = MF_BYPOSITION);
+
+	// Getting a submenu from it's name or position
+	BCMenu* GetSubBCMenu(char* lpszSubMenuName);
+	BCMenu* GetSubBCMenu(wchar_t* lpszSubMenuName);
+	CMenu* GetSubMenu (LPCTSTR lpszSubMenuName);
+	CMenu* GetSubMenu (int nPos);
+	int GetMenuPosition(char* pText);
+	int GetMenuPosition(wchar_t* pText);
+
+	// Drawing: 
+	virtual void DrawItem( LPDRAWITEMSTRUCT);  // Draw an item
+	virtual void MeasureItem( LPMEASUREITEMSTRUCT );  // Measure an item
+
+	// Static functions used for handling menu's in the mainframe
+	static void UpdateMenu(CMenu *pmenu);
+	static BOOL IsMenu(CMenu *submenu);
+	static BOOL IsMenu(HMENU submenu);
+	static LRESULT FindKeyboardShortcut(UINT nChar,UINT nFlags,CMenu *pMenu);
+
+	// Function to set how menu is drawn, either original or XP style
+	static void SetMenuDrawMode(UINT mode){
+		BCMenu::original_drawmode=mode;
+		BCMenu::xp_drawmode=mode;
+	};
+	// Function to set how disabled items are drawn (mode=FALSE means they are not drawn selected)
+	static void SetSelectDisableMode(BOOL mode){
+		BCMenu::original_select_disabled=mode;
+		BCMenu::xp_select_disabled=mode;
+	};
+	static int BCMenu::GetMenuDrawMode(void);
+	static BOOL BCMenu::GetSelectDisableMode(void);
+
+	// how the bitmaps are drawn in XP Luna mode
+	static void SetXPBitmap3D(BOOL val){
+		BCMenu::xp_draw_3D_bitmaps=val;
+	};
+	static BOOL GetXPBitmap3D(void){return BCMenu::xp_draw_3D_bitmaps;}
+
+	// Customizing:
+	// Set icon size
+	void SetIconSize (int, int); 
+	// set the color in the bitmaps that is the background transparent color
+	void SetBitmapBackground(COLORREF color);
+	void UnSetBitmapBackground(void);
+	// obsolete functions for setting how menu images are dithered for disabled menu options
+	BOOL GetDisableOldStyle(void);
+	void SetDisableOldStyle(void);
+	void UnSetDisableOldStyle(void);
+	static COLORREF LightenColor(COLORREF col,double factor);
+	static COLORREF DarkenColor(COLORREF col,double factor);
+
+	// Miscellaneous Protected Member functions
+protected:
+	static BOOL IsNewShell(void);
+	static BOOL IsWinXPLuna(void);
+	static BOOL IsLunaMenuStyle(void);
+	static BOOL IsWindowsClassicTheme(void);
+	BCMenuData *BCMenu::FindMenuItem(UINT nID);
+	BCMenu *FindMenuOption(int nId,int& nLoc);
+	BCMenu *FindAnotherMenuOption(int nId,int& nLoc,CArray<BCMenu*,BCMenu*>&bcsubs,
+		CArray<int,int&>&bclocs);
+	BCMenuData *FindMenuOption(wchar_t *lpstrText);
+	void InsertSpaces(void);
+	void DrawCheckMark(CDC *pDC,int x,int y,COLORREF color);
+	void DrawRadioDot(CDC *pDC,int x,int y,COLORREF color);
+	BCMenuData *NewODMenu(UINT pos,UINT nFlags,UINT nID,CString string);
+	void SynchronizeMenu(void);
+	void BCMenu::InitializeMenuList(int value);
+	void BCMenu::DeleteMenuList(void);
+	BCMenuData *BCMenu::FindMenuList(UINT nID);
+	void DrawItem_Win9xNT2000 (LPDRAWITEMSTRUCT lpDIS);
+	void DrawItem_WinXP (LPDRAWITEMSTRUCT lpDIS);
+	BOOL Draw3DCheckmark(CDC *dc, const CRect& rc,BOOL bSelected,HBITMAP hbmCheck);
+	BOOL DrawXPCheckmark(CDC *dc, const CRect& rc, HBITMAP hbmCheck,COLORREF &colorout);
+	void DitherBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, 
+		int nHeight, HBITMAP hbm, int nXSrc, int nYSrc,COLORREF bgcolor);
+	void DitherBlt2(CDC *drawdc, int nXDest, int nYDest, int nWidth, 
+		int nHeight, CBitmap &bmp, int nXSrc, int nYSrc,COLORREF bgcolor);
+	BOOL GetBitmapFromImageList(CDC* pDC,CImageList *imglist,int nIndex,CBitmap &bmp);
+	BOOL ImageListDuplicate(CImageList *il,int xoffset,CImageList *newlist);
+	static WORD NumBitmapColors(LPBITMAPINFOHEADER lpBitmap);
+	void ColorBitmap(CDC* pDC, CBitmap& bmp,CSize bitmap_size,CSize icon_size,COLORREF fill,COLORREF border,int hatchstyle=-1);
+	void RemoveTopLevelOwnerDraw(void);
+	int GetMenuStart(void);
+	void GetFadedBitmap(CBitmap &bmp);
+	void GetShadowBitmap(CBitmap &bmp);
+	int AddToGlobalImageList(CImageList *il,int xoffset,int nID);
+	int GlobalImageListOffset(int nID);
+	BOOL CanDraw3DImageList(int offset);
+
+	// Member Variables
+protected:
+	CTypedPtrArray<CPtrArray, BCMenuData*> m_MenuList;  // Stores list of menu items 
+	// When loading an owner-drawn menu using a Resource, BCMenu must keep track of
+	// the popup menu's that it creates. Warning, this list *MUST* be destroyed
+	// last item first :)
+	CTypedPtrArray<CPtrArray, HMENU>  m_SubMenus;  // Stores list of sub-menus 
+	// Stores a list of all BCMenu's ever created 
+	static CTypedPtrArray<CPtrArray, HMENU>  m_AllSubMenus;
+	// Global ImageList
+	static CImageList m_AllImages;
+	static CArray<int,int&> m_AllImagesID;
+	// icon size
+	int m_iconX,m_iconY;
+	COLORREF m_bitmapBackground;
+	BOOL m_bitmapBackgroundFlag;
+	BOOL disable_old_style;
+	static UINT original_drawmode;
+	static BOOL original_select_disabled;
+	static UINT xp_drawmode;
+	static BOOL xp_select_disabled;
+	static BOOL xp_draw_3D_bitmaps;
+	CImageList *checkmaps;
+	BOOL checkmapsshare;
+	int m_selectcheck;
+	int m_unselectcheck;
+	BOOL m_bDynIcons;
+	BOOL m_loadmenu;
+}; 
+
+class BCMenuToolBar : public CToolBar{
+public:
+	BCMenuToolBar() : CToolBar() {m_iconX=m_iconY=0;}
+	BOOL LoadToolBar(LPCTSTR lpszResourceName);
+	BOOL LoadToolBar(UINT nIDResource){
+		return LoadToolBar(MAKEINTRESOURCE(nIDResource));
+	}
+	BOOL LoadBitmap(LPCTSTR lpszResourceName);
+	void GetIconSize(int &iconx,int &icony){iconx=m_iconX;icony=m_iconY;}
+protected:
+	int m_iconX,m_iconY;
+};
+
+#define BCMENU_USE_MEMDC
+
+#ifdef BCMENU_USE_MEMDC
+//////////////////////////////////////////////////
+// BCMenuMemDC - memory DC
+//
+// Author: Keith Rule
+// Email:  keithr@europa.com
+// Copyright 1996-1997, Keith Rule
+//
+// You may freely use or modify this code provided this
+// Copyright is included in all derived versions.
+//
+// History - 10/3/97 Fixed scrolling bug.
+//                   Added print support.
+//           25 feb 98 - fixed minor assertion bug
+//
+// This class implements a memory Device Context
+
+class BCMenuMemDC : public CDC
+{
+public:
+
+	// constructor sets up the memory DC
+	BCMenuMemDC(CDC* pDC,LPCRECT lpSrcRect) : CDC()
+	{
+		ASSERT(pDC != NULL);
+
+		m_rect.CopyRect(lpSrcRect);
+		m_pDC = pDC;
+		m_pOldBitmap = NULL;
+		m_bMemDC = !pDC->IsPrinting();
+
+		if (m_bMemDC)    // Create a Memory DC
+		{
+			CreateCompatibleDC(pDC);
+			m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
+			m_pOldBitmap = SelectObject(&m_bitmap);
+			SetWindowOrg(m_rect.left, m_rect.top);
+		}
+		else        // Make a copy of the relevent parts of the current DC for printing
+		{
+			m_bPrinting = pDC->m_bPrinting;
+			m_hDC       = pDC->m_hDC;
+			m_hAttribDC = pDC->m_hAttribDC;
+		}
+	}
+
+	// Destructor copies the contents of the mem DC to the original DC
+	~BCMenuMemDC()
+	{
+		if (m_bMemDC) 
+		{    
+			// Copy the offscreen bitmap onto the screen.
+			m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
+				this, m_rect.left, m_rect.top, SRCCOPY);
+
+			//Swap back the original bitmap.
+			SelectObject(m_pOldBitmap);
+		} else {
+			// All we need to do is replace the DC with an illegal value,
+			// this keeps us from accidently deleting the handles associated with
+			// the CDC that was passed to the constructor.
+			m_hDC = m_hAttribDC = NULL;
+		}
+	}
+
+	// Allow usage as a pointer
+	BCMenuMemDC* operator->() {return this;}
+
+	// Allow usage as a pointer
+	operator BCMenuMemDC*() {return this;}
+
+private:
+	CBitmap  m_bitmap;      // Offscreen bitmap
+	CBitmap* m_pOldBitmap;  // bitmap originally found in BCMenuMemDC
+	CDC*     m_pDC;         // Saves CDC passed in constructor
+	CRect    m_rect;        // Rectangle of drawing area.
+	BOOL     m_bMemDC;      // TRUE if CDC really is a Memory DC.
+};
+
+#endif
+
+#endif
+
+//*************************************************************************

+ 2460 - 0
source/hook/skinui/BtnST.cpp

@@ -0,0 +1,2460 @@
+#include "stdafx.h"
+#include "BtnST.h"
+
+#ifdef	BTNST_USE_SOUND
+#pragma comment(lib, "winmm.lib")
+#include <Mmsystem.h>
+#endif
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CButtonST
+
+// Mask for control's type
+#define BS_TYPEMASK SS_TYPEMASK
+
+#ifndef	TTM_SETTITLE
+#define TTM_SETTITLEA           (WM_USER + 32)  // wParam = TTI_*, lParam = char* szTitle
+#define TTM_SETTITLEW           (WM_USER + 33)  // wParam = TTI_*, lParam = wchar* szTitle
+#ifdef	UNICODE
+#define TTM_SETTITLE            TTM_SETTITLEW
+#else
+#define TTM_SETTITLE            TTM_SETTITLEA
+#endif
+#endif
+
+#ifndef	TTS_BALLOON
+#define	TTS_BALLOON		0x40
+#endif
+
+CButtonST::CButtonST()
+{
+	m_bIsPressed		= FALSE;
+	m_bIsFocused		= FALSE;
+	m_bIsDisabled		= FALSE;
+	m_bMouseOnButton	= FALSE;
+
+	FreeResources(FALSE);
+
+	// Default type is "flat" button
+	m_bIsFlat = TRUE;
+	// Button will be tracked also if when the window is inactive (like Internet Explorer)
+	m_bAlwaysTrack = TRUE;
+  
+	// By default draw border in "flat" button 
+	m_bDrawBorder = TRUE; 
+  
+	// By default icon is aligned horizontally
+	m_byAlign = ST_ALIGN_HORIZ; 
+
+	// By default use usual pressed style
+	SetPressedStyle(BTNST_PRESSED_LEFTRIGHT, FALSE);
+  
+	// By default, for "flat" button, don't draw the focus rect
+	m_bDrawFlatFocus = FALSE;
+
+	// By default the button is not the default button
+	m_bIsDefault = FALSE;
+	// Invalid value, since type still unknown
+	m_nTypeStyle = BS_TYPEMASK;
+
+	// By default the button is not a checkbox
+	m_bIsCheckBox = FALSE;
+	m_nCheck = 0;
+
+	// Set default colors
+	SetDefaultColors(FALSE);
+
+	// No tooltip created
+	m_ToolTip.m_hWnd = NULL;
+	m_dwToolTipStyle = 0;
+
+	// Do not draw as a transparent button
+	m_bDrawTransparent = FALSE;
+	m_pbmpOldBk = NULL;
+
+	// No URL defined
+	SetURL(NULL);
+
+	// No cursor defined
+	m_hCursor = NULL;
+
+	// No associated menu
+#ifndef	BTNST_USE_BCMENU
+	m_hMenu = NULL;
+#endif
+	m_hParentWndMenu = NULL;
+	m_bMenuDisplayed = FALSE;
+
+	m_bShowDisabledBitmap = TRUE;
+
+	m_ptImageOrg.x = 3;
+	m_ptImageOrg.y = 3;
+
+	// No defined callbacks
+	::ZeroMemory(&m_csCallbacks, sizeof(m_csCallbacks));
+
+#ifdef	BTNST_USE_SOUND
+	// No defined sounds
+	::ZeroMemory(&m_csSounds, sizeof(m_csSounds));
+#endif
+} // End of CButtonST
+
+CButtonST::~CButtonST()
+{
+	// Restore old bitmap (if any)
+	if (m_dcBk.m_hDC && m_pbmpOldBk)
+	{
+		m_dcBk.SelectObject(m_pbmpOldBk);
+	} // if
+
+	FreeResources();
+
+	// Destroy the cursor (if any)
+	if (m_hCursor) ::DestroyCursor(m_hCursor);
+
+	// Destroy the menu (if any)
+#ifdef	BTNST_USE_BCMENU
+	if (m_menuPopup.m_hMenu)	m_menuPopup.DestroyMenu();
+#else
+	if (m_hMenu)	::DestroyMenu(m_hMenu);
+#endif
+} // End of ~CButtonST
+
+BEGIN_MESSAGE_MAP(CButtonST, CButton)
+    //{{AFX_MSG_MAP(CButtonST)
+	ON_WM_SETCURSOR()
+	ON_WM_KILLFOCUS()
+	ON_WM_MOUSEMOVE()
+	ON_WM_SYSCOLORCHANGE()
+	ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
+	ON_WM_ACTIVATE()
+	ON_WM_ENABLE()
+	ON_WM_CANCELMODE()
+	ON_WM_GETDLGCODE()
+	ON_WM_CTLCOLOR_REFLECT()
+	//}}AFX_MSG_MAP
+#ifdef	BTNST_USE_BCMENU
+	ON_WM_MENUCHAR()
+	ON_WM_MEASUREITEM()
+#endif
+
+	ON_MESSAGE(BM_SETSTYLE, OnSetStyle)
+	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
+	ON_MESSAGE(BM_SETCHECK, OnSetCheck)
+	ON_MESSAGE(BM_GETCHECK, OnGetCheck)
+END_MESSAGE_MAP()
+
+void CButtonST::FreeResources(BOOL bCheckForNULL)
+{
+	if (bCheckForNULL)
+	{
+		// Destroy icons
+		// Note: the following two lines MUST be here! even if
+		// BoundChecker says they are unnecessary!
+		if (m_csIcons[0].hIcon)	::DestroyIcon(m_csIcons[0].hIcon);
+		if (m_csIcons[1].hIcon)	::DestroyIcon(m_csIcons[1].hIcon);
+
+		// Destroy bitmaps
+		if (m_csBitmaps[0].hBitmap)	::DeleteObject(m_csBitmaps[0].hBitmap);
+		if (m_csBitmaps[1].hBitmap)	::DeleteObject(m_csBitmaps[1].hBitmap);
+
+		// Destroy mask bitmaps
+		if (m_csBitmaps[0].hMask)	::DeleteObject(m_csBitmaps[0].hMask);
+		if (m_csBitmaps[1].hMask)	::DeleteObject(m_csBitmaps[1].hMask);
+	} // if
+
+	::ZeroMemory(&m_csIcons, sizeof(m_csIcons));
+	::ZeroMemory(&m_csBitmaps, sizeof(m_csBitmaps));
+} // End of FreeResources
+
+void CButtonST::PreSubclassWindow() 
+{
+	UINT nBS;
+
+	nBS = GetButtonStyle();
+
+	// Set initial control type
+	m_nTypeStyle = nBS & BS_TYPEMASK;
+
+	// Check if this is a checkbox
+	if (nBS & BS_CHECKBOX) m_bIsCheckBox = TRUE;
+
+	// Set initial default state flag
+	if (m_nTypeStyle == BS_DEFPUSHBUTTON)
+	{
+		// Set default state for a default button
+		m_bIsDefault = TRUE;
+
+		// Adjust style for default button
+		m_nTypeStyle = BS_PUSHBUTTON;
+	} // If
+
+	// You should not set the Owner Draw before this call
+	// (don't use the resource editor "Owner Draw" or
+	// ModifyStyle(0, BS_OWNERDRAW) before calling PreSubclassWindow() )
+	ASSERT(m_nTypeStyle != BS_OWNERDRAW);
+
+	// Switch to owner-draw
+	ModifyStyle(BS_TYPEMASK, BS_OWNERDRAW, SWP_FRAMECHANGED);
+
+	CButton::PreSubclassWindow();
+} // End of PreSubclassWindow
+
+UINT CButtonST::OnGetDlgCode() 
+{
+	UINT nCode = CButton::OnGetDlgCode();
+
+	// Tell the system if we want default state handling
+	// (losing default state always allowed)
+	nCode |= (m_bIsDefault ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON);
+
+	return nCode;
+} // End of OnGetDlgCode
+
+BOOL CButtonST::PreTranslateMessage(MSG* pMsg) 
+{
+	InitToolTip();
+	m_ToolTip.RelayEvent(pMsg);
+	
+	if (pMsg->message == WM_LBUTTONDBLCLK)
+		pMsg->message = WM_LBUTTONDOWN;
+
+	return CButton::PreTranslateMessage(pMsg);
+} // End of PreTranslateMessage
+
+HBRUSH CButtonST::CtlColor(CDC* pDC, UINT nCtlColor) 
+{
+	return (HBRUSH)::GetStockObject(NULL_BRUSH); 
+} // End of CtlColor
+
+void CButtonST::OnSysColorChange() 
+{
+	CButton::OnSysColorChange();
+
+	m_dcBk.DeleteDC();
+	m_bmpBk.DeleteObject();	
+	SetDefaultColors();
+} // End of OnSysColorChange
+
+LRESULT CButtonST::OnSetStyle(WPARAM wParam, LPARAM lParam)
+{
+	UINT nNewType = (wParam & BS_TYPEMASK);
+
+	// Update default state flag
+	if (nNewType == BS_DEFPUSHBUTTON)
+	{
+		m_bIsDefault = TRUE;
+	} // if
+	else if (nNewType == BS_PUSHBUTTON)
+	{
+		// Losing default state always allowed
+		m_bIsDefault = FALSE;
+	} // if
+
+	// Can't change control type after owner-draw is set.
+	// Let the system process changes to other style bits
+	// and redrawing, while keeping owner-draw style
+	return DefWindowProc(BM_SETSTYLE,
+		(wParam & ~BS_TYPEMASK) | BS_OWNERDRAW, lParam);
+} // End of OnSetStyle
+
+LRESULT CButtonST::OnSetCheck(WPARAM wParam, LPARAM lParam)
+{
+	ASSERT(m_bIsCheckBox);
+
+	switch (wParam)
+	{
+		case BST_CHECKED:
+		case BST_INDETERMINATE:	// Indeterminate state is handled like checked state
+			SetCheck(1);
+			break;
+		default:
+			SetCheck(0);
+			break;
+	} // switch
+
+	return 0;
+} // End of OnSetCheck
+
+LRESULT CButtonST::OnGetCheck(WPARAM wParam, LPARAM lParam)
+{
+	ASSERT(m_bIsCheckBox);
+	return GetCheck();
+} // End of OnGetCheck
+
+#ifdef	BTNST_USE_BCMENU
+LRESULT CButtonST::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu) 
+{
+	LRESULT lResult;
+	if (BCMenu::IsMenu(pMenu))
+		lResult = BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
+	else
+		lResult = CButton::OnMenuChar(nChar, nFlags, pMenu);
+	return lResult;
+} // End of OnMenuChar
+#endif
+
+#ifdef	BTNST_USE_BCMENU
+void CButtonST::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
+{
+	BOOL bSetFlag = FALSE;
+	if (lpMeasureItemStruct->CtlType == ODT_MENU)
+	{
+		if (IsMenu((HMENU)lpMeasureItemStruct->itemID) && BCMenu::IsMenu((HMENU)lpMeasureItemStruct->itemID))
+		{
+			m_menuPopup.MeasureItem(lpMeasureItemStruct);
+			bSetFlag = TRUE;
+		} // if
+	} // if
+	if (!bSetFlag) CButton::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
+} // End of OnMeasureItem
+#endif
+
+void CButtonST::OnEnable(BOOL bEnable) 
+{
+	CButton::OnEnable(bEnable);
+	
+	if (bEnable == FALSE)	
+	{
+		CWnd*	pWnd = GetParent()->GetNextDlgTabItem(this);
+		if (pWnd)
+			pWnd->SetFocus();
+		else
+			GetParent()->SetFocus();
+
+		CancelHover();
+	} // if
+} // End of OnEnable
+
+void CButtonST::OnKillFocus(CWnd * pNewWnd)
+{
+	CButton::OnKillFocus(pNewWnd);
+	CancelHover();
+} // End of OnKillFocus
+
+void CButtonST::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 
+{
+	CButton::OnActivate(nState, pWndOther, bMinimized);
+	if (nState == WA_INACTIVE)	CancelHover();
+} // End of OnActivate
+
+void CButtonST::OnCancelMode() 
+{
+	CButton::OnCancelMode();
+	CancelHover();
+} // End of OnCancelMode
+
+BOOL CButtonST::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
+{
+	// If a cursor was specified then use it!
+	if (m_hCursor != NULL)
+	{
+		::SetCursor(m_hCursor);
+		return TRUE;
+	} // if
+
+	return CButton::OnSetCursor(pWnd, nHitTest, message);
+} // End of OnSetCursor
+
+void CButtonST::CancelHover()
+{
+	// Only for flat buttons
+	if (m_bIsFlat)
+	{
+		if (m_bMouseOnButton)
+		{
+			m_bMouseOnButton = FALSE;
+			Invalidate();
+		} // if
+	} // if
+} // End of CancelHover
+
+void CButtonST::OnMouseMove(UINT nFlags, CPoint point)
+{
+	CWnd*				wndUnderMouse = NULL;
+	CWnd*				wndActive = this;
+	TRACKMOUSEEVENT		csTME;
+
+	CButton::OnMouseMove(nFlags, point);
+
+	ClientToScreen(&point);
+	wndUnderMouse = WindowFromPoint(point);
+
+	// If the mouse enter the button with the left button pressed then do nothing
+	if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE) return;
+
+	// If our button is not flat then do nothing
+	if (m_bIsFlat == FALSE) return;
+
+	if (m_bAlwaysTrack == FALSE)	wndActive = GetActiveWindow();
+
+	if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
+	{
+		if (!m_bMouseOnButton)
+		{
+			m_bMouseOnButton = TRUE;
+
+			Invalidate();
+
+#ifdef	BTNST_USE_SOUND
+			// Play sound ?
+			if (m_csSounds[0].lpszSound)
+				::PlaySound(m_csSounds[0].lpszSound, m_csSounds[0].hMod, m_csSounds[0].dwFlags);
+#endif
+
+			csTME.cbSize = sizeof(csTME);
+			csTME.dwFlags = TME_LEAVE;
+			csTME.hwndTrack = m_hWnd;
+			::_TrackMouseEvent(&csTME);
+		} // if
+	} else CancelHover();
+} // End of OnMouseMove
+
+// Handler for WM_MOUSELEAVE
+LRESULT CButtonST::OnMouseLeave(WPARAM wParam, LPARAM lParam)
+{
+	CancelHover();
+	return 0;
+} // End of OnMouseLeave
+
+BOOL CButtonST::OnClicked() 
+{	
+	SetFocus();
+
+#ifdef	BTNST_USE_SOUND
+	// Play sound ?
+	if (m_csSounds[1].lpszSound)
+		::PlaySound(m_csSounds[1].lpszSound, m_csSounds[1].hMod, m_csSounds[1].dwFlags);
+#endif
+
+	if (m_bIsCheckBox)
+	{
+		m_nCheck = !m_nCheck;
+		Invalidate();
+	} // if
+	else
+	{
+		// Handle the menu (if any)
+#ifdef	BTNST_USE_BCMENU
+		if (m_menuPopup.m_hMenu)
+#else
+		if (m_hMenu)
+#endif
+		{
+			CRect	rWnd;
+			GetWindowRect(rWnd);
+
+			m_bMenuDisplayed = TRUE;
+			Invalidate();
+
+#ifdef	BTNST_USE_BCMENU
+			BCMenu* psub = (BCMenu*)m_menuPopup.GetSubMenu(0);
+			if (m_csCallbacks.hWnd)	::SendMessage(m_csCallbacks.hWnd, m_csCallbacks.nMessage, (WPARAM)psub, m_csCallbacks.lParam);
+			DWORD dwRetValue = psub->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, rWnd.left, rWnd.bottom, this, NULL);
+#else
+			HMENU hSubMenu = ::GetSubMenu(m_hMenu, 0);
+			if (m_csCallbacks.hWnd)	::SendMessage(m_csCallbacks.hWnd, m_csCallbacks.nMessage, (WPARAM)hSubMenu, m_csCallbacks.lParam);
+			DWORD dwRetValue = ::TrackPopupMenuEx(hSubMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, rWnd.left, rWnd.bottom, m_hParentWndMenu, NULL);
+#endif
+
+			m_bMenuDisplayed = FALSE;
+			Invalidate();
+
+			if (dwRetValue)
+				::PostMessage(m_hParentWndMenu, WM_COMMAND, MAKEWPARAM(dwRetValue, 0), (LPARAM)NULL);
+		} // if
+		else
+		{
+			// Handle the URL (if any)
+			if (_tcslen(m_szURL) > 0)
+			{
+				SHELLEXECUTEINFO	csSEI;
+
+				memset(&csSEI, 0, sizeof(csSEI));
+				csSEI.cbSize = sizeof(SHELLEXECUTEINFO);
+				csSEI.fMask = SEE_MASK_FLAG_NO_UI;
+				csSEI.lpVerb = _T("open");
+				csSEI.lpFile = m_szURL;
+				csSEI.nShow = SW_SHOWMAXIMIZED;
+				::ShellExecuteEx(&csSEI);
+			} // if
+		} // else
+	} // else
+
+	return FALSE;
+} // End of OnClicked
+
+void CButtonST::DrawItem(LPDRAWITEMSTRUCT lpDIS)
+{
+	CDC*	pDC = CDC::FromHandle(lpDIS->hDC);
+
+	// Checkbox?
+	if (m_bIsCheckBox)
+	{
+		m_bIsPressed  =  (lpDIS->itemState & ODS_SELECTED) || (m_nCheck != 0);
+	} // if
+	else	// Normal button OR other button style ...
+	{
+		m_bIsPressed = (lpDIS->itemState & ODS_SELECTED);
+
+		// If there is a menu and it's displayed, draw the button as pressed
+		if (
+#ifdef	BTNST_USE_BCMENU
+			m_menuPopup.m_hMenu 
+#else
+			m_hMenu 
+#endif
+			&& m_bMenuDisplayed)	m_bIsPressed = TRUE;
+	} // else
+
+	m_bIsFocused  = (lpDIS->itemState & ODS_FOCUS);
+	m_bIsDisabled = (lpDIS->itemState & ODS_DISABLED);
+
+	CRect itemRect = lpDIS->rcItem;
+
+	pDC->SetBkMode(TRANSPARENT);
+
+	// Prepare draw... paint button background
+
+	// Draw transparent?
+	if (m_bDrawTransparent)
+		PaintBk(pDC);
+	else
+		OnDrawBackground(pDC, &itemRect);
+
+	// Draw button border
+	OnDrawBorder(pDC, &itemRect);
+
+	// Read the button's title
+	CString sTitle;
+	GetWindowText(sTitle);
+
+	CRect captionRect = lpDIS->rcItem;
+
+	// Draw the icon
+	if (m_csIcons[0].hIcon)
+	{
+		DrawTheIcon(pDC, !sTitle.IsEmpty(), &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+	} // if
+
+	if (m_csBitmaps[0].hBitmap)
+	{
+		pDC->SetBkColor(RGB(255,255,255));
+		DrawTheBitmap(pDC, !sTitle.IsEmpty(), &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+	} // if
+
+	// Write the button title (if any)
+	if (sTitle.IsEmpty() == FALSE)
+	{
+		DrawTheText(pDC, (LPCTSTR)sTitle, &lpDIS->rcItem, &captionRect, m_bIsPressed, m_bIsDisabled);
+	} // if
+
+	if (m_bIsFlat == FALSE || (m_bIsFlat && m_bDrawFlatFocus))
+	{
+		// Draw the focus rect
+		if (m_bIsFocused)
+		{
+			CRect focusRect = itemRect;
+			focusRect.DeflateRect(3, 3);
+			pDC->DrawFocusRect(&focusRect);
+		} // if
+	} // if
+} // End of DrawItem
+
+void CButtonST::PaintBk(CDC* pDC)
+{
+	CClientDC clDC(GetParent());
+	CRect rect;
+	CRect rect1;
+
+	GetClientRect(rect);
+
+	GetWindowRect(rect1);
+	GetParent()->ScreenToClient(rect1);
+
+	if (m_dcBk.m_hDC == NULL)
+	{
+		m_dcBk.CreateCompatibleDC(&clDC);
+		m_bmpBk.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());
+		m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+		m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);
+	} // if
+
+	pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
+} // End of PaintBk
+
+HBITMAP CButtonST::CreateBitmapMask(HBITMAP hSourceBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTransColor)
+{
+	HBITMAP		hMask		= NULL;
+	HDC			hdcSrc		= NULL;
+	HDC			hdcDest		= NULL;
+	HBITMAP		hbmSrcT		= NULL;
+	HBITMAP		hbmDestT	= NULL;
+	COLORREF	crSaveBk;
+	COLORREF	crSaveDestText;
+
+	hMask = ::CreateBitmap(dwWidth, dwHeight, 1, 1, NULL);
+	if (hMask == NULL)	return NULL;
+
+	hdcSrc	= ::CreateCompatibleDC(NULL);
+	hdcDest	= ::CreateCompatibleDC(NULL);
+
+	hbmSrcT = (HBITMAP)::SelectObject(hdcSrc, hSourceBitmap);
+	hbmDestT = (HBITMAP)::SelectObject(hdcDest, hMask);
+
+	crSaveBk = ::SetBkColor(hdcSrc, crTransColor);
+
+	::BitBlt(hdcDest, 0, 0, dwWidth, dwHeight, hdcSrc, 0, 0, SRCCOPY);
+
+	crSaveDestText = ::SetTextColor(hdcSrc, RGB(255, 255, 255));
+	::SetBkColor(hdcSrc,RGB(0, 0, 0));
+
+	::BitBlt(hdcSrc, 0, 0, dwWidth, dwHeight, hdcDest, 0, 0, SRCAND);
+
+	SetTextColor(hdcDest, crSaveDestText);
+
+	::SetBkColor(hdcSrc, crSaveBk);
+	::SelectObject(hdcSrc, hbmSrcT);
+	::SelectObject(hdcDest, hbmDestT);
+
+	::DeleteDC(hdcSrc);
+	::DeleteDC(hdcDest);
+
+	return hMask;
+} // End of CreateBitmapMask
+
+//
+// Parameters:
+//		[IN]	bHasTitle
+//				TRUE if the button has a text
+//		[IN]	rpItem
+//				A pointer to a RECT structure indicating the allowed paint area
+//		[IN/OUT]rpTitle
+//				A pointer to a CRect object indicating the paint area reserved for the
+//				text. This structure will be modified if necessary.
+//		[IN]	bIsPressed
+//				TRUE if the button is currently pressed
+//		[IN]	dwWidth
+//				Width of the image (icon or bitmap)
+//		[IN]	dwHeight
+//				Height of the image (icon or bitmap)
+//		[OUT]	rpImage
+//				A pointer to a CRect object that will receive the area available to the image
+//
+void CButtonST::PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage)
+{
+	CRect rBtn;
+
+	rpImage->CopyRect(rpItem);
+
+	switch (m_byAlign)
+	{
+		case ST_ALIGN_HORIZ:
+			if (bHasTitle == FALSE)
+			{
+				// Center image horizontally
+				rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+			}
+			else
+			{
+				// Image must be placed just inside the focus rect
+				rpImage->left += m_ptImageOrg.x;  
+				rpTitle->left += dwWidth + m_ptImageOrg.x;
+			}
+			// Center image vertically
+			rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+			break;
+
+		case ST_ALIGN_HORIZ_RIGHT:
+			GetClientRect(&rBtn);
+			if (bHasTitle == FALSE)
+			{
+				// Center image horizontally
+				rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+			}
+			else
+			{
+				// Image must be placed just inside the focus rect
+				rpTitle->right = rpTitle->Width() - dwWidth - m_ptImageOrg.x;
+				rpTitle->left = m_ptImageOrg.x;
+				rpImage->left = rBtn.right - dwWidth - m_ptImageOrg.x;
+				// Center image vertically
+				rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
+			}
+			break;
+		
+		case ST_ALIGN_VERT:
+			// Center image horizontally
+			rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
+			if (bHasTitle == FALSE)
+			{
+				// Center image vertically
+				rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);           
+			}
+			else
+			{
+				rpImage->top = m_ptImageOrg.y;
+				rpTitle->top += dwHeight;
+			}
+			break;
+
+		case ST_ALIGN_OVERLAP:
+			break;
+	} // switch
+    
+	// If button is pressed then press image also
+	if (bIsPressed && m_bIsCheckBox == FALSE)
+		rpImage->OffsetRect(m_ptPressedOffset.x, m_ptPressedOffset.y);
+} // End of PrepareImageRect
+
+void CButtonST::DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+	BYTE		byIndex		= 0;
+
+	// Select the icon to use
+	if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
+		byIndex = 0;
+	else
+		byIndex = (m_csIcons[1].hIcon == NULL ? 0 : 1);
+
+	CRect	rImage;
+	PrepareImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, m_csIcons[byIndex].dwWidth, m_csIcons[byIndex].dwHeight, &rImage);
+
+	// Ole'!
+	pDC->DrawState(	rImage.TopLeft(),
+					rImage.Size(), 
+					m_csIcons[byIndex].hIcon,
+					(bIsDisabled ? DSS_DISABLED : DSS_NORMAL), 
+					(CBrush*)NULL);
+} // End of DrawTheIcon
+
+void CButtonST::DrawTheBitmap(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+	HDC			hdcBmpMem	= NULL;
+	HBITMAP		hbmOldBmp	= NULL;
+	HDC			hdcMem		= NULL;
+	HBITMAP		hbmT		= NULL;
+
+	BYTE		byIndex		= 0;
+
+	// Select the bitmap to use
+	if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
+		byIndex = 0;
+	else
+		byIndex = (m_csBitmaps[1].hBitmap == NULL ? 0 : 1);
+
+	CRect	rImage;
+	PrepareImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, &rImage);
+
+	hdcBmpMem = ::CreateCompatibleDC(pDC->m_hDC);
+
+	hbmOldBmp = (HBITMAP)::SelectObject(hdcBmpMem, m_csBitmaps[byIndex].hBitmap);
+
+	hdcMem = ::CreateCompatibleDC(NULL);
+
+	hbmT = (HBITMAP)::SelectObject(hdcMem, m_csBitmaps[byIndex].hMask);
+
+	if (bIsDisabled && m_bShowDisabledBitmap)
+	{
+		HDC		hDC = NULL;
+		HBITMAP	hBitmap = NULL;
+
+		hDC = ::CreateCompatibleDC(pDC->m_hDC);
+		hBitmap = ::CreateCompatibleBitmap(pDC->m_hDC, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight);
+		HBITMAP	hOldBmp2 = (HBITMAP)::SelectObject(hDC, hBitmap);
+
+		RECT	rRect;
+		rRect.left = 0;
+		rRect.top = 0;
+		rRect.right = rImage.right + 1;
+		rRect.bottom = rImage.bottom + 1;
+		::FillRect(hDC, &rRect, (HBRUSH)RGB(255, 255, 255));
+
+		COLORREF crOldColor = ::SetBkColor(hDC, RGB(255,255,255));
+
+		::BitBlt(hDC, 0, 0, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcMem, 0, 0, SRCAND);
+		::BitBlt(hDC, 0, 0, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcBmpMem, 0, 0, SRCPAINT);
+
+		::SetBkColor(hDC, crOldColor);
+		::SelectObject(hDC, hOldBmp2);
+		::DeleteDC(hDC);
+
+		pDC->DrawState(	CPoint(rImage.left/*+1*/, rImage.top), 
+						CSize(m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight), 
+						hBitmap, DST_BITMAP | DSS_DISABLED);
+
+		::DeleteObject(hBitmap);
+	} // if
+	else
+	{
+		::BitBlt(pDC->m_hDC, rImage.left, rImage.top, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcMem, 0, 0, SRCAND);
+
+		::BitBlt(pDC->m_hDC, rImage.left, rImage.top, m_csBitmaps[byIndex].dwWidth, m_csBitmaps[byIndex].dwHeight, hdcBmpMem, 0, 0, SRCPAINT);
+	} // else
+
+	::SelectObject(hdcMem, hbmT);
+	::DeleteDC(hdcMem);
+
+	::SelectObject(hdcBmpMem, hbmOldBmp);
+	::DeleteDC(hdcBmpMem);
+} // End of DrawTheBitmap
+
+void CButtonST::DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
+{
+	// Draw the button's title
+	// If button is pressed then "press" title also
+	if (m_bIsPressed && m_bIsCheckBox == FALSE)
+		rpCaption->OffsetRect(m_ptPressedOffset.x, m_ptPressedOffset.y);
+
+	// ONLY FOR DEBUG 
+	//CBrush brBtnShadow(RGB(255, 0, 0));
+	//pDC->FrameRect(rCaption, &brBtnShadow);
+
+	// Center text
+	CRect centerRect = rpCaption;
+	pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER | DT_CALCRECT);
+	rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())/2, (centerRect.Height() - rpCaption->Height())/2);
+	/* RFU
+	rpCaption->OffsetRect(0, (centerRect.Height() - rpCaption->Height())/2);
+	rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())-4, (centerRect.Height() - rpCaption->Height())/2);
+	*/
+
+	pDC->SetBkMode(TRANSPARENT);
+	/*
+	pDC->DrawState(rCaption->TopLeft(), rCaption->Size(), (LPCTSTR)sTitle, (bIsDisabled ? DSS_DISABLED : DSS_NORMAL), 
+					TRUE, 0, (CBrush*)NULL);
+	*/
+	if (m_bIsDisabled)
+	{
+		rpCaption->OffsetRect(1, 1);
+		pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
+		pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+		rpCaption->OffsetRect(-1, -1);
+		pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
+		pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+	} // if
+	else
+	{
+		if (m_bMouseOnButton || m_bIsPressed) 
+		{
+			pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_IN]);
+			pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_IN]);
+		} // if
+		else 
+		{
+			if (m_bIsFocused)
+			{ 
+				pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_FOCUS]); 
+				pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_FOCUS]); 
+			} // if
+			else 
+			{
+				pDC->SetTextColor(m_crColors[BTNST_COLOR_FG_OUT]); 
+				pDC->SetBkColor(m_crColors[BTNST_COLOR_BK_OUT]); 
+			} // else
+		} // else
+		pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
+	} // if
+} // End of DrawTheText
+
+// This function creates a grayscale bitmap starting from a given bitmap.
+// The resulting bitmap will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hBitmap
+//				Handle to the original bitmap.
+//		[IN]	dwWidth
+//				Specifies the bitmap width, in pixels.
+//		[IN]	dwHeight
+//				Specifies the bitmap height, in pixels.
+//		[IN]	crTrans
+//				Color to be used as transparent color. This color will be left unchanged.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		grayscale bitmap.
+//		If the function fails, the return value is NULL.
+//
+HBITMAP CButtonST::CreateGrayscaleBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans)
+{
+	HBITMAP		hGrayBitmap = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+
+	hMainDC = ::GetDC(NULL);
+	if (hMainDC == NULL)	return NULL;
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC1 == NULL)
+	{
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC2 == NULL)
+	{
+		::DeleteDC(hMemDC1);
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+
+	hGrayBitmap = ::CreateCompatibleBitmap(hMainDC, dwWidth, dwHeight);
+	if (hGrayBitmap)
+	{
+		hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, hGrayBitmap);
+		hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, hBitmap);
+
+		//::BitBlt(hMemDC1, 0, 0, dwWidth, dwHeight, hMemDC2, 0, 0, SRCCOPY);
+
+		DWORD		dwLoopY = 0, dwLoopX = 0;
+		COLORREF	crPixel = 0;
+		BYTE		byNewPixel = 0;
+
+		for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+		{
+			for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+			{
+				crPixel = ::GetPixel(hMemDC2, dwLoopX, dwLoopY);
+				byNewPixel = (BYTE)((GetRValue(crPixel) * 0.299) + (GetGValue(crPixel) * 0.587) + (GetBValue(crPixel) * 0.114));
+
+				if (crPixel != crTrans)
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, RGB(byNewPixel, byNewPixel, byNewPixel));
+				else
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, crPixel);
+			} // for
+		} // for
+
+		::SelectObject(hMemDC1, hOldBmp1);
+		::SelectObject(hMemDC2, hOldBmp2);
+	} // if
+
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayBitmap;
+} // End of CreateGrayscaleBitmap
+
+// This function creates a bitmap that is 25% darker than the original.
+// The resulting bitmap will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hBitmap
+//				Handle to the original bitmap.
+//		[IN]	dwWidth
+//				Specifies the bitmap width, in pixels.
+//		[IN]	dwHeight
+//				Specifies the bitmap height, in pixels.
+//		[IN]	crTrans
+//				Color to be used as transparent color. This color will be left unchanged.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		darker bitmap.
+//		If the function fails, the return value is NULL.
+//
+HBITMAP CButtonST::CreateDarkerBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans)
+{
+	HBITMAP		hGrayBitmap = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+
+	hMainDC = ::GetDC(NULL);
+	if (hMainDC == NULL)	return NULL;
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC1 == NULL)
+	{
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMemDC2 == NULL)
+	{
+		::DeleteDC(hMemDC1);
+		::ReleaseDC(NULL, hMainDC);
+		return NULL;
+	} // if
+
+	hGrayBitmap = ::CreateCompatibleBitmap(hMainDC, dwWidth, dwHeight);
+	if (hGrayBitmap)
+	{
+		hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, hGrayBitmap);
+		hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, hBitmap);
+
+		//::BitBlt(hMemDC1, 0, 0, dwWidth, dwHeight, hMemDC2, 0, 0, SRCCOPY);
+
+		DWORD		dwLoopY = 0, dwLoopX = 0;
+		COLORREF	crPixel = 0;
+
+		for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+		{
+			for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+			{
+				crPixel = ::GetPixel(hMemDC2, dwLoopX, dwLoopY);
+
+				if (crPixel != crTrans)
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, DarkenColor(crPixel, 0.25));
+				else
+					::SetPixel(hMemDC1, dwLoopX, dwLoopY, crPixel);
+			} // for
+		} // for
+
+		::SelectObject(hMemDC1, hOldBmp1);
+		::SelectObject(hMemDC2, hOldBmp2);
+	} // if
+
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayBitmap;
+} // End of CreateDarkerBitmap
+
+// This function creates a grayscale icon starting from a given icon.
+// The resulting icon will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hIcon
+//				Handle to the original icon.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		grayscale icon.
+//		If the function fails, the return value is NULL.
+//
+// Updates:
+//		26/Nov/2002	Restored 1 BitBlt operation
+//		03/May/2002	Removed dependancy from m_hWnd
+//					Removed 1 BitBlt operation
+//
+HICON CButtonST::CreateGrayscaleIcon(HICON hIcon)
+{
+	HICON		hGrayIcon = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	BITMAP		bmp;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+	ICONINFO	csII, csGrayII;
+	BOOL		bRetValue = FALSE;
+
+	bRetValue = ::GetIconInfo(hIcon, &csII);
+	if (bRetValue == FALSE)	return NULL;
+
+	hMainDC = ::GetDC(NULL);
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMainDC == NULL || hMemDC1 == NULL || hMemDC2 == NULL)	return NULL;
+  
+	if (::GetObject(csII.hbmColor, sizeof(BITMAP), &bmp))
+	{
+		DWORD	dwWidth = csII.xHotspot*2;
+		DWORD	dwHeight = csII.yHotspot*2;
+
+		csGrayII.hbmColor = ::CreateBitmap(dwWidth, dwHeight, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
+		if (csGrayII.hbmColor)
+		{
+			hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, csII.hbmColor);
+			hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, csGrayII.hbmColor);
+
+			//::BitBlt(hMemDC2, 0, 0, dwWidth, dwHeight, hMemDC1, 0, 0, SRCCOPY);
+
+			DWORD		dwLoopY = 0, dwLoopX = 0;
+			COLORREF	crPixel = 0;
+			BYTE		byNewPixel = 0;
+
+			for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+			{
+				for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+				{
+					crPixel = ::GetPixel(hMemDC1, dwLoopX, dwLoopY);
+					byNewPixel = (BYTE)((GetRValue(crPixel) * 0.299) + (GetGValue(crPixel) * 0.587) + (GetBValue(crPixel) * 0.114));
+
+					if (crPixel)	
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, RGB(byNewPixel, byNewPixel, byNewPixel));
+					else
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, crPixel);
+				} // for
+			} // for
+
+			::SelectObject(hMemDC1, hOldBmp1);
+			::SelectObject(hMemDC2, hOldBmp2);
+
+			csGrayII.hbmMask = csII.hbmMask;
+
+			csGrayII.fIcon = TRUE;
+			hGrayIcon = ::CreateIconIndirect(&csGrayII);
+		} // if
+
+		::DeleteObject(csGrayII.hbmColor);
+		//::DeleteObject(csGrayII.hbmMask);
+	} // if
+
+	::DeleteObject(csII.hbmColor);
+	::DeleteObject(csII.hbmMask);
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayIcon;
+} // End of CreateGrayscaleIcon
+
+// This function creates a icon that is 25% darker than the original.
+// The resulting icon will have the same size of the original one.
+//
+// Parameters:
+//		[IN]	hIcon
+//				Handle to the original icon.
+//
+// Return value:
+//		If the function succeeds, the return value is the handle to the newly created
+//		darker icon.
+//		If the function fails, the return value is NULL.
+//
+HICON CButtonST::CreateDarkerIcon(HICON hIcon)
+{
+	HICON		hGrayIcon = NULL;
+	HDC			hMainDC = NULL, hMemDC1 = NULL, hMemDC2 = NULL;
+	BITMAP		bmp;
+	HBITMAP		hOldBmp1 = NULL, hOldBmp2 = NULL;
+	ICONINFO	csII, csGrayII;
+	BOOL		bRetValue = FALSE;
+
+	bRetValue = ::GetIconInfo(hIcon, &csII);
+	if (bRetValue == FALSE)	return NULL;
+
+	hMainDC = ::GetDC(NULL);
+	hMemDC1 = ::CreateCompatibleDC(hMainDC);
+	hMemDC2 = ::CreateCompatibleDC(hMainDC);
+	if (hMainDC == NULL || hMemDC1 == NULL || hMemDC2 == NULL)	return NULL;
+  
+	if (::GetObject(csII.hbmColor, sizeof(BITMAP), &bmp))
+	{
+		DWORD	dwWidth = csII.xHotspot*2;
+		DWORD	dwHeight = csII.yHotspot*2;
+
+		csGrayII.hbmColor = ::CreateBitmap(dwWidth, dwHeight, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
+		if (csGrayII.hbmColor)
+		{
+			hOldBmp1 = (HBITMAP)::SelectObject(hMemDC1, csII.hbmColor);
+			hOldBmp2 = (HBITMAP)::SelectObject(hMemDC2, csGrayII.hbmColor);
+
+			//::BitBlt(hMemDC2, 0, 0, dwWidth, dwHeight, hMemDC1, 0, 0, SRCCOPY);
+
+			DWORD		dwLoopY = 0, dwLoopX = 0;
+			COLORREF	crPixel = 0;
+
+			for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
+			{
+				for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
+				{
+					crPixel = ::GetPixel(hMemDC1, dwLoopX, dwLoopY);
+
+					if (crPixel)	
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, DarkenColor(crPixel, 0.25));
+					else
+						::SetPixel(hMemDC2, dwLoopX, dwLoopY, crPixel);
+				} // for
+			} // for
+
+			::SelectObject(hMemDC1, hOldBmp1);
+			::SelectObject(hMemDC2, hOldBmp2);
+
+			csGrayII.hbmMask = csII.hbmMask;
+
+			csGrayII.fIcon = TRUE;
+			hGrayIcon = ::CreateIconIndirect(&csGrayII);
+		} // if
+
+		::DeleteObject(csGrayII.hbmColor);
+		//::DeleteObject(csGrayII.hbmMask);
+	} // if
+
+	::DeleteObject(csII.hbmColor);
+	::DeleteObject(csII.hbmMask);
+	::DeleteDC(hMemDC1);
+	::DeleteDC(hMemDC2);
+	::ReleaseDC(NULL, hMainDC);
+
+	return hGrayIcon;
+} // End of CreateDarkerIcon
+
+COLORREF CButtonST::DarkenColor(COLORREF crColor, double dFactor)
+{
+	if (dFactor > 0.0 && dFactor <= 1.0)
+	{
+		BYTE red,green,blue,lightred,lightgreen,lightblue;
+		red = GetRValue(crColor);
+		green = GetGValue(crColor);
+		blue = GetBValue(crColor);
+		lightred = (BYTE)(red-(dFactor * red));
+		lightgreen = (BYTE)(green-(dFactor * green));
+		lightblue = (BYTE)(blue-(dFactor * blue));
+		crColor = RGB(lightred,lightgreen,lightblue);
+	} // if
+
+	return crColor;
+} // End of DarkenColor
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	nIconIn
+//				ID number of the icon resource to show when the mouse is over the button.
+//				Pass NULL to remove any icon from the button.
+//		[IN]	nCxDesiredIn
+//				Specifies the width, in pixels, of the icon to load.
+//		[IN]	nCyDesiredIn
+//				Specifies the height, in pixels, of the icon to load.
+//		[IN]	nIconOut
+//				ID number of the icon resource to show when the mouse is outside the button.
+//				Can be NULL.
+//				If this parameter is the special value BTNST_AUTO_GRAY (cast to int) the second
+//				icon will be automatically created starting from nIconIn and converted to grayscale.
+//				If this parameter is the special value BTNST_AUTO_DARKER (cast to int) the second
+//				icon will be automatically created 25% darker starting from nIconIn.
+//		[IN]	nCxDesiredOut
+//				Specifies the width, in pixels, of the icon to load.
+//		[IN]	nCyDesiredOut
+//				Specifies the height, in pixels, of the icon to load.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(int nIconIn, int nCxDesiredIn, int nCyDesiredIn, int nIconOut, int nCxDesiredOut, int nCyDesiredOut)
+{
+	HICON		hIconIn			= NULL;
+	HICON		hIconOut		= NULL;
+	HINSTANCE	hInstResource	= NULL;
+
+	// Find correct resource handle
+	hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIconIn), RT_GROUP_ICON);
+
+	// Set icon when the mouse is IN the button
+	hIconIn = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIconIn), IMAGE_ICON, nCxDesiredIn, nCyDesiredIn, 0);
+
+  	// Set icon when the mouse is OUT the button
+	switch (nIconOut)
+	{
+		case NULL:
+			break;
+		case (int)BTNST_AUTO_GRAY:
+			hIconOut = BTNST_AUTO_GRAY;
+			break;
+		case (int)BTNST_AUTO_DARKER:
+			hIconOut = BTNST_AUTO_DARKER;
+			break;
+		default:
+			hIconOut = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIconOut), IMAGE_ICON, nCxDesiredOut, nCyDesiredOut, 0);
+			break;
+	} // switch
+
+	return SetIcon(hIconIn, hIconOut);
+} // End of SetIcon
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	nIconIn
+//				ID number of the icon resource to show when the mouse is over the button.
+//				Pass NULL to remove any icon from the button.
+//		[IN]	nIconOut
+//				ID number of the icon resource to show when the mouse is outside the button.
+//				Can be NULL.
+//				If this parameter is the special value BTNST_AUTO_GRAY (cast to int) the second
+//				icon will be automatically created starting from nIconIn and converted to grayscale.
+//				If this parameter is the special value BTNST_AUTO_DARKER (cast to int) the second
+//				icon will be automatically created 25% darker starting from nIconIn.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(int nIconIn, int nIconOut)
+{
+	return SetIcon(nIconIn, 0, 0, nIconOut, 0, 0);
+} // End of SetIcon
+
+// This function assigns icons to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	hIconIn
+//				Handle fo the icon to show when the mouse is over the button.
+//				Pass NULL to remove any icon from the button.
+//		[IN]	hIconOut
+//				Handle to the icon to show when the mouse is outside the button.
+//				Can be NULL.
+//				If this parameter is the special value BTNST_AUTO_GRAY the second
+//				icon will be automatically created starting from hIconIn and converted to grayscale.
+//				If this parameter is the special value BTNST_AUTO_DARKER the second
+//				icon will be automatically created 25% darker starting from hIconIn.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetIcon(HICON hIconIn, HICON hIconOut)
+{
+	BOOL		bRetValue;
+	ICONINFO	ii;
+
+	// Free any loaded resource
+	FreeResources();
+
+	if (hIconIn)
+	{
+		// Icon when mouse over button?
+		m_csIcons[0].hIcon = hIconIn;
+		// Get icon dimension
+		::ZeroMemory(&ii, sizeof(ICONINFO));
+		bRetValue = ::GetIconInfo(hIconIn, &ii);
+		if (bRetValue == FALSE)
+		{
+			FreeResources();
+			return BTNST_INVALIDRESOURCE;
+		} // if
+
+		m_csIcons[0].dwWidth	= (DWORD)(ii.xHotspot * 2);
+		m_csIcons[0].dwHeight	= (DWORD)(ii.yHotspot * 2);
+		::DeleteObject(ii.hbmMask);
+		::DeleteObject(ii.hbmColor);
+
+		// Icon when mouse outside button?
+		if (hIconOut)
+		{
+			switch ((int)hIconOut)
+			{
+				case (int)BTNST_AUTO_GRAY:
+					hIconOut = CreateGrayscaleIcon(hIconIn);
+					break;
+				case (int)BTNST_AUTO_DARKER:
+					hIconOut = CreateDarkerIcon(hIconIn);
+					break;
+			} // switch
+
+			m_csIcons[1].hIcon = hIconOut;
+			// Get icon dimension
+			::ZeroMemory(&ii, sizeof(ICONINFO));
+			bRetValue = ::GetIconInfo(hIconOut, &ii);
+			if (bRetValue == FALSE)
+			{
+				FreeResources();
+				return BTNST_INVALIDRESOURCE;
+			} // if
+
+			m_csIcons[1].dwWidth	= (DWORD)(ii.xHotspot * 2);
+			m_csIcons[1].dwHeight	= (DWORD)(ii.yHotspot * 2);
+			::DeleteObject(ii.hbmMask);
+			::DeleteObject(ii.hbmColor);
+		} // if
+	} // if
+
+	Invalidate();
+
+	return BTNST_OK;
+} // End of SetIcon
+
+// This function assigns bitmaps to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	nBitmapIn
+//				ID number of the bitmap resource to show when the mouse is over the button.
+//				Pass NULL to remove any bitmap from the button.
+//		[IN]	crTransColorIn
+//				Color (inside nBitmapIn) to be used as transparent color.
+//		[IN]	nBitmapOut
+//				ID number of the bitmap resource to show when the mouse is outside the button.
+//				Can be NULL.
+//		[IN]	crTransColorOut
+//				Color (inside nBitmapOut) to be used as transparent color.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//		BTNST_FAILEDMASK
+//			Failed creating mask bitmap.
+//
+DWORD CButtonST::SetBitmaps(int nBitmapIn, COLORREF crTransColorIn, int nBitmapOut, COLORREF crTransColorOut)
+{
+	HBITMAP		hBitmapIn		= NULL;
+	HBITMAP		hBitmapOut		= NULL;
+	HINSTANCE	hInstResource	= NULL;
+	
+	// Find correct resource handle
+	hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nBitmapIn), RT_BITMAP);
+
+	// Load bitmap In
+	hBitmapIn = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(nBitmapIn), IMAGE_BITMAP, 0, 0, 0);
+
+	// Load bitmap Out
+	switch (nBitmapOut)
+	{
+		case NULL:
+			break;
+		case (int)BTNST_AUTO_GRAY:
+			hBitmapOut = (HBITMAP)BTNST_AUTO_GRAY;
+			break;
+		case (int)BTNST_AUTO_DARKER:
+			hBitmapOut = (HBITMAP)BTNST_AUTO_DARKER;
+			break;
+		default:
+			hBitmapOut = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(nBitmapOut), IMAGE_BITMAP, 0, 0, 0);
+			break;
+	} // if
+
+	return SetBitmaps(hBitmapIn, crTransColorIn, hBitmapOut, crTransColorOut);
+} // End of SetBitmaps
+
+// This function assigns bitmaps to the button.
+// Any previous icon or bitmap will be removed.
+//
+// Parameters:
+//		[IN]	hBitmapIn
+//				Handle fo the bitmap to show when the mouse is over the button.
+//				Pass NULL to remove any bitmap from the button.
+//		[IN]	crTransColorIn
+//				Color (inside hBitmapIn) to be used as transparent color.
+//		[IN]	hBitmapOut
+//				Handle to the bitmap to show when the mouse is outside the button.
+//				Can be NULL.
+//		[IN]	crTransColorOut
+//				Color (inside hBitmapOut) to be used as transparent color.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//		BTNST_FAILEDMASK
+//			Failed creating mask bitmap.
+//
+DWORD CButtonST::SetBitmaps(HBITMAP hBitmapIn, COLORREF crTransColorIn, HBITMAP hBitmapOut, COLORREF crTransColorOut)
+{
+	int		nRetValue = 0;
+	BITMAP	csBitmapSize;
+
+	// Free any loaded resource
+	FreeResources();
+
+	if (hBitmapIn)
+	{
+		m_csBitmaps[0].hBitmap = hBitmapIn;
+		m_csBitmaps[0].crTransparent = crTransColorIn;
+		// Get bitmap size
+		nRetValue = ::GetObject(hBitmapIn, sizeof(csBitmapSize), &csBitmapSize);
+		if (nRetValue == 0)
+		{
+			FreeResources();
+			return BTNST_INVALIDRESOURCE;
+		} // if
+		m_csBitmaps[0].dwWidth = (DWORD)csBitmapSize.bmWidth;
+		m_csBitmaps[0].dwHeight = (DWORD)csBitmapSize.bmHeight;
+
+		// Create grayscale/darker bitmap BEFORE mask (of hBitmapIn)
+		switch ((int)hBitmapOut)
+		{
+			case (int)BTNST_AUTO_GRAY:
+				hBitmapOut = CreateGrayscaleBitmap(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+				m_csBitmaps[1].hBitmap = hBitmapOut;
+				crTransColorOut = crTransColorIn;
+				break;
+			case (int)BTNST_AUTO_DARKER:
+				hBitmapOut = CreateDarkerBitmap(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+				m_csBitmaps[1].hBitmap = hBitmapOut;
+				crTransColorOut = crTransColorIn;
+				break;
+		} // switch
+
+		// Create mask for bitmap In
+		m_csBitmaps[0].hMask = CreateBitmapMask(hBitmapIn, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight, crTransColorIn);
+		if (m_csBitmaps[0].hMask == NULL)
+		{
+			FreeResources();
+			return BTNST_FAILEDMASK;
+		} // if
+
+		if (hBitmapOut)
+		{
+			m_csBitmaps[1].hBitmap = hBitmapOut;
+			m_csBitmaps[1].crTransparent = crTransColorOut;
+			// Get bitmap size
+			nRetValue = ::GetObject(hBitmapOut, sizeof(csBitmapSize), &csBitmapSize);
+			if (nRetValue == 0)
+			{
+				FreeResources();
+				return BTNST_INVALIDRESOURCE;
+			} // if
+			m_csBitmaps[1].dwWidth = (DWORD)csBitmapSize.bmWidth;
+			m_csBitmaps[1].dwHeight = (DWORD)csBitmapSize.bmHeight;
+
+			// Create mask for bitmap Out
+			m_csBitmaps[1].hMask = CreateBitmapMask(hBitmapOut, m_csBitmaps[1].dwWidth, m_csBitmaps[1].dwHeight, crTransColorOut);
+			if (m_csBitmaps[1].hMask == NULL)
+			{
+				FreeResources();
+				return BTNST_FAILEDMASK;
+			} // if
+		} // if
+	} // if
+
+	Invalidate();
+
+	return BTNST_OK;
+} // End of SetBitmaps
+
+// This functions sets the button to have a standard or flat style.
+//
+// Parameters:
+//		[IN]	bFlat
+//				If TRUE the button will have a flat style, else
+//				will have a standard style.
+//				By default, CButtonST buttons are flat.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetFlat(BOOL bFlat, BOOL bRepaint)
+{
+	m_bIsFlat = bFlat;
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetFlat
+
+// This function sets the alignment type between icon/bitmap and text.
+//
+// Parameters:
+//		[IN]	byAlign
+//				Alignment type. Can be one of the following values:
+//				ST_ALIGN_HORIZ			Icon/bitmap on the left, text on the right
+//				ST_ALIGN_VERT			Icon/bitmap on the top, text on the bottom
+//				ST_ALIGN_HORIZ_RIGHT	Icon/bitmap on the right, text on the left
+//				ST_ALIGN_OVERLAP		Icon/bitmap on the same space as text
+//				By default, CButtonST buttons have ST_ALIGN_HORIZ alignment.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDALIGN
+//			Alignment type not supported.
+//
+DWORD CButtonST::SetAlign(BYTE byAlign, BOOL bRepaint)
+{
+	switch (byAlign)
+	{    
+		case ST_ALIGN_HORIZ:
+		case ST_ALIGN_HORIZ_RIGHT:
+		case ST_ALIGN_VERT:
+		case ST_ALIGN_OVERLAP:
+			m_byAlign = byAlign;
+			if (bRepaint)	Invalidate();
+			return BTNST_OK;
+			break;
+	} // switch
+
+	return BTNST_INVALIDALIGN;
+} // End of SetAlign
+
+// This function sets the pressed style.
+//
+// Parameters:
+//		[IN]	byStyle
+//				Pressed style. Can be one of the following values:
+//				BTNST_PRESSED_LEFTRIGHT		Pressed style from left to right (as usual)
+//				BTNST_PRESSED_TOPBOTTOM		Pressed style from top to bottom
+//				By default, CButtonST buttons have BTNST_PRESSED_LEFTRIGHT style.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDPRESSEDSTYLE
+//			Pressed style not supported.
+//
+DWORD CButtonST::SetPressedStyle(BYTE byStyle, BOOL bRepaint)
+{
+	switch (byStyle)
+	{
+		case BTNST_PRESSED_LEFTRIGHT:
+			m_ptPressedOffset.x = 1;
+			m_ptPressedOffset.y = 1;
+			break;
+		case BTNST_PRESSED_TOPBOTTOM:
+			m_ptPressedOffset.x = 0;
+			m_ptPressedOffset.y = 2;
+			break;
+		default:
+			return BTNST_INVALIDPRESSEDSTYLE;
+	} // switch
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetPressedStyle
+
+// This function sets the state of the checkbox.
+// If the button is not a checkbox, this function has no meaning.
+//
+// Parameters:
+//		[IN]	nCheck
+//				1 to check the checkbox.
+//				0 to un-check the checkbox.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetCheck(int nCheck, BOOL bRepaint)
+{
+	if (m_bIsCheckBox)
+	{
+		if (nCheck == 0) m_nCheck = 0;
+		else m_nCheck = 1;
+
+		if (bRepaint) Invalidate();
+	} // if
+
+	return BTNST_OK;
+} // End of SetCheck
+
+// This function returns the current state of the checkbox.
+// If the button is not a checkbox, this function has no meaning.
+//
+// Return value:
+//		The current state of the checkbox.
+//			1 if checked.
+//			0 if not checked or the button is not a checkbox.
+//
+int CButtonST::GetCheck()
+{
+	return m_nCheck;
+} // End of GetCheck
+
+// This function sets all colors to a default value.
+//
+// Parameters:
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetDefaultColors(BOOL bRepaint)
+{
+	m_crColors[BTNST_COLOR_BK_IN]		= ::GetSysColor(COLOR_BTNFACE);
+	m_crColors[BTNST_COLOR_FG_IN]		= ::GetSysColor(COLOR_BTNTEXT);
+	m_crColors[BTNST_COLOR_BK_OUT]		= ::GetSysColor(COLOR_BTNFACE);
+	m_crColors[BTNST_COLOR_FG_OUT]		= ::GetSysColor(COLOR_BTNTEXT);
+	m_crColors[BTNST_COLOR_BK_FOCUS]	= ::GetSysColor(COLOR_BTNFACE);
+	m_crColors[BTNST_COLOR_FG_FOCUS]	= ::GetSysColor(COLOR_BTNTEXT);
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetDefaultColors
+
+// This function sets the color to use for a particular state.
+//
+// Parameters:
+//		[IN]	byColorIndex
+//				Index of the color to set. Can be one of the following values:
+//				BTNST_COLOR_BK_IN		Background color when mouse is over the button
+//				BTNST_COLOR_FG_IN		Text color when mouse is over the button
+//				BTNST_COLOR_BK_OUT		Background color when mouse is outside the button
+//				BTNST_COLOR_FG_OUT		Text color when mouse is outside the button
+//				BTNST_COLOR_BK_FOCUS	Background color when the button is focused
+//				BTNST_COLOR_FG_FOCUS	Text color when the button is focused
+//		[IN]	crColor
+//				New color.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDINDEX
+//			Invalid color index.
+//
+DWORD CButtonST::SetColor(BYTE byColorIndex, COLORREF crColor, BOOL bRepaint)
+{
+	if (byColorIndex >= BTNST_MAX_COLORS)	return BTNST_INVALIDINDEX;
+
+	// Set new color
+	m_crColors[byColorIndex] = crColor;
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of SetColor
+
+// This functions returns the color used for a particular state.
+//
+// Parameters:
+//		[IN]	byColorIndex
+//				Index of the color to get.
+//				See SetColor for the list of available colors.
+//		[OUT]	crpColor
+//				A pointer to a COLORREF that will receive the color.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDINDEX
+//			Invalid color index.
+//
+DWORD CButtonST::GetColor(BYTE byColorIndex, COLORREF* crpColor)
+{
+	if (byColorIndex >= BTNST_MAX_COLORS)	return BTNST_INVALIDINDEX;
+
+	// Get color
+	*crpColor = m_crColors[byColorIndex];
+
+	return BTNST_OK;
+} // End of GetColor
+
+// This function applies an offset to the RGB components of the specified color.
+// This function can be seen as an easy way to make a color darker or lighter than
+// its default value.
+//
+// Parameters:
+//		[IN]	byColorIndex
+//				Index of the color to set.
+//				See SetColor for the list of available colors.
+//		[IN]	shOffsetColor
+//				A short value indicating the offset to apply to the color.
+//				This value must be between -255 and 255.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDINDEX
+//			Invalid color index.
+//		BTNST_BADPARAM
+//			The specified offset is out of range.
+//
+DWORD CButtonST::OffsetColor(BYTE byColorIndex, short shOffset, BOOL bRepaint)
+{
+	BYTE	byRed = 0;
+	BYTE	byGreen = 0;
+	BYTE	byBlue = 0;
+	short	shOffsetR = shOffset;
+	short	shOffsetG = shOffset;
+	short	shOffsetB = shOffset;
+
+	if (byColorIndex >= BTNST_MAX_COLORS)	return BTNST_INVALIDINDEX;
+	if (shOffset < -255 || shOffset > 255)	return BTNST_BADPARAM;
+
+	// Get RGB components of specified color
+	byRed = GetRValue(m_crColors[byColorIndex]);
+	byGreen = GetGValue(m_crColors[byColorIndex]);
+	byBlue = GetBValue(m_crColors[byColorIndex]);
+
+	// Calculate max. allowed real offset
+	if (shOffset > 0)
+	{
+		if (byRed + shOffset > 255)		shOffsetR = 255 - byRed;
+		if (byGreen + shOffset > 255)	shOffsetG = 255 - byGreen;
+		if (byBlue + shOffset > 255)	shOffsetB = 255 - byBlue;
+
+		shOffset = min(min(shOffsetR, shOffsetG), shOffsetB);
+	} // if
+	else
+	{
+		if (byRed + shOffset < 0)		shOffsetR = -byRed;
+		if (byGreen + shOffset < 0)		shOffsetG = -byGreen;
+		if (byBlue + shOffset < 0)		shOffsetB = -byBlue;
+
+		shOffset = max(max(shOffsetR, shOffsetG), shOffsetB);
+	} // else
+
+	// Set new color
+	m_crColors[byColorIndex] = RGB(byRed + shOffset, byGreen + shOffset, byBlue + shOffset);
+
+	if (bRepaint)	Invalidate();
+
+	return BTNST_OK;
+} // End of OffsetColor
+
+// This function sets the hilight logic for the button.
+// Applies only to flat buttons.
+//
+// Parameters:
+//		[IN]	bAlwaysTrack
+//				If TRUE the button will be hilighted even if the window that owns it, is
+//				not the active window.
+//				If FALSE the button will be hilighted only if the window that owns it,
+//				is the active window.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetAlwaysTrack(BOOL bAlwaysTrack)
+{
+	m_bAlwaysTrack = bAlwaysTrack;
+	return BTNST_OK;
+} // End of SetAlwaysTrack
+
+// This function sets the cursor to be used when the mouse is over the button.
+//
+// Parameters:
+//		[IN]	nCursorId
+//				ID number of the cursor resource.
+//				Pass NULL to remove a previously loaded cursor.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+DWORD CButtonST::SetBtnCursor(int nCursorId, BOOL bRepaint)
+{
+	HINSTANCE	hInstResource = NULL;
+	// Destroy any previous cursor
+	if (m_hCursor)
+	{
+		::DestroyCursor(m_hCursor);
+		m_hCursor = NULL;
+	} // if
+
+	// Load cursor
+	if (nCursorId)
+	{
+		hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nCursorId), RT_GROUP_CURSOR);
+		// Load cursor resource
+		m_hCursor = (HCURSOR)::LoadImage(hInstResource, MAKEINTRESOURCE(nCursorId), IMAGE_CURSOR, 0, 0, 0);
+		// Repaint the button
+		if (bRepaint) Invalidate();
+		// If something wrong
+		if (m_hCursor == NULL) return BTNST_INVALIDRESOURCE;
+	} // if
+
+	return BTNST_OK;
+} // End of SetBtnCursor
+
+// This function sets if the button border must be drawn.
+// Applies only to flat buttons.
+//
+// Parameters:
+//		[IN]	bDrawBorder
+//				If TRUE the border will be drawn.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::DrawBorder(BOOL bDrawBorder, BOOL bRepaint)
+{
+	m_bDrawBorder = bDrawBorder;
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of DrawBorder
+
+// This function sets if the focus rectangle must be drawn for flat buttons.
+//
+// Parameters:
+//		[IN]	bDrawFlatFocus
+//				If TRUE the focus rectangle will be drawn also for flat buttons.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::DrawFlatFocus(BOOL bDrawFlatFocus, BOOL bRepaint)
+{
+	m_bDrawFlatFocus = bDrawFlatFocus;
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of DrawFlatFocus
+
+void CButtonST::InitToolTip()
+{
+	if (m_ToolTip.m_hWnd == NULL)
+	{
+		// Create ToolTip control
+		m_ToolTip.Create(this, m_dwToolTipStyle);
+		// Create inactive
+		m_ToolTip.Activate(FALSE);
+		// Enable multiline
+		m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, 400);
+		//m_ToolTip.SendMessage(TTM_SETTITLE, TTI_INFO, (LPARAM)_T("Title"));
+	} // if
+} // End of InitToolTip
+
+// This function sets the text to show in the button tooltip.
+//
+// Parameters:
+//		[IN]	nText
+//				ID number of the string resource containing the text to show.
+//		[IN]	bActivate
+//				If TRUE the tooltip will be created active.
+//
+void CButtonST::SetTooltipText(int nText, BOOL bActivate)
+{
+	CString sText;
+
+	// Load string resource
+	sText.LoadString(nText);
+	// If string resource is not empty
+	if (sText.IsEmpty() == FALSE) SetTooltipText((LPCTSTR)sText, bActivate);
+} // End of SetTooltipText
+
+// This function sets the text to show in the button tooltip.
+//
+// Parameters:
+//		[IN]	lpszText
+//				Pointer to a null-terminated string containing the text to show.
+//		[IN]	bActivate
+//				If TRUE the tooltip will be created active.
+//
+void CButtonST::SetTooltipText(LPCTSTR lpszText, BOOL bActivate)
+{
+	// We cannot accept NULL pointer
+	if (lpszText == NULL) return;
+
+	// Initialize ToolTip
+	InitToolTip();
+
+	// If there is no tooltip defined then add it
+	if (m_ToolTip.GetToolCount() == 0)
+	{
+		CRect rectBtn; 
+		GetClientRect(rectBtn);
+		m_ToolTip.AddTool(this, lpszText, rectBtn, 1);
+	} // if
+
+	// Set text for tooltip
+	m_ToolTip.UpdateTipText(lpszText, this, 1);
+	m_ToolTip.Activate(bActivate);
+} // End of SetTooltipText
+
+// This function enables or disables the button tooltip.
+//
+// Parameters:
+//		[IN]	bActivate
+//				If TRUE the tooltip will be activated.
+//
+void CButtonST::ActivateTooltip(BOOL bActivate)
+{
+	// If there is no tooltip then do nothing
+	if (m_ToolTip.GetToolCount() == 0) return;
+
+	// Activate tooltip
+	m_ToolTip.Activate(bActivate);
+} // End of EnableTooltip
+
+// This function enables the tooltip to be displayed using the balloon style.
+// This function must be called before any call to SetTooltipText is made.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::EnableBalloonTooltip()
+{
+	m_dwToolTipStyle |= TTS_BALLOON;
+	return BTNST_OK;
+} // End of EnableBalloonTooltip
+
+// This function returns if the button is the default button.
+//
+// Return value:
+//		TRUE
+//			The button is the default button.
+//		FALSE
+//			The button is not the default button.
+//
+BOOL CButtonST::GetDefault()
+{
+	return m_bIsDefault;
+} // End of GetDefault
+
+// This function enables the transparent mode.
+// Note: this operation is not reversible.
+// DrawTransparent should be called just after the button is created.
+// Do not use trasparent buttons until you really need it (you have a bitmapped
+// background) since each transparent button makes a copy in memory of its background.
+// This may bring unnecessary memory use and execution overload.
+//
+// Parameters:
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+void CButtonST::DrawTransparent(BOOL bRepaint)
+{
+	m_bDrawTransparent = TRUE;
+
+	// Restore old bitmap (if any)
+	if (m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+	{
+		m_dcBk.SelectObject(m_pbmpOldBk);
+	} // if
+
+	m_bmpBk.DeleteObject();
+	m_dcBk.DeleteDC();
+
+	// Repaint the button
+	if (bRepaint) Invalidate();
+} // End of DrawTransparent
+
+DWORD CButtonST::SetBk(CDC* pDC)
+{
+	if (m_bDrawTransparent && pDC)
+	{
+		// Restore old bitmap (if any)
+		if (m_dcBk.m_hDC != NULL && m_pbmpOldBk != NULL)
+		{
+			m_dcBk.SelectObject(m_pbmpOldBk);
+		} // if
+
+		m_bmpBk.DeleteObject();
+		m_dcBk.DeleteDC();
+
+		CRect rect;
+		CRect rect1;
+
+		GetClientRect(rect);
+
+		GetWindowRect(rect1);
+		GetParent()->ScreenToClient(rect1);
+
+		m_dcBk.CreateCompatibleDC(pDC);
+		m_bmpBk.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
+		m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
+		m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rect1.left, rect1.top, SRCCOPY);
+
+		return BTNST_OK;
+	} // if
+
+	return BTNST_BADPARAM;
+} // End of SetBk
+
+// This function sets the URL that will be opened when the button is clicked.
+//
+// Parameters:
+//		[IN]	lpszURL
+//				Pointer to a null-terminated string that contains the URL.
+//				Pass NULL to removed any previously specified URL.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetURL(LPCTSTR lpszURL)
+{
+	// Remove any existing URL
+	memset(m_szURL, 0, sizeof(m_szURL));
+
+	if (lpszURL)
+	{
+		// Store the URL
+		_tcsncpy(m_szURL, lpszURL, _MAX_PATH);
+	} // if
+
+	return BTNST_OK;
+} // End of SetURL
+
+// This function associates a menu to the button.
+// The menu will be displayed clicking the button.
+//
+// Parameters:
+//		[IN]	nMenu
+//				ID number of the menu resource.
+//				Pass NULL to remove any menu from the button.
+//		[IN]	hParentWnd
+//				Handle to the window that owns the menu.
+//				This window receives all messages from the menu.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+#ifndef	BTNST_USE_BCMENU
+DWORD CButtonST::SetMenu(UINT nMenu, HWND hParentWnd, BOOL bRepaint)
+{
+	HINSTANCE	hInstResource	= NULL;
+
+	// Destroy any previous menu
+	if (m_hMenu)
+	{
+		::DestroyMenu(m_hMenu);
+		m_hMenu = NULL;
+		m_hParentWndMenu = NULL;
+		m_bMenuDisplayed = FALSE;
+	} // if
+
+	// Load menu
+	if (nMenu)
+	{
+		// Find correct resource handle
+		hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nMenu), RT_MENU);
+		// Load menu resource
+		m_hMenu = ::LoadMenu(hInstResource, MAKEINTRESOURCE(nMenu));
+		m_hParentWndMenu = hParentWnd;
+		// If something wrong
+		if (m_hMenu == NULL) return BTNST_INVALIDRESOURCE;
+	} // if
+
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of SetMenu
+#endif
+
+// This function associates a menu to the button.
+// The menu will be displayed clicking the button.
+// The menu will be handled by the BCMenu class.
+//
+// Parameters:
+//		[IN]	nMenu
+//				ID number of the menu resource.
+//				Pass NULL to remove any menu from the button.
+//		[IN]	hParentWnd
+//				Handle to the window that owns the menu.
+//				This window receives all messages from the menu.
+//		[IN]	bWinXPStyle
+//				If TRUE the menu will be displayed using the new Windows XP style.
+//				If FALSE the menu will be displayed using the standard style.
+//		[IN]	nToolbarID
+//				Resource ID of the toolbar to be associated to the menu.
+//		[IN]	sizeToolbarIcon
+//				A CSize object indicating the size (in pixels) of each icon into the toolbar.
+//				All icons into the toolbar must have the same size.
+//		[IN]	crToolbarBk
+//				A COLORREF value indicating the color to use as background for the icons into the toolbar.
+//				This color will be used as the "transparent" color.
+//		[IN]	bRepaint
+//				If TRUE the control will be repainted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//		BTNST_INVALIDRESOURCE
+//			Failed loading the specified resource.
+//
+#ifdef	BTNST_USE_BCMENU
+DWORD CButtonST::SetMenu(UINT nMenu, HWND hParentWnd, BOOL bWinXPStyle, UINT nToolbarID, CSize sizeToolbarIcon, COLORREF crToolbarBk, BOOL bRepaint)
+{
+	BOOL	bRetValue = FALSE;
+
+	// Destroy any previous menu
+	if (m_menuPopup.m_hMenu)
+	{
+		m_menuPopup.DestroyMenu();
+		m_hParentWndMenu = NULL;
+		m_bMenuDisplayed = FALSE;
+	} // if
+
+	// Load menu
+	if (nMenu)
+	{
+		m_menuPopup.SetMenuDrawMode(bWinXPStyle);
+		// Load menu
+		bRetValue = m_menuPopup.LoadMenu(nMenu);
+		// If something wrong
+		if (bRetValue == FALSE) return BTNST_INVALIDRESOURCE;
+
+		// Load toolbar
+		if (nToolbarID)
+		{
+			m_menuPopup.SetBitmapBackground(crToolbarBk);
+			m_menuPopup.SetIconSize(sizeToolbarIcon.cx, sizeToolbarIcon.cy);
+
+			bRetValue = m_menuPopup.LoadToolbar(nToolbarID);
+			// If something wrong
+			if (bRetValue == FALSE) 
+			{
+				m_menuPopup.DestroyMenu();
+				return BTNST_INVALIDRESOURCE;
+			} // if
+		} // if
+
+		m_hParentWndMenu = hParentWnd;
+	} // if
+
+	// Repaint the button
+	if (bRepaint) Invalidate();
+
+	return BTNST_OK;
+} // End of SetMenu
+#endif
+
+// This function sets the callback message that will be sent to the
+// specified window just before the menu associated to the button is displayed.
+//
+// Parameters:
+//		[IN]	hWnd
+//				Handle of the window that will receive the callback message.
+//				Pass NULL to remove any previously specified callback message.
+//		[IN]	nMessage
+//				Callback message to send to window.
+//		[IN]	lParam
+//				A 32 bits user specified value that will be passed to the callback function.
+//
+// Remarks:
+//		the callback function must be in the form:
+//		LRESULT On_MenuCallback(WPARAM wParam, LPARAM lParam)
+//		Where:
+//				[IN]	wParam
+//						If support for BCMenu is enabled: a pointer to BCMenu
+//						else a HMENU handle to the menu that is being to be displayed.
+//				[IN]	lParam
+//						The 32 bits user specified value.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::SetMenuCallback(HWND hWnd, UINT nMessage, LPARAM lParam)
+{
+	m_csCallbacks.hWnd = hWnd;
+	m_csCallbacks.nMessage = nMessage;
+	m_csCallbacks.lParam = lParam;
+
+	return BTNST_OK;
+} // End of SetMenuCallback
+
+// This function resizes the button to the same size of the image.
+// To get good results both the IN and OUT images should have the same size.
+//
+void CButtonST::SizeToContent()
+{
+	if (m_csIcons[0].hIcon)
+	{
+		m_ptImageOrg.x = 0;
+		m_ptImageOrg.y = 0;
+	    SetWindowPos(	NULL, -1, -1, m_csIcons[0].dwWidth, m_csIcons[0].dwHeight,
+						SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
+	} // if
+	else
+	if (m_csBitmaps[0].hBitmap)
+	{
+		m_ptImageOrg.x = 0;
+		m_ptImageOrg.y = 0;
+	    SetWindowPos(	NULL, -1, -1, m_csBitmaps[0].dwWidth, m_csBitmaps[0].dwHeight,
+						SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
+	} // if
+} // End of SizeToContent
+
+// This function sets the sound that must be played on particular button states.
+//
+// Parameters:
+//		[IN]	lpszSound
+//				A string that specifies the sound to play.
+//				If hMod is NULL this string is interpreted as a filename, else it
+//				is interpreted as a resource identifier.
+//				Pass NULL to remove any previously specified sound.
+//		[IN]	hMod
+//				Handle to the executable file that contains the resource to be loaded.
+//				This parameter must be NULL unless lpszSound specifies a resource identifier.
+//		[IN]	bPlayOnClick
+//				TRUE if the sound must be played when the button is clicked.
+//				FALSE if the sound must be played when the mouse is moved over the button.
+//		[IN]	bPlayAsync
+//				TRUE if the sound must be played asynchronously.
+//				FALSE if the sound must be played synchronously. The application takes control
+//				when the sound is completely played.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+#ifdef	BTNST_USE_SOUND
+DWORD CButtonST::SetSound(LPCTSTR lpszSound, HMODULE hMod, BOOL bPlayOnClick, BOOL bPlayAsync)
+{
+	BYTE	byIndex = bPlayOnClick ? 1 : 0;
+
+	// Store new sound
+	if (lpszSound)
+	{
+		if (hMod)	// From resource identifier ?
+		{
+			m_csSounds[byIndex].lpszSound = lpszSound;
+		} // if
+		else
+		{
+			_tcscpy(m_csSounds[byIndex].szSound, lpszSound);
+			m_csSounds[byIndex].lpszSound = m_csSounds[byIndex].szSound;
+		} // else
+
+		m_csSounds[byIndex].hMod = hMod;
+		m_csSounds[byIndex].dwFlags = SND_NODEFAULT | SND_NOWAIT;
+		m_csSounds[byIndex].dwFlags |= hMod ? SND_RESOURCE : SND_FILENAME;
+		m_csSounds[byIndex].dwFlags |= bPlayAsync ? SND_ASYNC : SND_SYNC;
+	} // if
+	else
+	{
+		// Or remove any existing
+		::ZeroMemory(&m_csSounds[byIndex], sizeof(STRUCT_SOUND));
+	} // else
+
+	return BTNST_OK;
+} // End of SetSound
+#endif
+
+// This function is called every time the button background needs to be painted.
+// If the button is in transparent mode this function will NOT be called.
+// This is a virtual function that can be rewritten in CButtonST-derived classes
+// to produce a whole range of buttons not available by default.
+//
+// Parameters:
+//		[IN]	pDC
+//				Pointer to a CDC object that indicates the device context.
+//		[IN]	pRect
+//				Pointer to a CRect object that indicates the bounds of the
+//				area to be painted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::OnDrawBackground(CDC* pDC, CRect* pRect)
+{
+	COLORREF	crColor;
+
+	if (m_bIsFlat == FALSE)
+	{
+		if (m_bIsFocused || m_bIsDefault)
+		{
+			CBrush br(RGB(0,0,0));  
+			pDC->FrameRect(pRect, &br);
+			pRect->DeflateRect(1, 1);
+		} // if
+	} // if
+
+	if (m_bMouseOnButton || m_bIsPressed)
+		crColor = m_crColors[BTNST_COLOR_BK_IN];
+	else
+	{
+		if (m_bIsFocused)
+			crColor = m_crColors[BTNST_COLOR_BK_FOCUS];
+		else
+			crColor = m_crColors[BTNST_COLOR_BK_OUT];
+	} // else
+
+	CBrush		brBackground(crColor);
+
+	pDC->FillRect(pRect, &brBackground);
+
+	return BTNST_OK;
+} // End of OnDrawBackground
+
+// This function is called every time the button border needs to be painted.
+// This is a virtual function that can be rewritten in CButtonST-derived classes
+// to produce a whole range of buttons not available by default.
+//
+// Parameters:
+//		[IN]	pDC
+//				Pointer to a CDC object that indicates the device context.
+//		[IN]	pRect
+//				Pointer to a CRect object that indicates the bounds of the
+//				area to be painted.
+//
+// Return value:
+//		BTNST_OK
+//			Function executed successfully.
+//
+DWORD CButtonST::OnDrawBorder(CDC* pDC, CRect* pRect)
+{
+	// Draw pressed button
+	if (m_bIsPressed)
+	{
+		if (m_bIsFlat)
+		{
+			if (m_bDrawBorder)
+				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHILIGHT));
+		}
+		else    
+		{
+			CBrush brBtnShadow(GetSysColor(COLOR_BTNSHADOW));
+			pDC->FrameRect(pRect, &brBtnShadow);
+		}
+	}
+	else // ...else draw non pressed button
+	{
+		CPen penBtnHiLight(PS_SOLID, 0, GetSysColor(COLOR_BTNHILIGHT)); // White
+		CPen pen3DLight(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));       // Light gray
+		CPen penBtnShadow(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));   // Dark gray
+		CPen pen3DDKShadow(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW)); // Black
+
+		if (m_bIsFlat)
+		{
+			if (m_bMouseOnButton && m_bDrawBorder)
+				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNHILIGHT), ::GetSysColor(COLOR_BTNSHADOW));
+		}
+		else
+		{
+			// Draw top-left borders
+			// White line
+			CPen* pOldPen = pDC->SelectObject(&penBtnHiLight);
+			pDC->MoveTo(pRect->left, pRect->bottom-1);
+			pDC->LineTo(pRect->left, pRect->top);
+			pDC->LineTo(pRect->right, pRect->top);
+			// Light gray line
+			pDC->SelectObject(pen3DLight);
+			pDC->MoveTo(pRect->left+1, pRect->bottom-1);
+			pDC->LineTo(pRect->left+1, pRect->top+1);
+			pDC->LineTo(pRect->right, pRect->top+1);
+			// Draw bottom-right borders
+			// Black line
+			pDC->SelectObject(pen3DDKShadow);
+			pDC->MoveTo(pRect->left, pRect->bottom-1);
+			pDC->LineTo(pRect->right-1, pRect->bottom-1);
+			pDC->LineTo(pRect->right-1, pRect->top-1);
+			// Dark gray line
+			pDC->SelectObject(penBtnShadow);
+			pDC->MoveTo(pRect->left+1, pRect->bottom-2);
+			pDC->LineTo(pRect->right-2, pRect->bottom-2);
+			pDC->LineTo(pRect->right-2, pRect->top);
+			//
+			pDC->SelectObject(pOldPen);
+		} // else
+	} // else
+
+	return BTNST_OK;
+} // End of OnDrawBorder
+
+#undef BS_TYPEMASK

+ 310 - 0
source/hook/skinui/BtnST.h

@@ -0,0 +1,310 @@
+//
+//	Class:		CButtonST
+//
+//	Compiler:	Visual C++
+//	Tested on:	Visual C++ 5.0
+//				Visual C++ 6.0
+//
+//	Version:	See GetVersionC() or GetVersionI()
+//
+//	Created:	xx/xxxx/1998
+//	Updated:	03/March/2003
+//
+//	Author:		Davide Calabro'		davide_calabro@yahoo.com
+//									http://www.softechsoftware.it
+//
+//	Note:		Code for the PreSubclassWindow and OnSetStyle functions
+//				has been taken from the COddButton class
+//				published by Paolo Messina and Jerzy Kaczorowski
+//
+//	Disclaimer
+//	----------
+//	THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED "AS IS" AND WITHOUT
+//	ANY WARRANTIES WHETHER EXPRESSED OR IMPLIED. NO REPONSIBILITIES FOR POSSIBLE
+//	DAMAGES OR EVEN FUNCTIONALITY CAN BE TAKEN. THE USER MUST ASSUME THE ENTIRE
+//	RISK OF USING THIS SOFTWARE.
+//
+//	Terms of use
+//	------------
+//	THIS SOFTWARE IS FREE FOR PERSONAL USE OR FREEWARE APPLICATIONS.
+//	IF YOU USE THIS SOFTWARE IN COMMERCIAL OR SHAREWARE APPLICATIONS YOU
+//	ARE GENTLY ASKED TO DONATE 5$ (FIVE U.S. DOLLARS) TO THE AUTHOR:
+//
+//		Davide Calabro'
+//		P.O. Box 65
+//		21019 Somma Lombardo (VA)
+//		Italy
+//
+#ifndef _BTNST_H
+#define _BTNST_H
+
+// Uncomment the following 2 lines to enable support for BCMenu class
+#define	BTNST_USE_BCMENU
+#include "BCMenu.h"
+
+// Uncomment the following line to enable support for sound effects
+#define	BTNST_USE_SOUND
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+// Return values
+#ifndef	BTNST_OK
+#define	BTNST_OK						0
+#endif
+#ifndef	BTNST_INVALIDRESOURCE
+#define	BTNST_INVALIDRESOURCE			1
+#endif
+#ifndef	BTNST_FAILEDMASK
+#define	BTNST_FAILEDMASK				2
+#endif
+#ifndef	BTNST_INVALIDINDEX
+#define	BTNST_INVALIDINDEX				3
+#endif
+#ifndef	BTNST_INVALIDALIGN
+#define	BTNST_INVALIDALIGN				4
+#endif
+#ifndef	BTNST_BADPARAM
+#define	BTNST_BADPARAM					5
+#endif
+#ifndef	BTNST_INVALIDPRESSEDSTYLE
+#define	BTNST_INVALIDPRESSEDSTYLE		6
+#endif
+
+// Dummy identifier for grayscale icon
+#ifndef	BTNST_AUTO_GRAY
+#define	BTNST_AUTO_GRAY					(HICON)(0xffffffff - 1L)
+#endif
+// Dummy identifier for 15% darker icon
+#ifndef	BTNST_AUTO_DARKER
+#define	BTNST_AUTO_DARKER				(HICON)(0xffffffff - 2L)
+#endif
+
+class CButtonST : public CButton
+{
+public:
+    CButtonST();
+	~CButtonST();
+
+    enum	{	ST_ALIGN_HORIZ	= 0,			// Icon/bitmap on the left, text on the right
+				ST_ALIGN_VERT,					// Icon/bitmap on the top, text on the bottom
+				ST_ALIGN_HORIZ_RIGHT,			// Icon/bitmap on the right, text on the left
+				ST_ALIGN_OVERLAP				// Icon/bitmap on the same space as text
+			};
+
+	enum	{	BTNST_COLOR_BK_IN	= 0,		// Background color when mouse is INside
+				BTNST_COLOR_FG_IN,				// Text color when mouse is INside
+				BTNST_COLOR_BK_OUT,				// Background color when mouse is OUTside
+				BTNST_COLOR_FG_OUT,				// Text color when mouse is OUTside
+				BTNST_COLOR_BK_FOCUS,			// Background color when the button is focused
+				BTNST_COLOR_FG_FOCUS,			// Text color when the button is focused
+
+				BTNST_MAX_COLORS
+			};
+
+    enum	{	BTNST_PRESSED_LEFTRIGHT = 0,	// Pressed style from left to right (as usual)
+				BTNST_PRESSED_TOPBOTTOM			// Pressed style from top to bottom
+			};
+
+	// ClassWizard generated virtual function overrides
+    //{{AFX_VIRTUAL(CButtonST)
+	public:
+	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+	protected:
+	virtual void PreSubclassWindow();
+	//}}AFX_VIRTUAL
+
+public:
+	DWORD SetDefaultColors(BOOL bRepaint = TRUE);
+	DWORD SetColor(BYTE byColorIndex, COLORREF crColor, BOOL bRepaint = TRUE);
+	DWORD GetColor(BYTE byColorIndex, COLORREF* crpColor);
+	DWORD OffsetColor(BYTE byColorIndex, short shOffset, BOOL bRepaint = TRUE);
+
+	DWORD SetCheck(int nCheck, BOOL bRepaint = TRUE);
+	int GetCheck();
+
+	DWORD SetURL(LPCTSTR lpszURL = NULL);
+	void DrawTransparent(BOOL bRepaint = FALSE);
+	DWORD SetBk(CDC* pDC);
+
+	BOOL GetDefault();
+	DWORD SetAlwaysTrack(BOOL bAlwaysTrack = TRUE);
+
+	void SetTooltipText(int nText, BOOL bActivate = TRUE);
+	void SetTooltipText(LPCTSTR lpszText, BOOL bActivate = TRUE);
+	void ActivateTooltip(BOOL bEnable = TRUE);
+	DWORD EnableBalloonTooltip();
+
+	DWORD SetBtnCursor(int nCursorId = NULL, BOOL bRepaint = TRUE);
+
+	DWORD SetFlat(BOOL bFlat = TRUE, BOOL bRepaint = TRUE);
+	DWORD SetAlign(BYTE byAlign, BOOL bRepaint = TRUE);
+	DWORD SetPressedStyle(BYTE byStyle, BOOL bRepaint = TRUE);
+
+	DWORD DrawBorder(BOOL bDrawBorder = TRUE, BOOL bRepaint = TRUE);
+	DWORD DrawFlatFocus(BOOL bDrawFlatFocus, BOOL bRepaint = TRUE);
+
+	DWORD SetIcon(int nIconIn, int nCxDesiredIn, int nCyDesiredIn, int nIconOut = NULL, int nCxDesiredOut = 0, int nCyDesiredOut = 0);
+	DWORD SetIcon(int nIconIn, int nIconOut = NULL);
+	DWORD SetIcon(HICON hIconIn, HICON hIconOut = NULL);
+
+	DWORD SetBitmaps(int nBitmapIn, COLORREF crTransColorIn, int nBitmapOut = NULL, COLORREF crTransColorOut = 0);
+	DWORD SetBitmaps(HBITMAP hBitmapIn, COLORREF crTransColorIn, HBITMAP hBitmapOut = NULL, COLORREF crTransColorOut = 0);
+
+	void SizeToContent();
+
+#ifdef	BTNST_USE_BCMENU
+	DWORD SetMenu(UINT nMenu, HWND hParentWnd, BOOL bWinXPStyle = TRUE, UINT nToolbarID = NULL, CSize sizeToolbarIcon = CSize(16, 16), COLORREF crToolbarBk = RGB(255, 0, 255), BOOL bRepaint = TRUE);
+#else
+	DWORD SetMenu(UINT nMenu, HWND hParentWnd, BOOL bRepaint = TRUE);
+#endif
+	DWORD SetMenuCallback(HWND hWnd, UINT nMessage, LPARAM lParam = 0);
+
+#ifdef	BTNST_USE_SOUND
+	DWORD SetSound(LPCTSTR lpszSound, HMODULE hMod = NULL, BOOL bPlayOnClick = FALSE, BOOL bPlayAsync = TRUE);
+#endif
+
+	static short GetVersionI()		{return 39;}
+	static LPCTSTR GetVersionC()	{return (LPCTSTR)_T("3.9");}
+
+	BOOL	m_bShowDisabledBitmap;
+	POINT	m_ptImageOrg;
+	POINT	m_ptPressedOffset;
+
+protected:
+    //{{AFX_MSG(CButtonST)
+	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
+	afx_msg void OnKillFocus(CWnd* pNewWnd);
+	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+	afx_msg void OnSysColorChange();
+	afx_msg BOOL OnClicked();
+	afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
+	afx_msg void OnEnable(BOOL bEnable);
+	afx_msg void OnCancelMode();
+	afx_msg UINT OnGetDlgCode();
+	//}}AFX_MSG
+
+#ifdef	BTNST_USE_BCMENU
+	afx_msg LRESULT OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu);
+	afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
+#endif
+
+	afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
+	HICON CreateGrayscaleIcon(HICON hIcon);
+	HICON CreateDarkerIcon(HICON hIcon);
+	HBITMAP CreateGrayscaleBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans);
+	HBITMAP CreateDarkerBitmap(HBITMAP hBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTrans);
+	COLORREF DarkenColor(COLORREF crColor, double dFactor);
+	virtual DWORD OnDrawBackground(CDC* pDC, CRect* pRect);
+	virtual DWORD OnDrawBorder(CDC* pDC, CRect* pRect);
+
+	BOOL		m_bIsFlat;			// Is a flat button?
+	BOOL		m_bMouseOnButton;	// Is mouse over the button?
+	BOOL		m_bDrawTransparent;	// Draw transparent?
+	BOOL		m_bIsPressed;		// Is button pressed?
+	BOOL		m_bIsFocused;		// Is button focused?
+	BOOL		m_bIsDisabled;		// Is button disabled?
+	BOOL		m_bIsDefault;		// Is default button?
+	BOOL		m_bIsCheckBox;		// Is the button a checkbox?
+	BYTE		m_byAlign;			// Align mode
+	BOOL		m_bDrawBorder;		// Draw border?
+	BOOL		m_bDrawFlatFocus;	// Draw focus rectangle for flat button?
+	COLORREF	m_crColors[BTNST_MAX_COLORS];	// Colors to be used
+	HWND		m_hParentWndMenu;	// Handle to window for menu selection
+	BOOL		m_bMenuDisplayed;	// Is menu displayed ?
+
+#ifdef	BTNST_USE_BCMENU
+	BCMenu		m_menuPopup;		// BCMenu class instance
+#else
+	HMENU		m_hMenu;			// Handle to associated menu
+#endif
+
+private:
+	LRESULT OnSetCheck(WPARAM wParam, LPARAM lParam);
+	LRESULT OnGetCheck(WPARAM wParam, LPARAM lParam);
+	LRESULT OnSetStyle(WPARAM wParam, LPARAM lParam);
+	LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
+
+	void CancelHover();
+	void FreeResources(BOOL bCheckForNULL = TRUE);
+	void PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage);
+	HBITMAP CreateBitmapMask(HBITMAP hSourceBitmap, DWORD dwWidth, DWORD dwHeight, COLORREF crTransColor);
+	virtual void DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+	virtual void DrawTheBitmap(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+	virtual void DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled);
+	void PaintBk(CDC* pDC);
+
+	void InitToolTip();
+
+	HCURSOR		m_hCursor;			// Handle to cursor
+	CToolTipCtrl m_ToolTip;			// Tooltip
+
+	CDC			m_dcBk;
+	CBitmap		m_bmpBk;
+	CBitmap*	m_pbmpOldBk;
+
+	BOOL		m_bAlwaysTrack;		// Always hilight button?
+	int			m_nCheck;			// Current value for checkbox
+	UINT		m_nTypeStyle;		// Button style
+	DWORD		m_dwToolTipStyle;	// Style of tooltip control
+
+	TCHAR		m_szURL[_MAX_PATH];	// URL to open when clicked
+
+#pragma pack(1)
+	typedef struct _STRUCT_ICONS
+	{
+		HICON		hIcon;			// Handle to icon
+		DWORD		dwWidth;		// Width of icon
+		DWORD		dwHeight;		// Height of icon
+	} STRUCT_ICONS;
+#pragma pack()
+
+#pragma pack(1)
+	typedef struct _STRUCT_BITMAPS
+	{
+		HBITMAP		hBitmap;		// Handle to bitmap
+		DWORD		dwWidth;		// Width of bitmap
+		DWORD		dwHeight;		// Height of bitmap
+		HBITMAP		hMask;			// Handle to mask bitmap
+		COLORREF	crTransparent;	// Transparent color
+	} STRUCT_BITMAPS;
+#pragma pack()
+
+#pragma pack(1)
+	typedef struct _STRUCT_CALLBACK
+	{
+		HWND		hWnd;			// Handle to window
+		UINT		nMessage;		// Message identifier
+		WPARAM		wParam;
+		LPARAM		lParam;
+	} STRUCT_CALLBACK;
+#pragma pack()
+
+	STRUCT_ICONS	m_csIcons[2];
+	STRUCT_BITMAPS	m_csBitmaps[2];
+
+	STRUCT_CALLBACK	m_csCallbacks;
+
+#ifdef	BTNST_USE_SOUND
+#pragma pack(1)
+	typedef struct _STRUCT_SOUND
+	{
+		TCHAR		szSound[_MAX_PATH];
+		LPCTSTR		lpszSound;
+		HMODULE		hMod;
+		DWORD		dwFlags;
+	} STRUCT_SOUND;
+#pragma pack()
+
+	STRUCT_SOUND	m_csSounds[2];	// Index 0 = Over	1 = Clicked
+#endif
+
+	DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif

+ 161 - 0
source/hook/skinui/ComboTreeCtrl.cpp

@@ -0,0 +1,161 @@
+// ComboTreeCtrl.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "ComboTreeCtrl.h"
+
+#include "TreeComboBox.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CComboTreeCtrl
+
+CComboTreeCtrl::CComboTreeCtrl() :m_pCombo(NULL)
+{
+}
+
+CComboTreeCtrl::~CComboTreeCtrl()
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CComboTreeCtrl, CTreeCtrl)
+	ON_NOTIFY_REFLECT(NM_KILLFOCUS, OnKillfocus)
+	ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
+	ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
+	ON_MESSAGE(TREECTRL_CHECK_STATE_CHANGE, OnTreeCtrlCheckStateChange)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CComboTreeCtrl message handlers
+
+void CComboTreeCtrl::Display(IN CRect rc)
+{
+	if (GetSafeHwnd() == NULL)
+		return;
+
+	SetWindowPos(&wndNoTopMost, rc.left, rc.top, rc.right, rc.bottom, SWP_SHOWWINDOW);
+
+	CWnd* pTopParent = GetParent()->GetParentOwner();
+	if (pTopParent != NULL)
+	{
+		pTopParent->SendMessage(WM_NCACTIVATE, TRUE);
+		pTopParent->SetRedraw(TRUE);
+	}
+}
+
+BOOL CComboTreeCtrl::PreTranslateMessage(MSG* pMsg)
+{
+	if (pMsg->hwnd != m_hWnd)
+		return FALSE;
+
+	// 过滤掉Alt+F4;
+	if (WM_SYSKEYDOWN == pMsg->message && VK_F4 == pMsg->wParam)
+		return TRUE;
+
+	// 回车键和esc键关闭控件;
+	if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE))
+	{
+		// WMU_CLOSE_CONTROL->关闭控件;
+		::PostMessage(m_pCombo->m_hWnd, WMU_CLOSE_CONTROL, (WPARAM)0, (LPARAM)0);
+		return TRUE;
+	}
+
+	return CTreeCtrl::PreTranslateMessage(pMsg);
+}
+
+void CComboTreeCtrl::OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	// WMU_CLOSE_CONTROL->关闭控件;
+	::PostMessage(m_pCombo->m_hWnd, WMU_CLOSE_CONTROL, (WPARAM)0, (LPARAM)0);
+}
+
+LRESULT CComboTreeCtrl::OnTreeCtrlCheckStateChange(WPARAM wParam, LPARAM lParam)
+{
+	HTREEITEM hItemChanged = (HTREEITEM)lParam;
+	BOOL bCheckState = GetCheck(hItemChanged);
+	SelectItem(hItemChanged);
+
+	while (hItemChanged = GetParentItem(hItemChanged))
+		SetCheck(hItemChanged);
+
+	return 1;
+}
+
+void CComboTreeCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	UINT nFlags;
+	HTREEITEM hItem;
+	// verify that we have a mouse click in the check box area 
+	DWORD dwPos = GetMessagePos();
+	CPoint point(LOWORD(dwPos), HIWORD(dwPos));
+	ScreenToClient(&point);
+	hItem = HitTest(point, &nFlags);
+	if (hItem)
+	{
+		if (GetParentItem(hItem))
+			::PostMessage(m_pCombo->m_hWnd, WMU_CLOSE_CONTROL, (WPARAM)0, (LPARAM)hItem);// WMU_CLOSE_CONTROL->关闭控件;
+	}
+
+	*pResult = 0;
+}
+
+void CComboTreeCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	TV_KEYDOWN* pTVKeyDown = (TV_KEYDOWN*)pNMHDR;
+	if (pTVKeyDown->wVKey == VK_SPACE)
+	{
+		HTREEITEM hItem = GetSelectedItem();
+		if (hItem != NULL)
+		{
+			// handle state change here or post message to another handler
+			// Post message state has changed
+			PostMessage(TREECTRL_CHECK_STATE_CHANGE, 0, (LPARAM)hItem);
+		}
+	}
+
+	*pResult = 0;
+}
+
+/************************************************************************/
+/*  函数:FindItem[12/7/2016 IT];
+/*  描述:从指定父项开始查找指定名称的项;
+/*  参数:;
+/*  	[IN] strName:要查找的项名称;
+/*  	[IN] hRoot:要开始查找的父项;
+/*  返回:void;
+/*  注意:;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+HTREEITEM CComboTreeCtrl::FindItem(IN const CString& strName, IN HTREEITEM hRoot)
+{
+	// 判断父项是否就是要查找的项;
+	CString sText = GetItemText(hRoot);
+	if (sText.Compare(strName) == 0)
+		return hRoot;
+
+	HTREEITEM hItem = NULL;
+	// 获取第一个子项句柄;
+	HTREEITEM hSub = GetChildItem(hRoot);
+	// 遍历所有子项;
+	while (hSub)
+	{
+		// 当前子项是否是要查找的子项;
+		if ((hItem = FindItem(strName, hSub)) != NULL)
+			break;
+
+		// 若未到,查找下一个兄弟项;
+		hSub = GetNextSiblingItem(hSub);
+	}
+
+	return hItem;
+}

+ 59 - 0
source/hook/skinui/ComboTreeCtrl.h

@@ -0,0 +1,59 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];
+/*  作    者:[IT];
+/*  日    期:[11/4/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+
+#ifndef __COMBO_TREE_CTRL__
+#define __COMBO_TREE_CTRL__
+
+
+#pragma once
+
+// 自定义函数;
+#define TREECTRL_CHECK_STATE_CHANGE		(WM_USER + 100)
+#define WMU_CLOSE_CONTROL				(WM_USER + 101)
+
+// 前置声明;
+class CTreeComboBox;
+class CComboTreeCtrl : public CTreeCtrl
+{
+public:
+	CComboTreeCtrl();
+	virtual ~CComboTreeCtrl();
+public:
+	void Display(IN CRect rc);
+
+public:
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+
+public:
+	void Init(CTreeComboBox* pCombo)
+	{
+		m_pCombo = pCombo;
+	};
+
+protected:
+	afx_msg void OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnClick(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg LRESULT OnTreeCtrlCheckStateChange(WPARAM wParam, LPARAM lParam);
+	DECLARE_MESSAGE_MAP()
+private:
+	CTreeComboBox* m_pCombo;
+	HTREEITEM FindItem(IN const CString& strName, IN HTREEITEM hRoot);
+};
+
+#endif // __COMBO_TREE_CTRL__

+ 1216 - 0
source/hook/skinui/PictureEx.cpp

@@ -0,0 +1,1216 @@
+
+
+#include "stdafx.h"
+#include "PictureEx.h"
+#include <process.h>
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Nested structures member functions
+//////////////////////////////////////////////////////////////////////
+
+inline int CPictureEx::TGIFControlExt::GetPackedValue(enum ControlExtValues Value)
+{
+ int nRet = (int)m_cPacked;
+ switch (Value)
+ {
+ case GCX_PACKED_DISPOSAL:
+  nRet = (nRet & 28) >> 2;
+  break;
+
+ case GCX_PACKED_USERINPUT:
+  nRet = (nRet & 2) >> 1;
+  break;
+
+ case GCX_PACKED_TRANSPCOLOR:
+  nRet &= 1;
+  break;
+ };
+
+ return nRet;
+}
+
+inline int CPictureEx::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value)
+{
+ int nRet = (int)m_cPacked;
+
+ switch (Value)
+ {
+ case LSD_PACKED_GLOBALCT:
+  nRet = nRet >> 7;
+  break;
+
+ case LSD_PACKED_CRESOLUTION:
+  nRet = ((nRet & 0x70) >> 4) + 1;
+  break;
+
+ case LSD_PACKED_SORT:
+  nRet = (nRet & 8) >> 3;
+  break;
+
+ case LSD_PACKED_GLOBALCTSIZE:
+  nRet &= 7;
+  break;
+ };
+
+ return nRet;
+}
+
+inline int CPictureEx::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value)
+{
+ int nRet = (int)m_cPacked;
+
+ switch (Value)
+ {
+ case ID_PACKED_LOCALCT:
+  nRet >>= 7;
+  break;
+
+ case ID_PACKED_INTERLACE:
+  nRet = ((nRet & 0x40) >> 6);
+  break;
+
+ case ID_PACKED_SORT:
+  nRet = (nRet & 0x20) >> 5;
+  break;
+
+ case ID_PACKED_LOCALCTSIZE:
+  nRet &= 7;
+  break;
+ };
+
+ return nRet;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CPictureEx::CPictureEx()
+{
+ // check structures size
+ ASSERT(sizeof(TGIFImageDescriptor) == 10);
+ ASSERT(sizeof(TGIFAppExtension)    == 14);
+ ASSERT(sizeof(TGIFPlainTextExt)    == 15);
+ ASSERT(sizeof(TGIFLSDescriptor)    ==  7);
+ ASSERT(sizeof(TGIFControlExt)    ==  8);
+ ASSERT(sizeof(TGIFCommentExt)    ==  2);
+ ASSERT(sizeof(TGIFHeader)     ==  6);
+
+ m_pGIFLSDescriptor = NULL;
+ m_pGIFHeader    = NULL;
+ m_pPicture     = NULL;
+ m_pRawData     = NULL;
+ m_hThread     = NULL;
+ m_hBitmap          = NULL;
+ m_hMemDC     = NULL;
+
+ m_hDispMemDC       = NULL;
+ m_hDispMemBM       = NULL;
+ m_hDispOldBM       = NULL;
+
+ m_bIsInitialized   = FALSE;
+ m_bExitThread    = FALSE;
+ m_bIsPlaying       = FALSE;
+ m_bIsGIF     = FALSE;
+ m_clrBackground    = RGB(255,255,255); // white by default
+ m_nGlobalCTSize    = 0;
+ m_nCurrOffset    = 0;
+ m_nCurrFrame    = 0;
+ m_nDataSize     = 0;
+ m_PictureSize.cx = m_PictureSize.cy = 0;
+ SetRect(&m_PaintRect,0,0,0,0);
+
+ m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+}
+
+CPictureEx::~CPictureEx()
+{
+ UnLoad();
+ CloseHandle(m_hExitEvent);
+}
+
+BEGIN_MESSAGE_MAP(CPictureEx, CStatic)
+ //{{AFX_MSG_MAP(CPictureEx)
+ ON_WM_DESTROY()
+ ON_WM_PAINT()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+BOOL CPictureEx::Load(HGLOBAL hGlobal, DWORD dwSize)
+{
+ IStream *pStream = NULL;
+ UnLoad();
+
+ if (!(m_pRawData = reinterpret_cast<unsigned char*> (GlobalLock(hGlobal))) )
+ {
+  TRACE(_T("Load: Error locking memory\n"));
+  return FALSE;
+ };
+
+ m_nDataSize = dwSize;
+ m_pGIFHeader = reinterpret_cast<TGIFHeader *> (m_pRawData);
+
+ if ((memcmp(&m_pGIFHeader->m_cSignature,"GIF",3) != 0) &&
+  ((memcmp(&m_pGIFHeader->m_cVersion,"87a",3) != 0) ||
+   (memcmp(&m_pGIFHeader->m_cVersion,"89a",3) != 0)) )
+ {
+ // it's neither GIF87a nor GIF89a
+ // do the default processing
+
+  // clear GIF variables
+  m_pRawData = NULL;
+  GlobalUnlock(hGlobal);
+
+  // don't delete memory on object's release
+  if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
+   return FALSE;
+
+  if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
+   reinterpret_cast<LPVOID *>(&m_pPicture)) != S_OK)
+  {
+   pStream->Release();
+   return FALSE;
+  };
+  pStream->Release();
+
+  // store picture's size
+
+  long hmWidth;
+  long hmHeight;
+  m_pPicture->get_Width(&hmWidth);
+  m_pPicture->get_Height(&hmHeight);
+
+  HDC hDC = ::GetDC(m_hWnd);
+  m_PictureSize.cx = MulDiv(hmWidth, GetDeviceCaps(hDC,LOGPIXELSX), 2540);
+  m_PictureSize.cy = MulDiv(hmHeight, GetDeviceCaps(hDC,LOGPIXELSY), 2540);
+  ::ReleaseDC(m_hWnd,hDC);
+ }
+ else
+ {
+  // it's a GIF
+  m_bIsGIF = TRUE;
+  m_pGIFLSDescriptor = reinterpret_cast<TGIFLSDescriptor *>
+   (m_pRawData + sizeof(TGIFHeader));
+  if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1)
+  {
+   // calculate the globat color table size
+   m_nGlobalCTSize = static_cast<int>
+    (3* (1 << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1)));
+   // get the background color if GCT is present
+   unsigned char *pBkClr = m_pRawData + sizeof(TGIFHeader) + 
+    sizeof(TGIFLSDescriptor) + 3*m_pGIFLSDescriptor->m_cBkIndex;
+   m_clrBackground = RGB(pBkClr[0],pBkClr[1],pBkClr[2]);
+  };
+
+  // store the picture's size
+  m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth;
+  m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight;
+
+  // determine frame count for this picture
+  UINT nFrameCount=0;
+  ResetDataPointer();
+  while (SkipNextGraphicBlock())
+   nFrameCount++;
+
+#ifdef GIF_TRACING
+  TRACE(
+   _T(" -= GIF encountered\n"
+      "Logical Screen dimensions = %dx%d\n"
+      "Global color table = %d\n"
+      "Color depth = %d\n"
+      "Sort flag = %d\n"
+      "Size of Global Color Table = %d\n"
+      "Background color index = %d\n"
+      "Pixel aspect ratio = %d\n"
+      "Frame count = %d\n"
+      "Background color = %06Xh\n\n"
+     ),
+   m_pGIFLSDescriptor->m_wWidth,
+   m_pGIFLSDescriptor->m_wHeight,
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT),
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION),
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT),
+   m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE),
+   m_pGIFLSDescriptor->m_cBkIndex,
+   m_pGIFLSDescriptor->m_cPixelAspect,
+   nFrameCount,
+   m_clrBackground
+   );
+  EnumGIFBlocks();
+#endif
+
+  if (nFrameCount == 0) // it's an empty GIF!
+  {
+   m_pRawData = NULL;
+   GlobalUnlock(hGlobal);
+   return FALSE;
+  };
+
+  // now check the frame count
+  // if there's only one frame, no need to animate this GIF
+  // therefore, treat it like any other pic
+
+  if (nFrameCount == 1)
+  {
+   // clear GIF variables
+   m_pRawData = NULL;
+   GlobalUnlock(hGlobal);
+
+   // don't delete memory on object's release
+   if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
+    return FALSE;
+
+   if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
+    (LPVOID *)&m_pPicture) != S_OK)
+   {
+    pStream->Release();
+    return FALSE;
+   };
+
+   pStream->Release();
+  }
+  else
+  {
+  // if, on the contrary, there are several frames
+  // then store separate frames in an array
+
+   TFrame frame;
+   UINT nBlockLen;
+   HGLOBAL hFrameData;
+   UINT nCurFrame = 0;
+
+   ResetDataPointer();
+   while (hFrameData = GetNextGraphicBlock(&nBlockLen,
+    &frame.m_nDelay, &frame.m_frameSize,
+    &frame.m_frameOffset, &frame.m_nDisposal) )
+   {
+    #ifdef GIF_TRACING
+    //////////////////////////////////////////////
+    // uncomment the following strings if you want 
+    // to write separate frames on disk
+    //
+    // CString szName;
+    // szName.Format(_T("%.4d.gif"),nCurFrame);
+    // WriteDataOnDisk(szName,hFrameData,nBlockLen);
+    // nCurFrame++;
+    #endif // GIF_TRACING
+
+    IStream *pStream = NULL;
+
+    // delete memory on object's release
+    if (CreateStreamOnHGlobal(hFrameData,TRUE,&pStream) != S_OK)
+    {
+     GlobalFree(hFrameData);
+     continue;
+    };
+
+    if (OleLoadPicture(pStream,nBlockLen,FALSE,
+     IID_IPicture,
+     reinterpret_cast<LPVOID *>(&frame.m_pPicture)) != S_OK)
+    {
+     pStream->Release();
+     continue;
+    };
+    pStream->Release();
+   
+    // everything went well, add this frame
+    m_arrFrames.push_back(frame);
+   };
+
+   // clean after ourselves
+   m_pRawData = NULL;
+   GlobalUnlock(hGlobal);
+
+   if (m_arrFrames.empty()) // couldn't load any frames
+    return FALSE;
+  };
+ }; // if (!IsGIF...
+
+ return PrepareDC(m_PictureSize.cx,m_PictureSize.cy);
+}
+
+void CPictureEx::UnLoad()
+{
+ Stop();
+ if (m_pPicture)
+ {
+  m_pPicture->Release();
+  m_pPicture = NULL;
+ };
+ 
+ std::vector<TFrame>::iterator it;
+ for (it=m_arrFrames.begin();it<m_arrFrames.end();it++)
+  (*it).m_pPicture->Release();
+ m_arrFrames.clear();
+
+ if (m_hMemDC)
+ {
+  SelectObject(m_hMemDC,m_hOldBitmap);
+  ::DeleteDC(m_hMemDC);
+  ::DeleteObject(m_hBitmap);
+  m_hMemDC  = NULL;
+  m_hBitmap = NULL;
+ };
+
+ if (m_hDispMemDC)
+ {
+  SelectObject(m_hDispMemDC,m_hDispOldBM);
+  ::DeleteDC(m_hDispMemDC);
+  ::DeleteObject(m_hDispMemBM);
+  m_hDispMemDC  = NULL;
+  m_hDispMemBM = NULL;
+ };
+
+ SetRect(&m_PaintRect,0,0,0,0);
+ m_pGIFLSDescriptor = NULL;
+ m_pGIFHeader    = NULL;
+ m_pRawData     = NULL;
+ m_hThread     = NULL;
+ m_bIsInitialized   = FALSE;
+ m_bExitThread    = FALSE;
+ m_bIsGIF     = FALSE;
+ m_clrBackground    = RGB(255,255,255); // white by default
+ m_nGlobalCTSize    = 0;
+ m_nCurrOffset    = 0;
+ m_nCurrFrame    = 0;
+ m_nDataSize     = 0;
+}
+
+BOOL CPictureEx::Draw()
+{
+ if (!m_bIsInitialized)
+ {
+  TRACE(_T("Call one of the CPictureEx::Load() member functions before calling Draw()\n"));
+  return FALSE;
+ };
+
+ if (IsAnimatedGIF())
+ {
+ // the picture needs animation
+ // we'll start the thread that will handle it for us
+ 
+  unsigned int nDummy;
+  m_hThread = (HANDLE) _beginthreadex(NULL,0,_ThreadAnimation,this,
+   CREATE_SUSPENDED,&nDummy);
+  if (!m_hThread)
+  {
+   TRACE(_T("Draw: Couldn't start a GIF animation thread\n"));
+   return FALSE;
+  } 
+  else 
+   ResumeThread(m_hThread);
+ } 
+ else
+ {
+  if (m_pPicture)
+  {
+   long hmWidth;
+   long hmHeight;
+   m_pPicture->get_Width(&hmWidth);
+   m_pPicture->get_Height(&hmHeight);
+   if (m_pPicture->Render(m_hMemDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy, 
+    0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
+   {
+    Invalidate(FALSE);
+    return TRUE;
+   };
+  };
+ };
+
+ return FALSE; 
+}
+
+SIZE CPictureEx::GetSize() const
+{
+ return m_PictureSize;
+}
+
+BOOL CPictureEx::Load(LPCTSTR szFileName)
+{
+ ASSERT(szFileName);
+ 
+ CFile file;
+ HGLOBAL hGlobal;
+ DWORD dwSize;
+
+ if (!file.Open(szFileName,
+    CFile::modeRead | 
+    CFile::shareDenyWrite) )
+ {
+  TRACE(_T("Load (file): Error opening file %s\n"),szFileName);
+  return FALSE;
+ };
+
+ dwSize = file.GetLength();
+ hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
+ if (!hGlobal)
+ {
+  TRACE(_T("Load (file): Error allocating memory\n"));
+  return FALSE;
+ };
+ 
+ char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
+ if (!pData)
+ {
+  TRACE(_T("Load (file): Error locking memory\n"));
+  GlobalFree(hGlobal);
+  return FALSE;
+ };
+
+ TRY
+ {
+  file.Read(pData,dwSize);
+ }
+ CATCH(CFileException, e);                                          
+ {
+  TRACE(_T("Load (file): An exception occured while reading the file %s\n"),
+   szFileName);
+  GlobalFree(hGlobal);
+  e->Delete();
+  file.Close();
+  return FALSE;
+ }
+ END_CATCH
+ GlobalUnlock(hGlobal);
+ file.Close();
+
+ BOOL bRetValue = Load(hGlobal,dwSize);
+ GlobalFree(hGlobal);
+ return bRetValue;
+}
+
+BOOL CPictureEx::Load(LPCTSTR szResourceName, LPCTSTR szResourceType)
+{
+ ASSERT(szResourceName);
+ ASSERT(szResourceType);
+
+ HRSRC hPicture = FindResource(AfxGetResourceHandle(),szResourceName,szResourceType);
+ HGLOBAL hResData;
+ if (!hPicture || !(hResData = LoadResource(AfxGetResourceHandle(),hPicture)))
+ {
+  TRACE(_T("Load (resource): Error loading resource %s\n"),szResourceName);
+  return FALSE;
+ };
+ DWORD dwSize = SizeofResource(AfxGetResourceHandle(),hPicture);
+
+ // hResData is not the real HGLOBAL (we can't lock it)
+ // let's make it real
+
+ HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
+ if (!hGlobal)
+ {
+  TRACE(_T("Load (resource): Error allocating memory\n"));
+  FreeResource(hResData);
+  return FALSE;
+ };
+ 
+ char *pDest = reinterpret_cast<char *> (GlobalLock(hGlobal));
+ char *pSrc = reinterpret_cast<char *> (LockResource(hResData));
+ if (!pSrc || !pDest)
+ {
+  TRACE(_T("Load (resource): Error locking memory\n"));
+  GlobalFree(hGlobal);
+  FreeResource(hResData);
+  return FALSE;
+ };
+ CopyMemory(pDest,pSrc,dwSize);
+ FreeResource(hResData);
+ GlobalUnlock(hGlobal);
+
+ BOOL bRetValue = Load(hGlobal,dwSize);
+ GlobalFree(hGlobal);
+ return bRetValue;
+}
+
+void CPictureEx::ResetDataPointer()
+{
+ // skip header and logical screen descriptor
+ m_nCurrOffset = 
+  sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
+}
+
+BOOL CPictureEx::SkipNextGraphicBlock()
+{
+ if (!m_pRawData) return FALSE;
+
+ // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
+
+ enum GIFBlockTypes nBlock;
+
+ nBlock = GetNextBlock();
+
+ while ((nBlock != BLOCK_CONTROLEXT) &&
+     (nBlock != BLOCK_IMAGE) &&
+     (nBlock != BLOCK_PLAINTEXT) &&
+     (nBlock != BLOCK_UNKNOWN) &&
+     (nBlock != BLOCK_TRAILER) )
+ {
+  if (!SkipNextBlock()) return NULL;
+  nBlock = GetNextBlock();
+ };
+
+ if ((nBlock == BLOCK_UNKNOWN) ||
+  (nBlock == BLOCK_TRAILER))
+  return FALSE;
+
+ // it's either a control ext.block, an image or a plain text
+
+ if (GetNextBlockLen() <= 0) return FALSE;
+
+ if (nBlock == BLOCK_CONTROLEXT)
+ {
+  if (!SkipNextBlock()) return FALSE;
+  nBlock = GetNextBlock();
+
+  // skip everything until we meet an image block or a plain-text block
+  while ((nBlock != BLOCK_IMAGE) &&
+      (nBlock != BLOCK_PLAINTEXT) &&
+      (nBlock != BLOCK_UNKNOWN) &&
+      (nBlock != BLOCK_TRAILER) )
+  {
+   if (!SkipNextBlock()) return NULL;
+   nBlock = GetNextBlock();
+  };
+
+  if ((nBlock == BLOCK_UNKNOWN) ||
+   (nBlock == BLOCK_TRAILER))
+   return FALSE;
+ };
+
+ // skip the found data block (image or plain-text)
+ if (!SkipNextBlock()) return FALSE;
+
+ return TRUE;
+}
+
+UINT CPictureEx::GetSubBlocksLen(UINT nStartingOffset) const
+{
+ UINT nRet = 0;
+ UINT nCurOffset = nStartingOffset;
+ 
+ while (m_pRawData[nCurOffset] != 0)
+ {
+  nRet += m_pRawData[nCurOffset]+1;
+  nCurOffset += m_pRawData[nCurOffset]+1;
+ };
+
+ return nRet+1;
+}
+
+enum CPictureEx::GIFBlockTypes CPictureEx::GetNextBlock() const
+{
+ switch(m_pRawData[m_nCurrOffset])
+ {
+ case 0x21:
+ // extension block
+  switch(m_pRawData[m_nCurrOffset+1])
+  {
+  case 0x01:
+  // plain text extension
+   return BLOCK_PLAINTEXT;
+   break;
+
+  case 0xF9:
+  // graphic control extension
+   return BLOCK_CONTROLEXT;
+   break;
+
+  case 0xFE:
+  // comment extension
+   return BLOCK_COMMEXT;
+   break;
+
+  case 0xFF:
+  // application extension
+   return BLOCK_APPEXT;
+   break;
+  };
+  break;
+ 
+ case 0x3B:
+ // trailer
+  return BLOCK_TRAILER;
+  break;
+
+ case 0x2C:
+ // image data
+  return BLOCK_IMAGE;
+  break;
+ };
+
+ return BLOCK_UNKNOWN;
+}
+
+BOOL CPictureEx::SkipNextBlock()
+{
+ if (!m_pRawData) return FALSE;
+
+ int nLen = GetNextBlockLen();
+ if ((nLen <= 0) || ((m_nCurrOffset+nLen) > m_nDataSize))
+  return FALSE;
+
+ m_nCurrOffset += nLen;
+ return TRUE;
+}
+
+int CPictureEx::GetNextBlockLen() const
+{
+ GIFBlockTypes nBlock = GetNextBlock();
+
+ int nTmp;
+
+ switch(nBlock)
+ {
+ case BLOCK_UNKNOWN:
+  return -1;
+  break;
+
+ case BLOCK_TRAILER:
+  return 1;
+  break;
+
+ case BLOCK_APPEXT:
+  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFAppExtension));
+  if (nTmp > 0)
+   return sizeof(TGIFAppExtension)+nTmp;
+  break;
+
+ case BLOCK_COMMEXT:
+  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFCommentExt));
+  if (nTmp > 0)
+   return sizeof(TGIFCommentExt)+nTmp;
+  break;
+
+ case BLOCK_CONTROLEXT:
+  return sizeof(TGIFControlExt);
+  break;
+
+ case BLOCK_PLAINTEXT:
+  nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFPlainTextExt));
+  if (nTmp > 0)
+   return sizeof(TGIFPlainTextExt)+nTmp;
+  break;
+
+ case BLOCK_IMAGE:
+  TGIFImageDescriptor *pIDescr = 
+   reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
+  int nLCTSize = (int)
+   (pIDescr->GetPackedValue(ID_PACKED_LOCALCT)*3*
+   (1 << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE)+1)));
+
+  int nTmp = GetSubBlocksLen(m_nCurrOffset+
+   sizeof(TGIFImageDescriptor) + nLCTSize + 1);
+  if (nTmp > 0)
+   return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp;
+  break;
+ };
+
+ return 0;
+}
+
+UINT WINAPI CPictureEx::_ThreadAnimation(LPVOID pParam)
+{
+ ASSERT(pParam);
+ CPictureEx *pPic = reinterpret_cast<CPictureEx *> (pParam);
+
+ pPic->m_bIsPlaying = TRUE;
+ pPic->ThreadAnimation();
+ pPic->m_bIsPlaying = FALSE;
+
+ // this thread has finished its work so we close the handle
+ CloseHandle(pPic->m_hThread); 
+ // and init the handle to zero (so that Stop() doesn't Wait on it)
+ pPic->m_hThread = 0;
+ return 0;
+}
+
+void CPictureEx::ThreadAnimation()
+{
+ // first, restore background (for stop/draw support)
+ // disposal method #2
+ if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2)
+ {
+  HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
+  if (hBrush)
+  {
+   RECT rect = {
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy };
+   FillRect(m_hMemDC,&rect,hBrush);
+   DeleteObject(hBrush);
+  };
+ } 
+ else
+  // disposal method #3
+  if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) )
+  {
+   // put it back
+   BitBlt(m_hMemDC,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+    m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+    m_arrFrames[m_nCurrFrame].m_frameSize.cy,
+    m_hDispMemDC,0,0, SRCCOPY);
+   // init variables
+   SelectObject(m_hDispMemDC,m_hDispOldBM);
+   DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL;
+   DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL;
+  };
+
+ while (!m_bExitThread)
+ {
+  if (m_arrFrames[m_nCurrFrame].m_pPicture)
+  {
+  ///////////////////////////////////////////////////////
+  // Before rendering a frame we should take care of what's 
+  // behind that frame. TFrame::m_nDisposal will be our guide:
+  //   0 - no disposal specified (do nothing)
+  //   1 - do not dispose (again, do nothing)
+  //   2 - restore to background color (m_clrBackground)
+  //   3 - restore to previous
+
+   //////// disposal method #3
+   if (m_arrFrames[m_nCurrFrame].m_nDisposal == 3)
+   {
+    // prepare a memory DC and store the background in it
+    m_hDispMemDC = CreateCompatibleDC(m_hMemDC);
+    m_hDispMemBM = CreateCompatibleBitmap(m_hMemDC,
+       m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+       m_arrFrames[m_nCurrFrame].m_frameSize.cy);
+    
+    if (m_hDispMemDC && m_hDispMemBM)
+    {
+     m_hDispOldBM = reinterpret_cast<HBITMAP> (SelectObject(m_hDispMemDC,m_hDispMemBM));
+     BitBlt(m_hDispMemDC,0,0,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cy,
+      m_hMemDC,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+      SRCCOPY);
+    };
+   };
+   ///////////////////////
+
+   long hmWidth;
+   long hmHeight;
+   m_arrFrames[m_nCurrFrame].m_pPicture->get_Width(&hmWidth);
+   m_arrFrames[m_nCurrFrame].m_pPicture->get_Height(&hmHeight);
+
+   if (m_arrFrames[m_nCurrFrame].m_pPicture->Render(m_hMemDC, 
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cx, 
+    m_arrFrames[m_nCurrFrame].m_frameOffset.cy, 
+    m_arrFrames[m_nCurrFrame].m_frameSize.cx, 
+    m_arrFrames[m_nCurrFrame].m_frameSize.cy, 
+    0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
+   {
+    Invalidate(FALSE);
+   };
+   
+   if (m_bExitThread) break;
+
+   // if the delay time is too short (like in old GIFs), wait for 100ms
+   if (m_arrFrames[m_nCurrFrame].m_nDelay < 5) 
+    WaitForSingleObject(m_hExitEvent, 100);
+   else
+    WaitForSingleObject(m_hExitEvent, 10*m_arrFrames[m_nCurrFrame].m_nDelay);
+
+   if (m_bExitThread) break;
+
+   // disposal method #2
+   if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2)
+   {
+    HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
+    if (hBrush)
+    {
+     RECT rect = {
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy };
+     FillRect(m_hMemDC,&rect,hBrush);
+     DeleteObject(hBrush);
+    };
+   } 
+   else
+    if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) )
+    {
+     // put it back
+     BitBlt(m_hMemDC,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cx,
+      m_arrFrames[m_nCurrFrame].m_frameOffset.cy,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cx,
+      m_arrFrames[m_nCurrFrame].m_frameSize.cy,
+      m_hDispMemDC,0,0, SRCCOPY);
+     // init variables
+     SelectObject(m_hDispMemDC,m_hDispOldBM);
+     DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL;
+     DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL;
+    };
+  };
+  m_nCurrFrame++;
+  if (m_nCurrFrame == m_arrFrames.size())
+  {
+   m_nCurrFrame
+    = 0; 
+  // init the screen for the first frame,
+   HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
+   if (hBrush)
+   {
+    RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
+    FillRect(m_hMemDC,&rect,hBrush);
+    DeleteObject(hBrush);
+   };
+  };
+ };
+}
+
+void CPictureEx::Stop()
+{
+ m_bIsPlaying = FALSE;
+ m_bExitThread = TRUE;
+ SetEvent(m_hExitEvent);
+ if (m_hThread)
+ {
+  // we'll wait for 5 seconds then continue execution
+  WaitForSingleObject(m_hThread,5000);
+  CloseHandle(m_hThread);
+  m_hThread = NULL;
+ }
+
+ // make it possible to Draw() again
+ ResetEvent(m_hExitEvent);
+ m_bExitThread = FALSE;
+}
+
+HGLOBAL CPictureEx::GetNextGraphicBlock(UINT *pBlockLen, 
+ UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset, 
+ UINT *pDisposal)
+{
+ if (!m_pRawData) return NULL;
+
+ // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
+
+ *pDisposal = 0;
+ enum GIFBlockTypes nBlock;
+ nBlock = GetNextBlock();
+
+ while (
+  (nBlock != BLOCK_CONTROLEXT) &&
+  (nBlock != BLOCK_IMAGE) &&
+  (nBlock != BLOCK_PLAINTEXT) &&
+  (nBlock != BLOCK_UNKNOWN) &&
+  (nBlock != BLOCK_TRAILER)
+  )
+ {
+  if (!SkipNextBlock()) return NULL;
+  nBlock = GetNextBlock();
+ };
+
+ if ((nBlock == BLOCK_UNKNOWN) ||
+  (nBlock == BLOCK_TRAILER))
+  return NULL;
+
+ // it's either a control ext.block, an image or a plain text
+
+ int nStart = m_nCurrOffset;
+ int nBlockLen = GetNextBlockLen();
+
+ if (nBlockLen <= 0) return NULL;
+
+ if (nBlock == BLOCK_CONTROLEXT)
+ {
+  // get the following data
+  TGIFControlExt *pControl = 
+   reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
+  // store delay time
+  *pDelay = pControl->m_wDelayTime;
+  // store disposal method
+  *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL);
+
+  if (!SkipNextBlock()) return NULL;
+  nBlock = GetNextBlock();
+  
+  // skip everything until we find data to display 
+  // (image block or plain-text block)
+  
+  while (
+   (nBlock != BLOCK_IMAGE) &&
+   (nBlock != BLOCK_PLAINTEXT) &&
+   (nBlock != BLOCK_UNKNOWN) &&
+   (nBlock != BLOCK_TRAILER)
+   )
+  {
+   if (!SkipNextBlock()) return NULL;
+   nBlock = GetNextBlock();
+   nBlockLen += GetNextBlockLen();
+  };
+
+  if ((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER))
+   return NULL;
+  nBlockLen += GetNextBlockLen();
+ }
+ else
+  *pDelay = -1; // to indicate that there was no delay value
+
+ if (nBlock == BLOCK_IMAGE)
+ {
+  // store size and offsets
+  TGIFImageDescriptor *pImage = 
+   reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
+  pBlockSize->cx = pImage->m_wWidth;
+  pBlockSize->cy = pImage->m_wHeight;
+  pBlockOffset->cx = pImage->m_wLeftPos;
+  pBlockOffset->cy = pImage->m_wTopPos;
+ };
+
+ if (!SkipNextBlock()) return NULL;
+
+ HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED,
+  sizeof(TGIFHeader) +
+  sizeof(TGIFLSDescriptor) +
+  m_nGlobalCTSize +
+  nBlockLen + 
+  1);  // for the trailer
+
+ if (!hGlobal) return NULL;
+
+ int nOffset = 0; 
+
+ // GMEM_FIXED means we get a pointer
+ unsigned char *pGlobal = reinterpret_cast<unsigned char *> (hGlobal);
+
+ CopyMemory(pGlobal,m_pRawData, 
+  sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize);
+ nOffset += sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
+
+ CopyMemory(pGlobal + nOffset,&m_pRawData[nStart], nBlockLen);
+ nOffset += nBlockLen;
+
+ pGlobal[nOffset] = 0x3B; // trailer
+ nOffset++;
+
+ *pBlockLen = nOffset;
+
+ return hGlobal;
+}
+
+BOOL CPictureEx::IsGIF() const
+{
+ return m_bIsGIF;
+}
+
+BOOL CPictureEx::IsAnimatedGIF() const
+{
+ return (m_bIsGIF && (m_arrFrames.size() > 1));
+}
+
+BOOL CPictureEx::IsPlaying() const
+{
+ return m_bIsPlaying;
+}
+
+int CPictureEx::GetFrameCount() const
+{
+ if (!IsAnimatedGIF())
+  return 0;
+
+ return m_arrFrames.size();
+}
+
+COLORREF CPictureEx::GetBkColor() const
+{
+ return m_clrBackground;
+}
+
+void CPictureEx::OnPaint() 
+{
+ CPaintDC dc(this); // device context for painting
+
+ LONG nPaintWidth = m_PaintRect.right-m_PaintRect.left;
+
+ if (nPaintWidth > 0)
+ {
+  LONG nPaintHeight = m_PaintRect.bottom - m_PaintRect.top;
+  ::BitBlt(dc.m_hDC, 0, 0, nPaintWidth, nPaintHeight, 
+   m_hMemDC, m_PaintRect.left, m_PaintRect.top, SRCCOPY);
+ }
+ else
+ {
+  ::BitBlt(dc.m_hDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy,
+   m_hMemDC, 0, 0, SRCCOPY);
+ };
+}
+
+BOOL CPictureEx::PrepareDC(int nWidth, int nHeight)
+{
+ SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER);
+
+ HDC hWinDC = ::GetDC(m_hWnd);
+ if (!hWinDC) return FALSE;
+ 
+ m_hMemDC = CreateCompatibleDC(hWinDC);
+ if (!m_hMemDC) 
+ {
+  ::ReleaseDC(m_hWnd,hWinDC);
+  return FALSE;
+ };
+
+ m_hBitmap  = CreateCompatibleBitmap(hWinDC,nWidth,nHeight);
+ if (!m_hBitmap) 
+ {
+  ::ReleaseDC(m_hWnd,hWinDC);
+  ::DeleteDC(m_hMemDC);
+  return FALSE;
+ };
+
+ m_hOldBitmap = reinterpret_cast<HBITMAP> 
+      (SelectObject(m_hMemDC,m_hBitmap));
+ 
+ // fill the background
+ m_clrBackground = GetSysColor(COLOR_3DFACE);
+ RECT rect = {0,0,nWidth,nHeight};
+ FillRect(m_hMemDC,&rect,(HBRUSH)(COLOR_WINDOW));
+
+ ::ReleaseDC(m_hWnd,hWinDC);
+ m_bIsInitialized = TRUE;
+ return TRUE;
+}
+
+void CPictureEx::OnDestroy() 
+{
+ Stop(); 
+ CStatic::OnDestroy();
+}
+
+void CPictureEx::SetBkColor(COLORREF clr)
+{
+ if (!m_bIsInitialized) return;
+
+ m_clrBackground = clr;
+
+ HBRUSH hBrush = CreateSolidBrush(clr);
+ if (hBrush)
+ {
+  RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
+  FillRect(m_hMemDC,&rect,hBrush);
+  DeleteObject(hBrush);
+ };
+}
+
+#ifdef GIF_TRACING
+void CPictureEx::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize)
+{
+ CFile file;
+
+ if (!file.Open(szFileName,
+   CFile::modeCreate |
+   CFile::modeWrite |
+   CFile::shareDenyNone))
+ {
+  TRACE(_T("WriteData: Error creating file %s\n"),szFileName);
+  return;
+ };
+
+ char *pData = reinterpret_cast<char *> (GlobalLock(hData));
+ if (!pData)
+ {
+  TRACE(_T("WriteData: Error locking memory\n"));
+  return;
+ };
+
+ TRY
+ {
+  file.Write(pData,dwSize);
+ }
+ CATCH(CFileException, e);                                          
+ {
+  TRACE(_T("WriteData: An exception occured while writing to the file %s\n"),
+   szFileName);
+  e->Delete();
+  GlobalUnlock(hData);
+  file.Close();
+  return;
+ }
+ END_CATCH
+ 
+ GlobalUnlock(hData);
+ file.Close();
+}
+
+void CPictureEx::EnumGIFBlocks()
+{
+ enum GIFBlockTypes nBlock;
+
+ ResetDataPointer();
+ while(m_nCurrOffset < m_nDataSize)
+ {
+  nBlock = GetNextBlock();
+  switch(nBlock)
+  {
+  case BLOCK_UNKNOWN:
+   TRACE(_T("- Unknown block\n"));
+   return;
+   break;
+
+  case BLOCK_TRAILER:
+   TRACE(_T("- Trailer block\n"));
+   break;
+
+  case BLOCK_APPEXT:
+   TRACE(_T("- Application extension block\n"));
+   break;
+
+  case BLOCK_COMMEXT:
+   TRACE(_T("- Comment extension block\n"));
+   break;
+
+  case BLOCK_CONTROLEXT:
+   {
+   TGIFControlExt *pControl = 
+    reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
+   TRACE(_T("- Graphic control extension block (delay %d, disposal %d)\n"),
+     pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL));
+   };
+   break;
+
+  case BLOCK_PLAINTEXT:
+   TRACE(_T("- Plain text extension block\n"));
+   break;
+
+  case BLOCK_IMAGE:
+   TGIFImageDescriptor *pIDescr = 
+    reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
+   TRACE(_T("- Image data block (%dx%d  %d,%d)\n"),
+     pIDescr->m_wWidth,
+     pIDescr->m_wHeight,
+     pIDescr->m_wLeftPos,
+     pIDescr->m_wTopPos);
+   break;
+  };
+
+  SkipNextBlock(); 
+ };
+
+ TRACE(_T("\n"));
+}
+#endif // GIF_TRACING
+
+BOOL CPictureEx::SetPaintRect(const RECT *lpRect)
+{
+ return CopyRect(&m_PaintRect, lpRect);
+}
+
+BOOL CPictureEx::GetPaintRect(RECT *lpRect)
+{
+ return CopyRect(lpRect, &m_PaintRect);
+}

+ 305 - 0
source/hook/skinui/PictureEx.h

@@ -0,0 +1,305 @@
+//////////////////////////////////////////////////////////////////////
+// PictureEx.cpp: implementation of the CPictureEx class.
+//
+// Picture displaying control with support for the following formats:
+// GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR
+// 
+// Written by Oleg Bykov (oleg_bykoff@rsdn.ru)
+// Copyright (c) 2001
+//
+// To use CPictureEx, follow these steps:
+//   - place a static control on your dialog (either a text or a bitmap)
+//   - change its identifier to something else (e.g. IDC_MYPIC)
+//   - associate a CStatic with it using ClassWizard
+//   - in your dialog's header file replace CStatic with CPictureEx
+//     (don't forget to #include "PictureEx.h" and add 
+//     PictureEx.h and PictureEx.cpp to your project)
+//   - call one of the overloaded CPictureEx::Load() functions somewhere
+//     (OnInitDialog is a good place to start)
+//   - if the preceding Load() succeeded call Draw()
+//  
+// You can also add the control by defining a member variable of type 
+// CPictureEx, calling CPictureEx::Create (derived from CStatic), then 
+// CPictureEx::Load and CPictureEx::Draw.
+//
+// By default, the control initializes its background to COLOR_3DFACE
+// (see CPictureEx::PrepareDC()). You can change the background by
+// calling CPictureEx::SetBkColor(COLORREF) after CPictureEx::Load().
+//
+// I decided to leave in the class the functions to write separate frames from 
+// animated GIF to disk. If you want to use them, uncomment #define GIF_TRACING 
+// and an appropriate section in CPictureEx::Load(HGLOBAL, DWORD). These functions 
+// won't be compiled and linked to your project unless you uncomment #define GIF_TRACING,
+// so you don't have to worry.
+// 
+// Warning: this code hasn't been subject to a heavy testing, so
+// use it on your own risk. The author accepts no liability for the 
+// possible damage caused by this code.
+//
+// Version 1.0  7 Aug 2001
+//              Initial release
+//
+// Version 1.1  6 Sept 2001
+//              ATL version of the class
+//
+// Version 1.2  14 Oct 2001
+//              - Fixed a problem with loading GIFs from resources
+//                in MFC-version of the class for multi-modules apps.
+//                Thanks to Ruben Avila-Carretero for finding this out.
+//
+//              - Got rid of waitable timer in ThreadAnimation()
+//                Now CPictureEx[Wnd] works in Win95 too.
+//                Thanks to Alex Egiazarov and Wayne King for the idea.
+//
+//              - Fixed a visual glitch of using SetBkColor.
+//                Thanks to Kwangjin Lee for finding this out.
+//
+// Version 1.3  10 Nov 2001
+//              - Fixed a DC leak. One DC leaked per each UnLoad()
+//                (forgot to put a ReleaseDC() in the end of 
+//                CPictureExWnd::PrepareDC() function).
+//
+//              - Now it is possible to set a clipping rectangle using
+//                CPictureEx[Wnd]::SetPaintRect(const LPRECT) function.
+//                The LPRECT parameter tells the class what portion of
+//                a picture should it display. If the clipping rect is 
+//                not set, the whole picture is shown.
+//                Thanks to Fabrice Rodriguez for the idea.
+//
+//              - Added support for Stop/Draw. Now you can Stop() an
+//                animated GIF, then Draw() it again, it will continue
+//                animation from the frame it was stopped on. You can 
+//                also know if a GIF is currently playing with the 
+//                IsPlaying() function.
+//             
+//              - Got rid of math.h and made m_bExitThread volatile. 
+//                Thanks to Piotr Sawicki for the suggestion.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)
+#define AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <vector>
+
+//#define GIF_TRACING  // uncomment it if you want detailed TRACEs
+
+class CPictureEx : public CStatic
+{
+public:
+
+	struct TFrame    // structure that keeps a single frame info
+	{
+		IPicture *m_pPicture;  // pointer to the interface used for drawing
+		SIZE     m_frameSize;
+		SIZE     m_frameOffset;
+		UINT     m_nDelay;     // delay (in 1/100s of a second)
+		UINT     m_nDisposal;  // disposal method
+	};
+
+#pragma pack(1)   // turn byte alignment on
+
+	enum GIFBlockTypes
+	{
+		BLOCK_UNKNOWN,
+		BLOCK_APPEXT,
+		BLOCK_COMMEXT,
+		BLOCK_CONTROLEXT,
+		BLOCK_PLAINTEXT,
+		BLOCK_IMAGE,
+		BLOCK_TRAILER
+	};
+
+	enum ControlExtValues // graphic control extension packed field values
+	{
+		GCX_PACKED_DISPOSAL,  // disposal method
+		GCX_PACKED_USERINPUT,
+		GCX_PACKED_TRANSPCOLOR
+	};
+
+	enum LSDPackedValues  // logical screen descriptor packed field values
+	{
+		LSD_PACKED_GLOBALCT,
+		LSD_PACKED_CRESOLUTION,
+		LSD_PACKED_SORT,
+		LSD_PACKED_GLOBALCTSIZE
+	};
+
+	enum IDPackedValues   // image descriptor packed field values
+	{
+		ID_PACKED_LOCALCT,
+		ID_PACKED_INTERLACE,
+		ID_PACKED_SORT,
+		ID_PACKED_LOCALCTSIZE
+	};
+
+	struct TGIFHeader       // GIF header  
+	{
+		char m_cSignature[3]; // Signature - Identifies the GIF Data Stream
+		// This field contains the fixed value 'GIF'
+		char m_cVersion[3]; // Version number. May be one of the following:
+		// "87a" or "89a"
+	};
+
+	struct TGIFLSDescriptor // Logical Screen Descriptor
+	{
+		WORD m_wWidth; // 2 bytes. Logical screen width
+		WORD m_wHeight; // 2 bytes. Logical screen height
+
+		unsigned char m_cPacked;      // packed field 
+
+		unsigned char m_cBkIndex;     // 1 byte. Background color index
+		unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio
+		inline int GetPackedValue(enum LSDPackedValues Value);
+	};
+
+	struct TGIFAppExtension // application extension block
+	{
+		unsigned char m_cExtIntroducer; // extension introducer (0x21)
+		unsigned char m_cExtLabel; // app. extension label (0xFF)
+		unsigned char m_cBlockSize; // fixed value of 11
+		char m_cAppIdentifier[8];   // application identifier
+		char m_cAppAuth[3];  // application authentication code
+	};
+
+	struct TGIFControlExt // graphic control extension block
+	{
+		unsigned char m_cExtIntroducer; // extension introducer (0x21)
+		unsigned char m_cControlLabel;  // control extension label (0xF9)
+		unsigned char m_cBlockSize; // fixed value of 4
+		unsigned char m_cPacked;    // packed field
+		WORD m_wDelayTime; // delay time
+		unsigned char m_cTColorIndex; // transparent color index
+		unsigned char m_cBlockTerm;   // block terminator (0x00)
+	public:
+		inline int GetPackedValue(enum ControlExtValues Value);
+	};
+
+	struct TGIFCommentExt  // comment extension block
+	{
+		unsigned char m_cExtIntroducer; // extension introducer (0x21)
+		unsigned char m_cCommentLabel;  // comment extension label (0xFE)
+	};
+
+	struct TGIFPlainTextExt // plain text extension block
+	{
+		unsigned char m_cExtIntroducer;  // extension introducer (0x21)
+		unsigned char m_cPlainTextLabel; // text extension label (0x01)
+		unsigned char m_cBlockSize; // fixed value of 12
+		WORD m_wLeftPos;    // text grid left position
+		WORD m_wTopPos;     // text grid top position
+		WORD m_wGridWidth;  // text grid width
+		WORD m_wGridHeight; // text grid height
+		unsigned char m_cCellWidth;  // character cell width
+		unsigned char m_cCellHeight; // character cell height
+		unsigned char m_cFgColor; // text foreground color index
+		unsigned char m_cBkColor; // text background color index
+	};
+
+	struct TGIFImageDescriptor // image descriptor block
+	{
+		unsigned char m_cImageSeparator; // image separator byte (0x2C)
+		WORD m_wLeftPos; // image left position
+		WORD m_wTopPos;  // image top position
+		WORD m_wWidth;   // image width
+		WORD m_wHeight;  // image height
+		unsigned char m_cPacked; // packed field
+		inline int GetPackedValue(enum IDPackedValues Value);
+	};
+
+#pragma pack() // turn byte alignment off
+
+public:
+	BOOL GetPaintRect(RECT *lpRect);
+	BOOL SetPaintRect(const RECT *lpRect);
+	CPictureEx();
+	virtual ~CPictureEx();
+	void Stop();   // stops animation
+	void UnLoad(); // stops animation plus releases all resources
+
+	BOOL IsGIF() const;
+	BOOL IsPlaying() const;
+	BOOL IsAnimatedGIF() const;
+	SIZE GetSize() const;
+	int GetFrameCount() const;
+	COLORREF GetBkColor() const;
+	void SetBkColor(COLORREF clr);
+
+	// draws the picture (starts an animation thread if needed)
+	// if an animation was previously stopped by Stop(),
+	// continues it from the last displayed frame
+	BOOL Draw();
+
+	// loads a picture from a file
+	// i.e. Load(_T("mypic.gif"));
+	BOOL Load(LPCTSTR szFileName);
+
+	// loads a picture from a global memory block (allocated by GlobalAlloc)
+	// Warning: this function DOES NOT free the global memory, pointed to by hGlobal
+	BOOL Load(HGLOBAL hGlobal, DWORD dwSize);
+
+	// loads a picture from a program resource
+	// i.e. Load(MAKEINTRESOURCE(IDR_MYPIC),_T("GIFTYPE"));
+	BOOL Load(LPCTSTR szResourceName,LPCTSTR szResourceType);
+
+protected:
+
+#ifdef GIF_TRACING
+	void EnumGIFBlocks();
+	void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize);
+#endif // GIF_TRACING
+
+	RECT m_PaintRect;
+	SIZE m_PictureSize;
+	COLORREF m_clrBackground;
+	UINT m_nCurrFrame;
+	UINT m_nDataSize;
+	UINT m_nCurrOffset;
+	UINT m_nGlobalCTSize;
+	BOOL m_bIsGIF;
+	BOOL m_bIsPlaying;
+	volatile BOOL m_bExitThread;
+	BOOL m_bIsInitialized;
+	HDC m_hMemDC;
+
+	HDC m_hDispMemDC;
+	HBITMAP m_hDispMemBM;
+	HBITMAP m_hDispOldBM;
+
+	HBITMAP m_hBitmap;
+	HBITMAP m_hOldBitmap;
+	HANDLE m_hThread;
+	HANDLE m_hExitEvent;
+	IPicture * m_pPicture;
+	TGIFHeader * m_pGIFHeader;
+	unsigned char * m_pRawData;
+	TGIFLSDescriptor * m_pGIFLSDescriptor;
+	std::vector<TFrame> m_arrFrames;
+
+	void ThreadAnimation();
+	static UINT WINAPI _ThreadAnimation(LPVOID pParam);
+
+	int GetNextBlockLen() const;
+	BOOL SkipNextBlock();
+	BOOL SkipNextGraphicBlock();
+	BOOL PrepareDC(int nWidth, int nHeight);
+	void ResetDataPointer();
+	enum GIFBlockTypes GetNextBlock() const;
+	UINT GetSubBlocksLen(UINT nStartingOffset) const;
+	HGLOBAL GetNextGraphicBlock(UINT *pBlockLen, UINT *pDelay, 
+		SIZE *pBlockSize, SIZE *pBlockOffset, UINT *pDisposal);
+
+	// Generated message map functions
+	//{{AFX_MSG(CPictureEx)
+	afx_msg void OnDestroy();
+	afx_msg void OnPaint();
+	//}}AFX_MSG
+
+	DECLARE_MESSAGE_MAP()
+};
+
+#endif // !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)

+ 244 - 0
source/hook/skinui/TreeComboBox.cpp

@@ -0,0 +1,244 @@
+// TreeComboBox.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "TreeComboBox.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CTreeComboBox
+
+CTreeComboBox::CTreeComboBox()
+	:m_bTree(FALSE)
+	, m_bAlertBkg(FALSE)
+	, m_bAlertText(FALSE)
+	, m_droppedHeight(250)
+	, m_droppedWidth(100)
+{
+	m_hBrushAlert = CreateSolidBrush(COLOR_ALERT);
+}
+
+CTreeComboBox::~CTreeComboBox()
+{
+	DeleteObject(m_hBrushAlert);
+}
+
+
+BEGIN_MESSAGE_MAP(CTreeComboBox, CComboBox)
+	//{{AFX_MSG_MAP(CTreeComboBox)
+	ON_WM_LBUTTONDOWN()
+	ON_WM_LBUTTONDBLCLK()
+	ON_WM_CTLCOLOR_REFLECT()
+	//}}AFX_MSG_MAP
+	ON_MESSAGE(WMU_CLOSE_CONTROL, OnCloseControl)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CTreeComboBox message handlers
+void CTreeComboBox::PreSubclassWindow()
+{
+	// TODO: Add your specialized code here and/or call the base class
+	CComboBox::PreSubclassWindow();
+	CRect rect(0, 0, 0, 0);
+
+	DWORD dwStyle = WS_BORDER | TVS_DISABLEDRAGDROP | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_FULLROWSELECT | WS_POPUPWINDOW;
+	((CWnd&)m_Tree).CreateEx(0, WC_TREEVIEW, NULL, dwStyle, rect, GetParent(), NULL);
+	m_Tree.Init(this);
+
+	GetClientRect(rect);
+	SetDroppedWidth(rect.Width());
+	SetDroppedHeight(m_droppedHeight);
+}
+
+BOOL CTreeComboBox::PreTranslateMessage(MSG* pMsg)
+{
+	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DOWN)
+	{
+		DisplayTree();
+		return TRUE;
+	}
+
+	return CComboBox::PreTranslateMessage(pMsg);
+}
+
+void CTreeComboBox::OnLButtonDown(UINT nFlags, CPoint point)
+{
+	m_bTree = !m_bTree;
+	if (m_bTree)
+		DisplayTree();
+}
+
+void CTreeComboBox::OnLButtonDblClk(UINT nFlags, CPoint point)
+{
+	OnLButtonDown(nFlags, point);
+}
+
+void CTreeComboBox::TreeCtrlDone()
+{
+	CWnd* pParent = GetParent();
+	if (pParent != NULL)
+	{
+		CString str; GetWindowText(str);
+		if (m_pretext != str)
+		{
+			m_pretext = str;
+			WPARAM wParam = MAKEWPARAM(GetDlgCtrlID(), CBN_CLOSEUP);
+			pParent->SendMessage(WM_COMMAND, wParam, (LPARAM)m_hWnd);
+		}
+	}
+}
+
+LRESULT CTreeComboBox::OnCloseControl(WPARAM wParam, LPARAM lParam)
+{
+	m_Tree.ShowWindow(SW_HIDE);
+	m_bTree = FALSE;
+	HTREEITEM hItemChanged = (HTREEITEM)lParam;
+	if (hItemChanged)
+	{
+		SetTitle(m_Tree.GetItemText(hItemChanged));
+	}
+	AlertBkg(FALSE);
+	TreeCtrlDone();
+	return 1;
+}
+
+HBRUSH CTreeComboBox::CtlColor(CDC* pDC, UINT nCtlColor)
+{
+	HBRUSH hbr = NULL;
+	if (nCtlColor == CTLCOLOR_EDIT)
+	{
+		if (!m_bAlertText)pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
+		else pDC->SetTextColor(COLOR_RED);
+		if (!m_bAlertBkg)pDC->SetBkColor(GetSysColor(COLOR_WINDOW));
+		else pDC->SetBkColor(COLOR_ALERT);
+		if (m_bAlertText || m_bAlertBkg)hbr = m_hBrushAlert;
+		pDC->SetBkMode(TRANSPARENT);
+	}
+
+	return hbr;
+}
+
+void CTreeComboBox::DisplayTree()
+{
+	CRect rc;
+	GetWindowRect(rc);
+	rc.top = rc.bottom + 1;
+	rc.right = GetDroppedWidth();
+	rc.bottom = GetDroppedHeight();
+
+	m_Tree.Display(rc);
+}
+
+void CTreeComboBox::SetTitle(CString sTitle)
+{
+	ResetContent();
+	AddString(sTitle);
+	SetCurSel(0);
+}
+
+void CTreeComboBox::RefDroppedWidth()
+{
+	CRect rect;
+	GetClientRect(rect);
+	SetDroppedWidth(rect.Width());
+}
+
+/************************************************************************/
+/*  函数:InsertMyItem[12/7/2016 IT];
+/*  描述:;
+/*  参数:;
+/*  	[IN] :;
+/*  	[IN] :;
+/*  返回:void;
+/*  注意:只适合二层目录;
+/*  示例:;
+/*
+/*  修改:;
+/*  日期:;
+/*  内容:;
+/************************************************************************/
+void CTreeComboBox::InsertMyItem(IN CString strRoot, IN CString strItem)
+{
+	BOOL bExist = FALSE;
+	HTREEITEM hRoot = m_Tree.GetRootItem();
+	if (hRoot == NULL)
+	{
+		hRoot = m_Tree.InsertItem(strRoot, NULL);
+		m_Tree.InsertItem(strItem, hRoot);
+		return;
+	}
+
+	CString strName = m_Tree.GetItemText(hRoot);
+	if (strName == strRoot)
+	{
+		HTREEITEM hSubItem = m_Tree.GetChildItem(hRoot);
+		// 遍历所有子项;
+		while (hSubItem)
+		{
+			// 当前子项是否是要查找的子项;
+			if (strItem == m_Tree.GetItemText(hSubItem))
+			{
+				bExist = TRUE;
+				break;
+			}
+
+			// 若未到,查找下一个兄弟项;
+			hSubItem = m_Tree.GetNextSiblingItem(hSubItem);
+		}
+
+		if (!bExist)
+		{
+			// 插入新项;
+			m_Tree.InsertItem(strItem, hRoot);
+		}
+	}
+	else
+	{
+		hRoot = m_Tree.GetNextSiblingItem(hRoot);
+		while (hRoot)
+		{
+			// 当前兄弟项是否是要查找的子项;
+			if (strRoot == m_Tree.GetItemText(hRoot))
+			{
+				HTREEITEM hSubItem = m_Tree.GetChildItem(hRoot);
+				// 遍历所有子项;
+				while (hSubItem)
+				{
+					// 当前子项是否是要查找的子项;
+					if (strItem == m_Tree.GetItemText(hSubItem))
+					{
+						bExist = TRUE;
+						break;
+					}
+
+					// 若未到,查找下一个兄弟项;
+					hSubItem = m_Tree.GetNextSiblingItem(hSubItem);
+				}
+
+				if (!bExist)
+				{
+					// 插入新项;
+					bExist = TRUE;
+					m_Tree.InsertItem(strItem, hRoot);
+				}
+
+				break;
+			}
+
+			// 若未到,查找下一个兄弟项;
+			hRoot = m_Tree.GetNextSiblingItem(hRoot);
+		}
+
+		if (!bExist)
+		{
+			// 插入新项;
+			hRoot = m_Tree.InsertItem(strRoot, NULL);
+			m_Tree.InsertItem(strItem, hRoot);
+		}
+	}
+}

+ 90 - 0
source/hook/skinui/TreeComboBox.h

@@ -0,0 +1,90 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [IT], 保留所有权利;
+/*  模 块 名:;
+/*  描    述:;
+/*
+/*  版    本:[V];
+/*  作    者:[IT];
+/*  日    期:[11/4/2016];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[IT];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+#ifndef __TREECOMBOBOX__
+#define __TREECOMBOBOX__
+
+#include "ComboTreeCtrl.h"
+
+#pragma once
+
+#ifndef	COLOR_RED
+#define COLOR_RED			RGB(220,0,0)
+#endif	//COLOR_RED
+#ifndef	COLOR_ALERT
+#define COLOR_ALERT			RGB(255,180,180)
+#endif	//COLOR_ALERT
+/////////////////////////////////////////////////////////////////////////////
+// CTreeComboBox  window
+
+class CTreeComboBox : public CComboBox
+{
+	CString m_pretext;
+	// Construction
+public:
+	CTreeComboBox();
+
+	// Attributes
+public:
+	BOOL m_bTree;
+
+	// Operations
+public:
+	void DisplayTree();
+	void SetTitle(CString sTitle);
+	BOOL IsAlertBkg(){ return m_bAlertBkg; }
+	BOOL IsAlertText(){ return m_bAlertText; }
+	CTreeCtrl& GetTreeCtrl(){ return m_Tree; }
+	int GetDroppedWidth(){ return m_droppedWidth; };
+	int GetDroppedHeight(){ return m_droppedHeight; };
+	void SetDroppedWidth(int nWidth){ m_droppedWidth = nWidth; };
+	void SetDroppedHeight(int nHeight){ m_droppedHeight = nHeight; };
+	void AlertBkg(BOOL bAlert = TRUE){ m_bAlertBkg = bAlert; Invalidate(); };
+	void AlertText(BOOL bAlert = TRUE){ m_bAlertText = bAlert; Invalidate(); };
+
+public:
+	virtual BOOL PreTranslateMessage(MSG* pMsg);
+protected:
+	virtual void PreSubclassWindow();
+
+public:
+	void RefDroppedWidth();
+	UINT GetTreeCount();
+	void TreeCtrlDone();
+	virtual ~CTreeComboBox();
+
+protected:
+	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+	afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
+	afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
+	afx_msg LRESULT OnCloseControl(WPARAM wParam, LPARAM lParam);
+	DECLARE_MESSAGE_MAP()
+
+private:
+	BOOL m_bAlertBkg;
+	BOOL m_bAlertText;
+	int m_droppedWidth;
+	int m_droppedHeight;
+	HBRUSH m_hBrushAlert;
+	CComboTreeCtrl m_Tree;
+
+public:
+	void InsertMyItem(IN CString strRoot, IN CString strItem);
+};
+
+
+#endif // __TREECOMBOBOX__;