windows-nt/Source/XPSP1/NT/shell/themes/themeui/themeutils.cpp
2020-09-26 16:20:57 +08:00

1149 lines
37 KiB
C++

/*****************************************************************************\
FILE: themeutils.cpp
DESCRIPTION:
This class will load and save the "Theme" settings from their persisted
state.
BryanSt 5/26/2000
Copyright (C) Microsoft Corp 2000-2000. All rights reserved.
\*****************************************************************************/
#include "priv.h"
#include "ThemePg.h"
#include "ThemeFile.h"
// Determine if we need to get this value from the effects tab.
BOOL g_bGradient = TRUE;
BOOL g_bReadOK, g_bWroteOK; // Save: read from reg/sys, write to file
// Apply: not implemented since ignoring results anyway
#define MAX_MSGLEN 512 // TRANSLATORS: English strs max 256
// MAX_MSGLEN must be at least 2xMAX_STRLEN
// MAX_MSGLEN must be at least 2xMAX_PATH
TCHAR szCurDir[MAX_PATH+1]; // last dir opened a theme file from
TCHAR szMsg[MAX_MSGLEN+1]; // scratch buffer
//
// *Path
//
// These routines help to make themes transportable between computers.
// The problem is that the registry keeps filenames for the various
// theme elements and, of course, these are hard-coded paths that vary
// from machine to machine.
//
// The way we work around this problem is by storing filenames in the
// theme file as _relative_ paths: relative to the theme file directory
// or the Windows directory. (Actually, these routines are set up to
// be relative to any number of directories.) When saving a filename to
// a theme, we check to see if any relative paths can be abstracted out.
// When retrieving a filename from a theme, we take the abstract placeholder
// and replace it with the current sessions instances.
// these must parallel each other. abstract strs must start with %
void ExpandSZ(LPTSTR pszSrc, int cchSize)
{
LPTSTR pszTmp = (LPTSTR)LocalAlloc(GPTR, (MAX_PATH * sizeof(TCHAR)));
if (pszTmp)
{
if (ExpandEnvironmentStrings(pszSrc, pszTmp, MAX_PATH))
{
StrCpyN(pszSrc, pszTmp, cchSize);
}
LocalFree(pszTmp);
}
}
// HandGet
//
// Just a little helper routine, gets an individual string value from the
// registry and returns it to the caller. Takes care of registry headaches,
// including a paranoid length check before getting the string.
//
// NOTE that this function thinks it's getting a string value. If it's
// another kind, this function will do OK: but the caller may be surprised
// if expecting a string.
//
// Returns: success of string retrieval
BOOL HandGet(HKEY hKeyRoot, LPTSTR lpszSubKey, LPTSTR lpszValName, LPTSTR lpszRet)
{
LONG lret;
HKEY hKey; // cur open key
BOOL bOK = TRUE;
DWORD dwSize = 0;
DWORD dwType;
// inits
// get subkey
lret = RegOpenKeyEx( hKeyRoot, lpszSubKey,
(DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKey );
if (lret != ERROR_SUCCESS)
{
return (FALSE);
}
// now do our paranoid check of data size
lret = RegQueryValueEx(hKey, lpszValName,
(LPDWORD)NULL,
(LPDWORD)&dwType,
(LPBYTE)NULL, // null for size info only
(LPDWORD)&dwSize );
if (ERROR_SUCCESS == lret)
{ // saw something there
// here's the size check before getting the data
if (dwSize > (DWORD)(MAX_PATH * sizeof(TCHAR)))
{ // if string too big
bOK = FALSE; // can't read, so very bad news
g_bReadOK = FALSE;
}
else
{ // size is OK to continue
// now really get the value
lret = RegQueryValueEx(hKey, lpszValName,
(LPDWORD)NULL,
(LPDWORD)&dwType,
(LPBYTE)lpszRet, // getting actual value
(LPDWORD)&dwSize);
// If this is an EXPAND_SZ we need to expand it...
if (REG_EXPAND_SZ == dwType)
{
ExpandSZ(lpszRet, MAX_PATH);
}
if (ERROR_SUCCESS != lret)
bOK = FALSE;
}
}
else
{
bOK = FALSE;
}
// cleanup
// close subkey
RegCloseKey(hKey);
return (bOK);
}
HRESULT _GetPlus98ThemesDir(LPTSTR pszPath, DWORD cchSize)
{
HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_PLUS98DIR, SZ_REGVALUE_PLUS98DIR, pszPath, cchSize);
if (SUCCEEDED(hr))
{
TCHAR szSubDir[MAX_PATH];
LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
PathAppend(pszPath, szSubDir);
}
return hr;
}
HRESULT _GetPlus95ThemesDir(LPTSTR pszPath, DWORD cchSize)
{
HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_PLUS95DIR, SZ_REGVALUE_PLUS98DIR, pszPath, cchSize);
if (SUCCEEDED(hr))
{
TCHAR szSubDir[MAX_PATH];
LPTSTR pszFile = PathFindFileName(pszPath);
if (pszFile)
{
// Plus!95 DestPath has "Plus!.dll" on the end so get rid of that.
pszFile[0] = 0;
}
// Tack on a "Themes" onto the path
LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
PathAppend(pszPath, szSubDir);
}
return hr;
}
HRESULT _GetKidsThemesDir(LPTSTR pszPath, DWORD cchSize)
{
HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_KIDSDIR, SZ_REGVALUE_KIDSDIR, pszPath, cchSize);
if (SUCCEEDED(hr))
{
TCHAR szSubDir[MAX_PATH];
// Tack a "\Plus! for Kids\Themes" onto the path
PathAppend(pszPath, TEXT("Plus! for Kids"));
LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
PathAppend(pszPath, szSubDir);
}
return hr;
}
HRESULT _GetHardDirThemesDir(LPTSTR pszPath, DWORD cchSize)
{
HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_PROGRAMFILES, SZ_REGVALUE_PROGRAMFILESDIR, pszPath, cchSize);
if (SUCCEEDED(hr))
{
TCHAR szSubDir[MAX_PATH];
PathAppend(pszPath, TEXT("Plus!"));
LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
PathAppend(pszPath, szSubDir);
}
return hr;
}
/*****************************************************************************\
DESCRIPTION:
Find any one of the directories that a previous plus pack may have
installed. This may include Plus! 95, 98, kids, etc.
\*****************************************************************************/
HRESULT GetPlusThemeDir(IN LPTSTR pszPath, IN int cchSize)
{
HRESULT hr = S_OK;
// The follwoing directories can contain themes:
// Plus!98 Install Path\Themes
// Plus!95 Install Path\Themes
// Kids for Plus! Install Path\Themes
// Program Files\Plus!\Themes
hr = _GetPlus98ThemesDir(pszPath, cchSize);
if (FAILED(hr))
{
hr = _GetPlus95ThemesDir(pszPath, cchSize);
if (FAILED(hr))
{
hr = _GetKidsThemesDir(pszPath, cchSize);
if (FAILED(hr))
{
hr = _GetHardDirThemesDir(pszPath, cchSize);
}
}
}
return hr;
}
TCHAR g_szThemeDir[MAX_PATH]; // dir of most theme files
TCHAR g_szWinDir[MAX_PATH]; // Windows directory
LPTSTR g_pszThemeValues[] = {g_szThemeDir, g_szWinDir, g_szWinDir};
LPCTSTR g_pszThemeTokens[] = {TEXT("%ThemeDir%"), TEXT("%WinDir%"), TEXT("%SystemRoot%")};
void ReplaceStringToken(IN LPCTSTR pszSource, IN LPCTSTR pszToken, IN LPCTSTR pszReplacement, IN LPTSTR pszDest, IN int cchSize)
{
LPCTSTR pszLastPart = &pszSource[lstrlen(pszToken)];
if (L'\\' == pszLastPart[0])
{
pszLastPart++; // Skip past any slashes
}
StrCpyN(pszDest, pszReplacement, cchSize);
PathAppend(pszDest, pszLastPart);
}
/*****************************************************************************\
DESCRIPTION:
Find any tokens in the path (%ThemeDir%, %WinDir%) and replace them
with the correct paths.
\*****************************************************************************/
HRESULT ExpandThemeTokens(IN LPCTSTR pszThemeFile, IN LPTSTR pszPath, IN int cchSize)
{
HRESULT hr = S_OK;
int nIndex;
TCHAR szFinalPath[MAX_PATH];
TCHAR szOriginalPath[MAX_PATH];
szFinalPath[0] = 0;
StrCpyN(szFinalPath, pszPath, ARRAYSIZE(szFinalPath));
StrCpyN(szOriginalPath, pszPath, ARRAYSIZE(szOriginalPath));
InitFrost();
AssertMsg((0 != g_szThemeDir[0]), TEXT("Someone needs to call InitFrost() first to in this."));
AssertMsg((0 != g_szWinDir[0]), TEXT("Someone needs to call InitFrost() first to in this."));
for (nIndex = 0; nIndex < ARRAYSIZE(g_pszThemeTokens); nIndex++)
{
if (!StrCmpNI(g_pszThemeTokens[nIndex], szFinalPath, lstrlen(g_pszThemeTokens[nIndex]) - 1))
{
// We found the token to replace.
TCHAR szTempPath[MAX_PATH];
StrCpyN(szTempPath, szFinalPath, ARRAYSIZE(szTempPath));
ReplaceStringToken(szTempPath, g_pszThemeTokens[nIndex], g_pszThemeValues[nIndex], szFinalPath, ARRAYSIZE(szFinalPath));
if ((0 == nIndex) && !PathFileExists(szFinalPath))
{
// Sometimes the .theme file will not be in the Theme directory, so we need to try
// the directory containing the .theme file.
TCHAR szThemeDir[MAX_PATH];
StrCpyN(szThemeDir, pszThemeFile, ARRAYSIZE(szThemeDir));
PathRemoveFileSpec(szThemeDir);
StrCpyN(szTempPath, szOriginalPath, ARRAYSIZE(szTempPath));
ReplaceStringToken(szTempPath, g_pszThemeTokens[nIndex], szThemeDir, szFinalPath, ARRAYSIZE(szFinalPath));
}
else
{
// It worked
}
hr = S_OK;
break;
}
}
if (0 == SHExpandEnvironmentStringsForUserW(NULL, szFinalPath, pszPath, cchSize))
{
StrCpyN(pszPath, szFinalPath, cchSize);
}
return hr;
}
//
// ConfirmFile
//
// This function does the "smart" file searching that's supposed to be
// built into each resource file reference in applying themes.
//
// First see if the full pathname + file given actually exists.
// If it does not, then try looking for the same filename (stripped from path)
// in other standard directories, in this order:
// Current Theme file directory
// Theme switcher THEMES subdirectory
// Windows directory
// Windows/MEDIA directory
// Windows/CURSORS directory
// Windows/SYSTEM directory
//
// Input: LPTSTR lpszPath full pathname
// BOOL bUpdate whether to alter the filename string with found file
// Returns: int flag telling if and how file has been confirmed
// CF_EXISTS pathname passed in was actual file
// CF_FOUND file did not exist, but found same filename elsewhere
// CF_NOTFOUND file did not exist, could not find elsewhere
//
int ConfirmFile(IN LPTSTR lpszPath, IN BOOL bUpdate)
{
TCHAR szWork[MAX_PATH+1];
TCHAR szTest[MAX_PATH+1];
int iret = CF_NOTFOUND; // default value
LPTSTR lpFile;
LPTSTR lpNumber;
HANDLE hTest;
// special case easy return: if it's null, then trivially satisfied.
if (!*lpszPath)
return CF_EXISTS; // NO WORK EXIT
// Inits
// copy pathname to a work string for the function
StrCpyN(szWork, lpszPath, ARRAYSIZE(szWork));
// input can be of the form foo.dll,13. need to strip off that comma,#
// but hold onto it to put back at the end if we change the pathname
lpNumber = StrChr(szWork, TEXT(','));
if (lpNumber && *lpNumber)
{
// if there is a comma
lpFile = lpNumber; // temp
lpNumber = CharNext(lpNumber);// hold onto number
*lpFile = 0;
}
// TODO: In Blackcomb we should call SHPathPrepareForWrite() in case
// szWork is stored on removable media that the user should insert.
// Do the checks
// *** first check if the given file just exists as is
hTest = CreateFile(szWork, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{
// success
iret = CF_EXISTS; // assign ret value
// don't need to worry about bUpdate: found with input string
}
else // otherwise, let's go searching for the same filename in other dirs
{
// get ptr to the filename separated from the path
lpFile = PathFindFileName(szWork);
// *** try the cur theme file dir
StrCpyN(szTest, szCurDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
else // *** otherwise try the Theme switcher THEMES subdirectory
{
StrCpyN(szTest, g_szThemeDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
else // *** otherwise try the win dir
{
StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
else // *** otherwise try the win/media dir
{
// can get this one directly from Registry
HandGet(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),
TEXT("MediaPath"), szTest);
#ifdef THEYREMOVEREGSETTING
StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, TEXT("Media\\"), ARRAYSIZE(szTest));
#endif
StrCatBuff(szTest, TEXT("\\"), ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
else // *** otherwise try the win/cursors dir
{
StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, TEXT("CURSORS\\"), ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
else // *** otherwise try the win/system dir
{
StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, TEXT("SYSTEM\\"), ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
else // *** otherwise try the win/system32 dir
{
StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
StrCatBuff(szTest, TEXT("SYSTEM32\\"), ARRAYSIZE(szTest));
StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hTest != INVALID_HANDLE_VALUE)
{ // success
iret = CF_FOUND; // assign ret value
}
}
}
}
}
}
}
// if found anywhere other than orig, copy found path/str as requested
if ((iret == CF_FOUND) && bUpdate)
{
StrCpy(lpszPath, szTest);
// if we stripped off a number, let's add it back on
if (lpNumber && *lpNumber)
{
StrCatBuff(lpszPath, TEXT(","), MAX_PATH);
StrCatBuff(lpszPath, lpNumber, MAX_PATH);
}
} // endif found file by searching
}
// cleanup
if (iret != CF_NOTFOUND)
CloseHandle(hTest); // close file if opened
return (iret);
}
// InitFrost
// Since there are no window classes to register, this routine just loads
// the strings and, since there's only one instance, calls InitInstance().
//
// Returns: success of initialization
void InitFrost(void)
{
static BOOL s_fInited = FALSE;
if (FALSE == s_fInited)
{
BOOL bret;
HDC hdc;
s_fInited = TRUE;
if (!GetWindowsDirectory(g_szWinDir, ARRAYSIZE(g_szWinDir)))
{
g_szWinDir[0] = 0;
}
if (FAILED(GetPlusThemeDir(g_szThemeDir, ARRAYSIZE(g_szThemeDir))))
{
StrCpyN(g_szThemeDir, g_szWinDir, ARRAYSIZE(g_szThemeDir));
}
// Initialize our g_bGradient flag
// We may need to get the g_bGradient flag from the Effects tab.
hdc = GetDC(NULL);
g_bGradient = (BOOL)(GetDeviceCaps(hdc, BITSPIXEL) > 8);
ReleaseDC(NULL, hdc);
// init directory strings
szCurDir[0];
// default current dir
StrCpyN(szCurDir, g_szThemeDir, ARRAYSIZE(szCurDir));
// Windows directory
if (TEXT('\\') != g_szWinDir[lstrlen(g_szWinDir)-1])
{
StrCatBuff(g_szWinDir, TEXT("\\"), ARRAYSIZE(g_szWinDir));
}
// see if there is a previous theme file to return to
bret = HandGet(HKEY_CURRENT_USER, SZ_REGKEY_CURRENTTHEME, NULL, szMsg);
if (bret)
{
// get init cur dir from prev theme file
StrCpyN(szCurDir, szMsg, ARRAYSIZE(szCurDir));
PathFindFileName(szCurDir)[0] = 0;
}
}
}
// ascii to integer conversion routine
//
// ***DEBUG*** int'l: is this true?
// These don't need to be UNICODE/international ready, since they
// *only* deal with strings from the Registry and our own private
// INI files.
/* CAREFUL!! This atoi just IGNORES non-numerics like decimal points!!! */
/* checks for (>=1) leading negative sign */
int latoi(LPSTR s)
{
int n;
LPSTR pS;
BOOL bSeenNum;
BOOL bNeg;
n=0;
bSeenNum = bNeg = FALSE;
for (pS=s; *pS; pS++) {
if ((*pS >= '0') && (*pS <= '9')) {
bSeenNum = TRUE;
n = n*10 + (*pS - '0');
}
if (!bSeenNum && (*pS == '-')) {
bNeg = TRUE;
}
}
if (bNeg) {
n = -n;
}
return(n);
}
//
// Utility routine for above; takes ASCII string to binary in
// global pValue[] buffer.
//
// Since the values this guy is manipulating is purely ASCII
// numerics we should be able to get away with this char pointer
// arithmetic. If they were not simple ASCII numerics I think
// we could get into trouble with some DBCS chars
//
// Uses: writes binary data to global pValue[]
//
int WriteBytesToBuffer(IN LPTSTR pszInput, IN void * pOut, IN int cbSize)
{
LPTSTR lpszCur, lpszNext, lpszEnd;
BYTE * pbCur = (BYTE *)pOut;
int iTemp, iBytes;
#ifdef UNICODE
CHAR szTempA[10];
#endif
// inits
lpszNext = pszInput;
iBytes = 0;
lpszEnd = pszInput + lstrlen(pszInput); // points to null term
// translating loop
while (*lpszNext && (lpszNext < lpszEnd) && (iBytes < cbSize))
{
// update str pointers
// hold onto your starting place
lpszCur = lpszNext;
// advance pointer to next and null terminate cur
while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
*lpszNext = 0; lpszNext++;
// on last number, this leaves lpszNext pointing past lpszEnd
// translate this string-number into binary number and place in
// output buffer.
#ifdef UNICODE
wcstombs(szTempA, lpszCur, sizeof(szTempA));
iTemp = latoi(szTempA);
#else // !UNICODE
iTemp = latoi(lpszCur);
#endif
*pbCur = (BYTE)iTemp;
pbCur++; // incr byte loc in output buffer
// keep track of your bytes
iBytes++;
}
//
// cleanup
return (iBytes);
}
HRESULT ConvertBinaryToINIByteString(BYTE * pBytes, DWORD cbSize, LPWSTR * ppszStringOut)
{
HRESULT hr = E_OUTOFMEMORY;
*ppszStringOut = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * ((4 * cbSize) + 1));
if (*ppszStringOut)
{
LPWSTR pszCurrent = *ppszStringOut;
DWORD dwIndex;
TCHAR szTemp[10];
for (dwIndex = 0; dwIndex < cbSize; dwIndex++)
{
wnsprintf(szTemp, ARRAYSIZE(szTemp), TEXT("%d "), pBytes[dwIndex]);
StrCpy(pszCurrent, szTemp);
pszCurrent += lstrlen(szTemp);
}
hr = S_OK;
}
return hr;
}
void ConvertLogFontToWIDE(LPLOGFONTA aLF, LPLOGFONTW wLF)
{
ZeroMemory(wLF, sizeof(wLF));
wLF->lfHeight = aLF->lfHeight;
wLF->lfWidth = aLF->lfWidth;
wLF->lfEscapement = aLF->lfEscapement;
wLF->lfOrientation = aLF->lfOrientation;
wLF->lfWeight = aLF->lfWeight;
wLF->lfItalic = aLF->lfItalic;
wLF->lfUnderline = aLF->lfUnderline;
wLF->lfStrikeOut = aLF->lfStrikeOut;
wLF->lfCharSet = aLF->lfCharSet;
wLF->lfOutPrecision = aLF->lfOutPrecision;
wLF->lfClipPrecision = aLF->lfClipPrecision;
wLF->lfQuality = aLF->lfQuality;
wLF->lfPitchAndFamily = aLF->lfPitchAndFamily;
MultiByteToWideChar(CP_ACP, 0, aLF->lfFaceName, -1, wLF->lfFaceName, LF_FACESIZE);
}
void ConvertIconMetricsToWIDE(LPICONMETRICSA aIM, LPICONMETRICSW wIM)
{
ZeroMemory(wIM, sizeof(wIM));
wIM->cbSize = sizeof(*wIM);
wIM->iHorzSpacing = aIM->iHorzSpacing;
wIM->iVertSpacing = aIM->iVertSpacing;
wIM->iTitleWrap = aIM->iTitleWrap;
ConvertLogFontToWIDE(&aIM->lfFont, &wIM->lfFont);
}
void ConvertNCMetricsToWIDE(LPNONCLIENTMETRICSA aNCM, LPNONCLIENTMETRICSW wNCM)
{
ZeroMemory(wNCM, sizeof(wNCM));
wNCM->cbSize = sizeof(*wNCM);
wNCM->iBorderWidth = aNCM->iBorderWidth;
wNCM->iScrollWidth = aNCM->iScrollWidth;
wNCM->iScrollHeight = aNCM->iScrollHeight;
wNCM->iCaptionWidth = aNCM->iCaptionWidth;
wNCM->iCaptionHeight = aNCM->iCaptionHeight;
ConvertLogFontToWIDE(&aNCM->lfCaptionFont, &wNCM->lfCaptionFont);
wNCM->iSmCaptionWidth = aNCM->iSmCaptionWidth;
wNCM->iSmCaptionHeight = aNCM->iSmCaptionHeight;
ConvertLogFontToWIDE(&aNCM->lfSmCaptionFont, &wNCM->lfSmCaptionFont);
wNCM->iMenuWidth = aNCM->iMenuWidth;
wNCM->iMenuHeight = aNCM->iMenuHeight;
ConvertLogFontToWIDE(&aNCM->lfMenuFont, &wNCM->lfMenuFont);
ConvertLogFontToWIDE(&aNCM->lfStatusFont, &wNCM->lfStatusFont);
ConvertLogFontToWIDE(&aNCM->lfMessageFont, &wNCM->lfMessageFont);
}
void ConvertLogFontToANSI(LPLOGFONTW wLF, LPLOGFONTA aLF)
{
ZeroMemory(aLF, sizeof(aLF));
aLF->lfHeight = wLF->lfHeight;
aLF->lfWidth = wLF->lfWidth;
aLF->lfEscapement = wLF->lfEscapement;
aLF->lfOrientation = wLF->lfOrientation;
aLF->lfWeight = wLF->lfWeight;
aLF->lfItalic = wLF->lfItalic;
aLF->lfUnderline = wLF->lfUnderline;
aLF->lfStrikeOut = wLF->lfStrikeOut;
aLF->lfCharSet = wLF->lfCharSet;
aLF->lfOutPrecision = wLF->lfOutPrecision;
aLF->lfClipPrecision = wLF->lfClipPrecision;
aLF->lfQuality = wLF->lfQuality;
aLF->lfPitchAndFamily = wLF->lfPitchAndFamily;
SHUnicodeToAnsi(wLF->lfFaceName, aLF->lfFaceName, ARRAYSIZE(aLF->lfFaceName));
}
void ConvertIconMetricsToANSI(LPICONMETRICSW wIM, LPICONMETRICSA aIM)
{
ZeroMemory(aIM, sizeof(aIM));
aIM->cbSize = sizeof(aIM);
aIM->iHorzSpacing = wIM->iHorzSpacing;
aIM->iVertSpacing = wIM->iVertSpacing;
aIM->iTitleWrap = wIM->iTitleWrap;
ConvertLogFontToANSI(&wIM->lfFont, &aIM->lfFont);
}
void ConvertNCMetricsToANSI(LPNONCLIENTMETRICSW wNCM, LPNONCLIENTMETRICSA aNCM)
{
ZeroMemory(aNCM, sizeof(aNCM));
aNCM->cbSize = sizeof(*aNCM);
aNCM->iBorderWidth = wNCM->iBorderWidth;
aNCM->iScrollWidth = wNCM->iScrollWidth;
aNCM->iScrollHeight = wNCM->iScrollHeight;
aNCM->iCaptionWidth = wNCM->iCaptionWidth;
aNCM->iCaptionHeight = wNCM->iCaptionHeight;
ConvertLogFontToANSI(&wNCM->lfCaptionFont, &aNCM->lfCaptionFont);
aNCM->iSmCaptionWidth = wNCM->iSmCaptionWidth;
aNCM->iSmCaptionHeight = wNCM->iSmCaptionHeight;
ConvertLogFontToANSI(&wNCM->lfSmCaptionFont, &aNCM->lfSmCaptionFont);
aNCM->iMenuWidth = wNCM->iMenuWidth;
aNCM->iMenuHeight = wNCM->iMenuHeight;
ConvertLogFontToANSI(&wNCM->lfMenuFont, &aNCM->lfMenuFont);
ConvertLogFontToANSI(&wNCM->lfStatusFont, &aNCM->lfStatusFont);
ConvertLogFontToANSI(&wNCM->lfMessageFont, &aNCM->lfMessageFont);
}
HRESULT GetIconMetricsFromSysMetricsAll(SYSTEMMETRICSALL * pSystemMetrics, LPICONMETRICSA pIconMetrics, DWORD cchSize)
{
HRESULT hr = E_INVALIDARG;
if (pIconMetrics && (sizeof(*pIconMetrics) == cchSize))
{
ZeroMemory(pIconMetrics, sizeof(*pIconMetrics));
pIconMetrics->cbSize = sizeof(*pIconMetrics);
SystemParametersInfoA(SPI_GETICONMETRICS, sizeof(*pIconMetrics), pIconMetrics, 0);
ConvertLogFontToANSI(&pSystemMetrics->schemeData.lfIconTitle, &pIconMetrics->lfFont);
hr = S_OK;
}
return hr;
}
//
// TransmitFontCharacteristics
//
// This is actually a pretty key function. See, font characteristics are
// all set together: a LOGFONT has name and style and size info all in one.
// But when you are setting all the nonclient metrics like window caption
// and menu size, you need to stretch the font sizes with it. But we give the
// user a choice of changing window sizes without "changing" the font; i.e.
// without applying a new font name and style from the theme.
//
// So we need to be able to pick apart the name and style from the size
// characteristics. And here it is.
//
// Really just a helper routine for the above function, so we don't have all
// this gunk inline five times.
//
void TransmitFontCharacteristics(PLOGFONT plfDst, PLOGFONT plfSrc, int iXmit)
{
switch (iXmit)
{
case TFC_SIZE:
plfDst->lfHeight = plfSrc->lfHeight;
plfDst->lfWidth = plfSrc->lfWidth;
break;
case TFC_STYLE:
plfDst->lfEscapement = plfSrc->lfEscapement;
plfDst->lfOrientation = plfSrc->lfOrientation;
plfDst->lfWeight = plfSrc->lfWeight;
plfDst->lfItalic = plfSrc->lfItalic;
plfDst->lfUnderline = plfSrc->lfUnderline;
plfDst->lfStrikeOut = plfSrc->lfStrikeOut;
plfDst->lfCharSet = plfSrc->lfCharSet;
plfDst->lfOutPrecision = plfSrc->lfOutPrecision;
plfDst->lfClipPrecision = plfSrc->lfClipPrecision;
plfDst->lfQuality = plfSrc->lfQuality;
plfDst->lfPitchAndFamily = plfSrc->lfPitchAndFamily;
lstrcpy(plfDst->lfFaceName, plfSrc->lfFaceName);
break;
}
}
// RGB to String to RGB utilities.
COLORREF RGBStringToColor(LPTSTR lpszRGB)
{
LPTSTR lpszCur, lpszNext;
BYTE bRed, bGreen, bBlue;
#ifdef UNICODE
CHAR szTempA[10];
#endif
// inits
lpszNext = lpszRGB;
// set up R for translation
lpszCur = lpszNext;
while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
*lpszNext = 0; lpszNext++;
// get Red
#ifdef UNICODE
wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA));
bRed = (BYTE)latoi(szTempA);
#else // !UNICODE
bRed = (BYTE)latoi(lpszCur);
#endif
// set up G for translation
lpszCur = lpszNext;
while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
*lpszNext = 0; lpszNext++;
// get Green
#ifdef UNICODE
wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA));
bGreen = (BYTE)latoi(szTempA);
#else // !UNICODE
bGreen = (BYTE)latoi(lpszCur);
#endif
// set up B for translation
lpszCur = lpszNext;
while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
*lpszNext = 0; lpszNext++;
// get Blue
#ifdef UNICODE
wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA));
bBlue = (BYTE)latoi(szTempA);
#else // !UNICODE
bBlue = (BYTE)latoi(lpszCur);
#endif
// OK, now combine them all for the big finish.....!
return(RGB(bRed, bGreen, bBlue));
}
// IsValidThemeFile
//
// Answers the question.
BOOL IsValidThemeFile(IN LPCWSTR pszTest)
{
WCHAR szValue[MAX_PATH];
BOOL fIsValid = FALSE;
if (GetPrivateProfileString(SZ_INISECTION_MASTERSELECTOR, SZ_INIKEY_THEMEMAGICTAG, SZ_EMPTY, szValue, ARRAYSIZE(szValue), pszTest))
{
fIsValid = !StrCmp(szValue, SZ_INIKEY_THEMEMAGICVALUE);
}
return fIsValid;
}
HRESULT SnapCreateTemplate(LPCWSTR pszPath, ITheme ** ppTheme)
{
HRESULT hr = E_INVALIDARG;
if (ppTheme)
{
*ppTheme = NULL;
if (pszPath)
{
// Start with a template ("Windows Classic.theme").
TCHAR szTemplate[MAX_PATH];
DeleteFile(pszPath);
StrCpyN(szTemplate, pszPath, ARRAYSIZE(szTemplate));
PathRemoveFileSpec(szTemplate);
SHCreateDirectoryEx(NULL, szTemplate, NULL);
hr = SHGetResourcePath(TRUE, szTemplate, ARRAYSIZE(szTemplate));
if (SUCCEEDED(hr))
{
PathAppend(szTemplate, TEXT("Themes\\Windows Classic.theme"));
if (CopyFile(szTemplate, pszPath, FALSE))
{
hr = CThemeFile_CreateInstance(pszPath, ppTheme);
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
}
}
return hr;
}
/*****************************************************************************\
DESCRIPTION:
This function will grab the live settings (from pPropertyBag) and put
theme into a file specified by pszPath. A pointer to the Theme will be
returned in ppTheme. If the settings cannot be obtained, they will come
from "Windows Classic.theme". This includes the Display Name, so the call
will almost always want to specify that if this function returns successfully.
PARAMETERS:
pPropertyBag: This is were the settings will be read from.
pszPath: This is the file will be saved to. It will be replaced if it exists.
ppTheme: This will be created and returned if successful.
\*****************************************************************************/
HRESULT SnapShotLiveSettingsToTheme(IPropertyBag * pPropertyBag, LPCWSTR pszPath, ITheme ** ppTheme)
{
HRESULT hr = SnapCreateTemplate(pszPath, ppTheme);
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH];
ITheme * pTheme = *ppTheme;
// 1. Save the Background
// We may fail to get the background path if the policy turns it off.
if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_BACKGROUNDSRC_PATH, szPath, ARRAYSIZE(szPath))))
{
CComBSTR bstrPath(szPath);
hr = pTheme->put_Background(bstrPath);
if (SUCCEEDED(hr))
{
DWORD dwBackgroundTile;
if (SUCCEEDED(SHPropertyBag_ReadDWORD(pPropertyBag, SZ_PBPROP_BACKGROUND_TILE, &dwBackgroundTile)))
{
enumBkgdTile nTile = BKDGT_STRECH;
switch (dwBackgroundTile)
{
case WPSTYLE_CENTER:
nTile = BKDGT_CENTER;
break;
case WPSTYLE_TILE:
nTile = BKDGT_TILE;
break;
};
hr = pTheme->put_BackgroundTile(nTile);
}
}
}
// 2. Save Screen Saver
if (SUCCEEDED(hr))
{
// This will fail with policies enabled. In that case, we just use the default value.
if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_SCREENSAVER_PATH, szPath, ARRAYSIZE(szPath))))
{
CComBSTR bstrPath(szPath);
hr = pTheme->put_ScreenSaver(bstrPath);
}
}
// 3. Save Visual Style
if (SUCCEEDED(hr))
{
// It's okay to have no visual style selected.
if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_VISUALSTYLE_PATH, szPath, ARRAYSIZE(szPath))) && szPath[0])
{
CComBSTR bstrPath(szPath);
hr = pTheme->put_VisualStyle(bstrPath);
if (SUCCEEDED(hr) &&
SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_VISUALSTYLE_COLOR, szPath, ARRAYSIZE(szPath))))
{
bstrPath = szPath;
hr = pTheme->put_VisualStyleColor(bstrPath);
if (SUCCEEDED(hr) &&
SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_VISUALSTYLE_SIZE, szPath, ARRAYSIZE(szPath))))
{
bstrPath = szPath;
hr = pTheme->put_VisualStyleSize(bstrPath);
}
}
}
}
if (SUCCEEDED(hr))
{
// 4. Save System Metrics
IPropertyBag * pPropertyBagFile;
hr = pTheme->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBagFile));
if (SUCCEEDED(hr))
{
VARIANT var = {0};
// This call will return SYSTEMMETRICS relative to the currently live DPI.
hr = pPropertyBag->Read(SZ_PBPROP_SYSTEM_METRICS, &var, NULL);
if (SUCCEEDED(hr) && (VT_BYREF == var.vt) && var.byref)
{
SYSTEMMETRICSALL * pCurrent = (SYSTEMMETRICSALL *) var.byref;
IUnknown_SetSite(pPropertyBagFile, pPropertyBag); // Set the site so they can get settings.
hr = SHPropertyBag_WriteByRef(pPropertyBagFile, SZ_PBPROP_SYSTEM_METRICS, (void *)pCurrent);
IUnknown_SetSite(pPropertyBagFile, NULL); // Break any back pointers.
}
pPropertyBagFile->Release();
}
}
// 5. Save Sounds
int nIndex;
for (nIndex = 0; nIndex < ARRAYSIZE(s_ThemeSoundsValues); nIndex++)
{
if (FAILED(HrRegGetPath(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL, szPath, ARRAYSIZE(szPath))))
{
szPath[0] = 0;
}
pTheme->SetSound((BSTR)s_ThemeSoundsValues[nIndex].pszRegKey, szPath);
}
// 6. Save Icons
for (nIndex = 0; (nIndex < ARRAYSIZE(s_Icons)); nIndex++)
{
// This can fail if the background policy is enabled.
if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, s_Icons[nIndex], szPath, ARRAYSIZE(szPath))))
{
pTheme->SetIcon((BSTR)s_Icons[nIndex], szPath);
}
}
// 7. Save Cursors
for (nIndex = 0; nIndex < ARRAYSIZE(s_pszCursorArray); nIndex++)
{
if (FAILED(HrRegGetPath(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex], szPath, ARRAYSIZE(szPath))))
{
szPath[0] = 0;
}
pTheme->SetCursor((BSTR)s_pszCursorArray[nIndex], szPath);
}
if (SUCCEEDED(HrRegGetPath(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, NULL, szPath, ARRAYSIZE(szPath))))
{
pTheme->SetCursor(SZ_INIKEY_CURSORSCHEME, szPath);
}
if (FAILED(hr))
{
// Partially written files are very bad.
DeleteFile(pszPath);
ATOMICRELEASE(*ppTheme);
}
}
return hr;
}