Leaked source code of windows server 2003
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.

1368 lines
49 KiB

  1. /*****************************************************************************\
  2. FILE: ThemeFile.cpp
  3. DESCRIPTION:
  4. This is the Autmation Object to theme scheme object.
  5. BryanSt 4/3/2000 (Bryan Starbuck)
  6. Copyright (C) Microsoft Corp 2000-2000. All rights reserved.
  7. \*****************************************************************************/
  8. #include "priv.h"
  9. #include <atlbase.h>
  10. #include <mmsystem.h>
  11. #include "ThSettingsPg.h"
  12. #include "ThemeFile.h"
  13. LPCTSTR s_pszCursorArray[SIZE_CURSOR_ARRAY] =
  14. { // different cursors
  15. TEXT("Arrow"),
  16. TEXT("Help"),
  17. TEXT("AppStarting"),
  18. TEXT("Wait"),
  19. TEXT("NWPen"),
  20. TEXT("No"),
  21. TEXT("SizeNS"),
  22. TEXT("SizeWE"),
  23. TEXT("Crosshair"),
  24. TEXT("IBeam"),
  25. TEXT("SizeNWSE"),
  26. TEXT("SizeNESW"),
  27. TEXT("SizeAll"),
  28. TEXT("UpArrow"),
  29. TEXT("Link"),
  30. };
  31. // This is a list of string pairs. The first string in the pair is the RegKey and the second is the default sound.
  32. // NULL means to delete the key. If you use an environment string other than "%SystemRoot%", you need to
  33. // update _ApplySounds();
  34. #define SOUND_DEFAULT (UINT)-1
  35. THEME_FALLBACK_VALUES s_ThemeSoundsValues[SIZE_SOUNDS_ARRAY] =
  36. {
  37. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\.Default\\.Current"), SOUND_DEFAULT},
  38. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\AppGPFault\\.Current"), SOUND_DEFAULT},
  39. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Close\\.Current"), SOUND_DEFAULT},
  40. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\DeviceConnect\\.Current"), SOUND_DEFAULT},
  41. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\DeviceDisconnect\\.Current"), SOUND_DEFAULT},
  42. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\DeviceFail\\.Current"), SOUND_DEFAULT},
  43. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\LowBatteryAlarm\\.Current"), SOUND_DEFAULT},
  44. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\MailBeep\\.Current"), SOUND_DEFAULT},
  45. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Maximize\\.Current"), SOUND_DEFAULT},
  46. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\MenuCommand\\.Current"), SOUND_DEFAULT},
  47. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\MenuPopup\\.Current"), SOUND_DEFAULT},
  48. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Minimize\\.Current"), SOUND_DEFAULT},
  49. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Open\\.Current"), SOUND_DEFAULT},
  50. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\PrintComplete\\.Current"), SOUND_DEFAULT},
  51. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\RestoreDown\\.Current"), SOUND_DEFAULT},
  52. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\RestoreUp\\.Current"), SOUND_DEFAULT},
  53. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\RingIn\\.Current"), SOUND_DEFAULT},
  54. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Ringout\\.Current"), SOUND_DEFAULT},
  55. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemAsterisk\\.Current"), SOUND_DEFAULT},
  56. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemExclamation\\.Current"), SOUND_DEFAULT},
  57. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemExit\\.Current"), SOUND_DEFAULT},
  58. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemHand\\.Current"), SOUND_DEFAULT},
  59. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemNotification\\.Current"), SOUND_DEFAULT},
  60. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemQuestion\\.Current"), SOUND_DEFAULT},
  61. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemStart\\.Current"), SOUND_DEFAULT},
  62. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemStartMenu\\.Current"), SOUND_DEFAULT},
  63. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\WindowsLogoff\\.Current"), SOUND_DEFAULT},
  64. {TEXT("AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current"), SOUND_DEFAULT},
  65. {TEXT("AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current"), SOUND_DEFAULT},
  66. {TEXT("AppEvents\\Schemes\\Apps\\Explorer\\Navigating\\.Current"), SOUND_DEFAULT},
  67. };
  68. //===========================
  69. // *** Class Internals & Helpers ***
  70. //===========================
  71. HRESULT CThemeFile::_GetCustomFont(LPCTSTR pszFontName, LOGFONT * pLogFont)
  72. {
  73. HRESULT hr = S_OK;
  74. TCHAR szFont[MAX_PATH];
  75. if (GetPrivateProfileString(SZ_INISECTION_METRICS, pszFontName, SZ_EMPTY, szFont, ARRAYSIZE(szFont), m_pszThemeFile))
  76. {
  77. if (TEXT('@') == szFont[0]) // Is the string indirect for MUI?
  78. {
  79. TCHAR szTemp[MAX_PATH];
  80. if (SUCCEEDED(SHLoadIndirectString(szFont, szTemp, ARRAYSIZE(szTemp), NULL)))
  81. {
  82. StringCchCopy(szFont, ARRAYSIZE(szFont), szTemp);
  83. }
  84. }
  85. if (TEXT('{') == szFont[0])
  86. {
  87. LPTSTR pszStart = &szFont[1];
  88. BOOL fHasMore = TRUE;
  89. LPTSTR pszEnd = StrChr(pszStart, TEXT(','));
  90. if (!pszEnd)
  91. {
  92. pszEnd = StrChr(pszStart, TEXT('}'));
  93. fHasMore = FALSE;
  94. }
  95. if (pszEnd)
  96. {
  97. pszEnd[0] = 0; // Terminate Name.
  98. StringCchCopy(pLogFont->lfFaceName, ARRAYSIZE(pLogFont->lfFaceName), pszStart);
  99. if (fHasMore)
  100. {
  101. pszStart = &pszEnd[1];
  102. pszEnd = StrStr(pszStart, TEXT("pt"));
  103. if (pszEnd)
  104. {
  105. TCHAR szTemp[MAX_PATH];
  106. pszEnd[0] = 0; // Terminate Name.
  107. pszEnd += 2; // Skip past the "pt"
  108. StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszStart);
  109. PathRemoveBlanks(szTemp);
  110. pLogFont->lfHeight = -MulDiv(StrToInt(szTemp), DPI_PERSISTED, 72); // Map pt size to lfHeight
  111. pLogFont->lfHeight = min(-3, pLogFont->lfHeight); // Make sure the font doesn't get too small
  112. pLogFont->lfHeight = max(-100, pLogFont->lfHeight); // Make sure the font doesn't get too large
  113. if (TEXT(',') == pszEnd[0])
  114. {
  115. pszStart = &pszEnd[1];
  116. pszEnd = StrChr(pszStart, TEXT('}'));
  117. if (pszEnd)
  118. {
  119. pszEnd[0] = 0; // Terminate Name.
  120. pLogFont->lfCharSet = (BYTE) StrToInt(pszStart);
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. }
  128. return hr;
  129. }
  130. HRESULT CThemeFile::_LoadCustomFonts(void)
  131. {
  132. _GetCustomFont(TEXT("CaptionFont"), &(m_systemMetrics.schemeData.ncm.lfCaptionFont));
  133. _GetCustomFont(TEXT("SmCaptionFont"), &(m_systemMetrics.schemeData.ncm.lfSmCaptionFont));
  134. _GetCustomFont(TEXT("MenuFont"), &(m_systemMetrics.schemeData.ncm.lfMenuFont));
  135. _GetCustomFont(TEXT("StatusFont"), &(m_systemMetrics.schemeData.ncm.lfStatusFont));
  136. _GetCustomFont(TEXT("MessageFont"), &(m_systemMetrics.schemeData.ncm.lfMessageFont));
  137. _GetCustomFont(TEXT("IconFont"), &(m_systemMetrics.schemeData.lfIconTitle));
  138. return S_OK;
  139. }
  140. // Load the settings in memory
  141. HRESULT CThemeFile::_LoadLiveSettings(int * pnDPI)
  142. {
  143. HRESULT hr = S_OK;
  144. if (m_pszThemeFile)
  145. {
  146. if (pnDPI)
  147. {
  148. *pnDPI = DPI_PERSISTED;
  149. }
  150. // Get property bag with default settings.
  151. if (_punkSite)
  152. {
  153. IPropertyBag * pPropertyBag;
  154. hr = _punkSite->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBag));
  155. if (SUCCEEDED(hr))
  156. {
  157. hr = SHPropertyBag_ReadByRef(pPropertyBag, SZ_PBPROP_SYSTEM_METRICS, (void *)&m_systemMetrics, sizeof(m_systemMetrics));
  158. if (pnDPI && FAILED(SHPropertyBag_ReadInt(pPropertyBag, SZ_PBPROP_DPI_MODIFIED_VALUE, pnDPI)))
  159. {
  160. *pnDPI = DPI_PERSISTED; // Default to the default DPI.
  161. }
  162. pPropertyBag->Release();
  163. }
  164. }
  165. }
  166. return hr;
  167. }
  168. // Load the settings in the .theme file.
  169. HRESULT CThemeFile::_LoadSettings(void)
  170. {
  171. int nCurrentDPI = DPI_PERSISTED;
  172. HRESULT hr = _LoadLiveSettings(&nCurrentDPI);
  173. if (m_pszThemeFile)
  174. {
  175. BOOL fFontsFilter = _IsFiltered(THEMEFILTER_SMSTYLES);
  176. TCHAR szIconMetrics[2048];
  177. if (m_systemMetrics.nIcon && m_systemMetrics.nSmallIcon)
  178. {
  179. ////////////////////////////////////////////
  180. // Get the icon Metrics
  181. // if we somehow come up with no icon metrics in the theme, just
  182. // PUNT and leave cur settings
  183. if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_ICONMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile))
  184. { // if something there to set
  185. ICONMETRICSA iconMetricsA;
  186. // translate stored data string to ICONMETRICS bytes
  187. if ((sizeof(iconMetricsA) == WriteBytesToBuffer(szIconMetrics, (void *)&iconMetricsA, sizeof(iconMetricsA))) && // char str read from and binary bytes
  188. (sizeof(iconMetricsA) == iconMetricsA.cbSize))
  189. {
  190. // ICONMETRICS are stored in ANSI format in the Theme file so if
  191. // we're living in a UNICODE world we need to convert from ANSI
  192. // to UNICODE
  193. ICONMETRICSW iconMetricsW;
  194. if (!fFontsFilter)
  195. {
  196. ConvertIconMetricsToWIDE(&iconMetricsA, &iconMetricsW);
  197. m_systemMetrics.schemeData.lfIconTitle = iconMetricsW.lfFont;
  198. }
  199. }
  200. }
  201. ////////////////////////////////////////////
  202. // Get Non-Client Metrics
  203. // if we somehow come up with no icon metrics in the theme, just
  204. // PUNT and leave cur settings
  205. if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_NONCLIENTMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile))
  206. {
  207. BOOL fBordersFilter = _IsFiltered(THEMEFILTER_SMSIZES);
  208. NONCLIENTMETRICSA nonClientMetrics;
  209. // if something there to set
  210. // translate stored data string to ICONMETRICS bytes
  211. if ((sizeof(nonClientMetrics) == WriteBytesToBuffer(szIconMetrics, (void *)&nonClientMetrics, sizeof(nonClientMetrics))) && // char str read from and binary bytes
  212. (sizeof(nonClientMetrics) == nonClientMetrics.cbSize))
  213. {
  214. // ICONMETRICS are stored in ANSI format in the Theme file so if
  215. // we're living in a UNICODE world we need to convert from ANSI
  216. // to UNICODE
  217. NONCLIENTMETRICSW nonClientMetricsW = {0};
  218. ConvertNCMetricsToWIDE(&nonClientMetrics, &nonClientMetricsW);
  219. nonClientMetricsW.cbSize = sizeof(nonClientMetricsW); // paranoid
  220. // what we reset if the user checks Font names and styles
  221. if (!fFontsFilter)
  222. {
  223. // only (some) font information
  224. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfCaptionFont), &(nonClientMetricsW.lfCaptionFont), TFC_STYLE);
  225. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfSmCaptionFont), &(nonClientMetricsW.lfSmCaptionFont), TFC_STYLE);
  226. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMenuFont), &(nonClientMetricsW.lfMenuFont), TFC_STYLE);
  227. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfStatusFont), &(nonClientMetricsW.lfStatusFont), TFC_STYLE);
  228. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMessageFont), &(nonClientMetricsW.lfMessageFont), TFC_STYLE);
  229. }
  230. // what we reset if the user checks Font and window si&zes
  231. if (!fBordersFilter)
  232. {
  233. // fonts
  234. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfCaptionFont), &(nonClientMetricsW.lfCaptionFont), TFC_SIZE);
  235. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfSmCaptionFont), &(nonClientMetricsW.lfSmCaptionFont), TFC_SIZE);
  236. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMenuFont), &(nonClientMetricsW.lfMenuFont), TFC_SIZE);
  237. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfStatusFont), &(nonClientMetricsW.lfStatusFont), TFC_SIZE);
  238. TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMessageFont), &(nonClientMetricsW.lfMessageFont), TFC_SIZE);
  239. // Since we are copying the font sizes, scale them to the current DPI.
  240. // window elements sizes
  241. m_systemMetrics.schemeData.ncm.iBorderWidth = nonClientMetricsW.iBorderWidth;
  242. m_systemMetrics.schemeData.ncm.iScrollWidth = nonClientMetricsW.iScrollWidth;
  243. m_systemMetrics.schemeData.ncm.iScrollHeight = nonClientMetricsW.iScrollHeight;
  244. m_systemMetrics.schemeData.ncm.iCaptionWidth = nonClientMetricsW.iCaptionWidth;
  245. m_systemMetrics.schemeData.ncm.iCaptionHeight = nonClientMetricsW.iCaptionHeight;
  246. m_systemMetrics.schemeData.ncm.iSmCaptionWidth = nonClientMetricsW.iSmCaptionWidth;
  247. m_systemMetrics.schemeData.ncm.iSmCaptionHeight = nonClientMetricsW.iSmCaptionHeight;
  248. m_systemMetrics.schemeData.ncm.iMenuWidth = nonClientMetricsW.iMenuWidth;
  249. m_systemMetrics.schemeData.ncm.iMenuHeight = nonClientMetricsW.iMenuHeight;
  250. // Local custom fonts
  251. _LoadCustomFonts();
  252. if (nCurrentDPI != DPI_PERSISTED)
  253. {
  254. LogSystemMetrics("CThemeFile::_LoadSettings() BEFORE Loading from .theme", &m_systemMetrics);
  255. DPIConvert_SystemMetricsAll(TRUE, &m_systemMetrics, DPI_PERSISTED, nCurrentDPI);
  256. LogSystemMetrics("CThemeFile::_LoadSettings() AFTER Loading from .theme", &m_systemMetrics);
  257. }
  258. // CHARSET: In Win2k, fontfix.cpp was used as a hack to change the CHARSET from one language to another.
  259. // That doesn't work for many reasons: a) not called on roaming, b) not called for OS lang changes,
  260. // c) won't fix the problem for strings with multiple languages, d) etc.
  261. // Therefore, the SHELL team (BryanSt) had the NTUSER team (MSadek) agree to use DEFAULT_CHARSET all the time.
  262. // If some app has bad logic testing the charset parameter, then the NTUSER team will shim that app to fix it.
  263. // The shim would be really simple, on the return from a SystemParametersInfo(SPI_GETNONCLIENTMETRICS or ICONFONTS)
  264. // just patch the lfCharSet param to the current charset.
  265. // For all CHARSETs to DEFAULT_CHARSET
  266. m_systemMetrics.schemeData.ncm.lfCaptionFont.lfCharSet = DEFAULT_CHARSET;
  267. m_systemMetrics.schemeData.ncm.lfSmCaptionFont.lfCharSet = DEFAULT_CHARSET;
  268. m_systemMetrics.schemeData.ncm.lfMenuFont.lfCharSet = DEFAULT_CHARSET;
  269. m_systemMetrics.schemeData.ncm.lfStatusFont.lfCharSet = DEFAULT_CHARSET;
  270. m_systemMetrics.schemeData.ncm.lfMessageFont.lfCharSet = DEFAULT_CHARSET;
  271. m_systemMetrics.schemeData.lfIconTitle.lfCharSet = DEFAULT_CHARSET;
  272. }
  273. }
  274. }
  275. ////////////////////////////////////////////
  276. // Get Colors
  277. BOOL fGrad = FALSE; // Are gradient captions enabled?
  278. int nIndex;
  279. BOOL fColorFilter = _IsFiltered(THEMEFILTER_COLORS);
  280. ClassicSystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, (LPVOID)&fGrad, 0); // Init fGrad
  281. if (!fColorFilter)
  282. {
  283. for (nIndex = 0; nIndex < ARRAYSIZE(s_pszColorNames); nIndex++)
  284. {
  285. TCHAR szColor[MAX_PATH];
  286. // get string from theme
  287. if (!GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[nIndex], SZ_EMPTY, szColor, ARRAYSIZE(szColor), m_pszThemeFile))
  288. {
  289. if ((nIndex == COLOR_GRADIENTACTIVECAPTION) && !szColor[0])
  290. {
  291. // They didn't specify the COLOR_GRADIENTACTIVECAPTION color, so use COLOR_ACTIVECAPTION
  292. GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[COLOR_ACTIVECAPTION], SZ_EMPTY, szColor, ARRAYSIZE(szColor), m_pszThemeFile);
  293. }
  294. if ((nIndex == COLOR_GRADIENTINACTIVECAPTION) && !szColor[0])
  295. {
  296. // They didn't specify the COLOR_GRADIENTINACTIVECAPTION color, so use COLOR_INACTIVECAPTION
  297. GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[COLOR_INACTIVECAPTION], SZ_EMPTY, szColor, ARRAYSIZE(szColor), m_pszThemeFile);
  298. }
  299. }
  300. if (szColor[0])
  301. {
  302. m_systemMetrics.schemeData.rgb[nIndex] = RGBStringToColor(szColor);
  303. }
  304. }
  305. }
  306. }
  307. else
  308. {
  309. AssertMsg((NULL != _punkSite), TEXT("The caller needs to set our site or we can't succeed because we can't find out the icon size."));
  310. hr = E_INVALIDARG;
  311. }
  312. hr = S_OK;
  313. }
  314. return hr;
  315. }
  316. HRESULT CThemeFile::_SaveSystemMetrics(SYSTEMMETRICSALL * pSystemMetrics)
  317. {
  318. HRESULT hr = _LoadSettings();
  319. AssertMsg((NULL != m_pszThemeFile), TEXT("We don't have a file specified yet."));
  320. if (SUCCEEDED(hr) && m_pszThemeFile)
  321. {
  322. int nCurrentDPI = DPI_PERSISTED;
  323. _LoadLiveSettings(&nCurrentDPI);
  324. hr = SystemMetricsAll_Copy(pSystemMetrics, &m_systemMetrics);
  325. if (SUCCEEDED(hr))
  326. {
  327. // Write the following:
  328. LPWSTR pszStringOut;
  329. NONCLIENTMETRICSA nonClientMetricsA = {0};
  330. SYSTEMMETRICSALL systemMetricsPDPI; // SYSMETS in persist DPI
  331. SystemMetricsAll_Copy(pSystemMetrics, &systemMetricsPDPI);
  332. // Scale the values so they are persisted in a DPI independent way. (A.k.a., in 96 DPI)
  333. LogSystemMetrics("CThemeFile::_SaveSystemMetrics() BEFORE scale to P-DPI for .theme file", &systemMetricsPDPI);
  334. DPIConvert_SystemMetricsAll(TRUE, &systemMetricsPDPI, nCurrentDPI, DPI_PERSISTED);
  335. LogSystemMetrics("CThemeFile::_SaveSystemMetrics() AFTER scale to P-DPI for .theme file", &systemMetricsPDPI);
  336. ConvertNCMetricsToANSI(&(systemMetricsPDPI.schemeData.ncm), &nonClientMetricsA);
  337. // #1 "NonclientMetrics"
  338. hr = ConvertBinaryToINIByteString((BYTE *)&nonClientMetricsA, sizeof(nonClientMetricsA), &pszStringOut);
  339. if (SUCCEEDED(hr))
  340. {
  341. hr = _putThemeSetting(SZ_INISECTION_METRICS, SZ_INIKEY_NONCLIENTMETRICS, FALSE, pszStringOut);
  342. LocalFree(pszStringOut);
  343. if (SUCCEEDED(hr))
  344. {
  345. // #2 "IconMetrics"
  346. ICONMETRICSA iconMetricsA;
  347. iconMetricsA.cbSize = sizeof(iconMetricsA);
  348. GetIconMetricsFromSysMetricsAll(&systemMetricsPDPI, &iconMetricsA, sizeof(iconMetricsA));
  349. hr = ConvertBinaryToINIByteString((BYTE *)&iconMetricsA, sizeof(iconMetricsA), &pszStringOut);
  350. if (SUCCEEDED(hr))
  351. {
  352. hr = _putThemeSetting(SZ_INISECTION_METRICS, SZ_INIKEY_ICONMETRICS, FALSE, pszStringOut);
  353. if (SUCCEEDED(hr))
  354. {
  355. int nIndex;
  356. for (nIndex = 0; nIndex < ARRAYSIZE(s_pszColorNames); nIndex++)
  357. {
  358. LPWSTR pszColor;
  359. DWORD dwColor = systemMetricsPDPI.schemeData.rgb[nIndex];
  360. hr = ConvertBinaryToINIByteString((BYTE *)&dwColor, 3, &pszColor);
  361. if (SUCCEEDED(hr))
  362. {
  363. DWORD cchSize = lstrlen(pszColor);
  364. if (L' ' == pszColor[cchSize - 1])
  365. {
  366. pszColor[cchSize - 1] = 0;
  367. }
  368. hr = HrWritePrivateProfileStringW(SZ_INISECTION_COLORS, s_pszColorNames[nIndex], pszColor, m_pszThemeFile);
  369. LocalFree(pszColor);
  370. }
  371. }
  372. // Delete the MUI version of the fonts because we just got new NONCLIENTMETRICs
  373. _putThemeSetting(SZ_INISECTION_METRICS, TEXT("CaptionFont"), FALSE, NULL);
  374. _putThemeSetting(SZ_INISECTION_METRICS, TEXT("SmCaptionFont"), FALSE, NULL);
  375. _putThemeSetting(SZ_INISECTION_METRICS, TEXT("MenuFont"), FALSE, NULL);
  376. _putThemeSetting(SZ_INISECTION_METRICS, TEXT("StatusFont"), FALSE, NULL);
  377. _putThemeSetting(SZ_INISECTION_METRICS, TEXT("MessageFont"), FALSE, NULL);
  378. _putThemeSetting(SZ_INISECTION_METRICS, TEXT("IconFont"), FALSE, NULL);
  379. }
  380. LocalFree(pszStringOut);
  381. }
  382. }
  383. }
  384. }
  385. }
  386. return hr;
  387. }
  388. BOOL CThemeFile::_IsFiltered(IN DWORD dwFilter)
  389. {
  390. BOOL fFiltered = FALSE;
  391. // Get property bag with default settings.
  392. if (_punkSite)
  393. {
  394. IPropertyBag * pPropertyBag;
  395. HRESULT hr = _punkSite->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBag));
  396. if (SUCCEEDED(hr))
  397. {
  398. fFiltered = !SHPropertyBag_ReadBOOLDefRet(pPropertyBag, g_szCBNames[dwFilter], FALSE);
  399. pPropertyBag->Release();
  400. }
  401. }
  402. return fFiltered;
  403. }
  404. HRESULT CThemeFile::_ApplySounds(void)
  405. {
  406. HRESULT hr = S_OK;
  407. if (!_IsFiltered(THEMEFILTER_SOUNDS))
  408. {
  409. int nIndex;
  410. for (nIndex = 0; nIndex < ARRAYSIZE(s_ThemeSoundsValues); nIndex++)
  411. {
  412. CComBSTR bstrPath;
  413. hr = _GetSound(s_ThemeSoundsValues[nIndex].pszRegKey, &bstrPath);
  414. if (SUCCEEDED(hr))
  415. {
  416. DWORD dwError = SHRegSetPathW(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL, bstrPath, 0);
  417. hr = HRESULT_FROM_WIN32(dwError);
  418. }
  419. else
  420. {
  421. // First delete the value because we many need to switch from REG_SZ to REG_EXPAND_SZ
  422. // Ignore if this fails
  423. HrRegDeleteValue(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL);
  424. hr = E_FAIL;
  425. // The file didn't specify what to use, so reset to the default values.
  426. if (s_ThemeSoundsValues[nIndex].nResourceID)
  427. {
  428. // Use the specified value.
  429. TCHAR szReplacement[MAX_PATH];
  430. DWORD dwType;
  431. DWORD cbSize;
  432. if (s_ThemeSoundsValues[nIndex].nResourceID == SOUND_DEFAULT)
  433. {
  434. TCHAR szDefaultKey[MAX_PATH];
  435. StringCchCopy(szDefaultKey, ARRAYSIZE(szDefaultKey), s_ThemeSoundsValues[nIndex].pszRegKey);
  436. DWORD cchDefaultKey = lstrlen(szDefaultKey);
  437. DWORD cchCurrent = ARRAYSIZE(L".Current");
  438. DWORD cchp = ARRAYSIZE(szDefaultKey) - cchDefaultKey - cchCurrent + 1;
  439. LPTSTR p = szDefaultKey + cchDefaultKey - cchCurrent + 1;
  440. // Replace ".Current" with ".default"
  441. if (*p == L'.')
  442. {
  443. StringCchCopy(p, cchp, L".Default");
  444. cbSize = sizeof szReplacement;
  445. hr = HrSHGetValue(HKEY_CURRENT_USER, szDefaultKey, NULL, &dwType, (LPVOID) szReplacement, &cbSize);
  446. if (SUCCEEDED(hr))
  447. {
  448. PathUnExpandEnvStringsWrap(szReplacement, ARRAYSIZE(szReplacement));
  449. }
  450. }
  451. }
  452. else
  453. {
  454. if (0 != LoadString(HINST_THISDLL, s_ThemeSoundsValues[nIndex].nResourceID, szReplacement, ARRAYSIZE(szReplacement)))
  455. {
  456. hr = S_OK;
  457. }
  458. }
  459. if (SUCCEEDED(hr))
  460. {
  461. dwType = (StrStrW(szReplacement, L"%SystemRoot%")) ? REG_EXPAND_SZ : REG_SZ;
  462. cbSize = ((lstrlen(szReplacement) + 1) * sizeof(szReplacement[0]));
  463. hr = HrSHSetValue(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL, dwType, (LPVOID) szReplacement, cbSize);
  464. }
  465. }
  466. else
  467. {
  468. // We leave the value deleted because the default was empty.
  469. }
  470. }
  471. }
  472. hr = S_OK; // We don't care if it fails.
  473. // Need to flush buffer and ensure new sounds used for next events
  474. sndPlaySoundW(NULL, SND_ASYNC | SND_NODEFAULT);
  475. // Clear the current pointer scheme string from the registry so that Mouse
  476. // cpl doesn't display a bogus name. Don't care if this fails.
  477. RegSetValue(HKEY_CURRENT_USER, SZ_REGKEY_SOUNDS, REG_SZ, TEXT(".current"), 0);
  478. }
  479. return hr;
  480. }
  481. HRESULT CThemeFile::_ApplyCursors(void)
  482. {
  483. HRESULT hr = S_OK;
  484. if (!_IsFiltered(THEMEFILTER_CURSORS))
  485. {
  486. int nIndex;
  487. for (nIndex = 0; nIndex < ARRAYSIZE(s_pszCursorArray); nIndex++)
  488. {
  489. BSTR bstrPath;
  490. hr = _getThemeSetting(SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex], THEMESETTING_LOADINDIRECT, &bstrPath);
  491. if (FAILED(hr) || !bstrPath[0])
  492. {
  493. // The caller didn't specify a value so delete the key so we use default values.
  494. hr = HrRegDeleteValue(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex]);
  495. if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
  496. {
  497. hr = S_OK; // it may already not exist, which is fine.
  498. }
  499. }
  500. else if (SUCCEEDED(hr))
  501. {
  502. hr = HrRegSetValueString(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex], bstrPath);
  503. }
  504. }
  505. BSTR bstrCursor;
  506. if (SUCCEEDED(_getThemeSetting(SZ_INISECTION_CURSORS, SZ_INIKEY_CURSORSCHEME, THEMESETTING_LOADINDIRECT, &bstrCursor)) && bstrCursor && bstrCursor[0])
  507. {
  508. // Set the cursor scheme
  509. HrRegSetValueString(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, NULL, bstrCursor);
  510. // GPease wants me to mark this regkey -1 so he knows it was changed from the display CPL. See
  511. // him with questions.
  512. HrRegSetDWORD(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, SZ_REGVALUE_CURSOR_CURRENTSCHEME, 2);
  513. }
  514. else
  515. {
  516. HrRegDeleteValue(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, NULL);
  517. HrRegDeleteValue(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, SZ_REGVALUE_CURSOR_CURRENTSCHEME);
  518. }
  519. // For the system to start using the new cursors.
  520. SystemParametersInfoAsync(SPI_SETCURSORS, 0, 0, 0, SPIF_SENDCHANGE, NULL);
  521. }
  522. return hr;
  523. }
  524. HRESULT CThemeFile::_ApplyWebview(void)
  525. {
  526. HRESULT hr = S_OK;
  527. // We aren't going to support this.
  528. return hr;
  529. }
  530. HRESULT CThemeFile::_ApplyThemeSettings(void)
  531. {
  532. HRESULT hr = E_INVALIDARG;
  533. if (m_pszThemeFile)
  534. {
  535. HCURSOR hCursorOld = ::SetCursor(LoadCursor(NULL, IDC_WAIT));
  536. hr = S_OK;
  537. if (!((METRIC_CHANGE | COLOR_CHANGE | SCHEME_CHANGE) & m_systemMetrics.dwChanged))
  538. {
  539. // Only load settings if we haven't loaded the settings yet.
  540. hr = _LoadSettings();
  541. }
  542. if (SUCCEEDED(hr))
  543. {
  544. hr = _ApplySounds();
  545. if (SUCCEEDED(hr))
  546. {
  547. hr = _ApplyCursors();
  548. if (SUCCEEDED(hr))
  549. {
  550. hr = _ApplyWebview();
  551. }
  552. }
  553. }
  554. // OTHERS:
  555. // 1. Save Icon: SPI_SETICONMETRICS w/iconMetricsW.iHorzSpacing, iVertSpacing, (Policy bIconSpacing).
  556. // 2. Save Icon: SPI_SETICONMETRICS w/iconMetricsW.lfFont (Policy bIconFont).
  557. // 2. Save Icon: from Theme:"Control Panel\\Desktop\\WindowMetrics","Shell Icon Size" to reg same. (Policy bIconSpacing). Repeate for "Shell Small Icon Size"
  558. ::SetCursor(hCursorOld);
  559. }
  560. return hr;
  561. }
  562. HRESULT CThemeFile::_getThemeSetting(IN LPCWSTR pszIniSection, IN LPCWSTR pszIniKey, DWORD dwFlags, OUT BSTR * pbstrPath)
  563. {
  564. HRESULT hr = E_INVALIDARG;
  565. if (pbstrPath)
  566. {
  567. *pbstrPath = 0;
  568. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  569. if (m_pszThemeFile)
  570. {
  571. WCHAR szPath[MAX_PATH];
  572. DWORD cbRead = 0;
  573. szPath[0] = 0;
  574. if (THEMESETTING_LOADINDIRECT & dwFlags)
  575. {
  576. TCHAR szMUIIniKey[MAX_PATH];
  577. StringCchPrintf(szMUIIniKey, ARRAYSIZE(szMUIIniKey), TEXT("%s.MUI"), pszIniKey);
  578. cbRead = SHGetIniStringW(pszIniSection, szMUIIniKey, szPath, ARRAYSIZE(szPath), m_pszThemeFile);
  579. }
  580. if (0 == cbRead)
  581. {
  582. cbRead = SHGetIniStringW(pszIniSection, pszIniKey, szPath, ARRAYSIZE(szPath), m_pszThemeFile);
  583. }
  584. if (cbRead)
  585. {
  586. if (L'@' == szPath[0])
  587. {
  588. TCHAR szTemp[MAX_PATH];
  589. if (SUCCEEDED(SHLoadIndirectString(szPath, szTemp, ARRAYSIZE(szTemp), NULL)))
  590. {
  591. StringCchCopy(szPath, ARRAYSIZE(szPath), szTemp);
  592. }
  593. }
  594. hr = ExpandResourceDir(szPath, ARRAYSIZE(szPath));
  595. hr = ExpandThemeTokens(m_pszThemeFile, szPath, ARRAYSIZE(szPath)); // Expand %ThemeDir% or %WinDir%
  596. // Sometimes szPath won't be a path.
  597. if (SUCCEEDED(hr) && !PathIsFileSpec(szPath))
  598. {
  599. hr = ((CF_NOTFOUND == ConfirmFile(szPath, TRUE)) ? HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : S_OK);
  600. }
  601. if (SUCCEEDED(hr))
  602. {
  603. hr = HrSysAllocString(szPath, pbstrPath);
  604. }
  605. }
  606. }
  607. }
  608. return hr;
  609. }
  610. // pszPath - NULL means delete value
  611. HRESULT CThemeFile::_putThemeSetting(IN LPCWSTR pszIniSection, IN LPCWSTR pszIniKey, BOOL fUTF7, IN OPTIONAL LPWSTR pszPath)
  612. {
  613. HRESULT hr = E_INVALIDARG;
  614. if (m_pszThemeFile)
  615. {
  616. TCHAR szPath[MAX_PATH];
  617. LPCWSTR pszValue = pszPath;
  618. szPath[0] = 0;
  619. if (pszValue && !PathIsRelative(pszValue) && PathFileExists(pszValue))
  620. {
  621. if (PathUnExpandEnvStringsForUser(NULL, pszValue, szPath, ARRAYSIZE(szPath)))
  622. {
  623. pszValue = szPath;
  624. }
  625. }
  626. StrReplaceToken(TEXT("%WinDir%\\"), TEXT("%WinDir%"), szPath, ARRAYSIZE(szPath));
  627. StrReplaceToken(TEXT("%SystemRoot%\\"), TEXT("%WinDir%"), szPath, ARRAYSIZE(szPath));
  628. if (fUTF7)
  629. {
  630. if (SHSetIniStringW(pszIniSection, pszIniKey, pszValue, m_pszThemeFile))
  631. {
  632. hr = S_OK;
  633. }
  634. else
  635. {
  636. hr = HRESULT_FROM_WIN32(GetLastError());
  637. }
  638. }
  639. else
  640. {
  641. hr = HrWritePrivateProfileStringW(pszIniSection, pszIniKey, pszValue, m_pszThemeFile);
  642. }
  643. TCHAR szMUIIniKey[MAX_PATH];
  644. // Delete any ".MUI" copies because they are out of date.
  645. StringCchPrintf(szMUIIniKey, ARRAYSIZE(szMUIIniKey), TEXT("%s.MUI"), pszIniKey);
  646. HrWritePrivateProfileStringW(pszIniSection, szMUIIniKey, NULL, m_pszThemeFile);
  647. }
  648. return hr;
  649. }
  650. HRESULT CThemeFile::_getIntSetting(IN LPCWSTR pszIniSection, IN LPCWSTR pszIniKey, int nDefault, OUT int * pnValue)
  651. {
  652. HRESULT hr = E_INVALIDARG;
  653. if (pnValue)
  654. {
  655. *pnValue = 0;
  656. hr = E_FAIL;
  657. if (m_pszThemeFile)
  658. {
  659. *pnValue = GetPrivateProfileInt(pszIniSection, pszIniKey, nDefault, m_pszThemeFile);
  660. hr = S_OK;
  661. }
  662. }
  663. return hr;
  664. }
  665. //===========================
  666. // *** ITheme Interface ***
  667. //===========================
  668. HRESULT CThemeFile::get_DisplayName(OUT BSTR * pbstrDisplayName)
  669. {
  670. HRESULT hr = E_INVALIDARG;
  671. if (pbstrDisplayName)
  672. {
  673. WCHAR szDisplayName[MAX_PATH];
  674. *pbstrDisplayName = NULL;
  675. hr = _getThemeSetting(SZ_INISECTION_THEME, SZ_INIKEY_DISPLAYNAME, THEMESETTING_NORMAL, pbstrDisplayName);
  676. if (FAILED(hr))
  677. {
  678. LPCTSTR pszFileName = PathFindFileName(m_pszThemeFile);
  679. hr = E_FAIL;
  680. if (pszFileName)
  681. {
  682. SHTCharToUnicode(pszFileName, szDisplayName, ARRAYSIZE(szDisplayName));
  683. PathRemoveExtensionW(szDisplayName);
  684. hr = HrSysAllocStringW(szDisplayName, pbstrDisplayName);
  685. }
  686. }
  687. }
  688. return hr;
  689. }
  690. HRESULT CThemeFile::put_DisplayName(IN BSTR bstrDisplayName)
  691. {
  692. HRESULT hr = E_INVALIDARG;
  693. // NULL bstrDisplayName is allowed, it means to delete the name in the file
  694. // so the filename will be used in the future.
  695. if (bstrDisplayName)
  696. {
  697. hr = _putThemeSetting(SZ_INISECTION_THEME, SZ_INIKEY_DISPLAYNAME, TRUE, bstrDisplayName);
  698. }
  699. else
  700. {
  701. SHSetIniStringW(SZ_INISECTION_THEME, SZ_INIKEY_DISPLAYNAME, NULL, m_pszThemeFile);
  702. hr = S_OK;
  703. }
  704. return hr;
  705. }
  706. HRESULT CThemeFile::get_ScreenSaver(OUT BSTR * pbstrPath)
  707. {
  708. return _getThemeSetting(SZ_INISECTION_SCREENSAVER, SZ_INIKEY_SCREENSAVER, THEMESETTING_NORMAL, pbstrPath);
  709. }
  710. HRESULT CThemeFile::put_ScreenSaver(IN BSTR bstrPath)
  711. {
  712. return _putThemeSetting(SZ_INISECTION_SCREENSAVER, SZ_INIKEY_SCREENSAVER, TRUE, bstrPath);
  713. }
  714. HRESULT CThemeFile::get_Background(OUT BSTR * pbstrPath)
  715. {
  716. HRESULT hr = E_INVALIDARG;
  717. if (pbstrPath)
  718. {
  719. hr = _getThemeSetting(SZ_INISECTION_BACKGROUND, SZ_INIKEY_BACKGROUND, THEMESETTING_LOADINDIRECT, pbstrPath);
  720. if (SUCCEEDED(hr))
  721. {
  722. TCHAR szNone[MAX_PATH];
  723. LoadString(HINST_THISDLL, IDS_NONE, szNone, ARRAYSIZE(szNone));
  724. if (!StrCmpI(szNone, *pbstrPath))
  725. {
  726. (*pbstrPath)[0] = 0;
  727. }
  728. }
  729. }
  730. return hr;
  731. }
  732. HRESULT CThemeFile::put_Background(IN BSTR bstrPath)
  733. {
  734. return _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_INIKEY_BACKGROUND, TRUE, bstrPath);
  735. }
  736. HRESULT CThemeFile::get_BackgroundTile(OUT enumBkgdTile * pnTile)
  737. {
  738. HRESULT hr = E_INVALIDARG;
  739. if (pnTile)
  740. {
  741. TCHAR szSize[10];
  742. int tile = 0; // Zero is the default value to use if the registry is empty.
  743. int stretch = 0;
  744. if (SUCCEEDED(HrRegGetValueString(HKEY_CURRENT_USER, SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, szSize, ARRAYSIZE(szSize))))
  745. {
  746. tile = StrToInt(szSize);
  747. }
  748. if (SUCCEEDED(HrRegGetValueString(HKEY_CURRENT_USER, SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, szSize, ARRAYSIZE(szSize))))
  749. {
  750. tile = (2 & StrToInt(szSize));
  751. }
  752. // If a theme is selected, and we are using a plus wall paper then
  753. // find out if tiling is on, and what style to use from the ini file.
  754. // Otherwise, we already got the information from the registry.
  755. _getIntSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, tile, &tile);
  756. _getIntSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, stretch, &stretch);
  757. stretch &= 2;
  758. _getIntSetting(SZ_INISECTION_MASTERSELECTOR, SZ_REGVALUE_STRETCH, stretch, &stretch);
  759. if (tile)
  760. {
  761. *pnTile = BKDGT_TILE;
  762. }
  763. else if (stretch)
  764. {
  765. *pnTile = BKDGT_STRECH;
  766. }
  767. else
  768. {
  769. *pnTile = BKDGT_CENTER;
  770. }
  771. hr = S_OK;
  772. }
  773. return hr;
  774. }
  775. HRESULT CThemeFile::put_BackgroundTile(IN enumBkgdTile nTile)
  776. {
  777. HRESULT hr = E_INVALIDARG;
  778. switch (nTile)
  779. {
  780. case BKDGT_STRECH:
  781. hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, FALSE, TEXT("0"));
  782. hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, FALSE, TEXT("2"));
  783. break;
  784. case BKDGT_CENTER:
  785. hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, FALSE, TEXT("0"));
  786. hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, FALSE, TEXT("0"));
  787. break;
  788. case BKDGT_TILE:
  789. hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, FALSE, TEXT("1"));
  790. hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, FALSE, TEXT("0"));
  791. break;
  792. };
  793. return hr;
  794. }
  795. HRESULT CThemeFile::get_VisualStyle(OUT BSTR * pbstrPath)
  796. {
  797. return _getThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLE, THEMESETTING_NORMAL, pbstrPath);
  798. }
  799. HRESULT CThemeFile::put_VisualStyle(IN BSTR bstrPath)
  800. {
  801. return _putThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLE, TRUE, bstrPath);
  802. }
  803. HRESULT CThemeFile::get_VisualStyleColor(OUT BSTR * pbstrPath)
  804. {
  805. return _getThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLECOLOR, THEMESETTING_NORMAL, pbstrPath);
  806. }
  807. HRESULT CThemeFile::put_VisualStyleColor(IN BSTR bstrPath)
  808. {
  809. return _putThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLECOLOR, TRUE, bstrPath);
  810. }
  811. HRESULT CThemeFile::get_VisualStyleSize(OUT BSTR * pbstrPath)
  812. {
  813. return _getThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLESIZE, THEMESETTING_NORMAL, pbstrPath);
  814. }
  815. HRESULT CThemeFile::put_VisualStyleSize(IN BSTR bstrPath)
  816. {
  817. return _putThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLESIZE, TRUE, bstrPath);
  818. }
  819. HRESULT CThemeFile::GetPath(IN VARIANT_BOOL fExpand, OUT BSTR * pbstrPath)
  820. {
  821. HRESULT hr = E_INVALIDARG;
  822. if (pbstrPath && m_pszThemeFile)
  823. {
  824. TCHAR szPath[MAX_PATH];
  825. StringCchCopy(szPath, ARRAYSIZE(szPath), m_pszThemeFile);
  826. if (VARIANT_TRUE == fExpand)
  827. {
  828. TCHAR szPathTemp[MAX_PATH];
  829. if (SHExpandEnvironmentStrings(szPath, szPathTemp, ARRAYSIZE(szPathTemp)))
  830. {
  831. StringCchCopy(szPath, ARRAYSIZE(szPath), szPathTemp);
  832. }
  833. }
  834. hr = HrSysAllocString(szPath, pbstrPath);
  835. }
  836. return hr;
  837. }
  838. HRESULT CThemeFile::SetPath(IN BSTR bstrPath)
  839. {
  840. HRESULT hr = E_INVALIDARG;
  841. if (bstrPath)
  842. {
  843. Str_SetPtr(&m_pszThemeFile, bstrPath);
  844. hr = S_OK;
  845. }
  846. return hr;
  847. }
  848. HRESULT CThemeFile::GetCursor(IN BSTR bstrCursor, OUT BSTR * pbstrPath)
  849. {
  850. HRESULT hr = E_INVALIDARG;
  851. if (pbstrPath)
  852. {
  853. *pbstrPath = NULL;
  854. if (bstrCursor)
  855. {
  856. hr = _getThemeSetting(SZ_INISECTION_CURSORS, bstrCursor, THEMESETTING_LOADINDIRECT, pbstrPath);
  857. }
  858. }
  859. return hr;
  860. }
  861. HRESULT CThemeFile::SetCursor(IN BSTR bstrCursor, IN BSTR bstrPath)
  862. {
  863. HRESULT hr = E_INVALIDARG;
  864. if (bstrCursor)
  865. {
  866. hr = _putThemeSetting(SZ_INISECTION_CURSORS, bstrCursor, TRUE, bstrPath);
  867. }
  868. return hr;
  869. }
  870. HRESULT CThemeFile::_GetSound(LPCWSTR pszSoundName, OUT BSTR * pbstrPath)
  871. {
  872. HRESULT hr = E_INVALIDARG;
  873. if (pszSoundName && pbstrPath)
  874. {
  875. *pbstrPath = NULL;
  876. hr = _getThemeSetting(pszSoundName, SZ_INIKEY_DEFAULTVALUE, THEMESETTING_LOADINDIRECT, pbstrPath);
  877. }
  878. return hr;
  879. }
  880. HRESULT CThemeFile::GetSound(IN BSTR bstrSoundName, OUT BSTR * pbstrPath)
  881. {
  882. HRESULT hr = E_INVALIDARG;
  883. if (pbstrPath)
  884. {
  885. *pbstrPath = NULL;
  886. if (bstrSoundName)
  887. {
  888. hr = _GetSound(bstrSoundName, pbstrPath);
  889. if (FAILED(hr))
  890. {
  891. int nIndex;
  892. for (nIndex = 0; nIndex < ARRAYSIZE(s_ThemeSoundsValues); nIndex++)
  893. {
  894. if (!StrCmpI(bstrSoundName, s_ThemeSoundsValues[nIndex].pszRegKey))
  895. {
  896. // First delete the value because we many need to switch from REG_SZ to REG_EXPAND_SZ
  897. TCHAR szReplacement[MAX_PATH];
  898. LoadString(HINST_THISDLL, s_ThemeSoundsValues[nIndex].nResourceID, szReplacement, ARRAYSIZE(szReplacement));
  899. hr = HrSysAllocStringW(szReplacement, pbstrPath);
  900. break;
  901. }
  902. }
  903. }
  904. }
  905. }
  906. return hr;
  907. }
  908. HRESULT CThemeFile::SetSound(IN BSTR bstrSoundName, IN BSTR bstrPath)
  909. {
  910. HRESULT hr = E_INVALIDARG;
  911. if (bstrSoundName && bstrPath)
  912. {
  913. hr = _putThemeSetting(bstrSoundName, SZ_INIKEY_DEFAULTVALUE, TRUE, bstrPath);
  914. }
  915. return hr;
  916. }
  917. HRESULT CThemeFile::GetIcon(IN BSTR bstrIconName, OUT BSTR * pbstrIconPath)
  918. {
  919. HRESULT hr = E_INVALIDARG;
  920. if (pbstrIconPath)
  921. {
  922. *pbstrIconPath = NULL;
  923. if (bstrIconName)
  924. {
  925. WCHAR szPath[MAX_URL_STRING];
  926. WCHAR szIconType[MAX_PATH];
  927. StringCchCopy(szPath, ARRAYSIZE(szPath), bstrIconName);
  928. LPWSTR pszSeparator = StrChrW(szPath, L':');
  929. if (pszSeparator)
  930. {
  931. StringCchCopy(szIconType, ARRAYSIZE(szIconType), CharNext(pszSeparator));
  932. pszSeparator[0] = 0;
  933. }
  934. else
  935. {
  936. // The caller should specify this but this is a safe fallback.
  937. StringCchCopy(szIconType, ARRAYSIZE(szIconType), L"DefaultValue");
  938. }
  939. hr = _getThemeSetting(szPath, szIconType, THEMESETTING_NORMAL, pbstrIconPath);
  940. if (FAILED(hr))
  941. {
  942. // The Plus! 98 format started adding "Software\Classes" to the path.
  943. // So try that now.
  944. // Plus!95 format: "[CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}\DefaultIcon]"
  945. // Plus!98 format: "[Software\Classes\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}\DefaultIcon]"
  946. WCHAR szPath98[MAX_URL_STRING];
  947. StringCchPrintf(szPath98, ARRAYSIZE(szPath98), L"Software\\Classes\\%ls", szPath);
  948. hr = _getThemeSetting(szPath98, szIconType, THEMESETTING_NORMAL, pbstrIconPath);
  949. }
  950. }
  951. }
  952. return hr;
  953. }
  954. HRESULT CThemeFile::SetIcon(IN BSTR bstrIconName, IN BSTR bstrIconPath)
  955. {
  956. HRESULT hr = E_INVALIDARG;
  957. if (bstrIconName && bstrIconPath)
  958. {
  959. WCHAR szPath[MAX_URL_STRING];
  960. WCHAR szIconType[MAX_PATH];
  961. StringCchCopy(szPath, ARRAYSIZE(szPath), bstrIconName);
  962. LPWSTR pszSeparator = StrChrW(szPath, L':');
  963. if (pszSeparator)
  964. {
  965. StringCchCopy(szIconType, ARRAYSIZE(szIconType), CharNext(pszSeparator));
  966. pszSeparator[0] = 0;
  967. }
  968. else
  969. {
  970. // The caller should specify this but this is a safe fallback.
  971. StringCchCopy(szIconType, ARRAYSIZE(szIconType), L"DefaultValue");
  972. }
  973. hr = _putThemeSetting(szPath, szIconType, TRUE, bstrIconPath);
  974. }
  975. return hr;
  976. }
  977. //===========================
  978. // *** IPropertyBag Interface ***
  979. //===========================
  980. HRESULT CThemeFile::Read(IN LPCOLESTR pszPropName, IN VARIANT * pVar, IN IErrorLog *pErrorLog)
  981. {
  982. HRESULT hr = E_INVALIDARG;
  983. if (pszPropName && pVar)
  984. {
  985. if (!StrCmpW(pszPropName, SZ_PBPROP_SYSTEM_METRICS))
  986. {
  987. hr = _LoadSettings();
  988. // This is pretty ugly.
  989. pVar->vt = VT_BYREF;
  990. pVar->byref = &m_systemMetrics;
  991. }
  992. else if (!StrCmpW(pszPropName, SZ_PBPROP_HASSYSMETRICS))
  993. {
  994. hr = _LoadSettings();
  995. pVar->vt = VT_BOOL;
  996. pVar->boolVal = VARIANT_FALSE;
  997. if (SUCCEEDED(hr))
  998. {
  999. TCHAR szIconMetrics[2048];
  1000. if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_ICONMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile))
  1001. {
  1002. if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_NONCLIENTMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile))
  1003. {
  1004. GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[COLOR_ACTIVECAPTION], SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile);
  1005. pVar->boolVal = (szIconMetrics[0] ? VARIANT_TRUE : VARIANT_FALSE);
  1006. }
  1007. }
  1008. }
  1009. }
  1010. }
  1011. return hr;
  1012. }
  1013. HRESULT CThemeFile::Write(IN LPCOLESTR pszPropName, IN VARIANT *pVar)
  1014. {
  1015. HRESULT hr = E_NOTIMPL;
  1016. if (pszPropName && pVar)
  1017. {
  1018. if (!StrCmpW(pszPropName, SZ_PBPROP_APPLY_THEMEFILE))
  1019. {
  1020. VariantInit(pVar);
  1021. hr = _ApplyThemeSettings(); // This will do nothing if already loaded.
  1022. }
  1023. else if (!StrCmpW(pszPropName, SZ_PBPROP_SYSTEM_METRICS) && (VT_BYREF == pVar->vt) && pVar->byref)
  1024. {
  1025. SYSTEMMETRICSALL * pCurrent = (SYSTEMMETRICSALL *) pVar->byref;
  1026. // The caller will pass SYSTEMMETRICS in the live system DPI.
  1027. hr = _SaveSystemMetrics(pCurrent);
  1028. }
  1029. }
  1030. return hr;
  1031. }
  1032. //===========================
  1033. // *** IUnknown Interface ***
  1034. //===========================
  1035. ULONG CThemeFile::AddRef()
  1036. {
  1037. return InterlockedIncrement(&m_cRef);
  1038. }
  1039. ULONG CThemeFile::Release()
  1040. {
  1041. ASSERT( 0 != m_cRef );
  1042. ULONG cRef = InterlockedDecrement(&m_cRef);
  1043. if ( 0 == cRef )
  1044. {
  1045. delete this;
  1046. }
  1047. return cRef;
  1048. }
  1049. HRESULT CThemeFile::QueryInterface(REFIID riid, void **ppvObj)
  1050. {
  1051. static const QITAB qit[] = {
  1052. QITABENT(CThemeFile, IObjectWithSite),
  1053. QITABENT(CThemeFile, IPropertyBag),
  1054. QITABENT(CThemeFile, ITheme),
  1055. QITABENT(CThemeFile, IDispatch),
  1056. { 0 },
  1057. };
  1058. return QISearch(this, qit, riid, ppvObj);
  1059. }
  1060. //===========================
  1061. // *** Class Methods ***
  1062. //===========================
  1063. CThemeFile::CThemeFile(LPCTSTR pszThemeFile) : m_cRef(1)
  1064. {
  1065. DllAddRef();
  1066. // This needs to be allocated in Zero Inited Memory.
  1067. // Assert that all Member Variables are inited to Zero.
  1068. m_dwCachedState = 0xFFFFFFFF;
  1069. InitFrost();
  1070. }
  1071. CThemeFile::~CThemeFile()
  1072. {
  1073. Str_SetPtr(&m_pszThemeFile, NULL);
  1074. DllRelease();
  1075. }
  1076. HRESULT CThemeFile_CreateInstance(IN LPCWSTR pszThemeFile, OUT ITheme ** ppTheme)
  1077. {
  1078. HRESULT hr = E_INVALIDARG;
  1079. if (ppTheme)
  1080. {
  1081. CThemeFile * pObject = new CThemeFile(pszThemeFile);
  1082. hr = E_OUTOFMEMORY;
  1083. *ppTheme = NULL;
  1084. if (pObject)
  1085. {
  1086. hr = pObject->SetPath((BSTR)pszThemeFile);
  1087. if (SUCCEEDED(hr))
  1088. {
  1089. hr = pObject->QueryInterface(IID_PPV_ARG(ITheme, ppTheme));
  1090. }
  1091. pObject->Release();
  1092. }
  1093. }
  1094. return hr;
  1095. }