aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-10-27 15:29:19 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-11-01 16:34:09 -0500
commitdce1c97c7d3e76c18e4f80d4ffe288d2933a74bc (patch)
tree68dc0653ee1a6fe962d82dac2159c8ab00a95808
parentbad2e93524f376cfeb76d5231d4b08510bdad033 (diff)
downloadwix-dce1c97c7d3e76c18e4f80d4ffe288d2933a74bc.tar.gz
wix-dce1c97c7d3e76c18e4f80d4ffe288d2933a74bc.tar.bz2
wix-dce1c97c7d3e76c18e4f80d4ffe288d2933a74bc.zip
Make thmutil automatically load controls during window creation.
Add control loaded event.
-rw-r--r--src/api/burn/balutil/inc/BAFunctions.h15
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctions.h10
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h12
-rw-r--r--src/api/burn/balutil/inc/IBAFunctions.h9
-rw-r--r--src/api/burn/balutil/inc/balutil.h2
-rw-r--r--src/api/burn/bextutil/inc/bextutil.h2
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp188
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/thmutil.h43
-rw-r--r--src/libs/dutil/WixToolset.DUtil/thmutil.cpp137
-rw-r--r--src/samples/thmviewer/display.cpp58
-rw-r--r--src/samples/thmviewer/thmviewer.cpp15
-rw-r--r--src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp103
-rw-r--r--src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml21
-rw-r--r--src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingThemeLoose.xml158
14 files changed, 542 insertions, 231 deletions
diff --git a/src/api/burn/balutil/inc/BAFunctions.h b/src/api/burn/balutil/inc/BAFunctions.h
index ab6ea4d7..0eda95a1 100644
--- a/src/api/burn/balutil/inc/BAFunctions.h
+++ b/src/api/burn/balutil/inc/BAFunctions.h
@@ -88,6 +88,7 @@ enum BA_FUNCTIONS_MESSAGE
88 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADING, 88 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADING,
89 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMCOMMAND, 89 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMCOMMAND,
90 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMNOTIFY, 90 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMNOTIFY,
91 BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADED,
91}; 92};
92 93
93typedef HRESULT(WINAPI *PFN_BA_FUNCTIONS_PROC)( 94typedef HRESULT(WINAPI *PFN_BA_FUNCTIONS_PROC)(
@@ -115,6 +116,20 @@ struct BA_FUNCTIONS_CREATE_RESULTS
115 LPVOID pvBAFunctionsProcContext; 116 LPVOID pvBAFunctionsProcContext;
116}; 117};
117 118
119struct BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS
120{
121 DWORD cbSize;
122 LPCWSTR wzName;
123 WORD wId;
124 HWND hWnd;
125};
126
127struct BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS
128{
129 DWORD cbSize;
130 BOOL fProcessed;
131};
132
118struct BA_FUNCTIONS_ONTHEMECONTROLLOADING_ARGS 133struct BA_FUNCTIONS_ONTHEMECONTROLLOADING_ARGS
119{ 134{
120 DWORD cbSize; 135 DWORD cbSize;
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
index f6c33f58..d97df350 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
@@ -874,6 +874,16 @@ public: // IBAFunctions
874 return S_OK; 874 return S_OK;
875 } 875 }
876 876
877 virtual STDMETHODIMP OnThemeControlLoaded(
878 __in LPCWSTR /*wzName*/,
879 __in WORD /*wId*/,
880 __in HWND /*hWnd*/,
881 __inout BOOL* /*pfProcessed*/
882 )
883 {
884 return S_OK;
885 }
886
877protected: 887protected:
878 CBalBaseBAFunctions( 888 CBalBaseBAFunctions(
879 __in HMODULE hModule, 889 __in HMODULE hModule,
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
index 1d51c5b6..2af231e8 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
@@ -51,6 +51,15 @@ static HRESULT BalBaseBAFunctionsProcOnThemeControlWmNotify(
51 return pBAFunctions->OnThemeControlWmNotify(pArgs->lParam, pArgs->wzName, pArgs->wId, pArgs->hWnd, &pResults->fProcessed, &pResults->lResult); 51 return pBAFunctions->OnThemeControlWmNotify(pArgs->lParam, pArgs->wzName, pArgs->wId, pArgs->hWnd, &pResults->fProcessed, &pResults->lResult);
52} 52}
53 53
54static HRESULT BalBaseBAFunctionsProcOnThemeControlLoaded(
55 __in IBAFunctions* pBAFunctions,
56 __in BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS* pArgs,
57 __inout BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS* pResults
58 )
59{
60 return pBAFunctions->OnThemeControlLoaded(pArgs->wzName, pArgs->wId, pArgs->hWnd, &pResults->fProcessed);
61}
62
54/******************************************************************* 63/*******************************************************************
55BalBaseBAFunctionsProc - requires pvContext to be of type IBAFunctions. 64BalBaseBAFunctionsProc - requires pvContext to be of type IBAFunctions.
56Provides a default mapping between the message based BAFunctions interface and 65Provides a default mapping between the message based BAFunctions interface and
@@ -161,6 +170,9 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
161 case BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMNOTIFY: 170 case BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLWMNOTIFY:
162 hr = BalBaseBAFunctionsProcOnThemeControlWmNotify(pBAFunctions, reinterpret_cast<BA_FUNCTIONS_ONTHEMECONTROLWMNOTIFY_ARGS*>(pvArgs), reinterpret_cast<BA_FUNCTIONS_ONTHEMECONTROLWMNOTIFY_RESULTS*>(pvResults)); 171 hr = BalBaseBAFunctionsProcOnThemeControlWmNotify(pBAFunctions, reinterpret_cast<BA_FUNCTIONS_ONTHEMECONTROLWMNOTIFY_ARGS*>(pvArgs), reinterpret_cast<BA_FUNCTIONS_ONTHEMECONTROLWMNOTIFY_RESULTS*>(pvResults));
163 break; 172 break;
173 case BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADED:
174 hr = BalBaseBAFunctionsProcOnThemeControlLoaded(pBAFunctions, reinterpret_cast<BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS*>(pvArgs), reinterpret_cast<BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS*>(pvResults));
175 break;
164 } 176 }
165 } 177 }
166 178
diff --git a/src/api/burn/balutil/inc/IBAFunctions.h b/src/api/burn/balutil/inc/IBAFunctions.h
index 63395e1e..2e71608d 100644
--- a/src/api/burn/balutil/inc/IBAFunctions.h
+++ b/src/api/burn/balutil/inc/IBAFunctions.h
@@ -61,4 +61,13 @@ DECLARE_INTERFACE_IID_(IBAFunctions, IBootstrapperApplication, "0FB445ED-17BD-49
61 __inout BOOL* pfProcessed, 61 __inout BOOL* pfProcessed,
62 __inout LRESULT* plResult 62 __inout LRESULT* plResult
63 ) = 0; 63 ) = 0;
64
65 // OnThemeControlLoaded - Called after a control was created for the theme.
66 //
67 STDMETHOD(OnThemeControlLoaded)(
68 __in LPCWSTR wzName,
69 __in WORD wId,
70 __in HWND hWnd,
71 __inout BOOL* pfProcessed
72 ) = 0;
64}; 73};
diff --git a/src/api/burn/balutil/inc/balutil.h b/src/api/burn/balutil/inc/balutil.h
index 82fd1fe5..0c47301a 100644
--- a/src/api/burn/balutil/inc/balutil.h
+++ b/src/api/burn/balutil/inc/balutil.h
@@ -11,6 +11,7 @@ extern "C" {
11 11
12#define BalExitOnFailureSource(d, x, f, ...) if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 12#define BalExitOnFailureSource(d, x, f, ...) if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
13#define BalExitOnRootFailureSource(d, x, f, ...) if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 13#define BalExitOnRootFailureSource(d, x, f, ...) if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
14#define BalExitWithRootFailureSource(d, x, e, f, ...) { x = FAILED(e) ? e : E_FAIL; BalLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
14#define BalExitOnLastErrorSource(d, x, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } } 15#define BalExitOnLastErrorSource(d, x, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } }
15#define BalExitOnNullSource(d, p, x, e, f, ...) if (NULL == p) { x = e; BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 16#define BalExitOnNullSource(d, p, x, e, f, ...) if (NULL == p) { x = e; BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
16#define BalExitOnNullWithLastErrorSource(d, p, x, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 17#define BalExitOnNullWithLastErrorSource(d, p, x, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } BalLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
@@ -18,6 +19,7 @@ extern "C" {
18 19
19#define BalExitOnFailure(x, f, ...) BalExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) 20#define BalExitOnFailure(x, f, ...) BalExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__)
20#define BalExitOnRootFailure(x, f, ...) BalExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) 21#define BalExitOnRootFailure(x, f, ...) BalExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__)
22#define BalExitWithRootFailure(x, e, f, ...) BalExitWithRootFailureSource(DUTIL_SOURCE_DEFAULT, x, e, f, __VA_ARGS__)
21#define BalExitOnLastError(x, f, ...) BalExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) 23#define BalExitOnLastError(x, f, ...) BalExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__)
22#define BalExitOnNull(p, x, e, f, ...) BalExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, f, __VA_ARGS__) 24#define BalExitOnNull(p, x, e, f, ...) BalExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, f, __VA_ARGS__)
23#define BalExitOnNullWithLastError(p, x, f, ...) BalExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, f, __VA_ARGS__) 25#define BalExitOnNullWithLastError(p, x, f, ...) BalExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, f, __VA_ARGS__)
diff --git a/src/api/burn/bextutil/inc/bextutil.h b/src/api/burn/bextutil/inc/bextutil.h
index ac9c0062..facaf2e8 100644
--- a/src/api/burn/bextutil/inc/bextutil.h
+++ b/src/api/burn/bextutil/inc/bextutil.h
@@ -11,6 +11,7 @@ extern "C" {
11 11
12#define BextExitOnFailureSource(d, x, f, ...) if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 12#define BextExitOnFailureSource(d, x, f, ...) if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
13#define BextExitOnRootFailureSource(d, x, f, ...) if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 13#define BextExitOnRootFailureSource(d, x, f, ...) if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
14#define BextExitWithRootFailureSource(d, x, e, f, ...) { x = FAILED(e) ? e : E_FAIL; BextLogError(x, f, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
14#define BextExitOnLastErrorSource(d, x, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } } 15#define BextExitOnLastErrorSource(d, x, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } }
15#define BextExitOnNullSource(d, p, x, e, f, ...) if (NULL == p) { x = e; BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 16#define BextExitOnNullSource(d, p, x, e, f, ...) if (NULL == p) { x = e; BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
16#define BextExitOnNullWithLastErrorSource(d, p, x, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } 17#define BextExitOnNullWithLastErrorSource(d, p, x, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } BextLogError(x, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
@@ -18,6 +19,7 @@ extern "C" {
18 19
19#define BextExitOnFailure(x, f, ...) BextExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) 20#define BextExitOnFailure(x, f, ...) BextExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__)
20#define BextExitOnRootFailure(x, f, ...) BextExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) 21#define BextExitOnRootFailure(x, f, ...) BextExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__)
22#define BextExitWithRootFailure(x, e, f, ...) BextExitWithRootFailureSource(DUTIL_SOURCE_DEFAULT, x, e, f, __VA_ARGS__)
21#define BextExitOnLastError(x, f, ...) BextExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__) 23#define BextExitOnLastError(x, f, ...) BextExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, f, __VA_ARGS__)
22#define BextExitOnNull(p, x, e, f, ...) BextExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, f, __VA_ARGS__) 24#define BextExitOnNull(p, x, e, f, ...) BextExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, f, __VA_ARGS__)
23#define BextExitOnNullWithLastError(p, x, f, ...) BextExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, f, __VA_ARGS__) 25#define BextExitOnNullWithLastError(p, x, f, ...) BextExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, f, __VA_ARGS__)
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index 2283880c..5546b74f 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -2389,6 +2389,11 @@ private:
2389 hr = ThemeRegisterVariableCallbacks(m_pTheme, EvaluateVariableConditionCallback, FormatVariableStringCallback, GetVariableNumericCallback, SetVariableNumericCallback, GetVariableStringCallback, SetVariableStringCallback, NULL); 2389 hr = ThemeRegisterVariableCallbacks(m_pTheme, EvaluateVariableConditionCallback, FormatVariableStringCallback, GetVariableNumericCallback, SetVariableNumericCallback, GetVariableStringCallback, SetVariableStringCallback, NULL);
2390 BalExitOnFailure(hr, "Failed to register variable theme callbacks."); 2390 BalExitOnFailure(hr, "Failed to register variable theme callbacks.");
2391 2391
2392 C_ASSERT(COUNT_WIXSTDBA_PAGE == countof(vrgwzPageNames));
2393 C_ASSERT(countof(m_rgdwPageIds) == countof(vrgwzPageNames));
2394
2395 ThemeGetPageIds(m_pTheme, vrgwzPageNames, m_rgdwPageIds, countof(m_rgdwPageIds));
2396
2392 hr = ThemeLocalize(m_pTheme, m_pWixLoc); 2397 hr = ThemeLocalize(m_pTheme, m_pWixLoc);
2393 BalExitOnFailure(hr, "Failed to localize theme: %ls", sczThemePath); 2398 BalExitOnFailure(hr, "Failed to localize theme: %ls", sczThemePath);
2394 2399
@@ -2697,6 +2702,8 @@ private:
2697 hr = ThemeCreateParentWindow(m_pTheme, 0, wc.lpszClassName, m_pTheme->sczCaption, dwWindowStyle, x, y, HWND_DESKTOP, m_hModule, this, THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, &m_hWnd); 2702 hr = ThemeCreateParentWindow(m_pTheme, 0, wc.lpszClassName, m_pTheme->sczCaption, dwWindowStyle, x, y, HWND_DESKTOP, m_hModule, this, THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES, &m_hWnd);
2698 ExitOnFailure(hr, "Failed to create window."); 2703 ExitOnFailure(hr, "Failed to create window.");
2699 2704
2705 OnThemeLoaded();
2706
2700 hr = S_OK; 2707 hr = S_OK;
2701 2708
2702 LExit: 2709 LExit:
@@ -2831,16 +2838,12 @@ private:
2831 return lres; 2838 return lres;
2832 } 2839 }
2833 2840
2834 case WM_CREATE:
2835 if (!pBA->OnCreate(hWnd))
2836 {
2837 return -1;
2838 }
2839 break;
2840
2841 case WM_THMUTIL_LOADING_CONTROL: 2841 case WM_THMUTIL_LOADING_CONTROL:
2842 return pBA->OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam)); 2842 return pBA->OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam));
2843 2843
2844 case WM_THMUTIL_LOADED_CONTROL:
2845 return pBA->OnThemeLoadedControl(reinterpret_cast<THEME_LOADEDCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADEDCONTROL_RESULTS*>(lParam));
2846
2844 case WM_QUERYENDSESSION: 2847 case WM_QUERYENDSESSION:
2845 fCancel = true; 2848 fCancel = true;
2846 pBA->OnSystemShutdown(static_cast<DWORD>(lParam), &fCancel); 2849 pBA->OnSystemShutdown(static_cast<DWORD>(lParam), &fCancel);
@@ -2896,74 +2899,14 @@ private:
2896 2899
2897 2900
2898 // 2901 //
2899 // OnCreate - finishes loading the theme. 2902 // OnThemeLoaded - finishes loading the theme.
2900 // 2903 //
2901 BOOL OnCreate( 2904 BOOL OnThemeLoaded()
2902 __in HWND /*hWnd*/
2903 )
2904 { 2905 {
2905 HRESULT hr = S_OK; 2906 HRESULT hr = S_OK;
2906 LPWSTR sczLicenseFormatted = NULL;
2907 LPWSTR sczLicensePath = NULL;
2908 LPWSTR sczLicenseDirectory = NULL;
2909 LPWSTR sczLicenseFilename = NULL;
2910 BA_FUNCTIONS_ONTHEMELOADED_ARGS themeLoadedArgs = { }; 2907 BA_FUNCTIONS_ONTHEMELOADED_ARGS themeLoadedArgs = { };
2911 BA_FUNCTIONS_ONTHEMELOADED_RESULTS themeLoadedResults = { }; 2908 BA_FUNCTIONS_ONTHEMELOADED_RESULTS themeLoadedResults = { };
2912 2909
2913 hr = ThemeLoadControls(m_pTheme);
2914 BalExitOnFailure(hr, "Failed to load theme controls.");
2915
2916 C_ASSERT(COUNT_WIXSTDBA_PAGE == countof(vrgwzPageNames));
2917 C_ASSERT(countof(m_rgdwPageIds) == countof(vrgwzPageNames));
2918
2919 ThemeGetPageIds(m_pTheme, vrgwzPageNames, m_rgdwPageIds, countof(m_rgdwPageIds));
2920
2921 // Load the RTF EULA control with text if the control exists.
2922 if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT))
2923 {
2924 hr = (m_sczLicenseFile && *m_sczLicenseFile) ? S_OK : E_INVALIDDATA;
2925 if (SUCCEEDED(hr))
2926 {
2927 hr = StrAllocString(&sczLicenseFormatted, m_sczLicenseFile, 0);
2928 if (SUCCEEDED(hr))
2929 {
2930 hr = LocLocalizeString(m_pWixLoc, &sczLicenseFormatted);
2931 if (SUCCEEDED(hr))
2932 {
2933 // Assume there is no hidden variables to be formatted
2934 // so don't worry about securely freeing it.
2935 hr = BalFormatString(sczLicenseFormatted, &sczLicenseFormatted);
2936 if (SUCCEEDED(hr))
2937 {
2938 hr = PathRelativeToModule(&sczLicensePath, sczLicenseFormatted, m_hModule);
2939 if (SUCCEEDED(hr))
2940 {
2941 hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory);
2942 if (SUCCEEDED(hr))
2943 {
2944 hr = StrAllocString(&sczLicenseFilename, PathFile(sczLicenseFormatted), 0);
2945 if (SUCCEEDED(hr))
2946 {
2947 hr = LocProbeForFile(sczLicenseDirectory, sczLicenseFilename, m_sczLanguage, &sczLicensePath);
2948 if (SUCCEEDED(hr))
2949 {
2950 hr = ThemeLoadRichEditFromFile(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT, sczLicensePath, m_hModule);
2951 }
2952 }
2953 }
2954 }
2955 }
2956 }
2957 }
2958 }
2959
2960 if (FAILED(hr))
2961 {
2962 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load file into license richedit control from path '%ls' manifest value: %ls", sczLicensePath, m_sczLicenseFile);
2963 hr = S_OK;
2964 }
2965 }
2966
2967 if (m_pfnBAFunctionsProc) 2910 if (m_pfnBAFunctionsProc)
2968 { 2911 {
2969 themeLoadedArgs.cbSize = sizeof(themeLoadedArgs); 2912 themeLoadedArgs.cbSize = sizeof(themeLoadedArgs);
@@ -2975,11 +2918,6 @@ private:
2975 } 2918 }
2976 2919
2977 LExit: 2920 LExit:
2978 ReleaseStr(sczLicenseFilename);
2979 ReleaseStr(sczLicenseDirectory);
2980 ReleaseStr(sczLicensePath);
2981 ReleaseStr(sczLicenseFormatted);
2982
2983 return SUCCEEDED(hr); 2921 return SUCCEEDED(hr);
2984 } 2922 }
2985 2923
@@ -3034,6 +2972,108 @@ private:
3034 return fProcessed || FAILED(hr); 2972 return fProcessed || FAILED(hr);
3035 } 2973 }
3036 2974
2975 BOOL OnThemeLoadedControl(
2976 __in const THEME_LOADEDCONTROL_ARGS* pArgs,
2977 __in THEME_LOADEDCONTROL_RESULTS* pResults
2978 )
2979 {
2980 HRESULT hr = S_OK;
2981 BOOL fProcessed = FALSE;
2982 BA_FUNCTIONS_ONTHEMECONTROLLOADED_ARGS themeControlLoadedArgs = { };
2983 BA_FUNCTIONS_ONTHEMECONTROLLOADED_RESULTS themeControlLoadedResults = { };
2984
2985 if (WIXSTDBA_CONTROL_EULA_RICHEDIT == pArgs->pThemeControl->wId)
2986 {
2987 // Best effort to load the RTF EULA control with text.
2988 OnLoadedEulaRtfControl(pArgs->pThemeControl);
2989 fProcessed = TRUE;
2990 ExitFunction();
2991 }
2992
2993 if (m_pfnBAFunctionsProc)
2994 {
2995 themeControlLoadedArgs.cbSize = sizeof(themeControlLoadedArgs);
2996 themeControlLoadedArgs.wzName = pArgs->pThemeControl->sczName;
2997 themeControlLoadedArgs.wId = pArgs->pThemeControl->wId;
2998 themeControlLoadedArgs.hWnd = pArgs->pThemeControl->hWnd;
2999
3000 themeControlLoadedResults.cbSize = sizeof(themeControlLoadedResults);
3001
3002 hr = m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONTHEMECONTROLLOADED, &themeControlLoadedArgs, &themeControlLoadedResults, m_pvBAFunctionsProcContext);
3003
3004 if (E_NOTIMPL == hr)
3005 {
3006 hr = S_OK;
3007 }
3008 else
3009 {
3010 BalExitOnFailure(hr, "BAFunctions OnThemeControlLoaded failed.");
3011
3012 if (themeControlLoadedResults.fProcessed)
3013 {
3014 fProcessed = TRUE;
3015 }
3016 }
3017 }
3018
3019 LExit:
3020 pResults->hr = hr;
3021 return fProcessed || FAILED(hr);
3022 }
3023
3024 HRESULT OnLoadedEulaRtfControl(
3025 const THEME_CONTROL* pThemeControl
3026 )
3027 {
3028 HRESULT hr = S_OK;
3029 LPWSTR sczLicenseFormatted = NULL;
3030 LPWSTR sczLicensePath = NULL;
3031 LPWSTR sczLicenseDirectory = NULL;
3032 LPWSTR sczLicenseFilename = NULL;
3033
3034 if (!m_sczLicenseFile || !*m_sczLicenseFile)
3035 {
3036 ExitWithRootFailure(hr, E_INVALIDDATA, "No license file in manifest.");
3037 }
3038
3039 hr = StrAllocString(&sczLicenseFormatted, m_sczLicenseFile, 0);
3040 ExitOnFailure(hr, "Failed to copy manifest license file.");
3041
3042 hr = LocLocalizeString(m_pWixLoc, &sczLicenseFormatted);
3043 ExitOnFailure(hr, "Failed to localize manifest license file.");
3044
3045 hr = BalFormatString(sczLicenseFormatted, &sczLicenseFormatted);
3046 ExitOnFailure(hr, "Failed to expand localized manifest license file.");
3047
3048 hr = PathRelativeToModule(&sczLicensePath, sczLicenseFormatted, m_hModule);
3049 ExitOnFailure(hr, "Failed to get relative path for license file.");
3050
3051 hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory);
3052 ExitOnFailure(hr, "Failed to get license file directory.");
3053
3054 hr = StrAllocString(&sczLicenseFilename, PathFile(sczLicenseFormatted), 0);
3055 ExitOnFailure(hr, "Failed to copy license file name.");
3056
3057 hr = LocProbeForFile(sczLicenseDirectory, sczLicenseFilename, m_sczLanguage, &sczLicensePath);
3058 ExitOnFailure(hr, "Failed to probe for localized license file.");
3059
3060 hr = ThemeLoadRichEditFromFile(m_pTheme, pThemeControl->wId, sczLicensePath, m_hModule);
3061 ExitOnFailure(hr, "Failed to load license file into richedit control.");
3062
3063 LExit:
3064 if (FAILED(hr))
3065 {
3066 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load file into license richedit control from path '%ls' manifest value: %ls", sczLicensePath, m_sczLicenseFile);
3067 }
3068
3069 ReleaseStr(sczLicenseFilename);
3070 ReleaseStr(sczLicenseDirectory);
3071 ReleaseStr(sczLicensePath);
3072 ReleaseStr(sczLicenseFormatted);
3073
3074 return hr;
3075 }
3076
3037 3077
3038 // 3078 //
3039 // OnShowFailure - display the failure page. 3079 // OnShowFailure - display the failure page.
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
index 9557c11c..eda81485 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
@@ -6,6 +6,10 @@
6extern "C" { 6extern "C" {
7#endif 7#endif
8 8
9// forward declare
10
11typedef struct _THEME THEME;
12
9#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; } 13#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; }
10 14
11typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)( 15typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)(
@@ -111,6 +115,10 @@ typedef enum _WM_THMUTIL
111 // wparam is THEME_CONTROLWMNOTIFY_ARGS* and lparam is THEME_CONTROLWMNOTIFY_RESULTS*. 115 // wparam is THEME_CONTROLWMNOTIFY_ARGS* and lparam is THEME_CONTROLWMNOTIFY_RESULTS*.
112 // Return code is TRUE to prevent further processing of the message. 116 // Return code is TRUE to prevent further processing of the message.
113 WM_THMUTIL_CONTROL_WM_NOTIFY = WM_APP - 3, 117 WM_THMUTIL_CONTROL_WM_NOTIFY = WM_APP - 3,
118 // Sent after created a control.
119 // wparam is THEME_LOADEDCONTROL_ARGS* and lparam is THEME_LOADEDCONTROL_RESULTS*.
120 // Return code is TRUE if it was processed.
121 WM_THMUTIL_LOADED_CONTROL = WM_APP - 4,
114} WM_THMUTIL; 122} WM_THMUTIL;
115 123
116struct THEME_COLUMN 124struct THEME_COLUMN
@@ -287,6 +295,7 @@ struct THEME_CONTROL
287 // state variables that should be ignored 295 // state variables that should be ignored
288 HWND hWnd; 296 HWND hWnd;
289 DWORD dwData; // type specific data 297 DWORD dwData; // type specific data
298 THEME* pTheme;
290}; 299};
291 300
292 301
@@ -340,7 +349,7 @@ struct THEME_FONT
340}; 349};
341 350
342 351
343struct THEME 352typedef struct _THEME
344{ 353{
345 WORD wNextControlId; 354 WORD wNextControlId;
346 355
@@ -403,7 +412,7 @@ struct THEME
403 PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable; 412 PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable;
404 413
405 LPVOID pvVariableContext; 414 LPVOID pvVariableContext;
406}; 415} THEME;
407 416
408typedef struct _THEME_CONTROLWMCOMMAND_ARGS 417typedef struct _THEME_CONTROLWMCOMMAND_ARGS
409{ 418{
@@ -431,6 +440,18 @@ typedef struct _THEME_CONTROLWMNOTIFY_RESULTS
431 LRESULT lResult; 440 LRESULT lResult;
432} THEME_CONTROLWMNOTIFY_RESULTS; 441} THEME_CONTROLWMNOTIFY_RESULTS;
433 442
443typedef struct _THEME_LOADEDCONTROL_ARGS
444{
445 DWORD cbSize;
446 const THEME_CONTROL* pThemeControl;
447} THEME_LOADEDCONTROL_ARGS;
448
449typedef struct _THEME_LOADEDCONTROL_RESULTS
450{
451 DWORD cbSize;
452 HRESULT hr;
453} THEME_LOADEDCONTROL_RESULTS;
454
434typedef struct _THEME_LOADINGCONTROL_ARGS 455typedef struct _THEME_LOADINGCONTROL_ARGS
435{ 456{
436 DWORD cbSize; 457 DWORD cbSize;
@@ -528,24 +549,6 @@ HRESULT DAPI ThemeCreateParentWindow(
528 ); 549 );
529 550
530/******************************************************************** 551/********************************************************************
531 ThemeLoadControls - creates the windows for all the theme controls
532 using the window created in ThemeCreateParentWindow.
533
534*******************************************************************/
535HRESULT DAPI ThemeLoadControls(
536 __in THEME* pTheme
537 );
538
539/********************************************************************
540 ThemeUnloadControls - resets all the theme control windows so the theme
541 controls can be reloaded.
542
543*******************************************************************/
544void DAPI ThemeUnloadControls(
545 __in THEME* pTheme
546 );
547
548/********************************************************************
549 ThemeLocalize - Localizes all of the strings in the theme. 552 ThemeLocalize - Localizes all of the strings in the theme.
550 553
551*******************************************************************/ 554*******************************************************************/
diff --git a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
index a3c5d80c..068638f6 100644
--- a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
@@ -205,6 +205,7 @@ static HRESULT ParseControl(
205 __in_opt THEME_PAGE* pPage 205 __in_opt THEME_PAGE* pPage
206 ); 206 );
207static void InitializeThemeControl( 207static void InitializeThemeControl(
208 THEME* pTheme,
208 THEME_CONTROL* pControl 209 THEME_CONTROL* pControl
209 ); 210 );
210static HRESULT ParseActions( 211static HRESULT ParseActions(
@@ -273,6 +274,12 @@ static HRESULT FindImageList(
273 __in_z LPCWSTR wzImageListName, 274 __in_z LPCWSTR wzImageListName,
274 __out HIMAGELIST *phImageList 275 __out HIMAGELIST *phImageList
275 ); 276 );
277static HRESULT LoadThemeControls(
278 __in THEME* pTheme
279 );
280static void UnloadThemeControls(
281 __in THEME* pTheme
282 );
276static HRESULT OnLoadingControl( 283static HRESULT OnLoadingControl(
277 __in THEME* pTheme, 284 __in THEME* pTheme,
278 __in const THEME_CONTROL* pControl, 285 __in const THEME_CONTROL* pControl,
@@ -452,6 +459,10 @@ static BOOL OnNotifyEnMsgFilter(
452 __in const THEME_CONTROL* pThemeControl, 459 __in const THEME_CONTROL* pThemeControl,
453 __in MSGFILTER* msgFilter 460 __in MSGFILTER* msgFilter
454 ); 461 );
462static BOOL OnPanelCreate(
463 __in THEME_CONTROL* pControl,
464 __in HWND hWnd
465 );
455static BOOL OnWmCommand( 466static BOOL OnWmCommand(
456 __in THEME* pTheme, 467 __in THEME* pTheme,
457 __in WPARAM wParam, 468 __in WPARAM wParam,
@@ -888,35 +899,6 @@ LExit:
888 return hr; 899 return hr;
889} 900}
890 901
891
892DAPI_(HRESULT) ThemeLoadControls(
893 __in THEME* pTheme
894 )
895{
896 HRESULT hr = S_OK;
897
898 if (!pTheme->hwndParent)
899 {
900 ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created.");
901 }
902
903 hr = LoadControls(pTheme, NULL);
904
905LExit:
906 return hr;
907}
908
909
910DAPI_(void) ThemeUnloadControls(
911 __in THEME* pTheme
912 )
913{
914 UnloadControls(pTheme->cControls, pTheme->rgControls);
915
916 pTheme->hwndHover = NULL;
917 pTheme->hwndParent = NULL;
918}
919
920DAPI_(HRESULT) ThemeLocalize( 902DAPI_(HRESULT) ThemeLocalize(
921 __in THEME *pTheme, 903 __in THEME *pTheme,
922 __in const WIX_LOCALIZATION *pWixLoc 904 __in const WIX_LOCALIZATION *pWixLoc
@@ -1089,6 +1071,17 @@ extern "C" LRESULT CALLBACK ThemeDefWindowProc(
1089 } 1071 }
1090 break; 1072 break;
1091 1073
1074 case WM_CREATE:
1075 if (FAILED(LoadThemeControls(pTheme)))
1076 {
1077 return -1;
1078 }
1079 break;
1080
1081 case WM_DESTROY:
1082 UnloadThemeControls(pTheme);
1083 break;
1084
1092 case WM_NCHITTEST: 1085 case WM_NCHITTEST:
1093 if (pTheme->dwStyle & WS_POPUP) 1086 if (pTheme->dwStyle & WS_POPUP)
1094 { 1087 {
@@ -3406,7 +3399,7 @@ static HRESULT ParseControl(
3406 BOOL fAnyTextChildren = FALSE; 3399 BOOL fAnyTextChildren = FALSE;
3407 BOOL fAnyNoteChildren = FALSE; 3400 BOOL fAnyNoteChildren = FALSE;
3408 3401
3409 InitializeThemeControl(pControl); 3402 InitializeThemeControl(pTheme, pControl);
3410 3403
3411 hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName); 3404 hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName);
3412 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control Name attribute."); 3405 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control Name attribute.");
@@ -3715,6 +3708,7 @@ LExit:
3715} 3708}
3716 3709
3717static void InitializeThemeControl( 3710static void InitializeThemeControl(
3711 THEME* pTheme,
3718 THEME_CONTROL* pControl 3712 THEME_CONTROL* pControl
3719 ) 3713 )
3720{ 3714{
@@ -3722,6 +3716,7 @@ static void InitializeThemeControl(
3722 pControl->dwFontId = THEME_INVALID_ID; 3716 pControl->dwFontId = THEME_INVALID_ID;
3723 pControl->dwFontSelectedId = THEME_INVALID_ID; 3717 pControl->dwFontSelectedId = THEME_INVALID_ID;
3724 pControl->uStringId = UINT_MAX; 3718 pControl->uStringId = UINT_MAX;
3719 pControl->pTheme = pTheme;
3725} 3720}
3726 3721
3727 3722
@@ -3854,7 +3849,7 @@ static HRESULT ParseBillboardPanels(
3854 pControl = pParentControl->rgControls + pParentControl->cControls; 3849 pControl = pParentControl->rgControls + pParentControl->cControls;
3855 pParentControl->cControls += 1; 3850 pParentControl->cControls += 1;
3856 pControl->type = THEME_CONTROL_TYPE_PANEL; 3851 pControl->type = THEME_CONTROL_TYPE_PANEL;
3857 InitializeThemeControl(pControl); 3852 InitializeThemeControl(pTheme, pControl);
3858 3853
3859 if (pPage) 3854 if (pPage)
3860 { 3855 {
@@ -5400,6 +5395,24 @@ static BOOL OnNotifyEnMsgFilter(
5400 return fProcessed; 5395 return fProcessed;
5401} 5396}
5402 5397
5398static BOOL OnPanelCreate(
5399 __in THEME_CONTROL* pControl,
5400 __in HWND hWnd
5401 )
5402{
5403 HRESULT hr = S_OK;
5404
5405 ThmExitOnNull(pControl, hr, E_INVALIDSTATE, "Null control for OnPanelCreate");
5406
5407 pControl->hWnd = hWnd;
5408
5409 hr = LoadControls(pControl->pTheme, pControl);
5410 ThmExitOnFailure(hr, "Failed to load panel controls.");
5411
5412LExit:
5413 return SUCCEEDED(hr);
5414}
5415
5403static BOOL OnWmCommand( 5416static BOOL OnWmCommand(
5404 __in THEME* pTheme, 5417 __in THEME* pTheme,
5405 __in WPARAM wParam, 5418 __in WPARAM wParam,
@@ -6017,17 +6030,24 @@ static LRESULT CALLBACK PanelWndProc(
6017 ) 6030 )
6018{ 6031{
6019 LRESULT lres = 0; 6032 LRESULT lres = 0;
6020 THEME* pTheme = reinterpret_cast<THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); 6033 THEME_CONTROL* pControl = reinterpret_cast<THEME_CONTROL*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
6021 6034
6022 switch (uMsg) 6035 switch (uMsg)
6023 { 6036 {
6024 case WM_NCCREATE: 6037 case WM_NCCREATE:
6025 { 6038 {
6026 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam); 6039 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
6027 pTheme = reinterpret_cast<THEME*>(lpcs->lpCreateParams); 6040 pControl = reinterpret_cast<THEME_CONTROL*>(lpcs->lpCreateParams);
6028 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pTheme)); 6041 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pControl));
6042 break;
6029 } 6043 }
6030 break; 6044
6045 case WM_CREATE:
6046 if (!OnPanelCreate(pControl, hWnd))
6047 {
6048 return -1;
6049 }
6050 break;
6031 6051
6032 case WM_NCDESTROY: 6052 case WM_NCDESTROY:
6033 lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); 6053 lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
@@ -6039,7 +6059,7 @@ static LRESULT CALLBACK PanelWndProc(
6039 break; 6059 break;
6040 } 6060 }
6041 6061
6042 return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam); 6062 return ControlGroupDefWindowProc(pControl ? pControl->pTheme : NULL, hWnd, uMsg, wParam, lParam);
6043} 6063}
6044 6064
6045static LRESULT CALLBACK StaticOwnerDrawWndProc( 6065static LRESULT CALLBACK StaticOwnerDrawWndProc(
@@ -6087,6 +6107,30 @@ static HRESULT OnLoadingControl(
6087 return hr; 6107 return hr;
6088} 6108}
6089 6109
6110static HRESULT LoadThemeControls(
6111 __in THEME* pTheme
6112 )
6113{
6114 HRESULT hr = S_OK;
6115
6116 ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "LoadThemeControls called before theme parent window created.");
6117
6118 hr = LoadControls(pTheme, NULL);
6119
6120LExit:
6121 return hr;
6122}
6123
6124static void UnloadThemeControls(
6125 __in THEME* pTheme
6126 )
6127{
6128 UnloadControls(pTheme->cControls, pTheme->rgControls);
6129
6130 pTheme->hwndHover = NULL;
6131 pTheme->hwndParent = NULL;
6132}
6133
6090static HRESULT LoadControls( 6134static HRESULT LoadControls(
6091 __in THEME* pTheme, 6135 __in THEME* pTheme,
6092 __in_opt THEME_CONTROL* pParentControl 6136 __in_opt THEME_CONTROL* pParentControl
@@ -6103,10 +6147,15 @@ static HRESULT LoadControls(
6103 int h = 0; 6147 int h = 0;
6104 int x = 0; 6148 int x = 0;
6105 int y = 0; 6149 int y = 0;
6150 THEME_LOADEDCONTROL_ARGS loadedControlArgs = { };
6151 THEME_LOADEDCONTROL_RESULTS loadedControlResults = { };
6106 6152
6107 GetControls(pTheme, pParentControl, cControls, rgControls); 6153 GetControls(pTheme, pParentControl, cControls, rgControls);
6108 ::GetClientRect(hwndParent, &rcParent); 6154 ::GetClientRect(hwndParent, &rcParent);
6109 6155
6156 loadedControlArgs.cbSize = sizeof(loadedControlArgs);
6157 loadedControlResults.cbSize = sizeof(loadedControlResults);
6158
6110 for (DWORD i = 0; i < cControls; ++i) 6159 for (DWORD i = 0; i < cControls; ++i)
6111 { 6160 {
6112 THEME_CONTROL* pControl = rgControls + i; 6161 THEME_CONTROL* pControl = rgControls + i;
@@ -6124,8 +6173,7 @@ static HRESULT LoadControls(
6124 6173
6125 switch (pControl->type) 6174 switch (pControl->type)
6126 { 6175 {
6127 case THEME_CONTROL_TYPE_BILLBOARD: 6176 case THEME_CONTROL_TYPE_BILLBOARD: __fallthrough;
6128 __fallthrough;
6129 case THEME_CONTROL_TYPE_PANEL: 6177 case THEME_CONTROL_TYPE_PANEL:
6130 wzWindowClass = vsczPanelClass; 6178 wzWindowClass = vsczPanelClass;
6131 dwWindowExBits |= WS_EX_CONTROLPARENT; 6179 dwWindowExBits |= WS_EX_CONTROLPARENT;
@@ -6309,7 +6357,7 @@ static HRESULT LoadControls(
6309 pControl->dwStyle &= ~WS_VISIBLE; 6357 pControl->dwStyle &= ~WS_VISIBLE;
6310 } 6358 }
6311 6359
6312 pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast<HMENU>(wControlId), NULL, pTheme); 6360 pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast<HMENU>(wControlId), NULL, pControl);
6313 ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window."); 6361 ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window.");
6314 6362
6315 if (pControl->sczTooltip) 6363 if (pControl->sczTooltip)
@@ -6445,10 +6493,15 @@ static HRESULT LoadControls(
6445 } 6493 }
6446 } 6494 }
6447 6495
6448 if (pControl->cControls) 6496 loadedControlArgs.pThemeControl = pControl;
6497 loadedControlResults.hr = E_NOTIMPL;
6498 if (::SendMessageW(pTheme->hwndParent, WM_THMUTIL_LOADED_CONTROL, reinterpret_cast<WPARAM>(&loadedControlArgs), reinterpret_cast<LPARAM>(&loadedControlResults)))
6449 { 6499 {
6450 hr = LoadControls(pTheme, pControl); 6500 if (E_NOTIMPL != loadedControlResults.hr)
6451 ThmExitOnFailure(hr, "Failed to load child controls."); 6501 {
6502 hr = loadedControlResults.hr;
6503 ThmExitOnFailure(hr, "ThmLoadedControl failed");
6504 }
6452 } 6505 }
6453 } 6506 }
6454 6507
diff --git a/src/samples/thmviewer/display.cpp b/src/samples/thmviewer/display.cpp
index c0e6c7e1..e64f79c6 100644
--- a/src/samples/thmviewer/display.cpp
+++ b/src/samples/thmviewer/display.cpp
@@ -21,9 +21,10 @@ static LRESULT CALLBACK DisplayWndProc(
21 __in WPARAM wParam, 21 __in WPARAM wParam,
22 __in LPARAM lParam 22 __in LPARAM lParam
23 ); 23 );
24static BOOL DisplayOnCreate( 24static BOOL DisplayOnThmLoadedControl(
25 __in THEME* pTheme, 25 __in THEME* pTheme,
26 __in HWND hWnd 26 __in const THEME_LOADEDCONTROL_ARGS* args,
27 __in THEME_LOADEDCONTROL_RESULTS* results
27 ); 28 );
28 29
29 30
@@ -270,13 +271,6 @@ static LRESULT CALLBACK DisplayWndProc(
270 } 271 }
271 break; 272 break;
272 273
273 case WM_CREATE:
274 if (!DisplayOnCreate(pHandleTheme->pTheme, hWnd))
275 {
276 return -1;
277 }
278 break;
279
280 case WM_TIMER: 274 case WM_TIMER:
281 if (!lParam && SUCCEEDED(ThemeSetProgressControl(pHandleTheme->pTheme, wParam, dwProgress))) 275 if (!lParam && SUCCEEDED(ThemeSetProgressControl(pHandleTheme->pTheme, wParam, dwProgress)))
282 { 276 {
@@ -308,47 +302,45 @@ static LRESULT CALLBACK DisplayWndProc(
308 } 302 }
309 break; 303 break;
310 304
311 case WM_DESTROY:
312 ThemeUnloadControls(pHandleTheme->pTheme);
313 ::PostQuitMessage(0);
314 break;
315
316 case WM_NCDESTROY: 305 case WM_NCDESTROY:
317 DecrementHandleTheme(pHandleTheme); 306 DecrementHandleTheme(pHandleTheme);
318 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); 307 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
308 ::PostQuitMessage(0);
319 break; 309 break;
310
311 case WM_THMUTIL_LOADED_CONTROL:
312 if (pHandleTheme)
313 {
314 return DisplayOnThmLoadedControl(pHandleTheme->pTheme, reinterpret_cast<THEME_LOADEDCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADEDCONTROL_RESULTS*>(lParam));
315 }
320 } 316 }
321 317
322 return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam); 318 return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam);
323} 319}
324 320
325static BOOL DisplayOnCreate( 321static BOOL DisplayOnThmLoadedControl(
326 __in THEME* pTheme, 322 __in THEME* pTheme,
327 __in HWND hWnd 323 __in const THEME_LOADEDCONTROL_ARGS* args,
324 __in THEME_LOADEDCONTROL_RESULTS* results
328 ) 325 )
329{ 326{
330 HRESULT hr = S_OK; 327 HRESULT hr = S_OK;
331 328 const THEME_CONTROL* pControl = args->pThemeControl;
332 hr = ThemeLoadControls(pTheme);
333 ExitOnFailure(hr, "Failed to load theme controls");
334 329
335 // Pre-populate some control types with data. 330 // Pre-populate some control types with data.
336 for (DWORD i = 0; i < pTheme->cControls; ++i) 331 if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
337 { 332 {
338 THEME_CONTROL* pControl = pTheme->rgControls + i; 333 hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL));
339 if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type) 334 ExitOnFailure(hr, "Failed to load richedit text.");
340 { 335 }
341 hr = ThemeLoadRichEditFromResource(pTheme, pControl->wId, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL)); 336 else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
342 ExitOnFailure(hr, "Failed to load richedit text."); 337 {
343 } 338 DWORD dwId = ::SetTimer(pTheme->hwndParent, pControl->wId, 500, NULL);
344 else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type) 339 dwId = dwId; // prevents warning in "ship" build.
345 { 340 Assert(dwId == pControl->wId);
346 DWORD dwId = ::SetTimer(hWnd, pControl->wId, 500, NULL);
347 dwId = dwId; // prevents warning in "ship" build.
348 Assert(dwId == pControl->wId);
349 }
350 } 341 }
351 342
352LExit: 343LExit:
353 return SUCCEEDED(hr); 344 results->hr = hr;
345 return TRUE;
354} 346}
diff --git a/src/samples/thmviewer/thmviewer.cpp b/src/samples/thmviewer/thmviewer.cpp
index e593d6ad..1d941323 100644
--- a/src/samples/thmviewer/thmviewer.cpp
+++ b/src/samples/thmviewer/thmviewer.cpp
@@ -353,16 +353,7 @@ static LRESULT CALLBACK MainWndProc(
353 case WM_NCDESTROY: 353 case WM_NCDESTROY:
354 DecrementHandleTheme(pHandleTheme); 354 DecrementHandleTheme(pHandleTheme);
355 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); 355 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
356 break; 356 ::PostQuitMessage(0);
357
358 case WM_CREATE:
359 {
360 HRESULT hr = ThemeLoadControls(vpTheme);
361 if (FAILED(hr))
362 {
363 return -1;
364 }
365 }
366 break; 357 break;
367 358
368 case WM_THMVWR_THEME_LOAD_BEGIN: 359 case WM_THMVWR_THEME_LOAD_BEGIN:
@@ -377,10 +368,6 @@ static LRESULT CALLBACK MainWndProc(
377 OnNewTheme(vpTheme, hWnd, reinterpret_cast<HANDLE_THEME*>(lParam)); 368 OnNewTheme(vpTheme, hWnd, reinterpret_cast<HANDLE_THEME*>(lParam));
378 return 0; 369 return 0;
379 370
380 case WM_DESTROY:
381 ::PostQuitMessage(0);
382 break;
383
384 case WM_THMUTIL_LOADING_CONTROL: 371 case WM_THMUTIL_LOADING_CONTROL:
385 return OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam)); 372 return OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam));
386 373
diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
index a5bcba3e..8304403a 100644
--- a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
+++ b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
@@ -230,18 +230,14 @@ private:
230 return lres; 230 return lres;
231 } 231 }
232 232
233 case WM_CREATE:
234 if (!pBaf->OnCreate(hWnd))
235 {
236 return -1;
237 }
238 break;
239
240 case WM_THMUTIL_LOADING_CONTROL: 233 case WM_THMUTIL_LOADING_CONTROL:
241 return pBaf->OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam)); 234 return pBaf->OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam));
242 235
236 case WM_THMUTIL_LOADED_CONTROL:
237 return pBaf->OnThemeLoadedControl(hWnd, reinterpret_cast<THEME_LOADEDCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADEDCONTROL_RESULTS*>(lParam));
238
243 case WM_TIMER: 239 case WM_TIMER:
244 if (!lParam && pBaf) 240 if (!lParam && BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_IMAGE == wParam && pBaf)
245 { 241 {
246 pBaf->UpdateProgressBarProgress(); 242 pBaf->UpdateProgressBarProgress();
247 243
@@ -253,33 +249,14 @@ private:
253 return ThemeDefWindowProc(pBaf ? pBaf->m_pBafTheme : NULL, hWnd, uMsg, wParam, lParam); 249 return ThemeDefWindowProc(pBaf ? pBaf->m_pBafTheme : NULL, hWnd, uMsg, wParam, lParam);
254 } 250 }
255 251
256 BOOL OnCreate( 252 HRESULT OnCreatedListView(
257 __in HWND hWnd 253 __in HWND hWndListView
258 ) 254 )
259 { 255 {
260 HRESULT hr = S_OK; 256 HRESULT hr = S_OK;
261 LVITEMW lvitem = { }; 257 LVITEMW lvitem = { };
262 LVGROUP lvgroup = { }; 258 LVGROUP lvgroup = { };
263 static UINT puColumns[] = { 0, 1, 2 }; 259 static UINT puColumns[] = { 0, 1, 2 };
264 HWND hwndTopLeft = NULL;
265 HWND hwndTopRight = NULL;
266 HWND hwndBottomLeft = NULL;
267 HWND hwndBottomRight = NULL;
268
269 hr = ThemeLoadControls(m_pBafTheme);
270 BalExitOnFailure(hr, "Failed to load theme controls.");
271
272 hwndTopLeft = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_LEFT);
273 BalExitOnNull(hwndTopLeft, hr, E_INVALIDSTATE, "Failed to get top left list view hWnd.");
274
275 hwndTopRight = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_RIGHT);
276 BalExitOnNull(hwndTopRight, hr, E_INVALIDSTATE, "Failed to get top right list view hWnd.");
277
278 hwndBottomLeft = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_LEFT);
279 BalExitOnNull(hwndBottomLeft, hr, E_INVALIDSTATE, "Failed to get bottom left list view hWnd.");
280
281 hwndBottomRight = ::GetDlgItem(m_pBafTheme->hwndParent, BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_RIGHT);
282 BalExitOnNull(hwndBottomRight, hr, E_INVALIDSTATE, "Failed to get bottom right list view hWnd.");
283 260
284 lvgroup.cbSize = sizeof(LVGROUP); 261 lvgroup.cbSize = sizeof(LVGROUP);
285 lvgroup.mask = LVGF_GROUPID | LVGF_TITLEIMAGE | LVGF_DESCRIPTIONTOP | LVGF_HEADER; 262 lvgroup.mask = LVGF_GROUPID | LVGF_TITLEIMAGE | LVGF_DESCRIPTIONTOP | LVGF_HEADER;
@@ -295,10 +272,7 @@ private:
295 hr = StrAllocFormatted(&lvgroup.pszHeader, L"Header_%d", i); 272 hr = StrAllocFormatted(&lvgroup.pszHeader, L"Header_%d", i);
296 BalExitOnFailure(hr, "Failed to alloc list view group header."); 273 BalExitOnFailure(hr, "Failed to alloc list view group header.");
297 274
298 ListView_InsertGroup(hwndTopLeft, -1, &lvgroup); 275 ListView_InsertGroup(hWndListView, -1, &lvgroup);
299 ListView_InsertGroup(hwndTopRight, -1, &lvgroup);
300 ListView_InsertGroup(hwndBottomLeft, -1, &lvgroup);
301 ListView_InsertGroup(hwndBottomRight, -1, &lvgroup);
302 276
303 lvitem.mask = LVIF_COLUMNS | LVIF_GROUPID | LVIF_IMAGE | LVIF_TEXT; 277 lvitem.mask = LVIF_COLUMNS | LVIF_GROUPID | LVIF_IMAGE | LVIF_TEXT;
304 lvitem.iItem = i; 278 lvitem.iItem = i;
@@ -312,10 +286,7 @@ private:
312 lvitem.cColumns = countof(puColumns); 286 lvitem.cColumns = countof(puColumns);
313 lvitem.puColumns = puColumns; 287 lvitem.puColumns = puColumns;
314 288
315 ListView_InsertItem(hwndTopLeft, &lvitem); 289 ListView_InsertItem(hWndListView, &lvitem);
316 ListView_InsertItem(hwndTopRight, &lvitem);
317 ListView_InsertItem(hwndBottomLeft, &lvitem);
318 ListView_InsertItem(hwndBottomRight, &lvitem);
319 290
320 for (int j = 0; j < 3; ++j) 291 for (int j = 0; j < 3; ++j)
321 { 292 {
@@ -325,23 +296,16 @@ private:
325 hr = StrAllocFormatted(&lvitem.pszText, L"%d_%d", j, i); 296 hr = StrAllocFormatted(&lvitem.pszText, L"%d_%d", j, i);
326 BalExitOnFailure(hr, "Failed to alloc list view subitem text."); 297 BalExitOnFailure(hr, "Failed to alloc list view subitem text.");
327 298
328 ListView_InsertItem(hwndTopLeft, &lvitem); 299 ListView_InsertItem(hWndListView, &lvitem);
329 ListView_InsertItem(hwndTopRight, &lvitem);
330 ListView_InsertItem(hwndBottomLeft, &lvitem);
331 ListView_InsertItem(hwndBottomRight, &lvitem);
332 } 300 }
333 } 301 }
334 302
335 ListView_EnableGroupView(hwndTopRight, TRUE);
336
337 ::SetTimer(hWnd, BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_IMAGE, 500, NULL);
338
339 LExit: 303 LExit:
340 ReleaseStr(lvgroup.pszDescriptionTop); 304 ReleaseStr(lvgroup.pszDescriptionTop);
341 ReleaseStr(lvgroup.pszHeader); 305 ReleaseStr(lvgroup.pszHeader);
342 ReleaseStr(lvitem.pszText); 306 ReleaseStr(lvitem.pszText);
343 307
344 return SUCCEEDED(hr); 308 return hr;
345 } 309 }
346 310
347 BOOL OnThemeLoadingControl( 311 BOOL OnThemeLoadingControl(
@@ -349,17 +313,60 @@ private:
349 __in THEME_LOADINGCONTROL_RESULTS* pResults 313 __in THEME_LOADINGCONTROL_RESULTS* pResults
350 ) 314 )
351 { 315 {
316 HRESULT hr = S_OK;
317 BOOL fProcessed = FALSE;
318
352 for (DWORD iAssignControl = 0; iAssignControl < countof(vrgInitControls); ++iAssignControl) 319 for (DWORD iAssignControl = 0; iAssignControl < countof(vrgInitControls); ++iAssignControl)
353 { 320 {
354 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pArgs->pThemeControl->sczName, -1, vrgInitControls[iAssignControl].wzName, -1)) 321 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pArgs->pThemeControl->sczName, -1, vrgInitControls[iAssignControl].wzName, -1))
355 { 322 {
323 fProcessed = TRUE;
356 pResults->wId = vrgInitControls[iAssignControl].wId; 324 pResults->wId = vrgInitControls[iAssignControl].wId;
357 break; 325 break;
358 } 326 }
359 } 327 }
360 328
361 pResults->hr = S_OK; 329 pResults->hr = hr;
362 return TRUE; 330 return fProcessed || FAILED(hr);
331 }
332
333 BOOL OnThemeLoadedControl(
334 __in HWND hWndParent,
335 __in const THEME_LOADEDCONTROL_ARGS* pArgs,
336 __in THEME_LOADEDCONTROL_RESULTS* pResults
337 )
338 {
339 HRESULT hr = S_OK;
340 BOOL fProcessed = FALSE;
341
342 switch (pArgs->pThemeControl->wId)
343 {
344 case BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_LEFT:
345 case BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_RIGHT:
346 case BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_LEFT:
347 case BAFTHMUTILTESTING_CONTROL_LISTVIEW_BOTTOM_RIGHT:
348 fProcessed = TRUE;
349
350 hr = OnCreatedListView(pArgs->pThemeControl->hWnd);
351 ExitOnFailure(hr, "Failed to populate list view.");
352
353 if (BAFTHMUTILTESTING_CONTROL_LISTVIEW_TOP_RIGHT == pArgs->pThemeControl->wId)
354 {
355 ListView_EnableGroupView(pArgs->pThemeControl->hWnd, TRUE);
356 }
357
358 break;
359
360 case BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_STANDARD:
361 fProcessed = TRUE;
362
363 ::SetTimer(hWndParent, BAFTHMUTILTESTING_CONTROL_PROGRESSBAR_IMAGE, 500, NULL);
364 break;
365 }
366
367 LExit:
368 pResults->hr = hr;
369 return fProcessed || FAILED(hr);
363 } 370 }
364 371
365 void UpdateProgressBarProgress() 372 void UpdateProgressBarProgress()
diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml b/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml
index 0d0dabf5..7491ab1c 100644
--- a/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml
+++ b/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingTheme.xml
@@ -124,6 +124,27 @@ There are currently four states for a button: default, focus, hover, and selecte
124 <Text>Back</Text> 124 <Text>Back</Text>
125 <ChangePageAction Page="Button" /> 125 <ChangePageAction Page="Button" />
126 </Button> 126 </Button>
127 <Button Name="BillboardButton" X="-183" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
128 <Text>Next</Text>
129 <ChangePageAction Page="Billboard" />
130 </Button>
131 </Page>
132 <Page Name="Billboard">
133 <Label X="6" Y="6" Width="-6" Height="43" FontId="Default">
134 This page has a billboard. It loops between two panels every 1.5 seconds. Only one button should be visible at all times (this is currently broken).
135 </Label>
136 <Billboard Name="FirstBillboard" X="11" Y="59" Width="-11" Height="-39" Interval="1500" Loop="yes">
137 <BillboardPanel>
138 <Button Name="FirstBillboardButton1" X="11" Y="11" Width="75" Height="23" TabStop="yes" FontId="Default">First Panel</Button>
139 </BillboardPanel>
140 <BillboardPanel>
141 <Button Name="FirstBillboardButton2" X="11" Y="39" Width="75" Height="23" TabStop="yes" FontId="Default">Second Panel</Button>
142 </BillboardPanel>
143 </Billboard>
144 <Button Name="BillboardBackButton" X="-269" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
145 <Text>Back</Text>
146 <ChangePageAction Page="Progressbar" />
147 </Button>
127 </Page> 148 </Page>
128 <Button Name="StartButton" X="-97" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default" Visible="yes"> 149 <Button Name="StartButton" X="-97" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default" Visible="yes">
129 <Text>Start</Text> 150 <Text>Start</Text>
diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingThemeLoose.xml b/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingThemeLoose.xml
new file mode 100644
index 00000000..7e360544
--- /dev/null
+++ b/src/test/burn/TestData/Manual/BafThmutilTesting/theme/BafThmUtilTestingThemeLoose.xml
@@ -0,0 +1,158 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4
5<Theme xmlns="http://wixtoolset.org/schemas/v4/thmutil">
6 <Font Id="Default" Height="-12" Weight="500" Foreground="windowtext" Background="window">Segoe UI</Font>
7 <Image Id="star_transparent.bmp" ImageFile="star_transparent.bmp" />
8 <Image Id="star_opaque.bmp" ImageFile="star_opaque.bmp" />
9 <Image Id="star_transparent.png" ImageFile="star_transparent.png" />
10 <Image Id="square_default" ImageFile="16x16.png">
11 <AlternateResolution ImageFile="32x32.png" />
12 <AlternateResolution ImageFile="64x64.png" />
13 <AlternateResolution ImageFile="128x128.png" />
14 <AlternateResolution ImageFile="256x256.png" />
15 </Image>
16 <Image Id="square_focus" ImageFile="64x64_focus.png">
17 <AlternateResolution ImageFile="128x128_focus.png" />
18 <AlternateResolution ImageFile="256x256_focus.png" />
19 </Image>
20 <Image Id="square_hover" ImageFile="64x64_hover.png">
21 <AlternateResolution ImageFile="128x128_hover.png" />
22 <AlternateResolution ImageFile="256x256_hover.png" />
23 </Image>
24 <Image Id="square_selected" ImageFile="64x64_selected.png">
25 <AlternateResolution ImageFile="128x128_selected.png" />
26 <AlternateResolution ImageFile="256x256_selected.png" />
27 </Image>
28 <Image Id="progressbar" ImageFile="progressbar.bmp" />
29 <Image Id="progressbar_reverse" ImageFile="progressbar_reverse.bmp" />
30
31 <Window Width="600" Height="450" FontId="Default" Caption="BafThmUtilTestingTheme" HexStyle="10cf0000" AutoResize="yes">
32 <ImageList Name="Stars">
33 <ImageListItem ImageFile="star_transparent.bmp" />
34 <ImageListItem ImageFile="star_opaque.bmp" />
35 <ImageListItem ImageFile="star_transparent.png" />
36 </ImageList>
37 <Page Name="Transparency">
38 <Label X="6" Y="6" Width="-6" Height="94" FontId="Default">
39 This page has three versions of an image. The top image is a bitmap with a transparent background, the yellow star should be visible but its black background should not (this is currently broken). The middle image is the same bitmap except the black background is fully opaque so the yellow star should be visible on a black background. The bottom image in a PNG version of the top image and should look exactly the same.
40 </Label>
41 <ImageControl X="6" Y="106" Width="64" Height="64" ImageId="star_transparent.bmp" />
42 <ImageControl X="6" Y="176" Width="64" Height="64" ImageId="star_opaque.bmp" />
43 <ImageControl X="6" Y="246" Width="64" Height="64" ImageId="star_transparent.png" />
44 <Button Name="ListViewButton" X="-183" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
45 <Text>Next</Text>
46 <ChangePageAction Page="ListView" />
47 </Button>
48 </Page>
49 <Page Name="ListView">
50 <Label X="6" Y="6" Width="-6" Height="18" FontId="Default">
51 This page is for testing ListView.
52 </Label>
53 <ListView Name="ListViewTopLeft" X="6" Y="30" Width="288" Height="176" FontId="Default" HexStyle="00" ImageList="Stars">
54 <Column Width="100">TL One</Column>
55 <Column Width="100">TL Two</Column>
56 <Column Width="88">TL Three</Column>
57 </ListView>
58 <ListView Name="ListViewTopRight" X="300" Y="30" Width="-11" Height="176" FontId="Default" HexStyle="01" ImageListGroupHeader="Stars">
59 <Column Width="100">TR One</Column>
60 <Column Width="100">TR Two</Column>
61 <Column Width="88" Expands="yes">TR Exp</Column>
62 </ListView>
63 <ListView Name="ListViewBottomLeft" X="6" Y="212" Width="288" Height="176" FontId="Default" HexStyle="02" ImageListSmall="Stars">
64 <Column Width="100">BL One</Column>
65 <Column Width="100">BL Two</Column>
66 <Column Width="88">BL Three</Column>
67 </ListView>
68 <ListView Name="ListViewBottomRight" X="300" Y="212" Width="-11" Height="176" FontId="Default" HexStyle="03">
69 <Column Width="100">BR One</Column>
70 <Column Width="100">BR Two</Column>
71 <Column Width="88" Expands="yes">BR Exp</Column>
72 </ListView>
73 <Button Name="ListViewBackButton" X="-269" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
74 <Text>Back</Text>
75 <ChangePageAction Page="Transparency" />
76 </Button>
77 <Button Name="ButtonButton" X="-183" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
78 <Text>Next</Text>
79 <ChangePageAction Page="Button" />
80 </Button>
81 </Page>
82 <Page Name="Button">
83 <Label X="6" Y="6" Width="-6" Height="132" FontId="Default">
84 This page is for testing graphical buttons. The left image is 16x16 at 100% scaling, the middle image is 32x32 at 100% scaling. The images for the buttons display their dimensions, so the left should be 16x16 at 100% but 32x32 at 200%. The right button starts at 32x32 at 100%, but grows and shrinks with the window and its image should also change.
85
86There are currently four states for a button: default, focus, hover, and selected (in that priority order). There should be a different image for each state, although the left button should have a focus rectangle over the default image since it didn't specify an image for the focused state.
87 </Label>
88 <Button Name="Square16Button" X="6" Y="140" Width="16" Height="16" FontId="Default" TabStop="yes">
89 <ButtonImage ImageId="square_default" />
90 <ButtonHoverImage ImageId="square_hover" />
91 <ButtonSelectedImage ImageId="square_selected" />
92 </Button>
93 <Button Name="Square32Button" X="54" Y="140" Width="32" Height="32" FontId="Default" TabStop="yes">
94 <ButtonImage ImageId="square_default" />
95 <ButtonFocusImage ImageId="square_focus" />
96 <ButtonHoverImage ImageId="square_hover" />
97 <ButtonSelectedImage ImageId="square_selected" />
98 </Button>
99 <Button Name="SquareExpandButton" X="92" Y="140" Width="-476" Height="-278" FontId="Default" TabStop="yes">
100 <ButtonImage ImageId="square_default" />
101 <ButtonFocusImage ImageId="square_focus" />
102 <ButtonHoverImage ImageId="square_hover" />
103 <ButtonSelectedImage ImageId="square_selected" />
104 </Button>
105 <Button Name="ButtonBackButton" X="-269" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
106 <Text>Back</Text>
107 <ChangePageAction Page="ListView" />
108 </Button>
109 <Button Name="ProgressbarButton" X="-183" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
110 <Text>Next</Text>
111 <ChangePageAction Page="Progressbar" />
112 </Button>
113 </Page>
114 <Page Name="Progressbar">
115 <Label X="6" Y="6" Width="-6" Height="94" FontId="Default">
116 This page has two progress bars. The top progress bar is a standard control, the bottom one is custom drawn from an image. The bottom one should have its left side one pixel wide as black, the right side one pixel wide as grey, the progress as green, and the remaining as blue. The colors should be reversed when the progress goes backward (this is a way to test different colors, not a standard feature of thmutil).
117 </Label>
118 <Progressbar Name="StandardProgressBar" X="6" Y="106" Width="-6" Height="23" />
119 <Progressbar Name="ImageProgressBar" X="6" Y="141" Width="-6" Height="23">
120 <ProgressbarImage ImageId="progressbar" />
121 <ProgressbarImage ImageId="progressbar_reverse" />
122 </Progressbar>
123 <Button Name="ProgressbarBackButton" X="-269" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
124 <Text>Back</Text>
125 <ChangePageAction Page="Button" />
126 </Button>
127 <Button Name="BillboardButton" X="-183" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
128 <Text>Next</Text>
129 <ChangePageAction Page="Billboard" />
130 </Button>
131 </Page>
132 <Page Name="Billboard">
133 <Label X="6" Y="6" Width="-6" Height="43" FontId="Default">
134 This page has a billboard. It loops between two panels every 1.5 seconds. Only one button should be visible at all times (this is currently broken).
135 </Label>
136 <Billboard Name="FirstBillboard" X="11" Y="59" Width="-11" Height="-39" Interval="1500" Loop="yes">
137 <BillboardPanel>
138 <Button Name="FirstBillboardButton1" X="11" Y="11" Width="75" Height="23" TabStop="yes" FontId="Default">First Panel</Button>
139 </BillboardPanel>
140 <BillboardPanel>
141 <Button Name="FirstBillboardButton2" X="11" Y="39" Width="75" Height="23" TabStop="yes" FontId="Default">Second Panel</Button>
142 </BillboardPanel>
143 </Billboard>
144 <Button Name="BillboardBackButton" X="-269" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default">
145 <Text>Back</Text>
146 <ChangePageAction Page="Progressbar" />
147 </Button>
148 </Page>
149 <Button Name="StartButton" X="-97" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default" Visible="yes">
150 <Text>Start</Text>
151 <ChangePageAction Page="Transparency" />
152 </Button>
153 <Button Name="CloseButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="Default" Visible="yes">
154 <Text>ThemeClose</Text>
155 <CloseWindowAction />
156 </Button>
157 </Window>
158</Theme>