Source code of Windows XP (NT5)
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.

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