main
cpp 6,938 lines 211 KB
Raw
1 // 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.
2
3 #include "precomp.h"
4
5
6 // Exit macros
7 #define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
8 #define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
9 #define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
10 #define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
11 #define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
12 #define ThmExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_THMUTIL, x, e, s, __VA_ARGS__)
13 #define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
14 #define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__)
15 #define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__)
16 #define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__)
17 #define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__)
18 #define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__)
19 #define ThmExitOnOptionalXmlQueryFailure(x, b, s, ...) ExitOnOptionalXmlQueryFailureSource(DUTIL_SOURCE_THMUTIL, x, b, s, __VA_ARGS__)
20 #define ThmExitOnRequiredXmlQueryFailure(x, s, ...) ExitOnRequiredXmlQueryFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
21 #define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__)
22
23 #define ThmExitOnUnexpectedAttribute(x, n, e, a) { x = ParseUnexpectedAttribute(n, e, a); if (FAILED(x)) { ExitFunction(); } }
24
25 // from CommCtrl.h
26 #ifndef BS_COMMANDLINK
27 #define BS_COMMANDLINK 0x0000000EL
28 #endif
29
30 #ifndef BCM_SETNOTE
31 #define BCM_SETNOTE (BCM_FIRST + 0x0009)
32 #endif
33
34 #ifndef BCM_SETSHIELD
35 #define BCM_SETSHIELD (BCM_FIRST + 0x000C)
36 #endif
37
38 #ifndef LWS_NOPREFIX
39 #define LWS_NOPREFIX 0x0004
40 #endif
41
42 const WORD THEME_FIRST_AUTO_ASSIGN_CONTROL_ID = 100;
43 const DWORD THEME_INVALID_ID = 0xFFFFFFFF;
44 const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF;
45 const DWORD GROW_FONT_INSTANCES = 3;
46 const DWORD GROW_IMAGE_INSTANCES = 5;
47
48 const LPCWSTR ALL_CONTROL_NAMES = L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView";
49 const LPCWSTR PANEL_CHILD_CONTROL_NAMES = L"Hyperlink|Hypertext|ImageControl|Label|Progressbar|Static";
50
51 static Gdiplus::GdiplusStartupInput vgsi;
52 static Gdiplus::GdiplusStartupOutput vgso = { };
53 static ULONG_PTR vgdiToken = 0;
54 static ULONG_PTR vgdiHookToken = 0;
55 static HMODULE vhHyperlinkRegisteredModule = NULL;
56 static HMODULE vhPanelRegisteredModule = NULL;
57 static HMODULE vhStaticOwnerDrawRegisteredModule = NULL;
58 static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL;
59 static HMODULE vhModuleMsftEdit = NULL;
60 static HMODULE vhModuleRichEd = NULL;
61 static HCURSOR vhCursorHand = NULL;
62 static LPWSTR vsczHyperlinkClass = NULL;
63 static LPWSTR vsczPanelClass = NULL;
64 static LPWSTR vsczStaticOwnerDrawClass = NULL;
65
66 enum INTERNAL_CONTROL_STYLE
67 {
68 INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001,
69 INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002,
70 INTERNAL_CONTROL_STYLE_DISABLED = 0x0004,
71 INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008,
72 INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010,
73 };
74
75
76 // prototypes
77 /********************************************************************
78 ThemeHoverControl - mark a control as hover.
79
80 *******************************************************************/
81 static BOOL ThemeHoverControl(
82 __in THEME* pTheme,
83 __in HWND hwndParent,
84 __in HWND hwndControl
85 );
86
87 /********************************************************************
88 ThemeSetControlColor - sets the color of text for a control.
89
90 *******************************************************************/
91 static BOOL ThemeSetControlColor(
92 __in THEME* pTheme,
93 __in HDC hdc,
94 __in HWND hWnd,
95 __out HBRUSH* phBackgroundBrush
96 );
97
98 static HRESULT RegisterWindowClasses(
99 __in_opt HMODULE hModule
100 );
101 static HRESULT ParseTheme(
102 __in_opt HMODULE hModule,
103 __in_opt LPCWSTR wzRelativePath,
104 __in IXMLDOMDocument* pixd,
105 __out THEME** ppTheme
106 );
107 static HRESULT AddStandaloneImage(
108 __in THEME* pTheme,
109 __in Gdiplus::Bitmap** ppBitmap,
110 __out DWORD* pdwIndex
111 );
112 static HRESULT GetAttributeImageFileOrResource(
113 __in_opt HMODULE hModule,
114 __in_z_opt LPCWSTR wzRelativePath,
115 __in IXMLDOMNode* pElement,
116 __out Gdiplus::Bitmap** ppBitmap
117 );
118 static HRESULT ParseOwnerDrawImage(
119 __in_opt HMODULE hModule,
120 __in_z_opt LPCWSTR wzRelativePath,
121 __in THEME* pTheme,
122 __in IXMLDOMNode* pElement,
123 __in_z LPCWSTR wzElementName,
124 __in THEME_CONTROL* pControl,
125 __in THEME_IMAGE_REFERENCE* pImageRef
126 );
127 static HRESULT ParseButtonImages(
128 __in_opt HMODULE hModule,
129 __in_z_opt LPCWSTR wzRelativePath,
130 __in THEME* pTheme,
131 __in IXMLDOMNode* pElement,
132 __in THEME_CONTROL* pControl
133 );
134 static HRESULT ParseCommandLinkImage(
135 __in_opt HMODULE hModule,
136 __in_z_opt LPCWSTR wzRelativePath,
137 __in IXMLDOMNode* pElement,
138 __in THEME_CONTROL* pControl
139 );
140 static HRESULT ParseProgressBarImages(
141 __in_opt HMODULE hModule,
142 __in_z_opt LPCWSTR wzRelativePath,
143 __in THEME* pTheme,
144 __in IXMLDOMNode* pElement,
145 __in THEME_CONTROL* pControl
146 );
147 static HRESULT GetAttributeCoordinateOrDimension(
148 __in IXMLDOMNode* pixn,
149 __in LPCWSTR wzAttribute,
150 __inout int* pnValue
151 );
152 static HRESULT GetAttributeFontId(
153 __in THEME* pTheme,
154 __in IXMLDOMNode* pixn,
155 __in LPCWSTR wzAttribute,
156 __inout DWORD* pdwValue
157 );
158 static HRESULT GetAttributeImageId(
159 __in THEME* pTheme,
160 __in IXMLDOMNode* pixn,
161 __in LPCWSTR wzAttribute,
162 __inout DWORD* pdwValue
163 );
164 static HRESULT ParseSourceXY(
165 __in IXMLDOMNode* pixn,
166 __in THEME* pTheme,
167 __in int nWidth,
168 __in int nHeight,
169 __inout THEME_IMAGE_REFERENCE* pReference
170 );
171 static HRESULT ParseWindow(
172 __in_opt HMODULE hModule,
173 __in_opt LPCWSTR wzRelativePath,
174 __in IXMLDOMElement* pElement,
175 __in THEME* pTheme
176 );
177 static HRESULT GetFontColor(
178 __in IXMLDOMNode* pixn,
179 __in_z LPCWSTR wzAttributeName,
180 __out COLORREF* pColorRef,
181 __out DWORD* pdwSystemColor
182 );
183 static HRESULT ParseFonts(
184 __in IXMLDOMElement* pElement,
185 __in THEME* pTheme
186 );
187 static HRESULT ParseImages(
188 __in_opt HMODULE hModule,
189 __in_opt LPCWSTR wzRelativePath,
190 __in IXMLDOMElement* pElement,
191 __in THEME* pTheme
192 );
193 static HRESULT ParsePages(
194 __in_opt HMODULE hModule,
195 __in_opt LPCWSTR wzRelativePath,
196 __in IXMLDOMNode* pElement,
197 __in THEME* pTheme
198 );
199 static HRESULT ParseImageLists(
200 __in_opt HMODULE hModule,
201 __in_opt LPCWSTR wzRelativePath,
202 __in IXMLDOMNode* pElement,
203 __in THEME* pTheme
204 );
205 static HRESULT ParseControls(
206 __in_opt HMODULE hModule,
207 __in_opt LPCWSTR wzRelativePath,
208 __in IXMLDOMNode* pElement,
209 __in THEME* pTheme,
210 __in_opt THEME_CONTROL* pParentControl,
211 __in_opt THEME_PAGE* pPage,
212 __in_opt LPCWSTR wzControlNames
213 );
214 static HRESULT ParseControl(
215 __in_opt HMODULE hModule,
216 __in_opt LPCWSTR wzRelativePath,
217 __in IXMLDOMNode* pixn,
218 __in_z LPCWSTR wzElementName,
219 __in THEME* pTheme,
220 __in THEME_CONTROL* pControl,
221 __in_opt THEME_PAGE* pPage
222 );
223 static void InitializeThemeControl(
224 THEME* pTheme,
225 THEME_CONTROL* pControl
226 );
227 static HRESULT ParseActions(
228 __in IXMLDOMNode* pixn,
229 __in THEME_CONTROL* pControl
230 );
231 static HRESULT ParseBillboardPanels(
232 __in_opt HMODULE hModule,
233 __in_opt LPCWSTR wzRelativePath,
234 __in IXMLDOMNode* pElement,
235 __in THEME* pTheme,
236 __in THEME_CONTROL* pParentControl,
237 __in_opt THEME_PAGE* pPage
238 );
239 static HRESULT ParseColumns(
240 __in IXMLDOMNode* pixn,
241 __in THEME_CONTROL* pControl
242 );
243 static HRESULT ParseRadioButtons(
244 __in_opt HMODULE hModule,
245 __in_opt LPCWSTR wzRelativePath,
246 __in IXMLDOMNode* pixn,
247 __in THEME* pTheme,
248 __in_opt THEME_CONTROL* pParentControl,
249 __in THEME_PAGE* pPage
250 );
251 static HRESULT ParseTabs(
252 __in IXMLDOMNode* pixn,
253 __in THEME_CONTROL* pControl
254 );
255 static HRESULT ParseText(
256 __in IXMLDOMNode* pixn,
257 __in THEME_CONTROL* pControl,
258 __inout BOOL* pfAnyChildren
259 );
260 static HRESULT ParseTooltips(
261 __in IXMLDOMNode* pixn,
262 __in THEME_CONTROL* pControl,
263 __inout BOOL* pfAnyChildren
264 );
265 static HRESULT ParseUnexpectedAttribute(
266 __in IXMLDOMNode* pixn,
267 __in_z LPCWSTR wzElementName,
268 __in_z LPCWSTR wzAttribute
269 );
270 static HRESULT ParseNotes(
271 __in IXMLDOMNode* pixn,
272 __in THEME_CONTROL* pControl,
273 __out BOOL* pfAnyChildren
274 );
275 static HRESULT StopBillboard(
276 __in THEME* pTheme,
277 __in THEME_CONTROL* pControl
278 );
279 static HRESULT StartBillboard(
280 __in THEME* pTheme,
281 __in THEME_CONTROL* pControl
282 );
283 static HRESULT EnsureFontInstance(
284 __in THEME* pTheme,
285 __in THEME_FONT* pFont,
286 __out THEME_FONT_INSTANCE** ppFontInstance
287 );
288 static HRESULT FindImageList(
289 __in THEME* pTheme,
290 __in_z LPCWSTR wzImageListName,
291 __out HIMAGELIST *phImageList
292 );
293 static HRESULT LoadThemeControls(
294 __in THEME* pTheme
295 );
296 static void UnloadThemeControls(
297 __in THEME* pTheme
298 );
299 static HRESULT OnLoadingControl(
300 __in THEME* pTheme,
301 __in const THEME_CONTROL* pControl,
302 __inout WORD* pwId,
303 __inout DWORD* pdwAutomaticBehaviorType
304 );
305 static HRESULT LoadControls(
306 __in THEME* pTheme,
307 __in_opt THEME_CONTROL* pParentControl
308 );
309 static HRESULT ShowControl(
310 __in THEME_CONTROL* pControl,
311 __in int nCmdShow,
312 __in BOOL fSaveEditboxes,
313 __in THEME_SHOW_PAGE_REASON reason,
314 __in DWORD dwPageId,
315 __inout_opt HWND* phwndFocus
316 );
317 static HRESULT ShowControls(
318 __in THEME* pTheme,
319 __in_opt const THEME_CONTROL* pParentControl,
320 __in int nCmdShow,
321 __in BOOL fSaveEditboxes,
322 __in BOOL fSetFocus,
323 __in THEME_SHOW_PAGE_REASON reason,
324 __in DWORD dwPageId
325 );
326 static HRESULT DrawButton(
327 __in THEME* pTheme,
328 __in DRAWITEMSTRUCT* pdis,
329 __in const THEME_CONTROL* pControl
330 );
331 static void DrawControlText(
332 __in THEME* pTheme,
333 __in DRAWITEMSTRUCT* pdis,
334 __in const THEME_CONTROL* pControl,
335 __in BOOL fCentered,
336 __in BOOL fDrawFocusRect
337 );
338 static HRESULT DrawHyperlink(
339 __in THEME* pTheme,
340 __in DRAWITEMSTRUCT* pdis,
341 __in const THEME_CONTROL* pControl
342 );
343 static HRESULT DrawImage(
344 __in THEME* pTheme,
345 __in DRAWITEMSTRUCT* pdis,
346 __in const THEME_CONTROL* pControl
347 );
348 static void GetImageInstance(
349 __in THEME* pTheme,
350 __in const THEME_IMAGE_REFERENCE* pReference,
351 __out const THEME_IMAGE_INSTANCE** ppInstance
352 );
353 static HRESULT DrawImageReference(
354 __in THEME* pTheme,
355 __in const THEME_IMAGE_REFERENCE* pReference,
356 __in HDC hdc,
357 __in int destX,
358 __in int destY,
359 __in int destWidth,
360 __in int destHeight
361 );
362 static HRESULT DrawGdipBitmap(
363 __in HDC hdc,
364 __in int destX,
365 __in int destY,
366 __in int destWidth,
367 __in int destHeight,
368 __in Gdiplus::Bitmap* pBitmap,
369 __in int srcX,
370 __in int srcY,
371 __in int srcWidth,
372 __in int srcHeight
373 );
374 static HRESULT DrawProgressBar(
375 __in THEME* pTheme,
376 __in DRAWITEMSTRUCT* pdis,
377 __in const THEME_CONTROL* pControl
378 );
379 static HRESULT DrawProgressBarImage(
380 __in THEME* pTheme,
381 __in const THEME_IMAGE_INSTANCE* pImageInstance,
382 __in int srcX,
383 __in int srcY,
384 __in int srcWidth,
385 __in int srcHeight,
386 __in HDC hdc,
387 __in int destX,
388 __in int destY,
389 __in int destWidth,
390 __in int destHeight
391 );
392 static BOOL DrawHoverControl(
393 __in THEME* pTheme,
394 __in BOOL fHover
395 );
396 static void FreeFontInstance(
397 __in THEME_FONT_INSTANCE* pFontInstance
398 );
399 static void FreeFont(
400 __in THEME_FONT* pFont
401 );
402 static void FreeImage(
403 __in THEME_IMAGE* pImage
404 );
405 static void FreeImageInstance(
406 __in THEME_IMAGE_INSTANCE* pImageInstance
407 );
408 static void FreePage(
409 __in THEME_PAGE* pPage
410 );
411 static void FreeControl(
412 __in THEME_CONTROL* pControl
413 );
414 static void FreeConditionalText(
415 __in THEME_CONDITIONAL_TEXT* pConditionalText
416 );
417 static void FreeImageList(
418 __in THEME_IMAGELIST* pImageList
419 );
420 static void FreeAction(
421 __in THEME_ACTION* pAction
422 );
423 static void FreeColumn(
424 __in THEME_COLUMN* pColumn
425 );
426 static void FreeTab(
427 __in THEME_TAB* pTab
428 );
429 static void CALLBACK OnBillboardTimer(
430 __in THEME* pTheme,
431 __in HWND hwnd,
432 __in UINT_PTR idEvent
433 );
434 static void OnBrowseDirectory(
435 __in THEME* pTheme,
436 __in const THEME_ACTION* pAction
437 );
438 static BOOL OnButtonClicked(
439 __in THEME* pTheme,
440 __in const THEME_CONTROL* pControl
441 );
442 static BOOL OnDpiChanged(
443 __in THEME* pTheme,
444 __in WPARAM wParam,
445 __in LPARAM lParam
446 );
447 static BOOL OnHypertextClicked(
448 __in THEME* pTheme,
449 __in const THEME_CONTROL* pThemeControl,
450 __in PNMLINK pnmlink
451 );
452 static void OnNcCreate(
453 __in THEME* pTheme,
454 __in HWND hWnd,
455 __in LPARAM lParam
456 );
457 static BOOL OnNotifyEnLink(
458 __in THEME* pTheme,
459 __in const THEME_CONTROL* pThemeControl,
460 __in ENLINK* link
461 );
462 static BOOL OnNotifyEnMsgFilter(
463 __in THEME* pTheme,
464 __in const THEME_CONTROL* pThemeControl,
465 __in MSGFILTER* msgFilter
466 );
467 static BOOL OnPanelCreate(
468 __in THEME_CONTROL* pControl,
469 __in HWND hWnd
470 );
471 static BOOL OnWmCommand(
472 __in THEME* pTheme,
473 __in WPARAM wParam,
474 __in const THEME_CONTROL* pThemeControl,
475 __inout LRESULT* plResult
476 );
477 static BOOL OnWmNotify(
478 __in THEME* pTheme,
479 __in LPNMHDR lParam,
480 __in const THEME_CONTROL* pThemeControl,
481 __inout LRESULT* plResult
482 );
483 static const THEME_CONTROL* FindControlFromId(
484 __in const THEME* pTheme,
485 __in WORD wId,
486 __in_opt const THEME_CONTROL* pParentControl = NULL
487 );
488 static const THEME_CONTROL* FindControlFromHWnd(
489 __in const THEME* pTheme,
490 __in HWND hWnd,
491 __in_opt const THEME_CONTROL* pParentControl = NULL
492 );
493 static void GetControlDimensions(
494 __in const THEME_CONTROL* pControl,
495 __in const RECT* prcParent,
496 __out int* piWidth,
497 __out int* piHeight,
498 __out int* piX,
499 __out int* piY
500 );
501 // Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns
502 // calculates final width of each column (storing result in each column's nWidth value)
503 static HRESULT SizeListViewColumns(
504 __inout THEME_CONTROL* pControl
505 );
506 static LRESULT CALLBACK ControlGroupDefWindowProc(
507 __in_opt THEME* pTheme,
508 __in HWND hWnd,
509 __in UINT uMsg,
510 __in WPARAM wParam,
511 __in LPARAM lParam,
512 __in BOOL fDialog
513 );
514 static LRESULT CALLBACK PanelWndProc(
515 __in HWND hWnd,
516 __in UINT uMsg,
517 __in WPARAM wParam,
518 __in LPARAM lParam
519 );
520 static LRESULT CALLBACK StaticOwnerDrawWndProc(
521 __in HWND hWnd,
522 __in UINT uMsg,
523 __in WPARAM wParam,
524 __in LPARAM lParam
525 );
526 static HRESULT LocalizeControls(
527 __in DWORD cControls,
528 __in THEME_CONTROL* rgControls,
529 __in const WIX_LOCALIZATION *pWixLoc
530 );
531 static HRESULT LocalizeControl(
532 __in THEME_CONTROL* pControl,
533 __in const WIX_LOCALIZATION *pWixLoc
534 );
535 static HRESULT LoadControlsString(
536 __in DWORD cControls,
537 __in THEME_CONTROL* rgControls,
538 __in HMODULE hResModule
539 );
540 static HRESULT LoadControlString(
541 __in THEME_CONTROL* pControl,
542 __in HMODULE hResModule
543 );
544 static void ResizeControls(
545 __in THEME* pTheme,
546 __in DWORD cControls,
547 __in THEME_CONTROL* rgControls,
548 __in const RECT* prcParent
549 );
550 static void ResizeControl(
551 __in THEME* pTheme,
552 __in THEME_CONTROL* pControl,
553 __in const RECT* prcParent
554 );
555 static void ScaleThemeFromWindow(
556 __in THEME* pTheme,
557 __in UINT nDpi,
558 __in int x,
559 __in int y
560 );
561 static void ScaleTheme(
562 __in THEME* pTheme,
563 __in UINT nDpi,
564 __in int x,
565 __in int y,
566 __in DWORD dwStyle,
567 __in BOOL fMenu,
568 __in DWORD dwExStyle
569 );
570 static void AdjustThemeWindowRect(
571 __in THEME* pTheme,
572 __in DWORD dwStyle,
573 __in BOOL fMenu,
574 __in DWORD dwExStyle
575 );
576 static void ScaleControls(
577 __in THEME* pTheme,
578 __in DWORD cControls,
579 __in THEME_CONTROL* rgControls,
580 __in UINT nDpi
581 );
582 static void ScaleControl(
583 __in THEME* pTheme,
584 __in THEME_CONTROL* pControl,
585 __in UINT nDpi
586 );
587 static void GetControls(
588 __in THEME* pTheme,
589 __in_opt THEME_CONTROL* pParentControl,
590 __out DWORD** ppcControls,
591 __out THEME_CONTROL*** pprgControls
592 );
593 static void GetControls(
594 __in const THEME* pTheme,
595 __in_opt const THEME_CONTROL* pParentControl,
596 __out DWORD& cControls,
597 __out THEME_CONTROL*& rgControls
598 );
599 static void ScaleImageReference(
600 __in THEME* pTheme,
601 __in THEME_IMAGE_REFERENCE* pImageRef,
602 __in int nDestWidth,
603 __in int nDestHeight
604 );
605 static void UnloadControls(
606 __in DWORD cControls,
607 __in THEME_CONTROL* rgControls
608 );
609
610
611 // Public functions.
612
613 DAPI_(HRESULT) ThemeInitialize(
614 __in_opt HMODULE hModule
615 )
616 {
617 HRESULT hr = S_OK;
618 INITCOMMONCONTROLSEX icex = { };
619
620 DpiuInitialize();
621
622 hr = XmlInitialize();
623 ThmExitOnFailure(hr, "Failed to initialize XML.");
624
625 hr = StrAllocFormatted(&vsczHyperlinkClass, L"ThemeHyperLink_%p", hModule);
626 ThmExitOnFailure(hr, "Failed to initialize hyperlink class name.");
627
628 hr = StrAllocFormatted(&vsczPanelClass, L"ThemePanel_%p", hModule);
629 ThmExitOnFailure(hr, "Failed to initialize panel class name.");
630
631 hr = StrAllocFormatted(&vsczStaticOwnerDrawClass, L"ThemeStaticOwnerDraw_%p", hModule);
632 ThmExitOnFailure(hr, "Failed to initialize static owner draw class name.");
633
634 hr = RegisterWindowClasses(hModule);
635 ThmExitOnFailure(hr, "Failed to register theme window classes.");
636
637 // Initialize GDI+ and common controls.
638 vgsi.SuppressBackgroundThread = TRUE;
639
640 hr = GdipInitialize(&vgsi, &vgdiToken, &vgso);
641 ThmExitOnFailure(hr, "Failed to initialize GDI+.");
642
643 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
644 icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS;
645 ::InitCommonControlsEx(&icex);
646
647 (*vgso.NotificationHook)(&vgdiHookToken);
648
649 LExit:
650 return hr;
651 }
652
653
654 DAPI_(void) ThemeUninitialize()
655 {
656 if (vhModuleMsftEdit)
657 {
658 ::FreeLibrary(vhModuleMsftEdit);
659 vhModuleMsftEdit = NULL;
660 }
661
662 if (vhModuleRichEd)
663 {
664 ::FreeLibrary(vhModuleRichEd);
665 vhModuleRichEd = NULL;
666 }
667
668 if (vhHyperlinkRegisteredModule)
669 {
670 ::UnregisterClassW(vsczHyperlinkClass, vhHyperlinkRegisteredModule);
671 vhHyperlinkRegisteredModule = NULL;
672 }
673
674 ReleaseStr(vsczHyperlinkClass);
675
676 if (vhPanelRegisteredModule)
677 {
678 ::UnregisterClassW(vsczPanelClass, vhPanelRegisteredModule);
679 vhPanelRegisteredModule = NULL;
680 }
681
682 ReleaseStr(vsczPanelClass);
683
684 if (vhStaticOwnerDrawRegisteredModule)
685 {
686 ::UnregisterClassW(vsczStaticOwnerDrawClass, vhStaticOwnerDrawRegisteredModule);
687 vhStaticOwnerDrawRegisteredModule = NULL;
688 vpfnStaticOwnerDrawBaseWndProc = NULL;
689 }
690
691 ReleaseStr(vsczStaticOwnerDrawClass);
692
693 if (vgdiToken)
694 {
695 GdipUninitialize(vgdiToken);
696 vgdiToken = 0;
697 }
698
699 XmlUninitialize();
700 DpiuUninitialize();
701 }
702
703
704 DAPI_(HRESULT) ThemeLoadFromFile(
705 __in_z LPCWSTR wzThemeFile,
706 __out THEME** ppTheme
707 )
708 {
709 HRESULT hr = S_OK;
710 IXMLDOMDocument* pixd = NULL;
711 LPWSTR sczRelativePath = NULL;
712
713 hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd);
714 ThmExitOnFailure(hr, "Failed to load theme file as XML document.");
715
716 hr = PathGetDirectory(wzThemeFile, &sczRelativePath);
717 ThmExitOnFailure(hr, "Failed to get relative path from theme file.");
718
719 hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme);
720 ThmExitOnFailure(hr, "Failed to parse theme.");
721
722 LExit:
723 ReleaseStr(sczRelativePath);
724 ReleaseObject(pixd);
725
726 return hr;
727 }
728
729
730 DAPI_(HRESULT) ThemeLoadFromResource(
731 __in_opt HMODULE hModule,
732 __in_z LPCSTR szResource,
733 __out THEME** ppTheme
734 )
735 {
736 HRESULT hr = S_OK;
737 LPVOID pvResource = NULL;
738 DWORD cbResource = 0;
739 LPWSTR sczXml = NULL;
740 IXMLDOMDocument* pixd = NULL;
741
742 hr = ResReadData(hModule, szResource, &pvResource, &cbResource);
743 ThmExitOnFailure(hr, "Failed to read theme from resource.");
744
745 hr = StrAllocStringAnsi(&sczXml, reinterpret_cast<LPCSTR>(pvResource), cbResource, CP_UTF8);
746 ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string.");
747
748 hr = XmlLoadDocument(sczXml, &pixd);
749 ThmExitOnFailure(hr, "Failed to load theme resource as XML document.");
750
751 hr = ParseTheme(hModule, NULL, pixd, ppTheme);
752 ThmExitOnFailure(hr, "Failed to parse theme.");
753
754 LExit:
755 ReleaseObject(pixd);
756 ReleaseStr(sczXml);
757
758 return hr;
759 }
760
761
762 DAPI_(void) ThemeFree(
763 __in THEME* pTheme
764 )
765 {
766 if (pTheme)
767 {
768 for (DWORD i = 0; i < pTheme->cFonts; ++i)
769 {
770 FreeFont(pTheme->rgFonts + i);
771 }
772
773 for (DWORD i = 0; i < pTheme->cImages; ++i)
774 {
775 FreeImage(pTheme->rgImages + i);
776 }
777
778 for (DWORD i = 0; i < pTheme->cStandaloneImages; ++i)
779 {
780 FreeImageInstance(pTheme->rgStandaloneImages + i);
781 }
782
783 for (DWORD i = 0; i < pTheme->cPages; ++i)
784 {
785 FreePage(pTheme->rgPages + i);
786 }
787
788 for (DWORD i = 0; i < pTheme->cImageLists; ++i)
789 {
790 FreeImageList(pTheme->rgImageLists + i);
791 }
792
793 for (DWORD i = 0; i < pTheme->cControls; ++i)
794 {
795 FreeControl(pTheme->rgControls + i);
796 }
797
798 ReleaseMem(pTheme->rgControls);
799 ReleaseMem(pTheme->rgPages);
800 ReleaseMem(pTheme->rgStandaloneImages);
801 ReleaseMem(pTheme->rgImages);
802 ReleaseMem(pTheme->rgFonts);
803
804 ReleaseStr(pTheme->sczCaption);
805 ReleaseDict(pTheme->sdhFontDictionary);
806 ReleaseDict(pTheme->sdhImageDictionary);
807 ReleaseMem(pTheme);
808 }
809 }
810
811 DAPI_(HRESULT) ThemeRegisterVariableCallbacks(
812 __in THEME* pTheme,
813 __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition,
814 __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString,
815 __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable,
816 __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable,
817 __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable,
818 __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable,
819 __in_opt LPVOID pvContext
820 )
821 {
822 HRESULT hr = S_OK;
823 ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first.");
824
825 pTheme->pfnEvaluateCondition = pfnEvaluateCondition;
826 pTheme->pfnFormatString = pfnFormatString;
827 pTheme->pfnGetNumericVariable = pfnGetNumericVariable;
828 pTheme->pfnSetNumericVariable = pfnSetNumericVariable;
829 pTheme->pfnGetStringVariable = pfnGetStringVariable;
830 pTheme->pfnSetStringVariable = pfnSetStringVariable;
831 pTheme->pvVariableContext = pvContext;
832
833 LExit:
834 return hr;
835 }
836
837
838 DAPI_(void) ThemeInitializeWindowClass(
839 __in THEME* pTheme,
840 __in WNDCLASSW* pWndClass,
841 __in WNDPROC pfnWndProc,
842 __in HINSTANCE hInstance,
843 __in LPCWSTR wzClassName
844 )
845 {
846 pWndClass->style = CS_HREDRAW | CS_VREDRAW;
847 pWndClass->cbWndExtra = DLGWINDOWEXTRA;
848 pWndClass->hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
849
850 pWndClass->lpfnWndProc = pfnWndProc;
851 pWndClass->hInstance = hInstance;
852 pWndClass->lpszClassName = wzClassName;
853
854 pWndClass->hIcon = reinterpret_cast<HICON>(pTheme->hIcon);
855 pWndClass->hbrBackground = pTheme->rgFonts[pTheme->dwFontId].hBackground;
856 }
857
858
859 DAPI_(HRESULT) ThemeCreateParentWindow(
860 __in THEME* pTheme,
861 __in DWORD dwExStyle,
862 __in LPCWSTR szClassName,
863 __in LPCWSTR szWindowName,
864 __in DWORD dwStyle,
865 __in int x,
866 __in int y,
867 __in_opt HWND hwndParent,
868 __in_opt HINSTANCE hInstance,
869 __in_opt LPVOID lpParam,
870 __in THEME_WINDOW_INITIAL_POSITION initialPosition,
871 __out_opt HWND* phWnd
872 )
873 {
874 HRESULT hr = S_OK;
875 DPIU_MONITOR_CONTEXT* pMonitorContext = NULL;
876 POINT pt = { };
877 RECT* pMonitorRect = NULL;
878 HMENU hMenu = NULL;
879 HWND hWnd = NULL;
880 BOOL fScaledTheme = FALSE;
881
882 if (pTheme->hwndParent)
883 {
884 ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded.");
885 }
886
887 if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition)
888 {
889 pt.x = x;
890 pt.y = y;
891 hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext);
892 if (SUCCEEDED(hr))
893 {
894 pMonitorRect = &pMonitorContext->mi.rcWork;
895 if (pMonitorContext->nDpi != pTheme->nDpi)
896 {
897 ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle);
898 fScaledTheme = TRUE;
899 }
900
901 x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2;
902 y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2;
903 }
904 else
905 {
906 hr = S_OK;
907 x = CW_USEDEFAULT;
908 y = CW_USEDEFAULT;
909 }
910 }
911
912 // Make sure the client area matches the specified width and height.
913 if (!fScaledTheme)
914 {
915 AdjustThemeWindowRect(pTheme, dwStyle, NULL != hMenu, dwExStyle);
916 }
917
918 hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam);
919 ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window.");
920 ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE.");
921 AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window.");
922
923 if (phWnd)
924 {
925 *phWnd = hWnd;
926 }
927
928 LExit:
929 ReleaseMem(pMonitorContext);
930
931 return hr;
932 }
933
934 DAPI_(HRESULT) ThemeLocalize(
935 __in THEME *pTheme,
936 __in const WIX_LOCALIZATION *pWixLoc
937 )
938 {
939 HRESULT hr = S_OK;
940 LPWSTR sczCaption = NULL;
941
942 hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption);
943 ThmExitOnFailure(hr, "Failed to localize theme caption.");
944
945 if (pTheme->pfnFormatString)
946 {
947 hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext);
948 if (SUCCEEDED(hr))
949 {
950 hr = ThemeUpdateCaption(pTheme, sczCaption);
951 }
952 }
953
954 hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc);
955
956 LExit:
957 ReleaseStr(sczCaption);
958
959 return hr;
960 }
961
962 /********************************************************************
963 ThemeLoadStrings - Loads string resources.
964 Must be called after loading a theme and before calling
965 ThemeLoadControls.
966 *******************************************************************/
967 DAPI_(HRESULT) ThemeLoadStrings(
968 __in THEME* pTheme,
969 __in HMODULE hResModule
970 )
971 {
972 HRESULT hr = S_OK;
973 ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first.");
974
975 if (UINT_MAX != pTheme->uStringId)
976 {
977 hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption);
978 ThmExitOnFailure(hr, "Failed to load theme caption.");
979 }
980
981 hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule);
982
983 LExit:
984 return hr;
985 }
986
987
988 DAPI_(HRESULT) ThemeLoadRichEditFromFile(
989 __in const THEME_CONTROL* pThemeControl,
990 __in_z LPCWSTR wzFileName,
991 __in HMODULE hModule
992 )
993 {
994 HRESULT hr = E_INVALIDARG;
995
996 if (pThemeControl)
997 {
998 AssertSz(THEME_CONTROL_TYPE_RICHEDIT == pThemeControl->type, "ThemeLoadRichEditFromFile called for non-RichEdit control.");
999
1000 hr = WnduLoadRichEditFromFile(pThemeControl->hWnd, wzFileName, hModule);
1001 }
1002
1003 return hr;
1004 }
1005
1006
1007 DAPI_(HRESULT) ThemeLoadRichEditFromResource(
1008 __in const THEME_CONTROL* pThemeControl,
1009 __in_z LPCSTR szResourceName,
1010 __in HMODULE hModule
1011 )
1012 {
1013 HRESULT hr = E_INVALIDARG;
1014
1015 if (pThemeControl)
1016 {
1017 AssertSz(THEME_CONTROL_TYPE_RICHEDIT == pThemeControl->type, "ThemeLoadRichEditFromResource called for non-RichEdit control.");
1018
1019 hr = WnduLoadRichEditFromResource(pThemeControl->hWnd, szResourceName, hModule);
1020 }
1021
1022 return hr;
1023 }
1024
1025
1026 DAPI_(BOOL) ThemeHandleKeyboardMessage(
1027 __in_opt THEME* pTheme,
1028 __in HWND /*hWnd*/,
1029 __in MSG* pMsg
1030 )
1031 {
1032 return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE;
1033 }
1034
1035
1036 extern "C" LRESULT CALLBACK ThemeDefWindowProc(
1037 __in_opt THEME* pTheme,
1038 __in HWND hWnd,
1039 __in UINT uMsg,
1040 __in WPARAM wParam,
1041 __in LPARAM lParam
1042 )
1043 {
1044 RECT rcParent = { };
1045 RECT *pRect = NULL;
1046
1047 if (pTheme)
1048 {
1049 switch (uMsg)
1050 {
1051 case WM_NCCREATE:
1052 if (pTheme->hwndParent)
1053 {
1054 AssertSz(FALSE, "WM_NCCREATE called multiple times");
1055 }
1056 else
1057 {
1058 OnNcCreate(pTheme, hWnd, lParam);
1059 }
1060 break;
1061
1062 case WM_CREATE:
1063 if (FAILED(LoadThemeControls(pTheme)))
1064 {
1065 return -1;
1066 }
1067 break;
1068
1069 case WM_DESTROY:
1070 UnloadThemeControls(pTheme);
1071 break;
1072
1073 case WM_NCHITTEST:
1074 if (pTheme->dwStyle & WS_POPUP)
1075 {
1076 return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control.
1077 }
1078 break;
1079
1080 case WM_DPICHANGED:
1081 if (OnDpiChanged(pTheme, wParam, lParam))
1082 {
1083 return 0;
1084 }
1085 break;
1086
1087 case WM_SIZING:
1088 if (pTheme->fAutoResize)
1089 {
1090 pRect = reinterpret_cast<RECT *>(lParam);
1091 if (pRect->right - pRect->left < pTheme->nMinimumWidth)
1092 {
1093 if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT)
1094 {
1095 pRect->left = pRect->right - pTheme->nMinimumWidth;
1096 }
1097 else
1098 {
1099 pRect->right = pRect->left + pTheme->nMinimumWidth;
1100 }
1101 }
1102 if (pRect->bottom - pRect->top < pTheme->nMinimumHeight)
1103 {
1104 if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT)
1105 {
1106 pRect->bottom = pRect->top + pTheme->nMinimumHeight;
1107 }
1108 else
1109 {
1110 pRect->top = pRect->bottom - pTheme->nMinimumHeight;
1111 }
1112 }
1113
1114 return TRUE;
1115 }
1116 break;
1117
1118 case WM_SIZE:
1119 if (pTheme->fAutoResize || pTheme->fForceResize)
1120 {
1121 pTheme->fForceResize = FALSE;
1122 ::GetClientRect(pTheme->hwndParent, &rcParent);
1123 ScaleImageReference(pTheme, &pTheme->windowImageRef, rcParent.right - rcParent.left, rcParent.bottom - rcParent.top);
1124 ResizeControls(pTheme, pTheme->cControls, pTheme->rgControls, &rcParent);
1125 return 0;
1126 }
1127 break;
1128 }
1129 }
1130
1131 return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam, TRUE);
1132 }
1133
1134
1135 DAPI_(void) ThemeGetPageIds(
1136 __in const THEME* pTheme,
1137 __in_ecount(cGetPages) LPCWSTR* rgwzFindNames,
1138 __inout_ecount(cGetPages) DWORD* rgdwPageIds,
1139 __in DWORD cGetPages
1140 )
1141 {
1142 for (DWORD i = 0; i < cGetPages; ++i)
1143 {
1144 LPCWSTR wzFindName = rgwzFindNames[i];
1145 for (DWORD j = 0; j < pTheme->cPages; ++j)
1146 {
1147 LPCWSTR wzPageName = pTheme->rgPages[j].sczName;
1148 if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1))
1149 {
1150 rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid).
1151 break;
1152 }
1153 }
1154 }
1155 }
1156
1157
1158 DAPI_(THEME_PAGE*) ThemeGetPage(
1159 __in const THEME* pTheme,
1160 __in DWORD dwPage
1161 )
1162 {
1163 DWORD iPage = dwPage - 1;
1164 THEME_PAGE* pPage = NULL;
1165
1166 if (iPage < pTheme->cPages)
1167 {
1168 pPage = pTheme->rgPages + iPage;
1169 }
1170
1171 return pPage;
1172 }
1173
1174
1175 DAPI_(HRESULT) ThemeShowPage(
1176 __in THEME* pTheme,
1177 __in DWORD dwPage,
1178 __in int nCmdShow
1179 )
1180 {
1181 return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT);
1182 }
1183
1184
1185 DAPI_(HRESULT) ThemeShowPageEx(
1186 __in THEME* pTheme,
1187 __in DWORD dwPage,
1188 __in int nCmdShow,
1189 __in THEME_SHOW_PAGE_REASON reason
1190 )
1191 {
1192 HRESULT hr = S_OK;
1193 BOOL fHide = SW_HIDE == nCmdShow;
1194 BOOL fSaveEditboxes = FALSE;
1195 THEME_SAVEDVARIABLE* pSavedVariable = NULL;
1196 SIZE_T cb = 0;
1197 BOOL fSetFocus = dwPage != pTheme->dwCurrentPageId;
1198 THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage);
1199
1200 if (pPage)
1201 {
1202 if (fHide)
1203 {
1204 switch (reason)
1205 {
1206 case THEME_SHOW_PAGE_REASON_DEFAULT:
1207 // Set the variables in the loop below.
1208 fSaveEditboxes = TRUE;
1209 break;
1210 case THEME_SHOW_PAGE_REASON_CANCEL:
1211 if (pPage->cSavedVariables && pTheme->pfnSetStringVariable)
1212 {
1213 // Best effort to cancel any changes to the variables.
1214 for (DWORD v = 0; v < pPage->cSavedVariables; ++v)
1215 {
1216 pSavedVariable = pPage->rgSavedVariables + v;
1217 if (pSavedVariable->wzName)
1218 {
1219 pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext);
1220 }
1221 }
1222 }
1223 break;
1224 }
1225
1226 if (THEME_SHOW_PAGE_REASON_REFRESH != reason)
1227 {
1228 pPage->cSavedVariables = 0;
1229 if (pPage->rgSavedVariables && SUCCEEDED(MemSizeChecked(pPage->rgSavedVariables, &cb)))
1230 {
1231 SecureZeroMemory(pPage->rgSavedVariables, cb);
1232 }
1233 }
1234
1235 pTheme->dwCurrentPageId = 0;
1236 }
1237 else
1238 {
1239 if (THEME_SHOW_PAGE_REASON_REFRESH == reason)
1240 {
1241 fSaveEditboxes = TRUE;
1242 }
1243 else
1244 {
1245 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices);
1246 ThmExitOnFailure(hr, "Failed to allocate memory for saved variables.");
1247
1248 if (SUCCEEDED(MemSizeChecked(pPage->rgSavedVariables, &cb)))
1249 {
1250 SecureZeroMemory(pPage->rgSavedVariables, cb);
1251 }
1252
1253 pPage->cSavedVariables = pPage->cControlIndices;
1254
1255 // Save the variables in the loop below.
1256 }
1257
1258 pTheme->dwCurrentPageId = dwPage;
1259 }
1260 }
1261
1262 hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, fSetFocus, reason, dwPage);
1263 ThmExitOnFailure(hr, "Failed to show page controls.");
1264
1265 LExit:
1266 return hr;
1267 }
1268
1269
1270 DAPI_(BOOL) ThemeControlExistsByHWnd(
1271 __in const THEME* pTheme,
1272 __in HWND hWnd,
1273 __out_opt const THEME_CONTROL** ppThemeControl
1274 )
1275 {
1276 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1277
1278 if (ppThemeControl)
1279 {
1280 *ppThemeControl = pControl;
1281 }
1282
1283 return NULL != pControl;
1284 }
1285
1286
1287 DAPI_(BOOL) ThemeControlExistsById(
1288 __in const THEME* pTheme,
1289 __in WORD wId,
1290 __out_opt const THEME_CONTROL** ppThemeControl
1291 )
1292 {
1293 const THEME_CONTROL* pControl = FindControlFromId(pTheme, wId);
1294
1295 if (ppThemeControl)
1296 {
1297 *ppThemeControl = pControl;
1298 }
1299
1300 return NULL != pControl;
1301 }
1302
1303
1304 DAPI_(void) ThemeControlEnable(
1305 __in const THEME_CONTROL* pThemeControl,
1306 __in BOOL fEnable
1307 )
1308 {
1309 if (pThemeControl)
1310 {
1311 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(pThemeControl);
1312 pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED);
1313 ::EnableWindow(pControl->hWnd, fEnable);
1314
1315 if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED)
1316 {
1317 ::ShowWindow(pControl->hWnd, fEnable ? SW_SHOW : SW_HIDE);
1318 }
1319 }
1320 }
1321
1322
1323 DAPI_(BOOL) ThemeControlEnabled(
1324 __in const THEME_CONTROL* pThemeControl
1325 )
1326 {
1327 BOOL fEnabled = FALSE;
1328
1329 if (pThemeControl)
1330 {
1331 fEnabled = !(pThemeControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED);
1332 }
1333
1334 return fEnabled;
1335 }
1336
1337
1338 DAPI_(void) ThemeControlElevates(
1339 __in const THEME_CONTROL* pThemeControl,
1340 __in BOOL fElevates
1341 )
1342 {
1343 if (pThemeControl)
1344 {
1345 ::SendMessageW(pThemeControl->hWnd, BCM_SETSHIELD, 0, fElevates);
1346 }
1347 }
1348
1349
1350 DAPI_(void) ThemeShowControl(
1351 __in const THEME_CONTROL* pThemeControl,
1352 __in int nCmdShow
1353 )
1354 {
1355 if (pThemeControl)
1356 {
1357 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(pThemeControl);
1358 ::ShowWindow(pControl->hWnd, nCmdShow);
1359
1360 // Save the control's visible state.
1361 pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN);
1362 }
1363 }
1364
1365
1366 DAPI_(void) ThemeShowControlEx(
1367 __in const THEME_CONTROL* pThemeControl,
1368 __in int nCmdShow
1369 )
1370 {
1371 if (pThemeControl)
1372 {
1373 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(pThemeControl);
1374 ShowControl(pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL);
1375 }
1376 }
1377
1378
1379 DAPI_(BOOL) ThemeControlVisible(
1380 __in const THEME_CONTROL* pThemeControl
1381 )
1382 {
1383 BOOL fVisible = FALSE;
1384
1385 if (pThemeControl)
1386 {
1387 fVisible = ::IsWindowVisible(pThemeControl->hWnd);
1388 }
1389
1390 return fVisible;
1391 }
1392
1393
1394 DAPI_(HRESULT) ThemeDrawBackground(
1395 __in THEME* pTheme,
1396 __in PAINTSTRUCT* pps
1397 )
1398 {
1399 HRESULT hr = S_FALSE;
1400
1401 if (pps->fErase && THEME_IMAGE_REFERENCE_TYPE_NONE != pTheme->windowImageRef.type)
1402 {
1403 hr = DrawImageReference(pTheme, &pTheme->windowImageRef, pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight);
1404 }
1405
1406 return hr;
1407 }
1408
1409
1410 DAPI_(HRESULT) ThemeDrawControl(
1411 __in THEME* pTheme,
1412 __in DRAWITEMSTRUCT* pdis
1413 )
1414 {
1415 HRESULT hr = S_OK;
1416 const THEME_CONTROL* pControl = NULL;
1417 BOOL fExists = ThemeControlExistsByHWnd(pTheme, pdis->hwndItem, &pControl);
1418
1419 AssertSz(fExists, "Expected control window from owner draw window.");
1420 if (!fExists)
1421 {
1422 ExitFunction1(hr = E_INVALIDARG);
1423 }
1424
1425 AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window.");
1426 AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width.");
1427 AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height.");
1428
1429 switch (pControl->type)
1430 {
1431 case THEME_CONTROL_TYPE_BUTTON:
1432 hr = DrawButton(pTheme, pdis, pControl);
1433 ThmExitOnFailure(hr, "Failed to draw button.");
1434 break;
1435
1436 case THEME_CONTROL_TYPE_HYPERLINK:
1437 hr = DrawHyperlink(pTheme, pdis, pControl);
1438 ThmExitOnFailure(hr, "Failed to draw hyperlink.");
1439 break;
1440
1441 case THEME_CONTROL_TYPE_IMAGE:
1442 hr = DrawImage(pTheme, pdis, pControl);
1443 ThmExitOnFailure(hr, "Failed to draw image.");
1444 break;
1445
1446 case THEME_CONTROL_TYPE_PROGRESSBAR:
1447 hr = DrawProgressBar(pTheme, pdis, pControl);
1448 ThmExitOnFailure(hr, "Failed to draw progress bar.");
1449 break;
1450
1451 default:
1452 hr = E_UNEXPECTED;
1453 ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw.");
1454 }
1455
1456 LExit:
1457 return hr;
1458 }
1459
1460
1461 static BOOL ThemeHoverControl(
1462 __in THEME* pTheme,
1463 __in HWND hwndParent,
1464 __in HWND hwndControl
1465 )
1466 {
1467 BOOL fHovered = FALSE;
1468 if (hwndControl != pTheme->hwndHover)
1469 {
1470 if (pTheme->hwndHover && pTheme->hwndHover != hwndParent)
1471 {
1472 DrawHoverControl(pTheme, FALSE);
1473 }
1474
1475 pTheme->hwndHover = hwndControl;
1476
1477 if (pTheme->hwndHover && pTheme->hwndHover != hwndParent)
1478 {
1479 fHovered = DrawHoverControl(pTheme, TRUE);
1480 }
1481 }
1482
1483 return fHovered;
1484 }
1485
1486
1487 DAPI_(BOOL) ThemeIsControlChecked(
1488 __in const THEME_CONTROL* pThemeControl
1489 )
1490 {
1491 BOOL fChecked = FALSE;
1492
1493 if (pThemeControl)
1494 {
1495 fChecked = BST_CHECKED == ::SendMessageW(pThemeControl->hWnd, BM_GETCHECK, 0, 0);
1496 }
1497
1498 return fChecked;
1499 }
1500
1501
1502 static BOOL ThemeSetControlColor(
1503 __in THEME* pTheme,
1504 __in HDC hdc,
1505 __in HWND hWnd,
1506 __out HBRUSH* phBackgroundBrush
1507 )
1508 {
1509 THEME_FONT* pFont = NULL;
1510 BOOL fHasBackground = FALSE;
1511 const THEME_CONTROL* pControl = NULL;
1512
1513 *phBackgroundBrush = NULL;
1514
1515 if (hWnd == pTheme->hwndParent)
1516 {
1517 pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId;
1518 }
1519 else if (ThemeControlExistsByHWnd(pTheme, hWnd, &pControl))
1520 {
1521 pFont = THEME_INVALID_ID == pControl->dwFontId ? NULL : pTheme->rgFonts + pControl->dwFontId;
1522 }
1523
1524 if (pFont)
1525 {
1526 if (pFont->hForeground)
1527 {
1528 ::SetTextColor(hdc, pFont->crForeground);
1529 }
1530
1531 if (pFont->hBackground)
1532 {
1533 ::SetBkColor(hdc, pFont->crBackground);
1534
1535 *phBackgroundBrush = pFont->hBackground;
1536 fHasBackground = TRUE;
1537 }
1538 else
1539 {
1540 ::SetBkMode(hdc, TRANSPARENT);
1541 *phBackgroundBrush = static_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
1542 fHasBackground = TRUE;
1543 }
1544 }
1545
1546 return fHasBackground;
1547 }
1548
1549
1550 DAPI_(HRESULT) ThemeSetProgressControl(
1551 __in const THEME_CONTROL* pThemeControl,
1552 __in DWORD dwProgressPercentage
1553 )
1554 {
1555 HRESULT hr = E_INVALIDARG;
1556
1557 if (pThemeControl && THEME_CONTROL_TYPE_PROGRESSBAR == pThemeControl->type)
1558 {
1559 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(pThemeControl);
1560
1561 DWORD dwCurrentProgress = LOWORD(pControl->dwData);
1562
1563 if (dwCurrentProgress != dwProgressPercentage)
1564 {
1565 DWORD dwColor = HIWORD(pControl->dwData);
1566 pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor);
1567
1568 if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)
1569 {
1570 if (!::InvalidateRect(pControl->hWnd, NULL, FALSE))
1571 {
1572 ThmExitWithLastError(hr, "Failed to invalidate progress bar window.");
1573 }
1574 }
1575 else
1576 {
1577 ::SendMessageW(pControl->hWnd, PBM_SETPOS, dwProgressPercentage, 0);
1578 }
1579
1580 hr = S_OK;
1581 }
1582 else
1583 {
1584 hr = S_FALSE;
1585 }
1586 }
1587
1588 LExit:
1589 return hr;
1590 }
1591
1592
1593 DAPI_(HRESULT) ThemeSetProgressControlColor(
1594 __in const THEME_CONTROL* pThemeControl,
1595 __in DWORD dwColorIndex
1596 )
1597 {
1598 HRESULT hr = E_INVALIDARG;
1599
1600 // Only set color on owner draw progress bars.
1601 if (pThemeControl && THEME_CONTROL_TYPE_PROGRESSBAR == pThemeControl->type && (pThemeControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW))
1602 {
1603 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(pThemeControl);
1604
1605 if (pControl->ProgressBar.cImageRef <= dwColorIndex)
1606 {
1607 ThmExitWithRootFailure(hr, E_INVALIDARG, "Invalid progress bar color index: %u", dwColorIndex);
1608 }
1609
1610 if (HIWORD(pControl->dwData) != dwColorIndex)
1611 {
1612 DWORD dwCurrentProgress = LOWORD(pControl->dwData);
1613 pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex);
1614
1615 if (!::InvalidateRect(pControl->hWnd, NULL, FALSE))
1616 {
1617 ThmExitWithLastError(hr, "Failed to invalidate progress bar window.");
1618 }
1619
1620 hr = S_OK;
1621 }
1622 else
1623 {
1624 hr = S_FALSE;
1625 }
1626 }
1627
1628 LExit:
1629 return hr;
1630 }
1631
1632
1633 DAPI_(HRESULT) ThemeSetTextControl(
1634 __in const THEME_CONTROL* pThemeControl,
1635 __in_z_opt LPCWSTR wzText
1636 )
1637 {
1638 return ThemeSetTextControlEx(pThemeControl, FALSE, wzText);
1639 }
1640
1641
1642 DAPI_(HRESULT) ThemeSetTextControlEx(
1643 __in const THEME_CONTROL* pThemeControl,
1644 __in BOOL fUpdate,
1645 __in_z_opt LPCWSTR wzText
1646 )
1647 {
1648 HRESULT hr = E_INVALIDARG;
1649
1650 if (pThemeControl)
1651 {
1652 if (fUpdate)
1653 {
1654 ::ShowWindow(pThemeControl->hWnd, SW_HIDE);
1655 }
1656
1657 if (!::SetWindowTextW(pThemeControl->hWnd, wzText))
1658 {
1659 ThmExitWithLastError(hr, "Failed to set control text.");
1660 }
1661
1662 if (fUpdate)
1663 {
1664 ::ShowWindow(pThemeControl->hWnd, SW_SHOW);
1665 }
1666
1667 hr = S_OK;
1668 }
1669
1670 LExit:
1671 return hr;
1672 }
1673
1674
1675 DAPI_(HRESULT) ThemeGetTextControl(
1676 __in const THEME_CONTROL* pThemeControl,
1677 __inout_z LPWSTR* psczText
1678 )
1679 {
1680 HRESULT hr = E_INVALIDARG;
1681
1682 if (pThemeControl)
1683 {
1684 hr = WnduGetControlText(pThemeControl->hWnd, psczText);
1685 }
1686
1687 return hr;
1688 }
1689
1690
1691 DAPI_(HRESULT) ThemeUpdateCaption(
1692 __in THEME* pTheme,
1693 __in_z LPCWSTR wzCaption
1694 )
1695 {
1696 HRESULT hr = S_OK;
1697
1698 hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0);
1699 ThmExitOnFailure(hr, "Failed to update theme caption.");
1700
1701 LExit:
1702 return hr;
1703 }
1704
1705
1706 DAPI_(void) ThemeSetFocus(
1707 __in const THEME_CONTROL* pThemeControl
1708 )
1709 {
1710 if (pThemeControl)
1711 {
1712 HWND hwndFocus = pThemeControl->hWnd;
1713 if (hwndFocus && !ThemeControlEnabled(pThemeControl))
1714 {
1715 hwndFocus = ::GetNextDlgTabItem(pThemeControl->pTheme->hwndParent, hwndFocus, FALSE);
1716 }
1717
1718 if (hwndFocus)
1719 {
1720 ::SendMessage(pThemeControl->pTheme->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwndFocus, TRUE);
1721 }
1722 }
1723 }
1724
1725
1726 DAPI_(void) ThemeShowChild(
1727 __in THEME_CONTROL* pParentControl,
1728 __in DWORD dwIndex
1729 )
1730 {
1731 // show one child, hide the rest
1732 for (DWORD i = 0; i < pParentControl->cControls; ++i)
1733 {
1734 THEME_CONTROL* pControl = pParentControl->rgControls + i;
1735 ShowControl(pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL);
1736 }
1737 }
1738
1739
1740 // Internal functions.
1741
1742 static HRESULT RegisterWindowClasses(
1743 __in_opt HMODULE hModule
1744 )
1745 {
1746 HRESULT hr = S_OK;
1747 WNDCLASSW wcHyperlink = { };
1748 WNDCLASSW wcPanel = { };
1749 WNDCLASSW wcStaticOwnerDraw = { };
1750 WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL;
1751
1752 vhCursorHand = ::LoadCursorA(NULL, IDC_HAND);
1753
1754 // Base the theme hyperlink class on a button but give it the "hand" icon.
1755 if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink))
1756 {
1757 ThmExitWithLastError(hr, "Failed to get button window class.");
1758 }
1759
1760 wcHyperlink.lpszClassName = vsczHyperlinkClass;
1761 #pragma prefast(push)
1762 #pragma prefast(disable:25068)
1763 wcHyperlink.hCursor = vhCursorHand;
1764 #pragma prefast(pop)
1765
1766 if (!::RegisterClassW(&wcHyperlink))
1767 {
1768 ThmExitWithLastError(hr, "Failed to register hyperlink window class.");
1769 }
1770 vhHyperlinkRegisteredModule = hModule;
1771
1772 // Panel is its own do-nothing class.
1773 wcPanel.lpfnWndProc = PanelWndProc;
1774 wcPanel.hInstance = hModule;
1775 wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW);
1776 wcPanel.lpszClassName = vsczPanelClass;
1777 if (!::RegisterClassW(&wcPanel))
1778 {
1779 ThmExitWithLastError(hr, "Failed to register panel window class.");
1780 }
1781 vhPanelRegisteredModule = hModule;
1782
1783 if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw))
1784 {
1785 ThmExitWithLastError(hr, "Failed to get static window class.");
1786 }
1787
1788 pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc;
1789 wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc;
1790 wcStaticOwnerDraw.hInstance = hModule;
1791 wcStaticOwnerDraw.lpszClassName = vsczStaticOwnerDrawClass;
1792 if (!::RegisterClassW(&wcStaticOwnerDraw))
1793 {
1794 ThmExitWithLastError(hr, "Failed to register OwnerDraw window class.");
1795 }
1796 vhStaticOwnerDrawRegisteredModule = hModule;
1797 vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc;
1798
1799
1800 LExit:
1801 return hr;
1802 }
1803
1804 static HRESULT ParseTheme(
1805 __in_opt HMODULE hModule,
1806 __in_opt LPCWSTR wzRelativePath,
1807 __in IXMLDOMDocument* pixd,
1808 __out THEME** ppTheme
1809 )
1810 {
1811 HRESULT hr = S_OK;
1812 THEME* pTheme = NULL;
1813 IXMLDOMElement *pThemeElement = NULL;
1814 Gdiplus::Bitmap* pBitmap = NULL;
1815 BOOL fXmlFound = FALSE;
1816
1817 hr = pixd->get_documentElement(&pThemeElement);
1818 ThmExitOnFailure(hr, "Failed to get theme element.");
1819
1820 pTheme = static_cast<THEME*>(MemAlloc(sizeof(THEME), TRUE));
1821 ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme.");
1822
1823 pTheme->nDpi = USER_DEFAULT_SCREEN_DPI;
1824 pTheme->wNextControlId = THEME_FIRST_AUTO_ASSIGN_CONTROL_ID;
1825
1826 // Parse the optional background resource image.
1827 hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pThemeElement, &pBitmap);
1828 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed while parsing theme image.");
1829
1830 if (fXmlFound)
1831 {
1832 hr = AddStandaloneImage(pTheme, &pBitmap, &pTheme->dwSourceImageInstanceIndex);
1833 ThmExitOnFailure(hr, "Failed to store theme image.");
1834 }
1835 else
1836 {
1837 pTheme->dwSourceImageInstanceIndex = THEME_INVALID_ID;
1838 }
1839
1840 // Parse the fonts.
1841 hr = ParseFonts(pThemeElement, pTheme);
1842 ThmExitOnFailure(hr, "Failed to parse theme fonts.");
1843
1844 // Parse the images.
1845 hr = ParseImages(hModule, wzRelativePath, pThemeElement, pTheme);
1846 ThmExitOnFailure(hr, "Failed to parse theme images.");
1847
1848 // Parse any image lists.
1849 hr = ParseImageLists(hModule, wzRelativePath, pThemeElement, pTheme);
1850 ThmExitOnFailure(hr, "Failed to parse image lists.");
1851
1852 // Parse the window element.
1853 hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme);
1854 ThmExitOnFailure(hr, "Failed to parse theme window element.");
1855
1856 *ppTheme = pTheme;
1857 pTheme = NULL;
1858
1859 LExit:
1860 ReleaseObject(pThemeElement);
1861
1862 if (pBitmap)
1863 {
1864 delete pBitmap;
1865 }
1866
1867 if (pTheme)
1868 {
1869 ThemeFree(pTheme);
1870 }
1871
1872 return hr;
1873 }
1874
1875 static HRESULT AddStandaloneImage(
1876 __in THEME* pTheme,
1877 __in Gdiplus::Bitmap** ppBitmap,
1878 __out DWORD* pdwIndex
1879 )
1880 {
1881 HRESULT hr = S_OK;
1882 THEME_IMAGE_INSTANCE* pInstance = NULL;
1883
1884 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pTheme->rgStandaloneImages), pTheme->cStandaloneImages, 1, sizeof(THEME_IMAGE_INSTANCE), GROW_IMAGE_INSTANCES);
1885 ThmExitOnFailure(hr, "Failed to allocate memory for image instances.");
1886
1887 *pdwIndex = pTheme->cStandaloneImages;
1888 ++pTheme->cStandaloneImages;
1889
1890 pInstance = pTheme->rgStandaloneImages + *pdwIndex;
1891
1892 pInstance->pBitmap = *ppBitmap;
1893 *ppBitmap = NULL;
1894
1895 LExit:
1896 return hr;
1897 }
1898
1899 static HRESULT GetAttributeImageFileOrResource(
1900 __in_opt HMODULE hModule,
1901 __in_z_opt LPCWSTR wzRelativePath,
1902 __in IXMLDOMNode* pElement,
1903 __out Gdiplus::Bitmap** ppBitmap
1904 )
1905 {
1906 HRESULT hr = S_OK;
1907 BSTR bstr = NULL;
1908 LPWSTR sczImageFile = NULL;
1909 WORD wResourceId = 0;
1910 BOOL fFound = FALSE;
1911 Gdiplus::Bitmap* pBitmap = NULL;
1912 *ppBitmap = NULL;
1913
1914 hr = XmlGetAttributeUInt16(pElement, L"ImageResource", &wResourceId);
1915 ThmExitOnOptionalXmlQueryFailure(hr, fFound, "Failed to get image resource attribute.");
1916
1917 if (fFound)
1918 {
1919 hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(wResourceId), &pBitmap);
1920 ThmExitOnFailure(hr, "Failed to load image from resource: %hu", wResourceId);
1921 }
1922
1923 hr = XmlGetAttribute(pElement, L"ImageFile", &bstr);
1924 ThmExitOnOptionalXmlQueryFailure(hr, fFound, "Failed to get image file attribute.");
1925
1926 if (fFound)
1927 {
1928 if (pBitmap)
1929 {
1930 ThmExitWithRootFailure(hr, E_INVALIDDATA, "ImageFile attribute can't be specified with ImageResource attribute.");
1931 }
1932
1933 // Parse the optional background image from a given file.
1934 if (wzRelativePath)
1935 {
1936 hr = PathConcat(wzRelativePath, bstr, &sczImageFile);
1937 ThmExitOnFailure(hr, "Failed to combine image file path.");
1938 }
1939 else
1940 {
1941 hr = PathRelativeToModule(&sczImageFile, bstr, hModule);
1942 ThmExitOnFailure(hr, "Failed to get image filename.");
1943 }
1944
1945 hr = GdipBitmapFromFile(sczImageFile, &pBitmap);
1946 ThmExitOnFailure(hr, "Failed to load image from file: %ls", sczImageFile);
1947 }
1948
1949 if (pBitmap)
1950 {
1951 *ppBitmap = pBitmap;
1952 pBitmap = NULL;
1953 }
1954 else
1955 {
1956 hr = E_NOTFOUND;
1957 }
1958
1959 LExit:
1960 if (pBitmap)
1961 {
1962 delete pBitmap;
1963 }
1964
1965 ReleaseStr(sczImageFile);
1966 ReleaseBSTR(bstr);
1967
1968 return hr;
1969 }
1970
1971
1972 static HRESULT ParseOwnerDrawImage(
1973 __in_opt HMODULE hModule,
1974 __in_z_opt LPCWSTR wzRelativePath,
1975 __in THEME* pTheme,
1976 __in IXMLDOMNode* pElement,
1977 __in_z LPCWSTR wzElementName,
1978 __in THEME_CONTROL* pControl,
1979 __in THEME_IMAGE_REFERENCE* pImageRef
1980 )
1981 {
1982 HRESULT hr = S_OK;
1983 DWORD dwValue = 0;
1984 BOOL fXmlFound = FALSE;
1985 BOOL fFoundImage = FALSE;
1986 Gdiplus::Bitmap* pBitmap = NULL;
1987
1988 hr = GetAttributeImageId(pTheme, pElement, L"ImageId", &dwValue);
1989 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to parse ImageId attribute.");
1990
1991 if (fXmlFound)
1992 {
1993 pImageRef->type = THEME_IMAGE_REFERENCE_TYPE_COMPLETE;
1994 pImageRef->dwImageIndex = dwValue;
1995 pImageRef->dwImageInstanceIndex = 0;
1996 fFoundImage = TRUE;
1997 }
1998 else
1999 {
2000 pImageRef->dwImageIndex = THEME_INVALID_ID;
2001 }
2002
2003 // Parse the optional background resource image.
2004 hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pBitmap);
2005 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed while parsing control image.");
2006
2007 if (fXmlFound)
2008 {
2009 if (fFoundImage)
2010 {
2011 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Unexpected image attribute with ImageId attribute.");
2012 }
2013
2014 hr = AddStandaloneImage(pTheme, &pBitmap, &pImageRef->dwImageInstanceIndex);
2015 ThmExitOnFailure(hr, "Failed to store owner draw image.");
2016
2017 pImageRef->type = THEME_IMAGE_REFERENCE_TYPE_COMPLETE;
2018
2019 fFoundImage = TRUE;
2020 }
2021
2022 hr = ParseSourceXY(pElement, pTheme, pControl->nWidth, pControl->nHeight, pImageRef);
2023 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get control SourceX and SourceY attributes.");
2024
2025 if (fXmlFound)
2026 {
2027 if (fFoundImage)
2028 {
2029 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Unexpected SourceX attribute with image attribute.");
2030 }
2031 else if (1 > pControl->nWidth || 1 > pControl->nHeight)
2032 {
2033 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Control Width and Height must be positive when using SourceX and SourceY.");
2034 }
2035
2036 fFoundImage = TRUE;
2037 }
2038
2039 if (!fFoundImage)
2040 {
2041 ThmExitWithRootFailure(hr, E_INVALIDDATA, "%ls didn't specify an image.", wzElementName);
2042 }
2043
2044 LExit:
2045 if (pBitmap)
2046 {
2047 delete pBitmap;
2048 }
2049
2050 return hr;
2051 }
2052
2053
2054 static HRESULT ParseButtonImages(
2055 __in_opt HMODULE hModule,
2056 __in_z_opt LPCWSTR wzRelativePath,
2057 __in THEME* pTheme,
2058 __in IXMLDOMNode* pElement,
2059 __in THEME_CONTROL* pControl
2060 )
2061 {
2062 HRESULT hr = S_OK;
2063 DWORD i = 0;
2064 IXMLDOMNodeList* pixnl = NULL;
2065 IXMLDOMNode* pixnChild = NULL;
2066 BSTR bstrType = NULL;
2067 THEME_IMAGE_REFERENCE* pImageRef = NULL;
2068 THEME_IMAGE_REFERENCE* pDefaultImageRef = NULL;
2069 THEME_IMAGE_REFERENCE* pFocusImageRef = NULL;
2070 THEME_IMAGE_REFERENCE* pHoverImageRef = NULL;
2071 THEME_IMAGE_REFERENCE* pSelectedImageRef = NULL;
2072
2073 hr = XmlSelectNodes(pElement, L"ButtonImage|ButtonFocusImage|ButtonHoverImage|ButtonSelectedImage", &pixnl);
2074 ThmExitOnFailure(hr, "Failed to select child ButtonImage nodes.");
2075
2076 i = 0;
2077 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType)))
2078 {
2079 if (!bstrType)
2080 {
2081 hr = E_UNEXPECTED;
2082 ThmExitOnFailure(hr, "Null element encountered!");
2083 }
2084
2085 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ButtonFocusImage", -1))
2086 {
2087 if (pFocusImageRef)
2088 {
2089 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Duplicate ButtonFocusImage element.");
2090 }
2091
2092 pImageRef = pFocusImageRef = pControl->Button.rgImageRef + 3;
2093 }
2094 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ButtonHoverImage", -1))
2095 {
2096 if (pHoverImageRef)
2097 {
2098 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Duplicate ButtonHoverImage element.");
2099 }
2100
2101 pImageRef = pHoverImageRef = pControl->Button.rgImageRef + 1;
2102 }
2103 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ButtonSelectedImage", -1))
2104 {
2105 if (pSelectedImageRef)
2106 {
2107 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Duplicate ButtonSelectedImage element.");
2108 }
2109
2110 pImageRef = pSelectedImageRef = pControl->Button.rgImageRef + 2;
2111 }
2112 else
2113 {
2114 if (pDefaultImageRef)
2115 {
2116 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Duplicate ButtonImage element.");
2117 }
2118
2119 pImageRef = pDefaultImageRef = pControl->Button.rgImageRef;
2120 }
2121
2122 hr = ParseOwnerDrawImage(hModule, wzRelativePath, pTheme, pixnChild, bstrType, pControl, pImageRef);
2123 ThmExitOnFailure(hr, "Failed when parsing %ls", bstrType);
2124
2125 ReleaseBSTR(bstrType);
2126 ReleaseNullObject(pixnChild);
2127 ++i;
2128 }
2129
2130 if (!pDefaultImageRef && (pFocusImageRef || pHoverImageRef || pSelectedImageRef) ||
2131 pDefaultImageRef && (!pHoverImageRef || !pSelectedImageRef))
2132 {
2133 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Graphic buttons require ButtonImage, ButtonHoverImage, and ButtonSelectedImage.");
2134 }
2135
2136 LExit:
2137 ReleaseObject(pixnl);
2138 ReleaseObject(pixnChild);
2139 ReleaseBSTR(bstrType);
2140
2141 return hr;
2142 }
2143
2144
2145 static HRESULT ParseCommandLinkImage(
2146 __in_opt HMODULE hModule,
2147 __in_z_opt LPCWSTR wzRelativePath,
2148 __in IXMLDOMNode* pElement,
2149 __in THEME_CONTROL* pControl
2150 )
2151 {
2152 HRESULT hr = S_OK;
2153 BSTR bstr = NULL;
2154 BOOL fImageFound = FALSE;
2155 BOOL fXmlFound = FALSE;
2156 LPWSTR sczIconFile = NULL;
2157 WORD wResourceId = 0;
2158 Gdiplus::Bitmap* pBitmap = NULL;
2159
2160 hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pElement, &pBitmap);
2161 ThmExitOnOptionalXmlQueryFailure(hr, fImageFound, "Failed to parse image attributes for CommandLink.");
2162
2163 if (pBitmap)
2164 {
2165 hr = GdipBitmapToGdiBitmap(pBitmap, &pControl->CommandLink.hImage);
2166 ThmExitOnFailure(hr, "Failed to convert bitmap for CommandLink.");
2167 }
2168
2169 hr = XmlGetAttributeUInt16(pElement, L"IconResource", &wResourceId);
2170 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get icon resource attribute.");
2171
2172 if (fXmlFound)
2173 {
2174 if (fImageFound)
2175 {
2176 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Unexpected IconResource attribute with image attribute.");
2177 }
2178
2179 pControl->CommandLink.hIcon = reinterpret_cast<HICON>(::LoadImageW(hModule, MAKEINTRESOURCEW(wResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
2180 ThmExitOnNullWithLastError(pControl->CommandLink.hIcon, hr, "Failed to load icon.");
2181 }
2182
2183 hr = XmlGetAttribute(pElement, L"IconFile", &bstr);
2184 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get icon file attribute.");
2185
2186 if (fXmlFound)
2187 {
2188 if (fImageFound)
2189 {
2190 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Unexpected IconFile attribute with image attribute.");
2191 }
2192 else if (pControl->CommandLink.hIcon)
2193 {
2194 ThmExitWithRootFailure(hr, E_INVALIDDATA, "IconFile attribute can't be specified with IconResource attribute.");
2195 }
2196
2197 if (wzRelativePath)
2198 {
2199 hr = PathConcat(wzRelativePath, bstr, &sczIconFile);
2200 ThmExitOnFailure(hr, "Failed to combine image file path.");
2201 }
2202 else
2203 {
2204 hr = PathRelativeToModule(&sczIconFile, bstr, hModule);
2205 ThmExitOnFailure(hr, "Failed to get image filename.");
2206 }
2207
2208 pControl->CommandLink.hIcon = reinterpret_cast<HICON>(::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE));
2209 ThmExitOnNullWithLastError(pControl->CommandLink.hIcon, hr, "Failed to load icon: %ls.", sczIconFile);
2210 }
2211
2212 ThmExitOnUnexpectedAttribute(hr, pElement, L"CommandLink", L"SourceX");
2213 ThmExitOnUnexpectedAttribute(hr, pElement, L"CommandLink", L"SourceY");
2214
2215 LExit:
2216 if (pBitmap)
2217 {
2218 delete pBitmap;
2219 }
2220
2221 ReleaseStr(sczIconFile);
2222 ReleaseBSTR(bstr);
2223
2224 return hr;
2225 }
2226
2227
2228 static HRESULT ParseProgressBarImages(
2229 __in_opt HMODULE hModule,
2230 __in_z_opt LPCWSTR wzRelativePath,
2231 __in THEME* pTheme,
2232 __in IXMLDOMNode* pElement,
2233 __in THEME_CONTROL* pControl
2234 )
2235 {
2236 HRESULT hr = S_OK;
2237 DWORD i = 0;
2238 IXMLDOMNodeList* pixnl = NULL;
2239 IXMLDOMNode* pixnChild = NULL;
2240
2241 hr = XmlSelectNodes(pElement, L"ProgressbarImage", &pixnl);
2242 ThmExitOnFailure(hr, "Failed to select child ProgressbarImage nodes.");
2243
2244 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->ProgressBar.cImageRef));
2245 ThmExitOnFailure(hr, "Failed to count the number of ProgressbarImage nodes.");
2246
2247 if (!pControl->ProgressBar.cImageRef)
2248 {
2249 ExitFunction();
2250 }
2251
2252 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->ProgressBar.rgImageRef), sizeof(THEME_IMAGE_REFERENCE), pControl->ProgressBar.cImageRef);
2253 ThmExitOnNull(pControl->ProgressBar.rgImageRef, hr, E_OUTOFMEMORY, "Failed to allocate progress bar images.");
2254
2255 i = 0;
2256 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
2257 {
2258 THEME_IMAGE_REFERENCE* pImageRef = pControl->ProgressBar.rgImageRef + i;
2259
2260 hr = ParseOwnerDrawImage(hModule, wzRelativePath, pTheme, pixnChild, L"ProgressbarImage", pControl, pImageRef);
2261 ThmExitOnFailure(hr, "Failed when parsing ProgressbarImage image: %u.", i);
2262
2263 ReleaseNullObject(pixnChild);
2264 ++i;
2265 }
2266
2267 LExit:
2268 ReleaseObject(pixnl);
2269 ReleaseObject(pixnChild);
2270
2271 return hr;
2272 }
2273
2274
2275 static HRESULT GetAttributeCoordinateOrDimension(
2276 __in IXMLDOMNode* pixn,
2277 __in LPCWSTR wzAttribute,
2278 __inout int* pnValue
2279 )
2280 {
2281 HRESULT hr = S_OK;
2282 int nValue = 0;
2283 BOOL fXmlFound = FALSE;
2284
2285 hr = XmlGetAttributeInt32(pixn, wzAttribute, &nValue);
2286 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get coordinate or dimension attribute.");
2287
2288 if (!fXmlFound)
2289 {
2290 ExitFunction1(hr = E_NOTFOUND);
2291 }
2292 else if (abs(nValue) > SHORT_MAX)
2293 {
2294 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Invalid coordinate or dimension attribute value: %i", nValue);
2295 }
2296
2297 *pnValue = nValue;
2298
2299 LExit:
2300 return hr;
2301 }
2302
2303 static HRESULT GetAttributeFontId(
2304 __in THEME* pTheme,
2305 __in IXMLDOMNode* pixn,
2306 __in LPCWSTR wzAttribute,
2307 __inout DWORD* pdwValue
2308 )
2309 {
2310 HRESULT hr = S_OK;
2311 BSTR bstrId = NULL;
2312 THEME_FONT* pFont = NULL;
2313 BOOL fXmlFound = FALSE;
2314
2315 hr = XmlGetAttribute(pixn, wzAttribute, &bstrId);
2316 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get font id attribute.");
2317
2318 if (!fXmlFound)
2319 {
2320 ExitFunction1(hr = E_NOTFOUND);
2321 }
2322
2323 hr = DictGetValue(pTheme->sdhFontDictionary, bstrId, reinterpret_cast<void**>(&pFont));
2324 if (E_NOTFOUND == hr)
2325 {
2326 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Unknown font id: %ls", bstrId);
2327 }
2328 ThmExitOnFailure(hr, "Failed to find font with id: %ls", bstrId);
2329
2330 *pdwValue = pFont->dwIndex;
2331
2332 LExit:
2333 ReleaseBSTR(bstrId);
2334
2335 return hr;
2336 }
2337
2338 static HRESULT GetAttributeImageId(
2339 __in THEME* pTheme,
2340 __in IXMLDOMNode* pixn,
2341 __in LPCWSTR wzAttribute,
2342 __inout DWORD* pdwValue
2343 )
2344 {
2345 HRESULT hr = S_OK;
2346 BSTR bstrId = NULL;
2347 THEME_IMAGE* pImage = NULL;
2348 BOOL fXmlFound = FALSE;
2349
2350 hr = XmlGetAttribute(pixn, wzAttribute, &bstrId);
2351 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get image id attribute.");
2352
2353 if (!fXmlFound)
2354 {
2355 ExitFunction1(hr = E_NOTFOUND);
2356 }
2357
2358 hr = DictGetValue(pTheme->sdhImageDictionary, bstrId, reinterpret_cast<void**>(&pImage));
2359 if (E_NOTFOUND == hr)
2360 {
2361 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Unknown image id: %ls", bstrId);
2362 }
2363 ThmExitOnFailure(hr, "Failed to find image with id: %ls", bstrId);
2364
2365 *pdwValue = pImage->dwIndex;
2366
2367 LExit:
2368 ReleaseBSTR(bstrId);
2369
2370 return hr;
2371 }
2372
2373 static HRESULT ParseSourceXY(
2374 __in IXMLDOMNode* pixn,
2375 __in THEME* pTheme,
2376 __in int nWidth,
2377 __in int nHeight,
2378 __inout THEME_IMAGE_REFERENCE* pReference
2379 )
2380 {
2381 HRESULT hr = S_OK;
2382 BOOL fXFound = FALSE;
2383 BOOL fYFound = FALSE;
2384 int nX = 0;
2385 int nY = 0;
2386 DWORD dwImageInstanceIndex = pTheme->dwSourceImageInstanceIndex;
2387 THEME_IMAGE_INSTANCE* pInstance = THEME_INVALID_ID != dwImageInstanceIndex ? pTheme->rgStandaloneImages + dwImageInstanceIndex : NULL;
2388 int nSourceWidth = pInstance ? pInstance->pBitmap->GetWidth() : 0;
2389 int nSourceHeight = pInstance ? pInstance->pBitmap->GetHeight() : 0;
2390
2391 hr = GetAttributeCoordinateOrDimension(pixn, L"SourceX", &nX);
2392 ThmExitOnOptionalXmlQueryFailure(hr, fXFound, "Failed to get SourceX attribute.");
2393
2394 if (!fXFound)
2395 {
2396 nX = -1;
2397 }
2398 else
2399 {
2400 if (!pInstance)
2401 {
2402 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX cannot be specified without an image specified on Theme.");
2403 }
2404 else if (0 > nX)
2405 {
2406 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX must be non-negative.");
2407 }
2408 else if (nSourceWidth <= nX)
2409 {
2410 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX (%i) must be less than the image width: %i.", nX, nSourceWidth);
2411 }
2412 else if (nSourceWidth <= (nX + nWidth))
2413 {
2414 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceX (%i) with width %i must be less than the image width: %i.", nX, nWidth, nSourceWidth);
2415 }
2416 }
2417
2418 hr = GetAttributeCoordinateOrDimension(pixn, L"SourceY", &nY);
2419 ThmExitOnOptionalXmlQueryFailure(hr, fYFound, "Failed to get SourceY attribute.");
2420
2421 if (!fYFound)
2422 {
2423 if (fXFound)
2424 {
2425 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY must be specified with SourceX.");
2426 }
2427
2428 ExitFunction1(hr = E_NOTFOUND);
2429 }
2430 else
2431 {
2432 if (!pInstance)
2433 {
2434 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY cannot be specified without an image specified on Theme.");
2435 }
2436 else if (!fXFound)
2437 {
2438 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY must be specified with SourceX.");
2439 }
2440 else if (0 > nY)
2441 {
2442 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY must be non-negative.");
2443 }
2444 else if (nSourceHeight <= nY)
2445 {
2446 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY (%i) must be less than the image height: %i.", nY, nSourceHeight);
2447 }
2448 else if (nSourceHeight <= (nY + nHeight))
2449 {
2450 ThmExitWithRootFailure(hr, E_INVALIDDATA, "SourceY (%i) with height %i must be less than the image height: %i.", nY, nHeight, nSourceHeight);
2451 }
2452 }
2453
2454 pReference->type = THEME_IMAGE_REFERENCE_TYPE_PARTIAL;
2455 pReference->dwImageIndex = THEME_INVALID_ID;
2456 pReference->dwImageInstanceIndex = dwImageInstanceIndex;
2457 pReference->nX = nX;
2458 pReference->nY = nY;
2459 pReference->nWidth = nWidth;
2460 pReference->nHeight = nHeight;
2461
2462 LExit:
2463 return hr;
2464 }
2465
2466 static HRESULT ParseWindow(
2467 __in_opt HMODULE hModule,
2468 __in_opt LPCWSTR wzRelativePath,
2469 __in IXMLDOMElement* pElement,
2470 __in THEME* pTheme
2471 )
2472 {
2473 HRESULT hr = S_OK;
2474 IXMLDOMNode* pixn = NULL;
2475 BOOL fXmlFound = FALSE;
2476 int nValue = 0;
2477 BSTR bstr = NULL;
2478 LPWSTR sczIconFile = NULL;
2479
2480 hr = XmlSelectSingleNode(pElement, L"Window", &pixn);
2481 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find window element.");
2482
2483 hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize);
2484 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window AutoResize attribute.");
2485
2486 hr = GetAttributeCoordinateOrDimension(pixn, L"Width", &nValue);
2487 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to get window Width attribute.");
2488
2489 if (1 > nValue)
2490 {
2491 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@Width must be positive: %i", nValue);
2492 }
2493
2494 pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = nValue;
2495
2496 hr = GetAttributeCoordinateOrDimension(pixn, L"Height", &nValue);
2497 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to get window Height attribute.");
2498
2499 if (1 > nValue)
2500 {
2501 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@Height must be positive: %i", nValue);
2502 }
2503
2504 pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = nValue;
2505
2506 hr = GetAttributeCoordinateOrDimension(pixn, L"MinimumWidth", &nValue);
2507 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window MinimumWidth attribute.");
2508
2509 if (fXmlFound)
2510 {
2511 if (!pTheme->fAutoResize)
2512 {
2513 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@MinimumWidth can't be specified unless AutoResize is enabled.");
2514 }
2515 else if (1 > nValue || pTheme->nWidth < nValue)
2516 {
2517 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@MinimumWidth must be positive and not greater than Window/@Width: %i", nValue);
2518 }
2519
2520 pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = nValue;
2521 }
2522
2523 hr = GetAttributeCoordinateOrDimension(pixn, L"MinimumHeight", &nValue);
2524 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window MinimumHeight attribute.");
2525
2526 if (fXmlFound)
2527 {
2528 if (!pTheme->fAutoResize)
2529 {
2530 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@MinimumHeight can't be specified unless AutoResize is enabled.");
2531 }
2532 else if (1 > nValue || pTheme->nHeight < nValue)
2533 {
2534 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@MinimumHeight must be positive and not greater than Window/@Height: %i", nValue);
2535 }
2536
2537 pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = nValue;
2538 }
2539
2540 hr = GetAttributeFontId(pTheme, pixn, L"FontId", &pTheme->dwFontId);
2541 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to get window FontId attribute.");
2542
2543 // Get the optional window icon from a resource.
2544 hr = XmlGetAttribute(pixn, L"IconResource", &bstr);
2545 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window IconResource attribute.");
2546
2547 if (fXmlFound)
2548 {
2549 pTheme->hIcon = ::LoadIconW(hModule, bstr);
2550 ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource.");
2551
2552 ReleaseNullBSTR(bstr);
2553 }
2554
2555 // Get the optional window icon from a file.
2556 hr = XmlGetAttribute(pixn, L"IconFile", &bstr);
2557 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window IconFile attribute.");
2558
2559 if (fXmlFound)
2560 {
2561 if (pTheme->hIcon)
2562 {
2563 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window/@IconFile can't be specified with IconResource.");
2564 }
2565
2566 if (wzRelativePath)
2567 {
2568 hr = PathConcat(wzRelativePath, bstr, &sczIconFile);
2569 ThmExitOnFailure(hr, "Failed to combine icon file path.");
2570 }
2571 else
2572 {
2573 hr = PathRelativeToModule(&sczIconFile, bstr, hModule);
2574 ThmExitOnFailure(hr, "Failed to get icon filename.");
2575 }
2576
2577 pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
2578 ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr);
2579
2580 ReleaseNullBSTR(bstr);
2581 }
2582
2583 hr = ParseSourceXY(pixn, pTheme, pTheme->nDefaultDpiWidth, pTheme->nDefaultDpiHeight, &pTheme->windowImageRef);
2584 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window SourceX and SourceY attributes.");
2585
2586 // Parse the optional window style.
2587 hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle);
2588 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get theme window style (Window@HexStyle) attribute.");
2589
2590 if (!fXmlFound)
2591 {
2592 pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION;
2593 pTheme->dwStyle |= (THEME_IMAGE_REFERENCE_TYPE_NONE != pTheme->windowImageRef.type) ? WS_POPUP : WS_OVERLAPPED;
2594 }
2595
2596 hr = XmlGetAttributeUInt32(pixn, L"StringId", reinterpret_cast<DWORD*>(&pTheme->uStringId));
2597 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window StringId attribute.");
2598
2599 if (!fXmlFound)
2600 {
2601 pTheme->uStringId = UINT_MAX;
2602 }
2603 else if (UINT_MAX == pTheme->uStringId)
2604 {
2605 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Invalid StringId: %u", pTheme->uStringId);
2606 }
2607
2608 hr = XmlGetAttributeEx(pixn, L"Caption", &pTheme->sczCaption);
2609 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get window Caption attribute.");
2610
2611 if (fXmlFound && UINT_MAX != pTheme->uStringId || !fXmlFound && UINT_MAX == pTheme->uStringId)
2612 {
2613 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Window elements must contain either the Caption or StringId attribute.");
2614 }
2615
2616 // Parse the pages.
2617 hr = ParsePages(hModule, wzRelativePath, pixn, pTheme);
2618 ThmExitOnFailure(hr, "Failed to parse theme pages.");
2619
2620 // Parse the non-paged controls.
2621 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL, NULL);
2622 ThmExitOnFailure(hr, "Failed to parse theme controls.");
2623
2624 LExit:
2625 ReleaseStr(sczIconFile);
2626 ReleaseBSTR(bstr);
2627 ReleaseObject(pixn);
2628
2629 return hr;
2630 }
2631
2632
2633 static HRESULT ParseFonts(
2634 __in IXMLDOMElement* pElement,
2635 __in THEME* pTheme
2636 )
2637 {
2638 HRESULT hr = S_OK;
2639 IXMLDOMNodeList* pixnl = NULL;
2640 IXMLDOMNode* pixn = NULL;
2641 LPWSTR sczFontId = NULL;
2642 BSTR bstrName = NULL;
2643 DWORD dwId = 0;
2644 BOOL fXmlFound = FALSE;
2645 COLORREF crForeground = THEME_INVISIBLE_COLORREF;
2646 COLORREF crBackground = THEME_INVISIBLE_COLORREF;
2647 DWORD dwSystemForegroundColor = FALSE;
2648 DWORD dwSystemBackgroundColor = FALSE;
2649
2650 hr = XmlSelectNodes(pElement, L"Font", &pixnl);
2651 ThmExitOnFailure(hr, "Failed to find font elements.");
2652
2653 hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cFonts));
2654 ThmExitOnFailure(hr, "Failed to count the number of theme fonts.");
2655
2656 if (!pTheme->cFonts)
2657 {
2658 ThmExitOnRootFailure(hr = E_INVALIDDATA, "No font elements found.");
2659 }
2660
2661 pTheme->rgFonts = static_cast<THEME_FONT*>(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE));
2662 ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts.");
2663
2664 hr = DictCreateWithEmbeddedKey(&pTheme->sdhFontDictionary, pTheme->cFonts, reinterpret_cast<void**>(&pTheme->rgFonts), offsetof(THEME_FONT, sczId), DICT_FLAG_NONE);
2665 ThmExitOnFailure(hr, "Failed to create font dictionary.");
2666
2667 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
2668 {
2669 hr = XmlGetAttributeEx(pixn, L"Id", &sczFontId);
2670 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find font id.");
2671
2672 hr = DictKeyExists(pTheme->sdhFontDictionary, sczFontId);
2673 if (E_NOTFOUND != hr)
2674 {
2675 ThmExitOnFailure(hr, "Failed to check for duplicate font id.");
2676 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Theme font id duplicated: %ls", sczFontId);
2677 }
2678
2679 THEME_FONT* pFont = pTheme->rgFonts + dwId;
2680 pFont->sczId = sczFontId;
2681 sczFontId = NULL;
2682 pFont->dwIndex = dwId;
2683 ++dwId;
2684
2685 pFont->lfQuality = CLEARTYPE_QUALITY;
2686
2687 hr = XmlGetText(pixn, &bstrName);
2688 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to get font name.");
2689
2690 hr = StrAllocString(&pFont->sczFaceName, bstrName, 0);
2691 ThmExitOnFailure(hr, "Failed to copy font name.");
2692
2693 hr = XmlGetAttributeInt32(pixn, L"Height", reinterpret_cast<int*>(&pFont->lfHeight));
2694 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find font height attribute.");
2695
2696 hr = XmlGetAttributeInt32(pixn, L"Weight", reinterpret_cast<int*>(&pFont->lfWeight));
2697 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find font weight attribute.");
2698
2699 if (!fXmlFound)
2700 {
2701 pFont->lfWeight = FW_DONTCARE;
2702 }
2703
2704 hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast<BOOL*>(&pFont->lfUnderline));
2705 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find font underline attribute.");
2706
2707 if (!fXmlFound)
2708 {
2709 pFont->lfUnderline = FALSE;
2710 }
2711
2712 hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor);
2713 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find font foreground color.");
2714
2715 hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor);
2716 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find font background color.");
2717
2718 pFont->crForeground = crForeground;
2719 if (THEME_INVISIBLE_COLORREF != pFont->crForeground)
2720 {
2721 pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground);
2722 ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush.");
2723 }
2724
2725 pFont->crBackground = crBackground;
2726 if (THEME_INVISIBLE_COLORREF != pFont->crBackground)
2727 {
2728 pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground);
2729 ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush.");
2730 }
2731
2732 hr = DictAddValue(pTheme->sdhFontDictionary, pFont);
2733 ThmExitOnFailure(hr, "Failed to add font to dictionary.");
2734
2735 ReleaseNullBSTR(bstrName);
2736 ReleaseNullObject(pixn);
2737 }
2738 ThmExitOnFailure(hr, "Failed to enumerate all fonts.");
2739
2740 if (S_FALSE == hr)
2741 {
2742 hr = S_OK;
2743 }
2744
2745 LExit:
2746 ReleaseBSTR(bstrName);
2747 ReleaseStr(sczFontId);
2748 ReleaseObject(pixn);
2749 ReleaseObject(pixnl);
2750
2751 return hr;
2752 }
2753
2754
2755 static HRESULT GetFontColor(
2756 __in IXMLDOMNode* pixn,
2757 __in_z LPCWSTR wzAttributeName,
2758 __out COLORREF* pColorRef,
2759 __out DWORD* pdwSystemColor
2760 )
2761 {
2762 HRESULT hr = S_OK;
2763 BSTR bstr = NULL;
2764 BOOL fXmlFound = FALSE;
2765
2766 *pdwSystemColor = 0;
2767
2768 hr = XmlGetAttribute(pixn, wzAttributeName, &bstr);
2769 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find font %ls color.", wzAttributeName);
2770
2771 if (!fXmlFound)
2772 {
2773 *pColorRef = THEME_INVISIBLE_COLORREF;
2774 ExitFunction1(hr = E_NOTFOUND);
2775 }
2776
2777 if (pdwSystemColor)
2778 {
2779 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1))
2780 {
2781 *pdwSystemColor = COLOR_BTNFACE;
2782 }
2783 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1))
2784 {
2785 *pdwSystemColor = COLOR_BTNTEXT;
2786 }
2787 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1))
2788 {
2789 *pdwSystemColor = COLOR_GRAYTEXT;
2790 }
2791 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1))
2792 {
2793 *pdwSystemColor = COLOR_HIGHLIGHT;
2794 }
2795 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1))
2796 {
2797 *pdwSystemColor = COLOR_HIGHLIGHTTEXT;
2798 }
2799 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1))
2800 {
2801 *pdwSystemColor = COLOR_HOTLIGHT;
2802 }
2803 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1))
2804 {
2805 *pdwSystemColor = COLOR_WINDOW;
2806 }
2807 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1))
2808 {
2809 *pdwSystemColor = COLOR_WINDOWTEXT;
2810 }
2811 else
2812 {
2813 *pColorRef = ::wcstoul(bstr, NULL, 16);
2814
2815 if (THEME_INVISIBLE_COLORREF == *pColorRef)
2816 {
2817 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Invalid %ls value: %ls.", wzAttributeName, bstr);
2818 }
2819 }
2820
2821 if (*pdwSystemColor)
2822 {
2823 *pColorRef = ::GetSysColor(*pdwSystemColor);
2824 }
2825 }
2826
2827 LExit:
2828 ReleaseBSTR(bstr);
2829
2830 return hr;
2831 }
2832
2833
2834 static HRESULT ParseImages(
2835 __in_opt HMODULE hModule,
2836 __in_opt LPCWSTR wzRelativePath,
2837 __in IXMLDOMElement* pElement,
2838 __in THEME* pTheme
2839 )
2840 {
2841 HRESULT hr = S_OK;
2842 IXMLDOMNodeList* pixnl = NULL;
2843 IXMLDOMNode* pixn = NULL;
2844 LPWSTR sczImageId = NULL;
2845 DWORD dwImageIndex = 0;
2846 Gdiplus::Bitmap* pDefaultBitmap = NULL;
2847 IXMLDOMNodeList* pixnlAlternates = NULL;
2848 IXMLDOMNode* pixnAlternate = NULL;
2849 DWORD dwInstances = 0;
2850 THEME_IMAGE_INSTANCE* pInstance = NULL;
2851 BOOL fXmlFound = FALSE;
2852
2853 hr = XmlSelectNodes(pElement, L"Image", &pixnl);
2854 ThmExitOnFailure(hr, "Failed to find font elements.");
2855
2856 hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cImages));
2857 ThmExitOnFailure(hr, "Failed to count the number of theme images.");
2858
2859 if (!pTheme->cImages)
2860 {
2861 ExitFunction1(hr = S_OK);
2862 }
2863
2864 pTheme->rgImages = static_cast<THEME_IMAGE*>(MemAlloc(sizeof(THEME_IMAGE) * pTheme->cImages, TRUE));
2865 ThmExitOnNull(pTheme->rgImages, hr, E_OUTOFMEMORY, "Failed to allocate theme images.");
2866
2867 hr = DictCreateWithEmbeddedKey(&pTheme->sdhImageDictionary, pTheme->cImages, reinterpret_cast<void**>(&pTheme->rgImages), offsetof(THEME_IMAGE, sczId), DICT_FLAG_NONE);
2868 ThmExitOnFailure(hr, "Failed to create image dictionary.");
2869
2870 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
2871 {
2872 hr = XmlGetAttributeEx(pixn, L"Id", &sczImageId);
2873 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find image id.");
2874
2875 hr = DictKeyExists(pTheme->sdhImageDictionary, sczImageId);
2876 if (E_NOTFOUND != hr)
2877 {
2878 ThmExitOnFailure(hr, "Failed to check for duplicate image id.");
2879 ThmExitOnRootFailure(hr = E_INVALIDDATA, "Theme image id duplicated: %ls", sczImageId);
2880 }
2881
2882 THEME_IMAGE* pImage = pTheme->rgImages + dwImageIndex;
2883 pImage->sczId = sczImageId;
2884 sczImageId = NULL;
2885 pImage->dwIndex = dwImageIndex;
2886 ++dwImageIndex;
2887
2888 hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pixn, &pDefaultBitmap);
2889 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to parse Image: %ls", pImage->sczId);
2890
2891 if (!fXmlFound)
2892 {
2893 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Image didn't specify an image: %ls.", pImage->sczId);
2894 }
2895
2896 hr = DictAddValue(pTheme->sdhImageDictionary, pImage);
2897 ThmExitOnFailure(hr, "Failed to add image to dictionary.");
2898
2899 // Parse alternates, if any.
2900 hr = XmlSelectNodes(pixn, L"AlternateResolution", &pixnlAlternates);
2901 ThmExitOnFailure(hr, "Failed to select child AlternateResolution nodes.");
2902
2903 hr = pixnlAlternates->get_length(reinterpret_cast<long*>(&dwInstances));
2904 ThmExitOnFailure(hr, "Failed to count the number of alternates.");
2905
2906 dwInstances += 1;
2907
2908 pImage->rgImageInstances = static_cast<THEME_IMAGE_INSTANCE*>(MemAlloc(sizeof(THEME_IMAGE_INSTANCE) * dwInstances, TRUE));
2909 ThmExitOnNull(pImage->rgImageInstances, hr, E_OUTOFMEMORY, "Failed to allocate image instances.");
2910
2911 pInstance = pImage->rgImageInstances;
2912 pInstance->pBitmap = pDefaultBitmap;
2913 pDefaultBitmap = NULL;
2914 pImage->cImageInstances += 1;
2915
2916 while (S_OK == (hr = XmlNextElement(pixnlAlternates, &pixnAlternate, NULL)))
2917 {
2918 pInstance = pImage->rgImageInstances + pImage->cImageInstances;
2919
2920 hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pixnAlternate, &pInstance->pBitmap);
2921 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to parse Image: '%ls', alternate resolution: %u", pImage->sczId, pImage->cImageInstances);
2922
2923 if (!fXmlFound)
2924 {
2925 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Image: '%ls', alternate resolution: %u, didn't specify an image.", pImage->sczId, pImage->cImageInstances);
2926 }
2927
2928 ReleaseNullObject(pixnAlternate);
2929
2930 pImage->cImageInstances += 1;
2931 }
2932
2933 ReleaseNullObject(pixnlAlternates);
2934 ReleaseNullObject(pixn);
2935 }
2936 ThmExitOnFailure(hr, "Failed to enumerate all images.");
2937
2938 if (S_FALSE == hr)
2939 {
2940 hr = S_OK;
2941 }
2942
2943 LExit:
2944 if (pDefaultBitmap)
2945 {
2946 delete pDefaultBitmap;
2947 }
2948
2949 ReleaseObject(pixnAlternate);
2950 ReleaseObject(pixnlAlternates);
2951 ReleaseStr(sczImageId);
2952 ReleaseObject(pixn);
2953 ReleaseObject(pixnl);
2954
2955 return hr;
2956 }
2957
2958 static HRESULT ParsePages(
2959 __in_opt HMODULE hModule,
2960 __in_opt LPCWSTR wzRelativePath,
2961 __in IXMLDOMNode* pElement,
2962 __in THEME* pTheme
2963 )
2964 {
2965 HRESULT hr = S_OK;
2966 IXMLDOMNodeList* pixnl = NULL;
2967 IXMLDOMNode* pixn = NULL;
2968 BSTR bstrType = NULL;
2969 THEME_PAGE* pPage = NULL;
2970 DWORD iPage = 0;
2971 BOOL fXmlFound = FALSE;
2972
2973 hr = XmlSelectNodes(pElement, L"Page", &pixnl);
2974 ThmExitOnFailure(hr, "Failed to find page elements.");
2975
2976 hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cPages));
2977 ThmExitOnFailure(hr, "Failed to count the number of theme pages.");
2978
2979 if (!pTheme->cPages)
2980 {
2981 ExitFunction1(hr = S_OK);
2982 }
2983
2984 pTheme->rgPages = static_cast<THEME_PAGE*>(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE));
2985 ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages.");
2986
2987 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType)))
2988 {
2989 pPage = pTheme->rgPages + iPage;
2990
2991 pPage->wId = static_cast<WORD>(iPage + 1);
2992
2993 hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName);
2994 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying page Name.");
2995
2996 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage, NULL);
2997 ThmExitOnFailure(hr, "Failed to parse page controls.");
2998
2999 ++iPage;
3000
3001 ReleaseNullBSTR(bstrType);
3002 ReleaseNullObject(pixn);
3003 }
3004 ThmExitOnFailure(hr, "Failed to enumerate all pages.");
3005
3006 if (S_FALSE == hr)
3007 {
3008 hr = S_OK;
3009 }
3010
3011 LExit:
3012 ReleaseBSTR(bstrType);
3013 ReleaseObject(pixn);
3014 ReleaseObject(pixnl);
3015
3016 return hr;
3017 }
3018
3019
3020 static HRESULT ParseImageLists(
3021 __in_opt HMODULE hModule,
3022 __in_opt LPCWSTR wzRelativePath,
3023 __in IXMLDOMNode* pElement,
3024 __in THEME* pTheme
3025 )
3026 {
3027 HRESULT hr = S_OK;
3028 IXMLDOMNodeList* pixnlImageLists = NULL;
3029 IXMLDOMNode* pixnImageList = NULL;
3030 IXMLDOMNodeList* pixnlImages = NULL;
3031 IXMLDOMNode* pixnImage = NULL;
3032 DWORD dwImageListIndex = 0;
3033 DWORD dwImageCount = 0;
3034 THEME_IMAGELIST* pThemeImageList = NULL;
3035 BOOL fXmlFound = FALSE;
3036 Gdiplus::Bitmap* pBitmap = NULL;
3037 HBITMAP hImage = NULL;
3038 DWORD i = 0;
3039 int iRetVal = 0;
3040
3041 hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists);
3042 ThmExitOnFailure(hr, "Failed to find ImageList elements.");
3043
3044 hr = pixnlImageLists->get_length(reinterpret_cast<long*>(&pTheme->cImageLists));
3045 ThmExitOnFailure(hr, "Failed to count the number of image lists.");
3046
3047 if (!pTheme->cImageLists)
3048 {
3049 ExitFunction1(hr = S_OK);
3050 }
3051
3052 pTheme->rgImageLists = static_cast<THEME_IMAGELIST*>(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE));
3053 ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists.");
3054
3055 while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL)))
3056 {
3057 pThemeImageList = pTheme->rgImageLists + dwImageListIndex;
3058 ++dwImageListIndex;
3059 i = 0;
3060
3061 hr = XmlGetAttributeEx(pixnImageList, L"Name", &pThemeImageList->sczName);
3062 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find ImageList/@Name attribute.");
3063
3064 hr = XmlSelectNodes(pixnImageList, L"ImageListItem", &pixnlImages);
3065 ThmExitOnFailure(hr, "Failed to select child ImageListItem nodes.");
3066
3067 hr = pixnlImages->get_length(reinterpret_cast<long*>(&dwImageCount));
3068 ThmExitOnFailure(hr, "Failed to count the number of images in list.");
3069
3070 if (!dwImageCount)
3071 {
3072 ThmExitOnRootFailure(hr = E_INVALIDDATA, "ImageList '%ls' has no images.", pThemeImageList->sczName);
3073 }
3074
3075 while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL)))
3076 {
3077 if (pBitmap)
3078 {
3079 delete pBitmap;
3080 pBitmap = NULL;
3081 }
3082 if (hImage)
3083 {
3084 ::DeleteObject(hImage);
3085 hImage = NULL;
3086 }
3087
3088 hr = GetAttributeImageFileOrResource(hModule, wzRelativePath, pixnImage, &pBitmap);
3089 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to parse image list: '%ls', item: %u", pThemeImageList->sczName, i);
3090
3091 if (!fXmlFound)
3092 {
3093 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Image list: '%ls', item %u didn't specify an image.", pThemeImageList->sczName, i);
3094 }
3095
3096 if (0 == i)
3097 {
3098 pThemeImageList->hImageList = ImageList_Create(pBitmap->GetWidth(), pBitmap->GetHeight(), ILC_COLOR24, dwImageCount, 0);
3099 ThmExitOnNullWithLastError(pThemeImageList->hImageList, hr, "Failed to create image list.");
3100 }
3101
3102 hr = GdipBitmapToGdiBitmap(pBitmap, &hImage);
3103 ThmExitOnFailure(hr, "Failed to convert bitmap for CommandLink.");
3104
3105 iRetVal = ImageList_Add(pThemeImageList->hImageList, hImage, NULL);
3106 if (-1 == iRetVal)
3107 {
3108 ThmExitWithLastError(hr, "Failed to add image %u to image list.", i);
3109 }
3110
3111 ++i;
3112 ReleaseNullObject(pixnImage);
3113 }
3114
3115 ReleaseNullObject(pixnlImages);
3116 ReleaseNullObject(pixnImageList);
3117 }
3118
3119 LExit:
3120 if (hImage)
3121 {
3122 ::DeleteObject(hImage);
3123 }
3124 if (pBitmap)
3125 {
3126 delete pBitmap;
3127 }
3128 ReleaseObject(pixnlImageLists);
3129 ReleaseObject(pixnImageList);
3130 ReleaseObject(pixnlImages);
3131 ReleaseObject(pixnImage);
3132
3133 return hr;
3134 }
3135
3136 static void GetControls(
3137 __in THEME* pTheme,
3138 __in_opt THEME_CONTROL* pParentControl,
3139 __out DWORD** ppcControls,
3140 __out THEME_CONTROL*** pprgControls
3141 )
3142 {
3143 if (pParentControl)
3144 {
3145 *ppcControls = &pParentControl->cControls;
3146 *pprgControls = &pParentControl->rgControls;
3147 }
3148 else
3149 {
3150 *ppcControls = &pTheme->cControls;
3151 *pprgControls = &pTheme->rgControls;
3152 }
3153 }
3154
3155 static void GetControls(
3156 __in const THEME* pTheme,
3157 __in_opt const THEME_CONTROL* pParentControl,
3158 __out DWORD& cControls,
3159 __out THEME_CONTROL*& rgControls
3160 )
3161 {
3162 if (pParentControl)
3163 {
3164 cControls = pParentControl->cControls;
3165 rgControls = pParentControl->rgControls;
3166 }
3167 else
3168 {
3169 cControls = pTheme->cControls;
3170 rgControls = pTheme->rgControls;
3171 }
3172 }
3173
3174 static HRESULT ParseControls(
3175 __in_opt HMODULE hModule,
3176 __in_opt LPCWSTR wzRelativePath,
3177 __in IXMLDOMNode* pElement,
3178 __in THEME* pTheme,
3179 __in_opt THEME_CONTROL* pParentControl,
3180 __in_opt THEME_PAGE* pPage,
3181 __in_opt LPCWSTR wzControlNames
3182 )
3183 {
3184 HRESULT hr = S_OK;
3185 IXMLDOMNodeList* pixnl = NULL;
3186 IXMLDOMNode* pixn = NULL;
3187 BSTR bstrType = NULL;
3188 DWORD cNewControls = 0;
3189 DWORD iControl = 0;
3190 DWORD* pcControls = NULL;
3191 THEME_CONTROL** prgControls = NULL;
3192
3193 GetControls(pTheme, pParentControl, &pcControls, &prgControls);
3194
3195 hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage);
3196 ThmExitOnFailure(hr, "Failed to parse radio buttons.");
3197
3198 hr = XmlSelectNodes(pElement, wzControlNames ? wzControlNames : ALL_CONTROL_NAMES, &pixnl);
3199 ThmExitOnFailure(hr, "Failed to find control elements.");
3200
3201 hr = pixnl->get_length(reinterpret_cast<long*>(&cNewControls));
3202 ThmExitOnFailure(hr, "Failed to count the number of theme controls.");
3203
3204 if (!cNewControls)
3205 {
3206 ExitFunction1(hr = S_OK);
3207 }
3208
3209 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgControls), *pcControls, cNewControls, sizeof(THEME_CONTROL), 0);
3210 ThmExitOnFailure(hr, "Failed to reallocate theme controls.");
3211
3212 cNewControls += *pcControls;
3213
3214 if (pPage)
3215 {
3216 pPage->cControlIndices += cNewControls;
3217 }
3218
3219 iControl = *pcControls;
3220 *pcControls = cNewControls;
3221
3222 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType)))
3223 {
3224 THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN;
3225
3226 if (!bstrType)
3227 {
3228 hr = E_UNEXPECTED;
3229 ThmExitOnFailure(hr, "Null element encountered!");
3230 }
3231
3232 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1))
3233 {
3234 type = THEME_CONTROL_TYPE_BILLBOARD;
3235 }
3236 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1))
3237 {
3238 type = THEME_CONTROL_TYPE_BUTTON;
3239 }
3240 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1))
3241 {
3242 type = THEME_CONTROL_TYPE_CHECKBOX;
3243 }
3244 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1))
3245 {
3246 type = THEME_CONTROL_TYPE_COMBOBOX;
3247 }
3248 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1))
3249 {
3250 type = THEME_CONTROL_TYPE_COMMANDLINK;
3251 }
3252 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1))
3253 {
3254 type = THEME_CONTROL_TYPE_EDITBOX;
3255 }
3256 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1))
3257 {
3258 type = THEME_CONTROL_TYPE_HYPERLINK;
3259 }
3260 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1))
3261 {
3262 type = THEME_CONTROL_TYPE_HYPERTEXT;
3263 }
3264 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1))
3265 {
3266 type = THEME_CONTROL_TYPE_IMAGE;
3267 }
3268 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1))
3269 {
3270 type = THEME_CONTROL_TYPE_LABEL;
3271 }
3272 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1))
3273 {
3274 type = THEME_CONTROL_TYPE_LISTVIEW;
3275 }
3276 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1))
3277 {
3278 type = THEME_CONTROL_TYPE_PANEL;
3279 }
3280 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1))
3281 {
3282 type = THEME_CONTROL_TYPE_PROGRESSBAR;
3283 }
3284 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1))
3285 {
3286 type = THEME_CONTROL_TYPE_RICHEDIT;
3287 }
3288 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1))
3289 {
3290 type = THEME_CONTROL_TYPE_STATIC;
3291 }
3292 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1))
3293 {
3294 type = THEME_CONTROL_TYPE_TAB;
3295 }
3296 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1))
3297 {
3298 type = THEME_CONTROL_TYPE_TREEVIEW;
3299 }
3300
3301 if (THEME_CONTROL_TYPE_UNKNOWN != type)
3302 {
3303 THEME_CONTROL* pControl = *prgControls + iControl;
3304 pControl->type = type;
3305
3306 hr = ParseControl(hModule, wzRelativePath, pixn, bstrType, pTheme, pControl, pPage);
3307 ThmExitOnFailure(hr, "Failed to parse control.");
3308
3309 if (pPage)
3310 {
3311 pControl->wPageId = pPage->wId;
3312 }
3313
3314 ++iControl;
3315 }
3316
3317 ReleaseNullBSTR(bstrType);
3318 ReleaseNullObject(pixn);
3319 }
3320 ThmExitOnFailure(hr, "Failed to enumerate all controls.");
3321
3322 if (S_FALSE == hr)
3323 {
3324 hr = S_OK;
3325 }
3326
3327 AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls.");
3328
3329 LExit:
3330 ReleaseBSTR(bstrType);
3331 ReleaseObject(pixn);
3332 ReleaseObject(pixnl);
3333
3334 return hr;
3335 }
3336
3337
3338 static HRESULT ParseControl(
3339 __in_opt HMODULE hModule,
3340 __in_opt LPCWSTR wzRelativePath,
3341 __in IXMLDOMNode* pixn,
3342 __in_z LPCWSTR wzElementName,
3343 __in THEME* pTheme,
3344 __in THEME_CONTROL* pControl,
3345 __in_opt THEME_PAGE* pPage
3346 )
3347 {
3348 HRESULT hr = S_OK;
3349 BOOL fXmlFound = FALSE;
3350 DWORD dwValue = 0;
3351 int nValue = 0;
3352 BOOL fValue = FALSE;
3353 BSTR bstrText = NULL;
3354 BOOL fAnyTextChildren = FALSE;
3355 BOOL fAnyNoteChildren = FALSE;
3356
3357 InitializeThemeControl(pTheme, pControl);
3358
3359 hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName);
3360 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control Name attribute.");
3361
3362 hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition);
3363 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' EnableCondition attribute.", pControl->sczName);
3364
3365 hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition);
3366 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' VisibleCondition attribute.", pControl->sczName);
3367
3368 hr = GetAttributeCoordinateOrDimension(pixn, L"X", &nValue);
3369 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find control '%ls' X attribute.", pControl->sczName);
3370
3371 pControl->nX = pControl->nDefaultDpiX = nValue;
3372
3373 hr = GetAttributeCoordinateOrDimension(pixn, L"Y", &nValue);
3374 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find control '%ls' Y attribute.", pControl->sczName);
3375
3376 pControl->nY = pControl->nDefaultDpiY = nValue;
3377
3378 hr = GetAttributeCoordinateOrDimension(pixn, L"Height", &nValue);
3379 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find control '%ls' Height attribute.", pControl->sczName);
3380
3381 pControl->nHeight = pControl->nDefaultDpiHeight = nValue;
3382
3383 hr = GetAttributeCoordinateOrDimension(pixn, L"Width", &nValue);
3384 ThmExitOnRequiredXmlQueryFailure(hr, "Failed to find control '%ls' Width attribute.", pControl->sczName);
3385
3386 pControl->nWidth = pControl->nDefaultDpiWidth = nValue;
3387
3388 switch (pControl->type)
3389 {
3390 case THEME_CONTROL_TYPE_COMMANDLINK:
3391 hr = ParseCommandLinkImage(hModule, wzRelativePath, pixn, pControl);
3392 ThmExitOnFailure(hr, "Failed while parsing CommandLink '%ls' image.", pControl->sczName);
3393 break;
3394 case THEME_CONTROL_TYPE_BUTTON:
3395 hr = ParseButtonImages(hModule, wzRelativePath, pTheme, pixn, pControl);
3396 ThmExitOnFailure(hr, "Failed while parsing Button '%ls' images.", pControl->sczName);
3397 break;
3398 case THEME_CONTROL_TYPE_IMAGE:
3399 hr = ParseOwnerDrawImage(hModule, wzRelativePath, pTheme, pixn, wzElementName, pControl, &pControl->Image.imageRef);
3400 ThmExitOnFailure(hr, "Failed while parsing ImageControl '%ls' image.", pControl->sczName);
3401 break;
3402 case THEME_CONTROL_TYPE_PROGRESSBAR:
3403 hr = ParseProgressBarImages(hModule, wzRelativePath, pTheme, pixn, pControl);
3404 ThmExitOnFailure(hr, "Failed while parsing Progressbar '%ls' images.", pControl->sczName);
3405 break;
3406 default:
3407 ThmExitOnUnexpectedAttribute(hr, pixn, wzElementName, L"ImageId");
3408 ThmExitOnUnexpectedAttribute(hr, pixn, wzElementName, L"ImageFile");
3409 ThmExitOnUnexpectedAttribute(hr, pixn, wzElementName, L"ImageResource");
3410 ThmExitOnUnexpectedAttribute(hr, pixn, wzElementName, L"SourceX");
3411 ThmExitOnUnexpectedAttribute(hr, pixn, wzElementName, L"SourceY");
3412 break;
3413 }
3414
3415
3416 hr = GetAttributeFontId(pTheme, pixn, L"FontId", &pControl->dwFontId);
3417 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' FontId attribute.", pControl->sczName);
3418
3419 // Parse the optional window style.
3420 hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle);
3421 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' HexStyle attribute.", pControl->sczName);
3422
3423 // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above.
3424 hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue);
3425 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' TabStop attribute.", pControl->sczName);
3426
3427 if (fXmlFound && fValue)
3428 {
3429 pControl->dwStyle |= WS_TABSTOP;
3430 }
3431
3432 hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue);
3433 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' Visible attribute.", pControl->sczName);
3434
3435 if (fXmlFound && fValue)
3436 {
3437 pControl->dwStyle |= WS_VISIBLE;
3438 }
3439
3440 hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue);
3441 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' HideWhenDisabled attribute.", pControl->sczName);
3442
3443 if (fXmlFound && fValue)
3444 {
3445 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED;
3446 }
3447
3448 hr = ParseActions(pixn, pControl);
3449 ThmExitOnFailure(hr, "Failed to parse action nodes of the control '%ls'.", pControl->sczName);
3450
3451 hr = ParseText(pixn, pControl, &fAnyTextChildren);
3452 ThmExitOnFailure(hr, "Failed to parse text nodes of the control '%ls'.", pControl->sczName);
3453
3454 hr = ParseTooltips(pixn, pControl, &fAnyTextChildren);
3455 ThmExitOnFailure(hr, "Failed to parse control '%ls' Tooltip.", pControl->sczName);
3456
3457 if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
3458 {
3459 hr = ParseNotes(pixn, pControl, &fAnyNoteChildren);
3460 ThmExitOnFailure(hr, "Failed to parse note text nodes of the control '%ls'.", pControl->sczName);
3461 }
3462
3463 if (!fAnyTextChildren && !fAnyNoteChildren)
3464 {
3465 hr = XmlGetAttributeUInt32(pixn, L"StringId", &dwValue);
3466 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' StringId attribute.", pControl->sczName);
3467
3468 if (fXmlFound)
3469 {
3470 pControl->uStringId = dwValue;
3471 }
3472 else
3473 {
3474 // Billboards and panels have child elements and we don't want to pick up child element text in the parents.
3475 if (THEME_CONTROL_TYPE_BILLBOARD != pControl->type && THEME_CONTROL_TYPE_PANEL != pControl->type)
3476 {
3477 hr = XmlGetText(pixn, &bstrText);
3478 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get control '%ls' inner text.", pControl->sczName);
3479
3480 if (fXmlFound)
3481 {
3482 hr = StrAllocString(&pControl->sczText, bstrText, 0);
3483 ThmExitOnFailure(hr, "Failed to copy control text.");
3484
3485 ReleaseNullBSTR(bstrText);
3486 }
3487 }
3488 }
3489 }
3490
3491 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
3492 {
3493 hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops);
3494 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' Billboard/@Loop attribute.", pControl->sczName);
3495
3496 hr = XmlGetAttributeUInt16(pixn, L"Interval", &pControl->wBillboardInterval);
3497 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' Billboard/@Interval attribute.", pControl->sczName);
3498
3499 if (!pControl->wBillboardInterval)
3500 {
3501 pControl->wBillboardInterval = 5000;
3502 }
3503
3504 hr = ParseBillboardPanels(hModule, wzRelativePath, pixn, pTheme, pControl, pPage);
3505 ThmExitOnFailure(hr, "Failed to parse billboard '%ls' children.", pControl->sczName);
3506 }
3507 else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type)
3508 {
3509 hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue);
3510 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' Editbox/@FileSystemAutoComplete attribute.", pControl->sczName);
3511
3512 if (fXmlFound && fValue)
3513 {
3514 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE;
3515 }
3516 }
3517 else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type)
3518 {
3519 hr = GetAttributeFontId(pTheme, pixn, L"HoverFontId", &pControl->dwFontHoverId);
3520 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' HoverFontId attribute.", pControl->sczName);
3521
3522 hr = GetAttributeFontId(pTheme, pixn, L"SelectedFontId", &pControl->dwFontSelectedId);
3523 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying control '%ls' SelectedFontId attribute.", pControl->sczName);
3524 }
3525 else if (THEME_CONTROL_TYPE_LABEL == pControl->type)
3526 {
3527 hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue);
3528 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' Label/@Center attribute.", pControl->sczName);
3529
3530 if (fXmlFound && fValue)
3531 {
3532 pControl->dwStyle |= SS_CENTER;
3533 }
3534
3535 hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue);
3536 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' Label/@DisablePrefix attribute.", pControl->sczName);
3537
3538 if (fXmlFound && fValue)
3539 {
3540 pControl->dwStyle |= SS_NOPREFIX;
3541 }
3542 }
3543 else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
3544 {
3545 // Parse the optional extended window style.
3546 hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle);
3547 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' ListView/@HexExtendedStyle attribute.", pControl->sczName);
3548
3549 hr = XmlGetAttribute(pixn, L"ImageList", &bstrText);
3550 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' ListView/@ImageList attribute.", pControl->sczName);
3551
3552 if (fXmlFound)
3553 {
3554 hr = FindImageList(pTheme, bstrText, &pControl->ListView.rghImageList[0]);
3555 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText);
3556 }
3557
3558 hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText);
3559 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' ListView/@ImageListSmall attribute.", pControl->sczName);
3560
3561 if (fXmlFound)
3562 {
3563 hr = FindImageList(pTheme, bstrText, &pControl->ListView.rghImageList[1]);
3564 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText);
3565 }
3566
3567 hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText);
3568 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' ListView/@ImageListState attribute.", pControl->sczName);
3569
3570 if (fXmlFound)
3571 {
3572 hr = FindImageList(pTheme, bstrText, &pControl->ListView.rghImageList[2]);
3573 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText);
3574 }
3575
3576 hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText);
3577 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' ListView/@ImageListGroupHeader attribute.", pControl->sczName);
3578
3579 if (fXmlFound)
3580 {
3581 hr = FindImageList(pTheme, bstrText, &pControl->ListView.rghImageList[3]);
3582 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText);
3583 }
3584
3585 hr = ParseColumns(pixn, pControl);
3586 ThmExitOnFailure(hr, "Failed to parse columns.");
3587 }
3588 else if (THEME_CONTROL_TYPE_PANEL == pControl->type)
3589 {
3590 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage, PANEL_CHILD_CONTROL_NAMES);
3591 ThmExitOnFailure(hr, "Failed to parse panel children.");
3592 }
3593 else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type)
3594 {
3595 hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue);
3596 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' RadioButton/@Value attribute.", pControl->sczName);
3597 }
3598 else if (THEME_CONTROL_TYPE_TAB == pControl->type)
3599 {
3600 hr = ParseTabs(pixn, pControl);
3601 ThmExitOnFailure(hr, "Failed to parse tabs");
3602 }
3603 else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type)
3604 {
3605 pControl->dwStyle |= TVS_DISABLEDRAGDROP;
3606
3607 hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue);
3608 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' TreeView/@EnableDragDrop attribute.", pControl->sczName);
3609
3610 if (fXmlFound && fValue)
3611 {
3612 pControl->dwStyle &= ~TVS_DISABLEDRAGDROP;
3613 }
3614
3615 hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue);
3616 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' TreeView/@FullRowSelect attribute.", pControl->sczName);
3617
3618 if (fXmlFound && fValue)
3619 {
3620 pControl->dwStyle |= TVS_FULLROWSELECT;
3621 }
3622
3623 hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue);
3624 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' TreeView/@HasButtons attribute.", pControl->sczName);
3625
3626 if (fXmlFound && fValue)
3627 {
3628 pControl->dwStyle |= TVS_HASBUTTONS;
3629 }
3630
3631 hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue);
3632 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' TreeView/@AlwaysShowSelect attribute.", pControl->sczName);
3633
3634 if (fXmlFound && fValue)
3635 {
3636 pControl->dwStyle |= TVS_SHOWSELALWAYS;
3637 }
3638
3639 hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue);
3640 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' TreeView/@LinesAtRoot attribute.", pControl->sczName);
3641
3642 if (fXmlFound && fValue)
3643 {
3644 pControl->dwStyle |= TVS_LINESATROOT;
3645 }
3646
3647 hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue);
3648 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed when querying '%ls' TreeView/@HasLines attribute.", pControl->sczName);
3649
3650 if (fXmlFound && fValue)
3651 {
3652 pControl->dwStyle |= TVS_HASLINES;
3653 }
3654 }
3655
3656 LExit:
3657 ReleaseBSTR(bstrText);
3658
3659 return hr;
3660 }
3661
3662 static void InitializeThemeControl(
3663 THEME* pTheme,
3664 THEME_CONTROL* pControl
3665 )
3666 {
3667 pControl->dwFontHoverId = THEME_INVALID_ID;
3668 pControl->dwFontId = THEME_INVALID_ID;
3669 pControl->dwFontSelectedId = THEME_INVALID_ID;
3670 pControl->uStringId = UINT_MAX;
3671 pControl->pTheme = pTheme;
3672 }
3673
3674
3675 static HRESULT ParseActions(
3676 __in IXMLDOMNode* pixn,
3677 __in THEME_CONTROL* pControl
3678 )
3679 {
3680 HRESULT hr = S_OK;
3681 DWORD i = 0;
3682 IXMLDOMNodeList* pixnl = NULL;
3683 IXMLDOMNode* pixnChild = NULL;
3684 BSTR bstrType = NULL;
3685
3686 hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl);
3687 ThmExitOnFailure(hr, "Failed to select child action nodes.");
3688
3689 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cActions));
3690 ThmExitOnFailure(hr, "Failed to count the number of action nodes.");
3691
3692 if (0 < pControl->cActions)
3693 {
3694 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions);
3695 ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs.");
3696
3697 i = 0;
3698 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType)))
3699 {
3700 if (!bstrType)
3701 {
3702 hr = E_UNEXPECTED;
3703 ThmExitOnFailure(hr, "Null element encountered!");
3704 }
3705
3706 THEME_ACTION* pAction = pControl->rgActions + i;
3707
3708 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1))
3709 {
3710 pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY;
3711
3712 hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName);
3713 ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute.");
3714 }
3715 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1))
3716 {
3717 pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE;
3718
3719 hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName);
3720 ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute.");
3721
3722 hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel);
3723 if (E_NOTFOUND != hr)
3724 {
3725 ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute.");
3726 }
3727 }
3728 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1))
3729 {
3730 pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW;
3731 }
3732 else
3733 {
3734 hr = E_UNEXPECTED;
3735 ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType);
3736 }
3737
3738 hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition);
3739 if (E_NOTFOUND != hr)
3740 {
3741 ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType);
3742 }
3743
3744 if (!pAction->sczCondition)
3745 {
3746 if (pControl->pDefaultAction)
3747 {
3748 hr = E_INVALIDDATA;
3749 ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName);
3750 }
3751
3752 pControl->pDefaultAction = pAction;
3753 }
3754
3755 ++i;
3756 ReleaseNullBSTR(bstrType);
3757 ReleaseNullObject(pixnChild);
3758 }
3759 }
3760
3761 LExit:
3762 ReleaseObject(pixnl);
3763 ReleaseObject(pixnChild);
3764 ReleaseBSTR(bstrType);
3765
3766 return hr;
3767 }
3768
3769
3770 static HRESULT ParseBillboardPanels(
3771 __in_opt HMODULE hModule,
3772 __in_opt LPCWSTR wzRelativePath,
3773 __in IXMLDOMNode* pElement,
3774 __in THEME* pTheme,
3775 __in THEME_CONTROL* pParentControl,
3776 __in_opt THEME_PAGE* pPage
3777 )
3778 {
3779 HRESULT hr = S_OK;
3780 IXMLDOMNodeList* pixnl = NULL;
3781 IXMLDOMNode* pixnChild = NULL;
3782 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
3783 DWORD dwValue = 0;
3784 THEME_CONTROL* pControl = NULL;
3785
3786 hr = XmlSelectNodes(pElement, L"Panel", &pixnl);
3787 ThmExitOnFailure(hr, "Failed to select billboard child nodes.");
3788
3789 hr = pixnl->get_length(reinterpret_cast<long*>(&dwValue));
3790 ThmExitOnFailure(hr, "Failed to count the number of billboard panel nodes.");
3791
3792 if (!dwValue)
3793 {
3794 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Billboard must have at least one Panel.");
3795 }
3796
3797 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pParentControl->rgControls), pParentControl->cControls, dwValue, sizeof(THEME_CONTROL), 0);
3798 ThmExitOnFailure(hr, "Failed to ensure theme control array size for BillboardPanels.");
3799
3800 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3801 {
3802 hr = pixnChild->get_attributes(&pixnnmAttributes);
3803 ThmExitOnFailure(hr, "Failed to get attributes for billboard panel.");
3804
3805 hr = pixnnmAttributes->get_length(reinterpret_cast<long*>(&dwValue));
3806 ThmExitOnFailure(hr, "Failed to count attributes for billboard panel.");
3807
3808 if (dwValue)
3809 {
3810 ThmExitWithRootFailure(hr, E_INVALIDDATA, "Billboard panels cannot contain attributes.");
3811 }
3812
3813 pControl = pParentControl->rgControls + pParentControl->cControls;
3814 pParentControl->cControls += 1;
3815 pControl->type = THEME_CONTROL_TYPE_PANEL;
3816 InitializeThemeControl(pTheme, pControl);
3817
3818 if (pPage)
3819 {
3820 pControl->wPageId = pPage->wId;
3821 }
3822
3823 hr = ParseControls(hModule, wzRelativePath, pixnChild, pTheme, pControl, pPage, PANEL_CHILD_CONTROL_NAMES);
3824 ThmExitOnFailure(hr, "Failed to parse control.");
3825
3826 ReleaseNullObject(pixnnmAttributes);
3827 ReleaseNullObject(pixnChild);
3828 }
3829
3830 LExit:
3831 ReleaseObject(pixnl);
3832 ReleaseObject(pixnnmAttributes);
3833 ReleaseObject(pixnChild);
3834
3835 return hr;
3836 }
3837
3838
3839 static HRESULT ParseColumns(
3840 __in IXMLDOMNode* pixn,
3841 __in THEME_CONTROL* pControl
3842 )
3843 {
3844 HRESULT hr = S_OK;
3845 DWORD i = 0;
3846 IXMLDOMNodeList* pixnl = NULL;
3847 IXMLDOMNode* pixnChild = NULL;
3848 BSTR bstrText = NULL;
3849 int nValue = 0;
3850 BOOL fXmlFound = FALSE;
3851
3852 hr = XmlSelectNodes(pixn, L"Column", &pixnl);
3853 ThmExitOnFailure(hr, "Failed to select child column nodes.");
3854
3855 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->ListView.cColumns));
3856 ThmExitOnFailure(hr, "Failed to count the number of control columns.");
3857
3858 if (0 < pControl->ListView.cColumns)
3859 {
3860 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->ListView.ptcColumns), sizeof(THEME_COLUMN), pControl->ListView.cColumns);
3861 ThmExitOnFailure(hr, "Failed to allocate column structs.");
3862
3863 i = 0;
3864 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3865 {
3866 THEME_COLUMN* pColumn = pControl->ListView.ptcColumns + i;
3867
3868 hr = XmlGetText(pixnChild, &bstrText);
3869 ThmExitOnFailure(hr, "Failed to get inner text of column element.");
3870
3871 hr = GetAttributeCoordinateOrDimension(pixnChild, L"Width", &nValue);
3872 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get column width attribute.");
3873
3874 if (!fXmlFound)
3875 {
3876 nValue = 100;
3877 }
3878
3879 pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = nValue;
3880
3881 hr = XmlGetYesNoAttribute(pixnChild, L"Expands", &pColumn->fExpands);
3882 ThmExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get expands attribute.");
3883
3884 hr = StrAllocString(&pColumn->pszName, bstrText, 0);
3885 ThmExitOnFailure(hr, "Failed to copy column name.");
3886
3887 ++i;
3888 ReleaseNullBSTR(bstrText);
3889 ReleaseNullObject(pixnChild);
3890 }
3891 }
3892
3893 LExit:
3894 ReleaseObject(pixnl);
3895 ReleaseObject(pixnChild);
3896 ReleaseBSTR(bstrText);
3897
3898 return hr;
3899 }
3900
3901
3902 static HRESULT ParseRadioButtons(
3903 __in_opt HMODULE hModule,
3904 __in_opt LPCWSTR wzRelativePath,
3905 __in IXMLDOMNode* pixn,
3906 __in THEME* pTheme,
3907 __in_opt THEME_CONTROL* pParentControl,
3908 __in THEME_PAGE* pPage
3909 )
3910 {
3911 HRESULT hr = S_OK;
3912 DWORD cRadioButtons = 0;
3913 IXMLDOMNodeList* pixnlRadioButtons = NULL;
3914 IXMLDOMNodeList* pixnl = NULL;
3915 IXMLDOMNode* pixnRadioButtons = NULL;
3916 IXMLDOMNode* pixnChild = NULL;
3917 LPWSTR sczName = NULL;
3918 THEME_CONTROL* pControl = NULL;
3919 BOOL fFirst = FALSE;
3920 DWORD* pcControls = NULL;
3921 THEME_CONTROL** prgControls = NULL;
3922
3923 GetControls(pTheme, pParentControl, &pcControls, &prgControls);
3924
3925 hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons);
3926 ThmExitOnFailure(hr, "Failed to select RadioButtons nodes.");
3927
3928 while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL)))
3929 {
3930 hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName);
3931 if (E_NOTFOUND == hr)
3932 {
3933 hr = S_OK;
3934 }
3935 ThmExitOnFailure(hr, "Failed when querying RadioButtons Name.");
3936
3937 hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl);
3938 ThmExitOnFailure(hr, "Failed to select RadioButton nodes.");
3939
3940 hr = pixnl->get_length(reinterpret_cast<long*>(&cRadioButtons));
3941 ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes.");
3942
3943 if (!cRadioButtons)
3944 {
3945 ThmExitWithRootFailure(hr, E_INVALIDDATA, "RadioButtons must have at least one RadioButton.");
3946 }
3947
3948 if (pPage)
3949 {
3950 pPage->cControlIndices += cRadioButtons;
3951 }
3952
3953 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgControls), *pcControls, cRadioButtons, sizeof(THEME_CONTROL), 0);
3954 ThmExitOnFailure(hr, "Failed to ensure theme control array size for RadioButtons.");
3955
3956 fFirst = TRUE;
3957
3958 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3959 {
3960 pControl = *prgControls + *pcControls;
3961 pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON;
3962 *pcControls += 1;
3963
3964 hr = ParseControl(hModule, wzRelativePath, pixnChild, L"RadioButton", pTheme, pControl, pPage);
3965 ThmExitOnFailure(hr, "Failed to parse control.");
3966
3967 if (fFirst)
3968 {
3969 pControl->dwStyle |= WS_GROUP;
3970 fFirst = FALSE;
3971 }
3972
3973 hr = StrAllocString(&pControl->sczVariable, sczName, 0);
3974 ThmExitOnFailure(hr, "Failed to copy radio button variable.");
3975
3976 if (pPage)
3977 {
3978 pControl->wPageId = pPage->wId;
3979 }
3980
3981 ReleaseNullObject(pixnChild);
3982 }
3983
3984 if (!fFirst)
3985 {
3986 pControl->fLastRadioButton = TRUE;
3987 }
3988
3989 ReleaseNullObject(pixnl);
3990 ReleaseNullObject(pixnRadioButtons);
3991 }
3992
3993 LExit:
3994 ReleaseStr(sczName);
3995 ReleaseObject(pixnl);
3996 ReleaseObject(pixnChild);
3997 ReleaseObject(pixnlRadioButtons);
3998 ReleaseObject(pixnRadioButtons);
3999
4000 return hr;
4001 }
4002
4003
4004 static HRESULT ParseTabs(
4005 __in IXMLDOMNode* pixn,
4006 __in THEME_CONTROL* pControl
4007 )
4008 {
4009 HRESULT hr = S_OK;
4010 DWORD i = 0;
4011 IXMLDOMNodeList* pixnl = NULL;
4012 IXMLDOMNode* pixnChild = NULL;
4013 BSTR bstrText = NULL;
4014
4015 hr = XmlSelectNodes(pixn, L"Tab", &pixnl);
4016 ThmExitOnFailure(hr, "Failed to select child tab nodes.");
4017
4018 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cTabs));
4019 ThmExitOnFailure(hr, "Failed to count the number of tabs.");
4020
4021 if (0 < pControl->cTabs)
4022 {
4023 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs);
4024 ThmExitOnFailure(hr, "Failed to allocate tab structs.");
4025
4026 i = 0;
4027 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
4028 {
4029 hr = XmlGetText(pixnChild, &bstrText);
4030 ThmExitOnFailure(hr, "Failed to get inner text of tab element.");
4031
4032 hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0);
4033 ThmExitOnFailure(hr, "Failed to copy tab name.");
4034
4035 ++i;
4036 ReleaseNullBSTR(bstrText);
4037 }
4038 }
4039
4040 LExit:
4041 ReleaseObject(pixnl);
4042 ReleaseObject(pixnChild);
4043 ReleaseBSTR(bstrText);
4044
4045 return hr;
4046 }
4047
4048
4049 static HRESULT ParseText(
4050 __in IXMLDOMNode* pixn,
4051 __in THEME_CONTROL* pControl,
4052 __inout BOOL* pfAnyChildren
4053 )
4054 {
4055 HRESULT hr = S_OK;
4056 DWORD i = 0;
4057 IXMLDOMNodeList* pixnl = NULL;
4058 IXMLDOMNode* pixnChild = NULL;
4059 BSTR bstrText = NULL;
4060
4061 hr = XmlSelectNodes(pixn, L"Text", &pixnl);
4062 ThmExitOnFailure(hr, "Failed to select child Text nodes.");
4063
4064 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cConditionalText));
4065 ThmExitOnFailure(hr, "Failed to count the number of Text nodes.");
4066
4067 *pfAnyChildren |= 0 < pControl->cConditionalText;
4068
4069 if (0 < pControl->cConditionalText)
4070 {
4071 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText);
4072 ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs.");
4073
4074 i = 0;
4075 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
4076 {
4077 THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i;
4078
4079 hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition);
4080 if (E_NOTFOUND == hr)
4081 {
4082 hr = S_OK;
4083 }
4084 ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute.");
4085
4086 hr = XmlGetText(pixnChild, &bstrText);
4087 ThmExitOnFailure(hr, "Failed to get inner text of Text element.");
4088
4089 if (S_OK == hr)
4090 {
4091 if (pConditionalText->sczCondition)
4092 {
4093 hr = StrAllocString(&pConditionalText->sczText, bstrText, 0);
4094 ThmExitOnFailure(hr, "Failed to copy text to conditional text.");
4095
4096 ++i;
4097 }
4098 else
4099 {
4100 if (pControl->sczText)
4101 {
4102 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
4103 ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName);
4104 }
4105
4106 hr = StrAllocString(&pControl->sczText, bstrText, 0);
4107 ThmExitOnFailure(hr, "Failed to copy text to control.");
4108
4109 // Unconditional text entries aren't stored in the conditional text list.
4110 --pControl->cConditionalText;
4111 }
4112 }
4113
4114 ReleaseNullBSTR(bstrText);
4115 }
4116 }
4117
4118 LExit:
4119 ReleaseObject(pixnl);
4120 ReleaseObject(pixnChild);
4121 ReleaseBSTR(bstrText);
4122
4123 return hr;
4124 }
4125
4126
4127 static HRESULT ParseTooltips(
4128 __in IXMLDOMNode* pixn,
4129 __in THEME_CONTROL* pControl,
4130 __inout BOOL* pfAnyChildren
4131 )
4132 {
4133 HRESULT hr = S_OK;
4134 IXMLDOMNode* pixnChild = NULL;
4135 BSTR bstrText = NULL;
4136
4137 hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild);
4138 ThmExitOnFailure(hr, "Failed to select child Tooltip node.");
4139
4140 if (S_OK == hr)
4141 {
4142 *pfAnyChildren |= TRUE;
4143
4144 hr = XmlGetText(pixnChild, &bstrText);
4145 ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element.");
4146
4147 if (S_OK == hr)
4148 {
4149 hr = StrAllocString(&pControl->sczTooltip, bstrText, 0);
4150 ThmExitOnFailure(hr, "Failed to copy tooltip text to control.");
4151 }
4152 }
4153
4154 LExit:
4155 ReleaseObject(pixnChild);
4156 ReleaseBSTR(bstrText);
4157
4158 return hr;
4159 }
4160
4161
4162 static HRESULT ParseUnexpectedAttribute(
4163 __in IXMLDOMNode* pixn,
4164 __in_z LPCWSTR wzElementName,
4165 __in_z LPCWSTR wzAttribute
4166 )
4167 {
4168 HRESULT hr = S_OK;
4169 BSTR bstr = NULL;
4170
4171 hr = XmlGetAttribute(pixn, wzAttribute, &bstr);
4172 ThmExitOnFailure(hr, "Failed to get attribute %ls/@%ls", wzElementName, wzAttribute);
4173
4174 if (S_OK == hr)
4175 {
4176 ThmExitOnRootFailure(hr = E_INVALIDDATA, "Element '%ls' has unexpected attribute '%ls', value: %ls.", wzElementName, wzAttribute, bstr);
4177 }
4178
4179 hr = S_OK;
4180
4181 LExit:
4182 ReleaseBSTR(bstr);
4183
4184 return hr;
4185 }
4186
4187
4188 static HRESULT ParseNotes(
4189 __in IXMLDOMNode* pixn,
4190 __in THEME_CONTROL* pControl,
4191 __out BOOL* pfAnyChildren
4192 )
4193 {
4194 HRESULT hr = S_OK;
4195 DWORD i = 0;
4196 IXMLDOMNodeList* pixnl = NULL;
4197 IXMLDOMNode* pixnChild = NULL;
4198 BSTR bstrText = NULL;
4199
4200 hr = XmlSelectNodes(pixn, L"Note", &pixnl);
4201 ThmExitOnFailure(hr, "Failed to select child Note nodes.");
4202
4203 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->CommandLink.cConditionalNotes));
4204 ThmExitOnFailure(hr, "Failed to count the number of Note nodes.");
4205
4206 if (pfAnyChildren)
4207 {
4208 *pfAnyChildren = 0 < pControl->CommandLink.cConditionalNotes;
4209 }
4210
4211 if (0 < pControl->CommandLink.cConditionalNotes)
4212 {
4213 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->CommandLink.rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->CommandLink.cConditionalNotes);
4214 ThmExitOnNull(pControl->CommandLink.rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs.");
4215
4216 i = 0;
4217 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
4218 {
4219 THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->CommandLink.rgConditionalNotes + i;
4220
4221 hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition);
4222 if (E_NOTFOUND == hr)
4223 {
4224 hr = S_OK;
4225 }
4226 ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute.");
4227
4228 hr = XmlGetText(pixnChild, &bstrText);
4229 ThmExitOnFailure(hr, "Failed to get inner text of Note element.");
4230
4231 if (S_OK == hr)
4232 {
4233 if (pConditionalNote->sczCondition)
4234 {
4235 hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0);
4236 ThmExitOnFailure(hr, "Failed to copy text to conditional note text.");
4237
4238 ++i;
4239 }
4240 else
4241 {
4242 if (pControl->sczNote)
4243 {
4244 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
4245 ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName);
4246 }
4247
4248 hr = StrAllocString(&pControl->sczNote, bstrText, 0);
4249 ThmExitOnFailure(hr, "Failed to copy text to command link control.");
4250
4251 // Unconditional note entries aren't stored in the conditional notes list.
4252 --pControl->CommandLink.cConditionalNotes;
4253 }
4254 }
4255
4256 ReleaseNullBSTR(bstrText);
4257 }
4258 }
4259
4260 LExit:
4261 ReleaseObject(pixnl);
4262 ReleaseObject(pixnChild);
4263 ReleaseBSTR(bstrText);
4264
4265 return hr;
4266 }
4267
4268
4269 static HRESULT StartBillboard(
4270 __in THEME* pTheme,
4271 __in THEME_CONTROL* pControl
4272 )
4273 {
4274 HRESULT hr = E_NOTFOUND;
4275 UINT_PTR idEvent = reinterpret_cast<UINT_PTR>(pControl);
4276
4277 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
4278 {
4279 // kick off
4280 pControl->dwData = 0;
4281 OnBillboardTimer(pTheme, pTheme->hwndParent, idEvent);
4282
4283 if (!::SetTimer(pTheme->hwndParent, idEvent, pControl->wBillboardInterval, NULL))
4284 {
4285 ThmExitWithLastError(hr, "Failed to start billboard.");
4286 }
4287
4288 hr = S_OK;
4289 }
4290
4291 LExit:
4292 return hr;
4293 }
4294
4295
4296 static HRESULT StopBillboard(
4297 __in THEME* pTheme,
4298 __in THEME_CONTROL* pControl
4299 )
4300 {
4301 HRESULT hr = E_NOTFOUND;
4302 UINT_PTR idEvent = reinterpret_cast<UINT_PTR>(pControl);
4303
4304 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
4305 {
4306 if (::KillTimer(pTheme->hwndParent, idEvent))
4307 {
4308 hr = S_OK;
4309 }
4310 }
4311
4312 return hr;
4313 }
4314
4315 static HRESULT EnsureFontInstance(
4316 __in THEME* pTheme,
4317 __in THEME_FONT* pFont,
4318 __out THEME_FONT_INSTANCE** ppFontInstance
4319 )
4320 {
4321 HRESULT hr = S_OK;
4322 THEME_FONT_INSTANCE* pFontInstance = NULL;
4323 LOGFONTW lf = { };
4324
4325 for (DWORD i = 0; i < pFont->cFontInstances; ++i)
4326 {
4327 pFontInstance = pFont->rgFontInstances + i;
4328 if (pTheme->nDpi == pFontInstance->nDpi)
4329 {
4330 *ppFontInstance = pFontInstance;
4331 ExitFunction();
4332 }
4333 }
4334
4335 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pFont->rgFontInstances), pFont->cFontInstances, 1, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES);
4336 ThmExitOnFailure(hr, "Failed to allocate memory for font instances.");
4337
4338 pFontInstance = pFont->rgFontInstances + pFont->cFontInstances;
4339 pFontInstance->nDpi = pTheme->nDpi;
4340
4341 lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi);
4342 lf.lfWeight = pFont->lfWeight;
4343 lf.lfUnderline = pFont->lfUnderline;
4344 lf.lfQuality = pFont->lfQuality;
4345
4346 hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName);
4347 ThmExitOnFailure(hr, "Failed to copy font name to create font.");
4348
4349 pFontInstance->hFont = ::CreateFontIndirectW(&lf);
4350 ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font.");
4351
4352 ++pFont->cFontInstances;
4353 *ppFontInstance = pFontInstance;
4354
4355 LExit:
4356 return hr;
4357 }
4358
4359
4360 static HRESULT FindImageList(
4361 __in THEME* pTheme,
4362 __in_z LPCWSTR wzImageListName,
4363 __out HIMAGELIST *phImageList
4364 )
4365 {
4366 HRESULT hr = S_OK;
4367
4368 for (DWORD i = 0; i < pTheme->cImageLists; ++i)
4369 {
4370 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1))
4371 {
4372 *phImageList = pTheme->rgImageLists[i].hImageList;
4373 ExitFunction1(hr = S_OK);
4374 }
4375 }
4376
4377 hr = E_NOTFOUND;
4378
4379 LExit:
4380 return hr;
4381 }
4382
4383
4384 static HRESULT DrawButton(
4385 __in THEME* pTheme,
4386 __in DRAWITEMSTRUCT* pdis,
4387 __in const THEME_CONTROL* pControl
4388 )
4389 {
4390 HRESULT hr = S_OK;
4391 const THEME_IMAGE_REFERENCE* pImageRef = NULL;
4392 BOOL fDrawFocusRect = FALSE;
4393 int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
4394 int nWidth = pdis->rcItem.right - pdis->rcItem.left;
4395
4396 // "clicked" gets priority
4397 if (ODS_SELECTED & pdis->itemState)
4398 {
4399 pImageRef = pControl->Button.rgImageRef + 2;
4400 }
4401 // then hover
4402 else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
4403 {
4404 pImageRef = pControl->Button.rgImageRef + 1;
4405 }
4406 // then focused
4407 else if ((WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState))
4408 {
4409 if (THEME_IMAGE_REFERENCE_TYPE_NONE != pControl->Button.rgImageRef[3].type)
4410 {
4411 pImageRef = pControl->Button.rgImageRef + 3;
4412 }
4413 else
4414 {
4415 fDrawFocusRect = TRUE;
4416 pImageRef = pControl->Button.rgImageRef;
4417 }
4418 }
4419 else
4420 {
4421 pImageRef = pControl->Button.rgImageRef;
4422 }
4423
4424 hr = DrawImageReference(pTheme, pImageRef, pdis->hDC, 0, 0, nWidth, nHeight);
4425
4426 DrawControlText(pTheme, pdis, pControl, TRUE, fDrawFocusRect);
4427
4428 return hr;
4429 }
4430
4431
4432 static HRESULT DrawHyperlink(
4433 __in THEME* pTheme,
4434 __in DRAWITEMSTRUCT* pdis,
4435 __in const THEME_CONTROL* pControl
4436 )
4437 {
4438 DrawControlText(pTheme, pdis, pControl, FALSE, TRUE);
4439 return S_OK;
4440 }
4441
4442
4443 static void DrawControlText(
4444 __in THEME* pTheme,
4445 __in DRAWITEMSTRUCT* pdis,
4446 __in const THEME_CONTROL* pControl,
4447 __in BOOL fCentered,
4448 __in BOOL fDrawFocusRect
4449 )
4450 {
4451 HRESULT hr = S_OK;
4452 WCHAR wzText[256] = { };
4453 THEME_FONT* pFont = NULL;
4454 THEME_FONT_INSTANCE* pFontInstance = NULL;
4455 HFONT hfPrev = NULL;
4456 DWORD cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText));
4457
4458 if (cchText)
4459 {
4460 if (ODS_SELECTED & pdis->itemState)
4461 {
4462 pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId);
4463 }
4464 else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
4465 {
4466 pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId);
4467 }
4468 else
4469 {
4470 pFont = pTheme->rgFonts + pControl->dwFontId;
4471 }
4472
4473 hr = EnsureFontInstance(pTheme, pFont, &pFontInstance);
4474 if (SUCCEEDED(hr))
4475 {
4476 hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont);
4477 }
4478
4479 ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL);
4480
4481 if (hfPrev)
4482 {
4483 SelectFont(pdis->hDC, hfPrev);
4484 }
4485 }
4486
4487 if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState))
4488 {
4489 ::DrawFocusRect(pdis->hDC, &pdis->rcItem);
4490 }
4491 }
4492
4493
4494 static HRESULT DrawImage(
4495 __in THEME* pTheme,
4496 __in DRAWITEMSTRUCT* pdis,
4497 __in const THEME_CONTROL* pControl
4498 )
4499 {
4500 HRESULT hr = S_OK;
4501 int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
4502 int nWidth = pdis->rcItem.right - pdis->rcItem.left;
4503
4504 hr = DrawImageReference(pTheme, &pControl->Image.imageRef, pdis->hDC, 0, 0, nWidth, nHeight);
4505
4506 return hr;
4507 }
4508
4509 static void GetImageInstance(
4510 __in THEME* pTheme,
4511 __in const THEME_IMAGE_REFERENCE* pReference,
4512 __out const THEME_IMAGE_INSTANCE** ppInstance
4513 )
4514 {
4515 switch (pReference->type)
4516 {
4517 case THEME_IMAGE_REFERENCE_TYPE_PARTIAL:
4518 case THEME_IMAGE_REFERENCE_TYPE_COMPLETE:
4519 if (THEME_INVALID_ID == pReference->dwImageIndex)
4520 {
4521 *ppInstance = pTheme->rgStandaloneImages + pReference->dwImageInstanceIndex;
4522 }
4523 else
4524 {
4525 THEME_IMAGE* pImage = pTheme->rgImages + pReference->dwImageIndex;
4526 *ppInstance = pImage->rgImageInstances + pReference->dwImageInstanceIndex;
4527 }
4528 break;
4529 default:
4530 *ppInstance = NULL;
4531 break;
4532 }
4533 }
4534
4535 static HRESULT DrawImageReference(
4536 __in THEME* pTheme,
4537 __in const THEME_IMAGE_REFERENCE* pReference,
4538 __in HDC hdc,
4539 __in int destX,
4540 __in int destY,
4541 __in int destWidth,
4542 __in int destHeight
4543 )
4544 {
4545 HRESULT hr = S_OK;
4546 const THEME_IMAGE_INSTANCE* pImageInstance = NULL;
4547 int nX = 0;
4548 int nY = 0;
4549 int nWidth = 0;
4550 int nHeight = 0;
4551
4552 GetImageInstance(pTheme, pReference, &pImageInstance);
4553 ExitOnNull(pImageInstance, hr, E_INVALIDARG, "Invalid image reference for drawing.");
4554
4555 switch (pReference->type)
4556 {
4557 case THEME_IMAGE_REFERENCE_TYPE_PARTIAL:
4558 nX = pReference->nX;
4559 nY = pReference->nY;
4560 nWidth = pReference->nWidth;
4561 nHeight = pReference->nHeight;
4562 break;
4563 case THEME_IMAGE_REFERENCE_TYPE_COMPLETE:
4564 nX = 0;
4565 nY = 0;
4566 nWidth = pImageInstance->pBitmap->GetWidth();
4567 nHeight = pImageInstance->pBitmap->GetHeight();
4568 break;
4569 default:
4570 ExitFunction1(hr = E_INVALIDARG); // This should be unreachable because GetImageInstance should have returned null.
4571 }
4572
4573 hr = DrawGdipBitmap(hdc, destX, destY, destWidth, destHeight, pImageInstance->pBitmap, nX, nY, nWidth, nHeight);
4574
4575 LExit:
4576 return hr;
4577 }
4578
4579 static HRESULT DrawGdipBitmap(
4580 __in HDC hdc,
4581 __in int destX,
4582 __in int destY,
4583 __in int destWidth,
4584 __in int destHeight,
4585 __in Gdiplus::Bitmap* pBitmap,
4586 __in int srcX,
4587 __in int srcY,
4588 __in int srcWidth,
4589 __in int srcHeight
4590 )
4591 {
4592 // Note that this only indicates that GDI+ supports transparency from the source image type.
4593 // Bitmaps with alpha information will return FALSE, while fully opaque PNGs will return TRUE.
4594 BOOL fTransparency = (pBitmap->GetFlags() & Gdiplus::ImageFlagsHasAlpha) == Gdiplus::ImageFlagsHasAlpha;
4595 Gdiplus::ImageAttributes attrs;
4596 Gdiplus::Rect destRect(destX, destY, destWidth, destHeight);
4597 Gdiplus::Graphics graphics(hdc);
4598 Gdiplus::Status gs = Gdiplus::Status::Ok;
4599
4600 // This fixes GDI+ behavior where it badly handles the edges of an image when scaling.
4601 // This is easily seen when using an image Progressbar that's 4x1 - the progress bar fades to transparent in both directions.
4602 attrs.SetWrapMode(Gdiplus::WrapMode::WrapModeTileFlipXY);
4603
4604 // graphics.SmoothingMode is not set because it has no impact on DrawImage.
4605
4606 // This is the best interpolation mode, and the only one that is decent at downscaling.
4607 graphics.SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQualityBicubic);
4608
4609 // There's a significant quality improvement when scaling to use the HighQuality pixel offset mode with no measurable difference in performance.
4610 graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetMode::PixelOffsetModeHighQuality);
4611
4612 // If there's transparency, make sure that the blending is done with high quality.
4613 // If not, try to skip blending.
4614 if (fTransparency)
4615 {
4616 graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceOver);
4617 graphics.SetCompositingQuality(Gdiplus::CompositingQuality::CompositingQualityHighQuality);
4618 }
4619 else
4620 {
4621 graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceCopy);
4622 graphics.SetCompositingQuality(Gdiplus::CompositingQuality::CompositingQualityHighSpeed);
4623 }
4624
4625 gs = graphics.DrawImage(pBitmap, destRect, srcX, srcY, srcWidth, srcHeight, Gdiplus::Unit::UnitPixel, &attrs);
4626
4627 return GdipHresultFromStatus(gs);
4628 }
4629
4630
4631 static HRESULT DrawProgressBar(
4632 __in THEME* pTheme,
4633 __in DRAWITEMSTRUCT* pdis,
4634 __in const THEME_CONTROL* pControl
4635 )
4636 {
4637 const int nSideWidth = 1;
4638 HRESULT hr = S_OK;
4639 WORD wProgressColorIndex = HIWORD(pControl->dwData);
4640 WORD wProgressPercentage = LOWORD(pControl->dwData);
4641 const THEME_IMAGE_REFERENCE* pImageRef = pControl->ProgressBar.rgImageRef + wProgressColorIndex;
4642 const THEME_IMAGE_INSTANCE* pInstance = NULL;
4643 int nHeight = pdis->rcItem.bottom - pdis->rcItem.top;
4644 int nSourceHeight = 0;
4645 int nSourceX = 0;
4646 int nSourceY = 0;
4647 int nFillableWidth = pdis->rcItem.right - 2 * nSideWidth;
4648 int nCenter = nFillableWidth > 0 ? nFillableWidth * wProgressPercentage / 100 : 0;
4649
4650 if (0 > nFillableWidth)
4651 {
4652 ExitFunction1(hr = S_FALSE);
4653 }
4654
4655 GetImageInstance(pTheme, pImageRef, &pInstance);
4656 ExitOnNull(pInstance, hr, E_INVALIDARG, "Invalid image reference for drawing.");
4657
4658 switch (pImageRef->type)
4659 {
4660 case THEME_IMAGE_REFERENCE_TYPE_PARTIAL:
4661 nSourceHeight = pImageRef->nHeight;
4662 nSourceX = pImageRef->nX;
4663 nSourceY = pImageRef->nY;
4664 break;
4665 case THEME_IMAGE_REFERENCE_TYPE_COMPLETE:
4666 nSourceHeight = pInstance->pBitmap->GetHeight();
4667 nSourceX = 0;
4668 nSourceY = 0;
4669 break;
4670 default:
4671 ExitFunction1(hr = E_INVALIDARG); // This should be unreachable because GetImageInstance should have returned null.
4672 }
4673
4674 // Draw the left side of the progress bar.
4675 hr = DrawProgressBarImage(pTheme, pInstance, nSourceX, nSourceY, 1, nSourceHeight, pdis->hDC, 0, 0, nSideWidth, nHeight);
4676
4677 // Draw the filled side of the progress bar, if there is any.
4678 if (0 < nCenter)
4679 {
4680 hr = DrawProgressBarImage(pTheme, pInstance, nSourceX + 1, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth, 0, nCenter, nHeight);
4681 }
4682
4683 // Draw the unfilled side of the progress bar, if there is any.
4684 if (nCenter < nFillableWidth)
4685 {
4686 hr = DrawProgressBarImage(pTheme, pInstance, nSourceX + 2, nSourceY, 1, nSourceHeight, pdis->hDC, nSideWidth + nCenter, 0, pdis->rcItem.right - nCenter - nSideWidth, nHeight);
4687 }
4688
4689 // Draw the right side of the progress bar.
4690 hr = DrawProgressBarImage(pTheme, pInstance, nSourceX + 3, nSourceY, 1, nSourceHeight, pdis->hDC, pdis->rcItem.right - nSideWidth, 0, nSideWidth, nHeight);
4691
4692 LExit:
4693 return hr;
4694 }
4695
4696 static HRESULT DrawProgressBarImage(
4697 __in THEME* /*pTheme*/,
4698 __in const THEME_IMAGE_INSTANCE* pImageInstance,
4699 __in int srcX,
4700 __in int srcY,
4701 __in int srcWidth,
4702 __in int srcHeight,
4703 __in HDC hdc,
4704 __in int destX,
4705 __in int destY,
4706 __in int destWidth,
4707 __in int destHeight
4708 )
4709 {
4710 HRESULT hr = S_OK;
4711 Gdiplus::Rect dest(0, 0, srcWidth, srcHeight);
4712 Gdiplus::Bitmap isolated(dest.Width, dest.Height);
4713 Gdiplus::Graphics graphics(&isolated);
4714 graphics.SetCompositingMode(Gdiplus::CompositingMode::CompositingModeSourceCopy);
4715
4716 // Isolate the source rectangle into a temporary bitmap because otherwise GDI+ would use pixels outside of that rectangle when stretching.
4717 Gdiplus::Status gs = graphics.DrawImage(pImageInstance->pBitmap, dest, srcX, srcY, srcWidth, srcHeight, Gdiplus::Unit::UnitPixel);
4718 hr = GdipHresultFromStatus(gs);
4719 if (SUCCEEDED(hr))
4720 {
4721 hr = DrawGdipBitmap(hdc, destX, destY, destWidth, destHeight, &isolated, 0, 0, isolated.GetWidth(), isolated.GetHeight());
4722 }
4723
4724 return hr;
4725 }
4726
4727
4728 static BOOL DrawHoverControl(
4729 __in THEME* pTheme,
4730 __in BOOL fHover
4731 )
4732 {
4733 BOOL fChangedHover = FALSE;
4734 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, pTheme->hwndHover));
4735
4736 // Only hyperlinks and owner-drawn buttons have hover states.
4737 if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type ||
4738 (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW))))
4739 {
4740 if (fHover)
4741 {
4742 pControl->dwData |= THEME_CONTROL_DATA_HOVER;
4743 }
4744 else
4745 {
4746 pControl->dwData &= ~THEME_CONTROL_DATA_HOVER;
4747 }
4748
4749 ::InvalidateRect(pControl->hWnd, NULL, FALSE);
4750 fChangedHover = TRUE;
4751 }
4752
4753 return fChangedHover;
4754 }
4755
4756
4757 static void FreePage(
4758 __in THEME_PAGE* pPage
4759 )
4760 {
4761 if (pPage)
4762 {
4763 ReleaseStr(pPage->sczName);
4764
4765 if (pPage->cSavedVariables)
4766 {
4767 for (DWORD i = 0; i < pPage->cSavedVariables; ++i)
4768 {
4769 ReleaseStr(pPage->rgSavedVariables[i].sczValue);
4770 }
4771 }
4772
4773 ReleaseMem(pPage->rgSavedVariables);
4774 }
4775 }
4776
4777
4778 static void FreeImageList(
4779 __in THEME_IMAGELIST* pImageList
4780 )
4781 {
4782 if (pImageList)
4783 {
4784 ReleaseStr(pImageList->sczName);
4785 ImageList_Destroy(pImageList->hImageList);
4786 }
4787 }
4788
4789 static void FreeControl(
4790 __in THEME_CONTROL* pControl
4791 )
4792 {
4793 if (pControl)
4794 {
4795 if (::IsWindow(pControl->hWnd))
4796 {
4797 ::CloseWindow(pControl->hWnd);
4798 pControl->hWnd = NULL;
4799 }
4800
4801 ReleaseStr(pControl->sczName);
4802 ReleaseStr(pControl->sczText);
4803 ReleaseStr(pControl->sczTooltip);
4804 ReleaseStr(pControl->sczNote);
4805 ReleaseStr(pControl->sczEnableCondition);
4806 ReleaseStr(pControl->sczVisibleCondition);
4807 ReleaseStr(pControl->sczValue);
4808 ReleaseStr(pControl->sczVariable);
4809
4810 switch (pControl->type)
4811 {
4812 case THEME_CONTROL_TYPE_COMMANDLINK:
4813 if (pControl->CommandLink.hImage)
4814 {
4815 ::DeleteBitmap(pControl->CommandLink.hImage);
4816 }
4817
4818 if (pControl->CommandLink.hIcon)
4819 {
4820 ::DestroyIcon(pControl->CommandLink.hIcon);
4821 }
4822
4823 for (DWORD i = 0; i < pControl->CommandLink.cConditionalNotes; ++i)
4824 {
4825 FreeConditionalText(pControl->CommandLink.rgConditionalNotes + i);
4826 }
4827
4828 ReleaseMem(pControl->CommandLink.rgConditionalNotes);
4829 break;
4830 case THEME_CONTROL_TYPE_LISTVIEW:
4831 for (DWORD i = 0; i < pControl->ListView.cColumns; ++i)
4832 {
4833 FreeColumn(pControl->ListView.ptcColumns + i);
4834 }
4835
4836 ReleaseMem(pControl->ListView.ptcColumns);
4837 break;
4838 }
4839
4840 for (DWORD i = 0; i < pControl->cControls; ++i)
4841 {
4842 FreeControl(pControl->rgControls + i);
4843 }
4844
4845 for (DWORD i = 0; i < pControl->cActions; ++i)
4846 {
4847 FreeAction(&(pControl->rgActions[i]));
4848 }
4849
4850 for (DWORD i = 0; i < pControl->cConditionalText; ++i)
4851 {
4852 FreeConditionalText(&(pControl->rgConditionalText[i]));
4853 }
4854
4855 for (DWORD i = 0; i < pControl->cTabs; ++i)
4856 {
4857 FreeTab(&(pControl->pttTabs[i]));
4858 }
4859
4860 ReleaseMem(pControl->rgActions)
4861 ReleaseMem(pControl->rgConditionalText);
4862 ReleaseMem(pControl->pttTabs);
4863 }
4864 }
4865
4866
4867 static void FreeAction(
4868 __in THEME_ACTION* pAction
4869 )
4870 {
4871 switch (pAction->type)
4872 {
4873 case THEME_ACTION_TYPE_BROWSE_DIRECTORY:
4874 ReleaseStr(pAction->BrowseDirectory.sczVariableName);
4875 break;
4876 case THEME_ACTION_TYPE_CHANGE_PAGE:
4877 ReleaseStr(pAction->ChangePage.sczPageName);
4878 break;
4879 }
4880
4881 ReleaseStr(pAction->sczCondition);
4882 }
4883
4884
4885 static void FreeColumn(
4886 __in THEME_COLUMN* pColumn
4887 )
4888 {
4889 ReleaseStr(pColumn->pszName);
4890 }
4891
4892
4893 static void FreeConditionalText(
4894 __in THEME_CONDITIONAL_TEXT* pConditionalText
4895 )
4896 {
4897 ReleaseStr(pConditionalText->sczCondition);
4898 ReleaseStr(pConditionalText->sczText);
4899 }
4900
4901
4902 static void FreeTab(
4903 __in THEME_TAB* pTab
4904 )
4905 {
4906 ReleaseStr(pTab->pszName);
4907 }
4908
4909
4910 static void FreeFontInstance(
4911 __in THEME_FONT_INSTANCE* pFontInstance
4912 )
4913 {
4914 if (pFontInstance->hFont)
4915 {
4916 ::DeleteObject(pFontInstance->hFont);
4917 pFontInstance->hFont = NULL;
4918 }
4919 }
4920
4921
4922 static void FreeFont(
4923 __in THEME_FONT* pFont
4924 )
4925 {
4926 if (pFont)
4927 {
4928 if (pFont->hBackground)
4929 {
4930 ::DeleteObject(pFont->hBackground);
4931 pFont->hBackground = NULL;
4932 }
4933
4934 if (pFont->hForeground)
4935 {
4936 ::DeleteObject(pFont->hForeground);
4937 pFont->hForeground = NULL;
4938 }
4939
4940 for (DWORD i = 0; i < pFont->cFontInstances; ++i)
4941 {
4942 FreeFontInstance(&(pFont->rgFontInstances[i]));
4943 }
4944
4945 ReleaseMem(pFont->rgFontInstances);
4946 ReleaseStr(pFont->sczFaceName);
4947 ReleaseStr(pFont->sczId);
4948 }
4949 }
4950
4951
4952 static void FreeImage(
4953 __in THEME_IMAGE* pImage
4954 )
4955 {
4956 if (pImage)
4957 {
4958 for (DWORD i = 0; i < pImage->cImageInstances; ++i)
4959 {
4960 FreeImageInstance(pImage->rgImageInstances + i);
4961 }
4962
4963 ReleaseStr(pImage->sczId);
4964 }
4965 }
4966
4967 static void FreeImageInstance(
4968 __in THEME_IMAGE_INSTANCE* pImageInstance
4969 )
4970 {
4971 if (pImageInstance->pBitmap)
4972 {
4973 delete pImageInstance->pBitmap;
4974 }
4975 }
4976
4977
4978 static void CALLBACK OnBillboardTimer(
4979 __in THEME* /*pTheme*/,
4980 __in HWND hwnd,
4981 __in UINT_PTR idEvent
4982 )
4983 {
4984 THEME_CONTROL* pControl = reinterpret_cast<THEME_CONTROL*>(idEvent);
4985
4986 if (pControl)
4987 {
4988 AssertSz(THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages.");
4989
4990 if (pControl->dwData < pControl->cControls)
4991 {
4992 ThemeShowChild(pControl, pControl->dwData);
4993 }
4994 else if (pControl->fBillboardLoops)
4995 {
4996 pControl->dwData = 0;
4997 ThemeShowChild(pControl, pControl->dwData);
4998 }
4999 else // no more looping
5000 {
Showing first 5,000 of 6,938 lines. View raw