You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1040 lines
32 KiB
1040 lines
32 KiB
/*****************************************************************************\
|
|
FILE: stgTheme.cpp
|
|
|
|
DESCRIPTION:
|
|
This is the Autmation Object to theme manager object.
|
|
|
|
BryanSt 4/3/2000 (Bryan Starbuck)
|
|
Copyright (C) Microsoft Corp 2000-2000. All rights reserved.
|
|
\*****************************************************************************/
|
|
|
|
#include "priv.h"
|
|
|
|
extern BOOL IsUserHighContrastUser(void);
|
|
|
|
//===========================
|
|
// *** Class Internals & Helpers ***
|
|
//===========================
|
|
// lParam can be: 0 == do a case sensitive search. 1 == do a case insensitive search.
|
|
int DPA_StringCompareCB(LPVOID pvString1, LPVOID pvString2, LPARAM lParam)
|
|
{
|
|
// return < 0 for pvPidl1 before pvPidl2.
|
|
// return == 0 for pvPidl1 equals pvPidl2.
|
|
// return > 0 for pvPidl1 after pvPidl2.
|
|
int nSort = 0; // Default to equal
|
|
LPCTSTR pszToInsert = (LPCTSTR)pvString1;
|
|
LPCTSTR pszToComparePath = (LPCTSTR)pvString2;
|
|
|
|
if (pszToInsert && pszToComparePath)
|
|
{
|
|
LPCTSTR pszToCompareFileName = PathFindFileName(pszToComparePath);
|
|
|
|
if (pszToCompareFileName)
|
|
{
|
|
nSort = StrCmp(pszToInsert, pszToCompareFileName);
|
|
}
|
|
}
|
|
|
|
return nSort;
|
|
}
|
|
|
|
|
|
|
|
#define SZ_THEMES_FILTER TEXT("*.theme")
|
|
#define SZ_ALL_FILTER TEXT("*.*")
|
|
|
|
HRESULT CThemeManager::_AddThemesFromDir(LPCTSTR pszPath, BOOL fFirstLevel, int nInsertLoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WIN32_FIND_DATA findFileData;
|
|
TCHAR szSearch[MAX_PATH];
|
|
|
|
AssertMsg((nInsertLoc >= 0), TEXT("nInsertLoc should never be negative"));
|
|
StringCchCopy(szSearch, ARRAYSIZE(szSearch), pszPath);
|
|
PathAppend(szSearch, SZ_THEMES_FILTER);
|
|
|
|
HANDLE hFindFiles = FindFirstFile(szSearch, &findFileData);
|
|
if (hFindFiles && (INVALID_HANDLE_VALUE != hFindFiles))
|
|
{
|
|
while (hFindFiles && (INVALID_HANDLE_VALUE != hFindFiles))
|
|
{
|
|
if (!(FILE_ATTRIBUTE_DIRECTORY & findFileData.dwFileAttributes))
|
|
{
|
|
StringCchCopy(szSearch, ARRAYSIZE(szSearch), pszPath);
|
|
if (PathAppend(szSearch, findFileData.cFileName))
|
|
{
|
|
LPWSTR pszPath = StrDup(szSearch);
|
|
|
|
if (pszPath)
|
|
{
|
|
if (nInsertLoc)
|
|
{
|
|
if (-1 == DPA_InsertPtr(m_hdpaThemeDirs, nInsertLoc - 1, pszPath))
|
|
{
|
|
// We failed so free the memory
|
|
LocalFree(pszPath);
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
nInsertLoc++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (-1 == DPA_SortedInsertPtr(m_hdpaThemeDirs, PathFindFileName(pszPath), 0, DPA_StringCompareCB, NULL, DPAS_INSERTBEFORE, pszPath))
|
|
{
|
|
// We failed so free the memory
|
|
LocalFree(pszPath);
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!FindNextFile(hFindFiles, &findFileData))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
FindClose(hFindFiles);
|
|
}
|
|
|
|
// We only want to recurse one directory.
|
|
if (fFirstLevel)
|
|
{
|
|
StringCchCopy(szSearch, ARRAYSIZE(szSearch), pszPath);
|
|
PathAppend(szSearch, SZ_ALL_FILTER);
|
|
|
|
HANDLE hFindFiles = FindFirstFile(szSearch, &findFileData);
|
|
if (hFindFiles && (INVALID_HANDLE_VALUE != hFindFiles))
|
|
{
|
|
while (hFindFiles && (INVALID_HANDLE_VALUE != hFindFiles))
|
|
{
|
|
// We are looking for any directories. Of course we exclude "." and "..".
|
|
if ((FILE_ATTRIBUTE_DIRECTORY & findFileData.dwFileAttributes) &&
|
|
StrCmpI(findFileData.cFileName, TEXT(".")) &&
|
|
StrCmpI(findFileData.cFileName, TEXT("..")))
|
|
{
|
|
StringCchCopy(szSearch, ARRAYSIZE(szSearch), pszPath);
|
|
if (PathAppend(szSearch, findFileData.cFileName))
|
|
{
|
|
_AddThemesFromDir(szSearch, FALSE, nInsertLoc);
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!FindNextFile(hFindFiles, &findFileData))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
FindClose(hFindFiles);
|
|
}
|
|
}
|
|
|
|
// We will want to repeat this process recursively for directories. At least
|
|
// one level of recursively.
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::_InitThemeDirs(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!m_hdpaThemeDirs)
|
|
{
|
|
if (SHRegGetBoolUSValue(SZ_THEMES, SZ_REGVALUE_ENABLEPLUSTHEMES, FALSE, TRUE))
|
|
{
|
|
m_hdpaThemeDirs = DPA_Create(2);
|
|
if (m_hdpaThemeDirs)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// 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
|
|
if (SUCCEEDED(GetPlusThemeDir(szPath, ARRAYSIZE(szPath))))
|
|
{
|
|
_AddThemesFromDir(szPath, TRUE, 0);
|
|
}
|
|
|
|
hr = SHGetResourcePath(TRUE, szPath, ARRAYSIZE(szPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_AddThemesFromDir(szPath, TRUE, 1);
|
|
}
|
|
|
|
if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_APPDATA, TRUE))
|
|
{
|
|
if (PathAppend(szPath, TEXT("Microsoft\\Windows\\Themes")))
|
|
{
|
|
_AddThemesFromDir(szPath, TRUE, 1);
|
|
}
|
|
}
|
|
|
|
if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_PERSONAL, TRUE))
|
|
{
|
|
_AddThemesFromDir(szPath, TRUE, 1);
|
|
}
|
|
|
|
// Enum any paths 3rd parties add to the registry
|
|
HKEY hKey;
|
|
if (SUCCEEDED(HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_THEMES_THEMEDIRS, 0, KEY_READ, &hKey)))
|
|
{
|
|
for (int nDirIndex = 0; SUCCEEDED(hr); nDirIndex++)
|
|
{
|
|
TCHAR szValueName[MAXIMUM_VALUE_NAME_LENGTH];
|
|
DWORD cchSize = ARRAYSIZE(szValueName);
|
|
DWORD dwType;
|
|
DWORD cbSize = sizeof(szPath);
|
|
|
|
hr = HrRegEnumValue(hKey, nDirIndex, szValueName, &cchSize, 0, &dwType, (LPBYTE)szPath, &cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szFinalPath[MAX_PATH];
|
|
|
|
if (0 == SHExpandEnvironmentStringsForUserW(NULL, szPath, szFinalPath, ARRAYSIZE(szFinalPath)))
|
|
{
|
|
StringCchCopy(szFinalPath, ARRAYSIZE(szFinalPath), szPath);
|
|
}
|
|
_AddThemesFromDir(szFinalPath, TRUE, 1);
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#define SZ_THEMES TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Themes")
|
|
#define SZ_REGVALUE_REQUIRESIGNING TEXT("Require VisualStyle Signing")
|
|
|
|
HRESULT CThemeManager::_EnumSkinCB(THEMECALLBACK tcbType, LPCWSTR pszFileName, OPTIONAL LPCWSTR pszDisplayName, OPTIONAL LPCWSTR pszToolTip, OPTIONAL int iIndex)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// only signed theme files will be enumerated and passed to this function
|
|
|
|
if (pszFileName)
|
|
{
|
|
LPWSTR pszPath = StrDup(pszFileName);
|
|
|
|
AssertMsg((NULL != m_hdpaSkinDirs), TEXT("We should never hit this. We will leak. -BryanSt"));
|
|
if (pszPath)
|
|
{
|
|
if (-1 == DPA_AppendPtr(m_hdpaSkinDirs, pszPath))
|
|
{
|
|
// We failed so free the memory
|
|
LocalFree(pszPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL CThemeManager::EnumSkinCB(THEMECALLBACK tcbType, LPCWSTR pszFileName, OPTIONAL LPCWSTR pszDisplayName,
|
|
OPTIONAL LPCWSTR pszToolTip, OPTIONAL int iIndex, LPARAM lParam)
|
|
{
|
|
CThemeManager * pThis = (CThemeManager *) lParam;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (pThis)
|
|
{
|
|
hr = pThis->_EnumSkinCB(tcbType, pszFileName, pszDisplayName, pszToolTip, iIndex);
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::_EnumSkinsFromKey(HKEY hKey)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
for (int nDirIndex = 0; SUCCEEDED(hr); nDirIndex++)
|
|
{
|
|
TCHAR szValueName[MAXIMUM_VALUE_NAME_LENGTH];
|
|
DWORD cchSize = ARRAYSIZE(szValueName);
|
|
DWORD dwType;
|
|
DWORD cbSize = sizeof(szPath);
|
|
|
|
hr = HrRegEnumValue(hKey, nDirIndex, szValueName, &cchSize, 0, &dwType, (LPBYTE)szPath, &cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ExpandResourceDir(szPath, ARRAYSIZE(szPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = EnumThemes(szPath, CThemeManager::EnumSkinCB, (LPARAM) this);
|
|
LogStatus("EnumThemes(path=\"%ls\") returned %#08lx in CThemeManager::_EnumSkinsFromKey.\r\n", szPath, hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CThemeManager::_InitSkinDirs(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!m_hdpaSkinDirs)
|
|
{
|
|
m_hdpaSkinDirs = DPA_Create(2);
|
|
if (m_hdpaSkinDirs)
|
|
{
|
|
// We only want to add skins if they are supported. They are only supported if the VisualStyle manager
|
|
// can run. We will know that if QueryThemeServices() returns QTS_GLOBALAVAILABLE.
|
|
BOOL fVisualStylesSupported = (QueryThemeServicesWrap() & QTS_AVAILABLE);
|
|
LogStatus("QueryThemeServices() returned %hs in CThemeManager::_InitSkinDirs\r\n", (fVisualStylesSupported ? "TRUE" : "FALSE"));
|
|
|
|
// Note that the VisualStyle's Manager API only works when explorer is running. This means we will
|
|
// lack functionality, but it is their limitation, not ours.
|
|
if (fVisualStylesSupported)
|
|
{
|
|
HKEY hNewKey;
|
|
|
|
if (SUCCEEDED(HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_THEMES_MSTHEMEDIRS, 0, KEY_READ, &hNewKey)))
|
|
{
|
|
hr = _EnumSkinsFromKey(hNewKey);
|
|
RegCloseKey(hNewKey);
|
|
}
|
|
|
|
if (SUCCEEDED(HrRegOpenKeyEx(HKEY_CURRENT_USER, SZ_THEMES_MSTHEMEDIRS, 0, KEY_READ, &hNewKey)))
|
|
{
|
|
hr = _EnumSkinsFromKey(hNewKey);
|
|
RegCloseKey(hNewKey);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CThemeManager::_InitSelectedThemeFile(void)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (!_pszSelectTheme)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
DWORD dwError = SHRegGetPathW(HKEY_CURRENT_USER, SZ_REGKEY_CURRENT_THEME, NULL, szPath, 0);
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
|
|
// Is this the "<UserName>'s Custom Theme" item?
|
|
// Or did it fail? If it failed, then no theme is selected.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Str_SetPtr(&_pszSelectTheme, szPath);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::_SetSelectedThemeEntree(LPCWSTR pszPath)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
Str_SetPtr(&_pszSelectTheme, pszPath);
|
|
if (pszPath)
|
|
{
|
|
hr = HrRegSetPath(HKEY_CURRENT_USER, SZ_REGKEY_CURRENT_THEME, NULL, TRUE, pszPath);
|
|
}
|
|
else
|
|
{
|
|
hr = HrRegDeleteValue(HKEY_CURRENT_USER, SZ_REGKEY_CURRENT_THEME, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================
|
|
// *** IThemeManager Interface ***
|
|
//===========================
|
|
HRESULT CThemeManager::get_SelectedTheme(OUT ITheme ** ppTheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppTheme)
|
|
{
|
|
*ppTheme = NULL;
|
|
|
|
// In the future, we may want to call into PPID_Theme's IPropertyBag
|
|
// to find the current visual style. This will factor in "(Modified)"
|
|
// themes.
|
|
hr = _InitSelectedThemeFile();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CThemeFile_CreateInstance(_pszSelectTheme, ppTheme);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
DESCRIPTION:
|
|
Don't forget that this change is not applied until ::ApplyNow() is
|
|
called.
|
|
\*****************************************************************************/
|
|
HRESULT CThemeManager::put_SelectedTheme(IN ITheme * pTheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
CComBSTR bstrPath;
|
|
|
|
if (pTheme)
|
|
{
|
|
// Persist the filename to the registry.
|
|
hr = pTheme->GetPath(VARIANT_TRUE, &bstrPath);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPropertyBag * pPropertyBag;
|
|
|
|
hr = _GetPropertyBagByCLSID(&PPID_Theme, &pPropertyBag);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHPropertyBag_WriteStr(pPropertyBag, SZ_PBPROP_THEME_LOADTHEME, bstrPath);
|
|
pPropertyBag->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::get_WebviewCSS(OUT BSTR * pbstrPath)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pbstrPath)
|
|
{
|
|
*pbstrPath = NULL;
|
|
|
|
IThemeScheme * pThemeScheme;
|
|
|
|
hr = _saveGetSelectedScheme(&pThemeScheme);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IThemeStyle * pThemeStyle;
|
|
|
|
hr = pThemeScheme->get_SelectedStyle(&pThemeStyle);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IThemeSize * pThemeSize;
|
|
|
|
hr = pThemeStyle->get_SelectedSize(&pThemeSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pThemeSize->get_WebviewCSS(pbstrPath);
|
|
|
|
pThemeSize->Release();
|
|
}
|
|
|
|
pThemeStyle->Release();
|
|
}
|
|
|
|
pThemeScheme->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::get_length(OUT long * pnLength)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pnLength)
|
|
{
|
|
*pnLength = 0;
|
|
|
|
hr = _InitThemeDirs();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_hdpaThemeDirs)
|
|
{
|
|
*pnLength += DPA_GetPtrCount(m_hdpaThemeDirs);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::get_item(IN VARIANT varIndex, OUT ITheme ** ppTheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppTheme)
|
|
{
|
|
*ppTheme = NULL;
|
|
|
|
long nCount = 0;
|
|
|
|
hr = E_INVALIDARG;
|
|
get_length(&nCount);
|
|
|
|
// This is sortof gross, but if we are passed a pointer to another variant, simply
|
|
// update our copy here...
|
|
if (varIndex.vt == (VT_BYREF | VT_VARIANT) && varIndex.pvarVal)
|
|
varIndex = *(varIndex.pvarVal);
|
|
|
|
switch (varIndex.vt)
|
|
{
|
|
case VT_I2:
|
|
varIndex.lVal = (long)varIndex.iVal;
|
|
// And fall through...
|
|
|
|
case VT_I4:
|
|
if ((varIndex.lVal >= 0) && (varIndex.lVal < nCount))
|
|
{
|
|
if (m_hdpaThemeDirs)
|
|
{
|
|
LPWSTR pszFilename = (LPWSTR) DPA_GetPtr(m_hdpaThemeDirs, varIndex.lVal);
|
|
|
|
if (pszFilename)
|
|
{
|
|
hr = CThemeFile_CreateInstance(pszFilename, ppTheme);
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertMsg(0, TEXT("This should have been initiailized by get_Length(). -BryanSt"));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
break;
|
|
case VT_BSTR:
|
|
if (varIndex.bstrVal)
|
|
{
|
|
hr = CThemeFile_CreateInstance(varIndex.bstrVal, ppTheme);
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::get_SelectedScheme(OUT IThemeScheme ** ppThemeScheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppThemeScheme)
|
|
{
|
|
*ppThemeScheme = NULL;
|
|
|
|
if (!_pThemeSchemeSelected)
|
|
{
|
|
hr = _saveGetSelectedScheme(&_pThemeSchemeSelected);
|
|
}
|
|
|
|
if (_pThemeSchemeSelected)
|
|
{
|
|
IUnknown_Set((IUnknown **) ppThemeScheme, _pThemeSchemeSelected);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::_saveGetSelectedScheme(OUT IThemeScheme ** ppThemeScheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppThemeScheme)
|
|
{
|
|
*ppThemeScheme = NULL;
|
|
BOOL fIsThemeActive = IsThemeActive();
|
|
|
|
LogStatus("IsThemeActive() returned %hs in CThemeManager::_saveGetSelectedScheme.\r\n", (fIsThemeActive ? "TRUE" : "FALSE"));
|
|
|
|
// The selected Scheme can either be a legacy "Appearance Scheme" or
|
|
// a selected ".msstyles" (skin) file.
|
|
if (fIsThemeActive)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
hr = GetCurrentThemeName(szPath, ARRAYSIZE(szPath), NULL, 0, NULL, 0);
|
|
LogStatus("GetCurrentThemeName(path=\"%ls\", color=\"%ls\", size=\"%ls\") returned %#08lx in CThemeManager::_saveGetSelectedScheme.\r\n", szPath, TEXT("NULL"), TEXT("NULL"), hr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CSkinScheme_CreateInstance(szPath, ppThemeScheme);
|
|
}
|
|
|
|
// Currently, we create this object and get the size
|
|
// in order to force an upgrade in case it's neccessary.
|
|
IThemeScheme * pThemeSchemeTemp;
|
|
if (SUCCEEDED(CAppearanceScheme_CreateInstance(NULL, IID_PPV_ARG(IThemeScheme, &pThemeSchemeTemp))))
|
|
{
|
|
long nLength;
|
|
|
|
pThemeSchemeTemp->get_length(&nLength);
|
|
pThemeSchemeTemp->Release();
|
|
}
|
|
}
|
|
|
|
// We want to get the Appearance scheme if no visual style is selected (i.e. IsThemeActive() returns FALSE).
|
|
// However, if uxtheme gets confused, IsThemeActive() will return TRUE but GetCurrentThemeName() will fail.
|
|
// in that case, we want to also fallback to the classic visual style so the UI is usable.
|
|
if (FAILED(hr))
|
|
{
|
|
// The "Control Panel\\Appearance","Current" key will indicate the selected
|
|
// Appearance Scheme.
|
|
hr = CAppearanceScheme_CreateInstance(NULL, IID_PPV_ARG(IThemeScheme, ppThemeScheme));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
DESCRIPTION:
|
|
Don't forget that this change is not applied until ::ApplyNow() is
|
|
called.
|
|
\*****************************************************************************/
|
|
HRESULT CThemeManager::put_SelectedScheme(IN IThemeScheme * pThemeScheme)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pThemeScheme)
|
|
{
|
|
CComBSTR bstrPath;
|
|
IThemeStyle * pThemeStyle;
|
|
|
|
IUnknown_Set((IUnknown **) &_pThemeSchemeSelected, pThemeScheme);
|
|
|
|
pThemeScheme->get_Path(&bstrPath); // It's fine if it returns NULL or empty str.
|
|
hr = pThemeScheme->get_SelectedStyle(&pThemeStyle);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComBSTR bstrStyle;
|
|
|
|
hr = pThemeStyle->get_Name(&bstrStyle);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IThemeSize * pThemeSize;
|
|
|
|
hr = pThemeStyle->get_SelectedSize(&pThemeSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComBSTR bstrSize;
|
|
|
|
hr = pThemeSize->get_Name(&bstrSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT variant;
|
|
|
|
variant.vt = VT_BSTR;
|
|
variant.bstrVal = bstrPath;
|
|
hr = Write(SZ_PBPROP_VISUALSTYLE_PATH, &variant);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
variant.bstrVal = bstrStyle;
|
|
hr = Write(SZ_PBPROP_VISUALSTYLE_COLOR, &variant);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
variant.bstrVal = bstrSize;
|
|
hr = Write(SZ_PBPROP_VISUALSTYLE_SIZE, &variant);
|
|
}
|
|
}
|
|
}
|
|
|
|
pThemeSize->Release();
|
|
}
|
|
}
|
|
|
|
pThemeStyle->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::get_schemeLength(OUT long * pnLength)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pnLength)
|
|
{
|
|
*pnLength = 1;
|
|
|
|
hr = _InitSkinDirs();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_hdpaSkinDirs)
|
|
{
|
|
*pnLength += DPA_GetPtrCount(m_hdpaSkinDirs);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::get_schemeItem(IN VARIANT varIndex, OUT IThemeScheme ** ppThemeScheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (ppThemeScheme)
|
|
{
|
|
long nCount = 0;
|
|
|
|
hr = E_INVALIDARG;
|
|
get_schemeLength(&nCount);
|
|
*ppThemeScheme = NULL;
|
|
|
|
// This is sortof gross, but if we are passed a pointer to another variant, simply
|
|
// update our copy here...
|
|
if (varIndex.vt == (VT_BYREF | VT_VARIANT) && varIndex.pvarVal)
|
|
varIndex = *(varIndex.pvarVal);
|
|
|
|
switch (varIndex.vt)
|
|
{
|
|
case VT_I2:
|
|
varIndex.lVal = (long)varIndex.iVal;
|
|
// And fall through...
|
|
|
|
case VT_I4:
|
|
if ((varIndex.lVal >= 0) && (varIndex.lVal < nCount))
|
|
{
|
|
if (0 == varIndex.lVal)
|
|
{
|
|
// 0 is the Appearance scheme, which means there isn't a skin.
|
|
// This is the same as the legacy Appeanance tab.
|
|
hr = CAppearanceScheme_CreateInstance(NULL, IID_PPV_ARG(IThemeScheme, ppThemeScheme));
|
|
}
|
|
else
|
|
{
|
|
if (m_hdpaSkinDirs)
|
|
{
|
|
LPWSTR pszFilename = (LPWSTR) DPA_GetPtr(m_hdpaSkinDirs, varIndex.lVal-1);
|
|
|
|
if (pszFilename)
|
|
{
|
|
hr = CSkinScheme_CreateInstance(pszFilename, ppThemeScheme);
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertMsg(0, TEXT("This should have been initiailized by get_schemeLength(). -BryanSt"));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case VT_BSTR:
|
|
if (varIndex.bstrVal && varIndex.bstrVal[0] &&
|
|
!StrCmpI(PathFindExtension(varIndex.bstrVal), TEXT(".msstyles")))
|
|
{
|
|
hr = CSkinScheme_CreateInstance(varIndex.bstrVal, ppThemeScheme);
|
|
}
|
|
else
|
|
{
|
|
hr = CAppearanceScheme_CreateInstance(NULL, IID_PPV_ARG(IThemeScheme, ppThemeScheme));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::GetSelectedSchemeProperty(IN BSTR bstrName, OUT BSTR * pbstrValue)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (bstrName && pbstrValue)
|
|
{
|
|
*pbstrValue = NULL;
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
TCHAR szColor[MAX_PATH];
|
|
TCHAR szSize[MAX_PATH];
|
|
BOOL fIsThemeActive = IsThemeActive();
|
|
|
|
LogStatus("IsThemeActive() returned %hs in CThemeManager::GetSelectedSchemeProperty.\r\n", (fIsThemeActive ? "TRUE" : "FALSE"));
|
|
|
|
|
|
szPath[0] = 0;
|
|
szColor[0] = 0;
|
|
szSize[0] = 0;
|
|
if (fIsThemeActive)
|
|
{
|
|
hr = GetCurrentThemeName(szPath, ARRAYSIZE(szPath), szColor, ARRAYSIZE(szColor),
|
|
szSize, ARRAYSIZE(szSize));
|
|
LogStatus("GetCurrentThemeName() returned %#08lx in CThemeManager::GetSelectedSchemeProperty.\r\n", hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!StrCmpI(bstrName, SZ_CSP_PATH))
|
|
{
|
|
PathRemoveFileSpec(szPath);
|
|
hr = HrSysAllocString(szPath, pbstrValue);
|
|
}
|
|
else if (!StrCmpI(bstrName, SZ_CSP_FILE))
|
|
{
|
|
hr = HrSysAllocString(szPath, pbstrValue);
|
|
}
|
|
else if (!StrCmpI(bstrName, SZ_CSP_DISPLAYNAME))
|
|
{
|
|
if (szPath[0])
|
|
{
|
|
hr = GetThemeDocumentationProperty(szPath, SZ_THDOCPROP_DISPLAYNAME, szPath, ARRAYSIZE(szPath));
|
|
LogStatus("GetThemeDocumentationProperty() returned %#08lx in CThemeManager::GetSelectedSchemeProperty.\r\n", hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = HrSysAllocString(szPath, pbstrValue);
|
|
}
|
|
}
|
|
else if (!StrCmpI(bstrName, SZ_CSP_CANONICALNAME))
|
|
{
|
|
if (szPath[0])
|
|
{
|
|
hr = GetThemeDocumentationProperty(szPath, SZ_THDOCPROP_CANONICALNAME, szPath, ARRAYSIZE(szPath));
|
|
LogStatus("GetThemeDocumentationProperty() returned %#08lx in CThemeManager::GetSelectedSchemeProperty.\r\n", hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = HrSysAllocString(szPath, pbstrValue);
|
|
}
|
|
}
|
|
else if (!StrCmpI(bstrName, SZ_CSP_COLOR))
|
|
{
|
|
hr = HrSysAllocString(szColor, pbstrValue);
|
|
}
|
|
else if (!StrCmpI(bstrName, SZ_CSP_SIZE))
|
|
{
|
|
hr = HrSysAllocString(szSize, pbstrValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::GetSpecialTheme(IN BSTR bstrName, OUT ITheme ** ppTheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (bstrName && ppTheme)
|
|
{
|
|
*ppTheme = NULL;
|
|
|
|
if (!StrCmpI(SZ_STDEFAULTTHEME, bstrName))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
hr = HrRegGetPath(HKEY_CURRENT_USER, SZ_THEMES, SZ_REGVALUE_INSTALL_THEME, szPath, ARRAYSIZE(szPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ExpandResourceDir(szPath, ARRAYSIZE(szPath));
|
|
hr = CThemeFile_CreateInstance(szPath, ppTheme);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::SetSpecialTheme(IN BSTR bstrName, IN ITheme * pTheme)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (bstrName)
|
|
{
|
|
if (!StrCmpI(SZ_STDEFAULTTHEME, bstrName))
|
|
{
|
|
CComBSTR bstrPath;
|
|
|
|
if (pTheme)
|
|
{
|
|
hr = pTheme->GetPath(VARIANT_TRUE, &bstrPath);
|
|
}
|
|
else
|
|
{
|
|
bstrPath = L""; // This means use "Windows Classic".
|
|
}
|
|
|
|
if (bstrPath)
|
|
{
|
|
hr = HrRegSetPath(HKEY_CURRENT_USER, SZ_THEMES, SZ_REGVALUE_INSTALL_THEME, TRUE, bstrPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::GetSpecialScheme(IN BSTR bstrName, OUT IThemeScheme ** ppThemeScheme, OUT IThemeStyle ** ppThemeStyle, OUT IThemeSize ** ppThemeSize)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (bstrName && ppThemeScheme && ppThemeStyle && ppThemeSize)
|
|
{
|
|
*ppThemeScheme = NULL;
|
|
*ppThemeStyle = NULL;
|
|
*ppThemeSize = NULL;
|
|
|
|
TCHAR szVisualStylePath[MAX_PATH];
|
|
DWORD dwType;
|
|
DWORD cbSize = sizeof(szVisualStylePath);
|
|
|
|
// Is a SetVisualStyle policy is set, don't honor this call
|
|
if (ERROR_SUCCESS == SHRegGetUSValue(SZ_REGKEY_POLICIES_SYSTEM, SZ_REGVALUE_POLICY_SETVISUALSTYLE, &dwType, (void *) szVisualStylePath, &cbSize, FALSE, NULL, 0)
|
|
|| IsUserHighContrastUser())
|
|
{
|
|
hr = E_ACCESSDENIED; // Don't mess with visual styles when SetVisualStyle is enforced or high contrast is on
|
|
}
|
|
else
|
|
{
|
|
if (!StrCmpI(SZ_SSDEFAULVISUALSTYLEON, bstrName) ||
|
|
!StrCmpI(SZ_SSDEFAULVISUALSTYLEOFF, bstrName))
|
|
{
|
|
TCHAR szRegKey[MAX_PATH];
|
|
TCHAR szVisualStyle[MAX_PATH];
|
|
|
|
StringCchPrintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("%s\\%s"), SZ_THEMES, bstrName);
|
|
hr = HrRegGetPath(HKEY_CURRENT_USER, szRegKey, SZ_REGVALUE_INSTALL_VISUALSTYLE, szVisualStyle, ARRAYSIZE(szVisualStyle));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szColorStyle[MAX_PATH];
|
|
|
|
ExpandResourceDir(szVisualStyle, ARRAYSIZE(szVisualStyle));
|
|
hr = HrRegGetValueString(HKEY_CURRENT_USER, szRegKey, SZ_REGVALUE_INSTALL_VSCOLOR, szColorStyle, ARRAYSIZE(szColorStyle));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szSize[MAX_PATH];
|
|
|
|
hr = HrRegGetValueString(HKEY_CURRENT_USER, szRegKey, SZ_REGVALUE_INSTALL_VSSIZE, szSize, ARRAYSIZE(szSize));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComVariant varIndex(szVisualStyle);
|
|
|
|
hr = get_schemeItem(varIndex, ppThemeScheme);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComVariant varColorIndex(szColorStyle);
|
|
|
|
hr = (*ppThemeScheme)->get_item(varColorIndex, ppThemeStyle);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComVariant varSizeIndex(szSize);
|
|
|
|
hr = (*ppThemeStyle)->get_item(varSizeIndex, ppThemeSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// Return consistent results.
|
|
ATOMICRELEASE(*ppThemeScheme);
|
|
ATOMICRELEASE(*ppThemeStyle);
|
|
ATOMICRELEASE(*ppThemeSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::SetSpecialScheme(IN BSTR bstrName, IN IThemeScheme * pThemeScheme, IThemeStyle * pThemeStyle, IThemeSize * pThemeSize)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
HRESULT CThemeManager::ApplyNow(void)
|
|
{
|
|
return ApplyPressed(TUIAP_NONE | TUIAP_WAITFORAPPLY);
|
|
}
|