xref: /reactos/dll/win32/comctl32/commctrl.c (revision 8540ab04)
1 /*
2  * Common controls functions
3  *
4  * Copyright 1997 Dimitrie O. Paun
5  * Copyright 1998,2000 Eric Kohl
6  * Copyright 2014-2015 Michael M�ller
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  *
22  * NOTES
23  *
24  * This code was audited for completeness against the documented features
25  * of Comctl32.dll version 6.0 on Oct. 21, 2002, by Christian Neumair.
26  *
27  * Unless otherwise noted, we believe this code to be complete, as per
28  * the specification mentioned above.
29  * If you discover missing features, or bugs, please note them below.
30  *
31  * TODO
32  *   -- implement GetMUILanguage + InitMUILanguage
33  *   -- finish NOTES for MenuHelp, GetEffectiveClientRect and GetStatusTextW
34  *   -- FIXMEs + BUGS (search for them)
35  *
36  * Control Classes
37  *   -- ICC_ANIMATE_CLASS
38  *   -- ICC_BAR_CLASSES
39  *   -- ICC_COOL_CLASSES
40  *   -- ICC_DATE_CLASSES
41  *   -- ICC_HOTKEY_CLASS
42  *   -- ICC_INTERNET_CLASSES
43  *   -- ICC_LINK_CLASS
44  *   -- ICC_LISTVIEW_CLASSES
45  *   -- ICC_NATIVEFNTCTL_CLASS
46  *   -- ICC_PAGESCROLLER_CLASS
47  *   -- ICC_PROGRESS_CLASS
48  *   -- ICC_STANDARD_CLASSES (not yet implemented)
49  *   -- ICC_TAB_CLASSES
50  *   -- ICC_TREEVIEW_CLASSES
51  *   -- ICC_UPDOWN_CLASS
52  *   -- ICC_USEREX_CLASSES
53  *   -- ICC_WIN95_CLASSES
54  */
55 
56 #include <stdarg.h>
57 #include <string.h>
58 #include <stdlib.h>
59 
60 #include "windef.h"
61 #include "winbase.h"
62 #include "wingdi.h"
63 #include "winuser.h"
64 #include "winnls.h"
65 #include "commctrl.h"
66 #include "winerror.h"
67 #include "winreg.h"
68 #define NO_SHLWAPI_STREAM
69 #include "shlwapi.h"
70 #include "comctl32.h"
71 #include "wine/debug.h"
72 
73 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
74 
75 
76 static LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
77 
78 static LPWSTR COMCTL32_wSubclass = NULL;
79 HMODULE COMCTL32_hModule = 0;
80 static LANGID COMCTL32_uiLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
81 HBRUSH  COMCTL32_hPattern55AABrush = NULL;
82 COMCTL32_SysColor  comctl32_color;
83 
84 static HBITMAP COMCTL32_hPattern55AABitmap = NULL;
85 
86 static const WORD wPattern55AA[] =
87 {
88     0x5555, 0xaaaa, 0x5555, 0xaaaa,
89     0x5555, 0xaaaa, 0x5555, 0xaaaa
90 };
91 
92 static const WCHAR strCC32SubclassInfo[] = {
93     'C','C','3','2','S','u','b','c','l','a','s','s','I','n','f','o',0
94 };
95 
96 #ifdef __REACTOS__
97 
98 #include <strsafe.h>
99 
100 #define NAME       L"microsoft.windows.common-controls"
101 #define VERSION_V5 L"5.82.2600.2982"
102 #define VERSION    L"6.0.2600.2982"
103 #define PUBLIC_KEY L"6595b64144ccf1df"
104 
105 #ifdef __i386__
106 #define ARCH L"x86"
107 #elif defined __x86_64__
108 #define ARCH L"amd64"
109 #else
110 #define ARCH L"none"
111 #endif
112 
113 static const WCHAR manifest_filename[] = ARCH L"_" NAME L"_" PUBLIC_KEY L"_" VERSION L"_none_deadbeef.manifest";
114 static const WCHAR manifest_filename_v5[] = ARCH L"_" NAME L"_" PUBLIC_KEY L"_" VERSION_V5 L"_none_deadbeef.manifest";
115 
116 static WCHAR* GetManifestPath(BOOL create, BOOL bV6)
117 {
118     WCHAR *pwszBuf;
119     HRESULT hres;
120 
121     pwszBuf = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
122     if (!pwszBuf)
123         return NULL;
124 
125     GetWindowsDirectoryW(pwszBuf, MAX_PATH);
126     hres = StringCchCatW(pwszBuf, MAX_PATH, L"\\winsxs");
127     if (FAILED(hres))
128         return NULL;
129     if (create)
130         CreateDirectoryW(pwszBuf, NULL);
131     hres = StringCchCatW(pwszBuf, MAX_PATH, L"\\manifests\\");
132     if (FAILED(hres))
133         return NULL;
134     if (create)
135         CreateDirectoryW(pwszBuf, NULL);
136 
137     hres = StringCchCatW(pwszBuf, MAX_PATH, bV6 ? manifest_filename : manifest_filename_v5);
138     if (FAILED(hres))
139         return NULL;
140 
141     return pwszBuf;
142 }
143 
144 static HANDLE CreateComctl32ActCtx(BOOL bV6)
145 {
146     HANDLE ret;
147     WCHAR* pwstrSource;
148     ACTCTXW ActCtx = {sizeof(ACTCTX)};
149 
150     pwstrSource = GetManifestPath(FALSE, bV6);
151     if (!pwstrSource)
152     {
153         ERR("GetManifestPath failed! bV6=%d\n", bV6);
154         return INVALID_HANDLE_VALUE;
155     }
156     ActCtx.lpSource = pwstrSource;
157     ret = CreateActCtxW(&ActCtx);
158     HeapFree(GetProcessHeap(), 0, pwstrSource);
159     if (ret == INVALID_HANDLE_VALUE)
160         ERR("CreateActCtxW failed! bV6=%d\n", bV6);
161     return ret;
162 }
163 
164 static void RegisterControls(BOOL bV6)
165 {
166     ANIMATE_Register ();
167     COMBOEX_Register ();
168     DATETIME_Register ();
169     FLATSB_Register ();
170     HEADER_Register ();
171     HOTKEY_Register ();
172     IPADDRESS_Register ();
173     LISTVIEW_Register ();
174     MONTHCAL_Register ();
175     NATIVEFONT_Register ();
176     PAGER_Register ();
177     PROGRESS_Register ();
178     REBAR_Register ();
179     STATUS_Register ();
180     SYSLINK_Register ();
181     TAB_Register ();
182     TOOLTIPS_Register ();
183     TRACKBAR_Register ();
184     TREEVIEW_Register ();
185     UPDOWN_Register ();
186 
187     if (!bV6)
188     {
189         TOOLBAR_Register ();
190     }
191     else
192     {
193         BUTTON_Register ();
194         COMBO_Register ();
195         COMBOLBOX_Register ();
196         EDIT_Register ();
197         LISTBOX_Register ();
198         STATIC_Register ();
199 
200         TOOLBARv6_Register();
201     }
202 }
203 
204 static void UnregisterControls(BOOL bV6)
205 {
206     ANIMATE_Unregister ();
207     COMBOEX_Unregister ();
208     DATETIME_Unregister ();
209     FLATSB_Unregister ();
210     HEADER_Unregister ();
211     HOTKEY_Unregister ();
212     IPADDRESS_Unregister ();
213     LISTVIEW_Unregister ();
214     MONTHCAL_Unregister ();
215     NATIVEFONT_Unregister ();
216     PAGER_Unregister ();
217     PROGRESS_Unregister ();
218     REBAR_Unregister ();
219     STATUS_Unregister ();
220     SYSLINK_Unregister ();
221     TAB_Unregister ();
222     TOOLTIPS_Unregister ();
223     TRACKBAR_Unregister ();
224     TREEVIEW_Unregister ();
225     UPDOWN_Unregister ();
226 
227     if (!bV6)
228     {
229         TOOLBAR_Unregister ();
230     }
231     else
232     {
233         BUTTON_Unregister();
234         COMBO_Unregister ();
235         COMBOLBOX_Unregister ();
236         EDIT_Unregister ();
237         LISTBOX_Unregister ();
238         STATIC_Unregister ();
239 
240         TOOLBARv6_Unregister ();
241     }
242 
243 }
244 
245 static void InitializeClasses()
246 {
247     HANDLE hActCtx5, hActCtx6;
248     BOOL activated;
249     ULONG_PTR ulCookie;
250 
251     /* like comctl32 5.82+ register all the common control classes */
252     /* Register the classes once no matter what */
253     hActCtx5 = CreateComctl32ActCtx(FALSE);
254     activated = (hActCtx5 != INVALID_HANDLE_VALUE ? ActivateActCtx(hActCtx5, &ulCookie) : FALSE);
255     RegisterControls(FALSE);      /* Register the classes pretending to be v5 */
256     if (activated) DeactivateActCtx(0, ulCookie);
257 
258     hActCtx6 = CreateComctl32ActCtx(TRUE);
259     if (hActCtx6 != INVALID_HANDLE_VALUE)
260     {
261         activated = ActivateActCtx(hActCtx6, &ulCookie);
262         RegisterControls(TRUE);      /* Register the classes pretending to be v6 */
263         if (activated) DeactivateActCtx(0, ulCookie);
264 
265         /* Initialize the themed controls only when the v6 manifest is present */
266         THEMING_Initialize (hActCtx5, hActCtx6);
267     }
268 }
269 
270 static void UninitializeClasses()
271 {
272     HANDLE hActCtx5, hActCtx6;
273     BOOL activated;
274     ULONG_PTR ulCookie;
275 
276     hActCtx5 = CreateComctl32ActCtx(FALSE);
277     activated = (hActCtx5 != INVALID_HANDLE_VALUE ? ActivateActCtx(hActCtx5, &ulCookie) : FALSE);
278     UnregisterControls(FALSE);
279     if (activated) DeactivateActCtx(0, ulCookie);
280 
281     hActCtx6 = CreateComctl32ActCtx(TRUE);
282     if (hActCtx6 != INVALID_HANDLE_VALUE)
283     {
284         activated = ActivateActCtx(hActCtx6, &ulCookie);
285         THEMING_Uninitialize();
286         UnregisterControls(TRUE);
287         if (activated) DeactivateActCtx(0, ulCookie);
288     }
289 }
290 
291 /***********************************************************************
292  * RegisterClassNameW [COMCTL32.@]
293  *
294  * Register window class again while using as SxS module.
295  */
296 BOOLEAN WINAPI RegisterClassNameW(LPCWSTR className)
297 {
298     InitializeClasses();
299     return TRUE;
300 }
301 
302 #endif /* __REACTOS__ */
303 
304 #ifndef __REACTOS__
305 static void unregister_versioned_classes(void)
306 {
307 #define VERSION "6.0.2600.2982!"
308     static const char *classes[] =
309     {
310         VERSION WC_BUTTONA,
311         VERSION WC_COMBOBOXA,
312         VERSION "ComboLBox",
313         VERSION WC_EDITA,
314         VERSION WC_LISTBOXA,
315         VERSION WC_STATICA,
316     };
317     int i;
318 
319     for (i = 0; i < ARRAY_SIZE(classes); i++)
320         UnregisterClassA(classes[i], NULL);
321 
322 #undef VERSION
323 }
324 #endif
325 
326 /***********************************************************************
327  * DllMain [Internal]
328  *
329  * Initializes the internal 'COMCTL32.DLL'.
330  *
331  * PARAMS
332  *     hinstDLL    [I] handle to the 'dlls' instance
333  *     fdwReason   [I]
334  *     lpvReserved [I] reserved, must be NULL
335  *
336  * RETURNS
337  *     Success: TRUE
338  *     Failure: FALSE
339  */
340 
341 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
342 {
343     TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved);
344 
345     switch (fdwReason) {
346 	case DLL_PROCESS_ATTACH:
347             DisableThreadLibraryCalls(hinstDLL);
348 
349             COMCTL32_hModule = hinstDLL;
350 
351             /* add global subclassing atom (used by 'tooltip' and 'updown') */
352             COMCTL32_wSubclass = (LPWSTR)(DWORD_PTR)GlobalAddAtomW (strCC32SubclassInfo);
353             TRACE("Subclassing atom added: %p\n", COMCTL32_wSubclass);
354 
355             /* create local pattern brush */
356             COMCTL32_hPattern55AABitmap = CreateBitmap (8, 8, 1, 1, wPattern55AA);
357             COMCTL32_hPattern55AABrush = CreatePatternBrush (COMCTL32_hPattern55AABitmap);
358 
359 	    /* Get all the colors at DLL load */
360 	    COMCTL32_RefreshSysColors();
361 
362 #ifndef __REACTOS__
363             /* like comctl32 5.82+ register all the common control classes */
364             ANIMATE_Register ();
365             COMBOEX_Register ();
366             DATETIME_Register ();
367             FLATSB_Register ();
368             HEADER_Register ();
369             HOTKEY_Register ();
370             IPADDRESS_Register ();
371             LISTVIEW_Register ();
372             MONTHCAL_Register ();
373             NATIVEFONT_Register ();
374             PAGER_Register ();
375             PROGRESS_Register ();
376             REBAR_Register ();
377             STATUS_Register ();
378             SYSLINK_Register ();
379             TAB_Register ();
380             TOOLBAR_Register ();
381             TOOLTIPS_Register ();
382             TRACKBAR_Register ();
383             TREEVIEW_Register ();
384             UPDOWN_Register ();
385 
386             BUTTON_Register ();
387             COMBO_Register ();
388             COMBOLBOX_Register ();
389             EDIT_Register ();
390             LISTBOX_Register ();
391             STATIC_Register ();
392 
393             /* subclass user32 controls */
394             THEMING_Initialize ();
395 #else
396             InitializeClasses();
397 #endif
398 
399             break;
400 
401 	case DLL_PROCESS_DETACH:
402             if (lpvReserved) break;
403 #ifndef __REACTOS__
404             /* clean up subclassing */
405             THEMING_Uninitialize();
406 
407             /* unregister all common control classes */
408             ANIMATE_Unregister ();
409             COMBOEX_Unregister ();
410             DATETIME_Unregister ();
411             FLATSB_Unregister ();
412             HEADER_Unregister ();
413             HOTKEY_Unregister ();
414             IPADDRESS_Unregister ();
415             LISTVIEW_Unregister ();
416             MONTHCAL_Unregister ();
417             NATIVEFONT_Unregister ();
418             PAGER_Unregister ();
419             PROGRESS_Unregister ();
420             REBAR_Unregister ();
421             STATUS_Unregister ();
422             SYSLINK_Unregister ();
423             TAB_Unregister ();
424             TOOLBAR_Unregister ();
425             TOOLTIPS_Unregister ();
426             TRACKBAR_Unregister ();
427             TREEVIEW_Unregister ();
428             UPDOWN_Unregister ();
429 
430             unregister_versioned_classes ();
431 
432 #else
433             UninitializeClasses();
434 #endif
435             /* delete local pattern brush */
436             DeleteObject (COMCTL32_hPattern55AABrush);
437             DeleteObject (COMCTL32_hPattern55AABitmap);
438 
439             /* delete global subclassing atom */
440             GlobalDeleteAtom (LOWORD(COMCTL32_wSubclass));
441             TRACE("Subclassing atom deleted: %p\n", COMCTL32_wSubclass);
442             break;
443     }
444 
445     return TRUE;
446 }
447 
448 
449 /***********************************************************************
450  * MenuHelp [COMCTL32.2]
451  *
452  * Handles the setting of status bar help messages when the user
453  * selects menu items.
454  *
455  * PARAMS
456  *     uMsg       [I] message (WM_MENUSELECT) (see NOTES)
457  *     wParam     [I] wParam of the message uMsg
458  *     lParam     [I] lParam of the message uMsg
459  *     hMainMenu  [I] handle to the application's main menu
460  *     hInst      [I] handle to the module that contains string resources
461  *     hwndStatus [I] handle to the status bar window
462  *     lpwIDs     [I] pointer to an array of integers (see NOTES)
463  *
464  * RETURNS
465  *     No return value
466  *
467  * NOTES
468  *     The official documentation is incomplete!
469  *     This is the correct documentation:
470  *
471  *     uMsg:
472  *     MenuHelp() does NOT handle WM_COMMAND messages! It only handles
473  *     WM_MENUSELECT messages.
474  *
475  *     lpwIDs:
476  *     (will be written ...)
477  */
478 
479 VOID WINAPI
480 MenuHelp (UINT uMsg, WPARAM wParam, LPARAM lParam, HMENU hMainMenu,
481 	  HINSTANCE hInst, HWND hwndStatus, UINT* lpwIDs)
482 {
483     UINT uMenuID = 0;
484 
485     if (!IsWindow (hwndStatus))
486 	return;
487 
488     switch (uMsg) {
489 	case WM_MENUSELECT:
490 	    TRACE("WM_MENUSELECT wParam=0x%lX lParam=0x%lX\n",
491 		   wParam, lParam);
492 
493             if ((HIWORD(wParam) == 0xFFFF) && (lParam == 0)) {
494                 /* menu was closed */
495 		TRACE("menu was closed!\n");
496                 SendMessageW (hwndStatus, SB_SIMPLE, FALSE, 0);
497             }
498 	    else {
499 		/* menu item was selected */
500 		if (HIWORD(wParam) & MF_POPUP)
501 		    uMenuID = *(lpwIDs+1);
502 		else
503 		    uMenuID = (UINT)LOWORD(wParam);
504 		TRACE("uMenuID = %u\n", uMenuID);
505 
506 		if (uMenuID) {
507 		    WCHAR szText[256];
508 
509 		    if (!LoadStringW (hInst, uMenuID, szText, ARRAY_SIZE(szText)))
510 			szText[0] = '\0';
511 
512 		    SendMessageW (hwndStatus, SB_SETTEXTW,
513 				    255 | SBT_NOBORDERS, (LPARAM)szText);
514 		    SendMessageW (hwndStatus, SB_SIMPLE, TRUE, 0);
515 		}
516 	    }
517 	    break;
518 
519         case WM_COMMAND :
520 	    TRACE("WM_COMMAND wParam=0x%lX lParam=0x%lX\n",
521 		   wParam, lParam);
522 	    /* WM_COMMAND is not invalid since it is documented
523 	     * in the windows api reference. So don't output
524              * any FIXME for WM_COMMAND
525              */
526 	    WARN("We don't care about the WM_COMMAND\n");
527 	    break;
528 
529 	default:
530 	    FIXME("Invalid Message 0x%x!\n", uMsg);
531 	    break;
532     }
533 }
534 
535 
536 /***********************************************************************
537  * ShowHideMenuCtl [COMCTL32.3]
538  *
539  * Shows or hides controls and updates the corresponding menu item.
540  *
541  * PARAMS
542  *     hwnd   [I] handle to the client window.
543  *     uFlags [I] menu command id.
544  *     lpInfo [I] pointer to an array of integers. (See NOTES.)
545  *
546  * RETURNS
547  *     Success: TRUE
548  *     Failure: FALSE
549  *
550  * NOTES
551  *     The official documentation is incomplete!
552  *     This is the correct documentation:
553  *
554  *     hwnd
555  *     Handle to the window that contains the menu and controls.
556  *
557  *     uFlags
558  *     Identifier of the menu item to receive or lose a check mark.
559  *
560  *     lpInfo
561  *     The array of integers contains pairs of values. BOTH values of
562  *     the first pair must be the handles to the application's main menu.
563  *     Each subsequent pair consists of a menu id and control id.
564  */
565 
566 BOOL WINAPI
567 ShowHideMenuCtl (HWND hwnd, UINT_PTR uFlags, LPINT lpInfo)
568 {
569     LPINT lpMenuId;
570 
571     TRACE("%p, %lx, %p\n", hwnd, uFlags, lpInfo);
572 
573     if (lpInfo == NULL)
574 	return FALSE;
575 
576     if (!(lpInfo[0]) || !(lpInfo[1]))
577 	return FALSE;
578 
579     /* search for control */
580     lpMenuId = &lpInfo[2];
581     while (*lpMenuId != uFlags)
582 	lpMenuId += 2;
583 
584     if (GetMenuState ((HMENU)(DWORD_PTR)lpInfo[1], uFlags, MF_BYCOMMAND) & MFS_CHECKED) {
585 	/* uncheck menu item */
586 	CheckMenuItem ((HMENU)(DWORD_PTR)lpInfo[0], *lpMenuId, MF_BYCOMMAND | MF_UNCHECKED);
587 
588 	/* hide control */
589 	lpMenuId++;
590 	SetWindowPos (GetDlgItem (hwnd, *lpMenuId), 0, 0, 0, 0, 0,
591 			SWP_HIDEWINDOW);
592     }
593     else {
594 	/* check menu item */
595 	CheckMenuItem ((HMENU)(DWORD_PTR)lpInfo[0], *lpMenuId, MF_BYCOMMAND | MF_CHECKED);
596 
597 	/* show control */
598 	lpMenuId++;
599 	SetWindowPos (GetDlgItem (hwnd, *lpMenuId), 0, 0, 0, 0, 0,
600 			SWP_SHOWWINDOW);
601     }
602 
603     return TRUE;
604 }
605 
606 
607 /***********************************************************************
608  * GetEffectiveClientRect [COMCTL32.4]
609  *
610  * Calculates the coordinates of a rectangle in the client area.
611  *
612  * PARAMS
613  *     hwnd   [I] handle to the client window.
614  *     lpRect [O] pointer to the rectangle of the client window
615  *     lpInfo [I] pointer to an array of integers (see NOTES)
616  *
617  * RETURNS
618  *     No return value.
619  *
620  * NOTES
621  *     The official documentation is incomplete!
622  *     This is the correct documentation:
623  *
624  *     lpInfo
625  *     (will be written ...)
626  */
627 
628 VOID WINAPI
629 GetEffectiveClientRect (HWND hwnd, LPRECT lpRect, const INT *lpInfo)
630 {
631     RECT rcCtrl;
632     const INT *lpRun;
633     HWND hwndCtrl;
634 
635     TRACE("(%p %p %p)\n",
636 	   hwnd, lpRect, lpInfo);
637 
638     GetClientRect (hwnd, lpRect);
639     lpRun = lpInfo;
640 
641     do {
642 	lpRun += 2;
643 	if (*lpRun == 0)
644 	    return;
645 	lpRun++;
646 	hwndCtrl = GetDlgItem (hwnd, *lpRun);
647 	if (GetWindowLongW (hwndCtrl, GWL_STYLE) & WS_VISIBLE) {
648 	    TRACE("control id 0x%x\n", *lpRun);
649 	    GetWindowRect (hwndCtrl, &rcCtrl);
650 	    MapWindowPoints (NULL, hwnd, (LPPOINT)&rcCtrl, 2);
651 	    SubtractRect (lpRect, lpRect, &rcCtrl);
652 	}
653 	lpRun++;
654     } while (*lpRun);
655 }
656 
657 
658 /***********************************************************************
659  * DrawStatusTextW [COMCTL32.@]
660  *
661  * Draws text with borders, like in a status bar.
662  *
663  * PARAMS
664  *     hdc   [I] handle to the window's display context
665  *     lprc  [I] pointer to a rectangle
666  *     text  [I] pointer to the text
667  *     style [I] drawing style
668  *
669  * RETURNS
670  *     No return value.
671  *
672  * NOTES
673  *     The style variable can have one of the following values:
674  *     (will be written ...)
675  */
676 
677 void WINAPI DrawStatusTextW (HDC hdc, LPCRECT lprc, LPCWSTR text, UINT style)
678 {
679     RECT r = *lprc;
680     UINT border = BDR_SUNKENOUTER;
681 
682     if (style & SBT_POPOUT)
683         border = BDR_RAISEDOUTER;
684     else if (style & SBT_NOBORDERS)
685         border = 0;
686 
687     DrawEdge (hdc, &r, border, BF_RECT|BF_ADJUST);
688 
689     /* now draw text */
690     if (text) {
691         int oldbkmode = SetBkMode (hdc, TRANSPARENT);
692         UINT align = DT_LEFT;
693         int strCnt = 0;
694 
695         if (style & SBT_RTLREADING)
696             FIXME("Unsupported RTL style!\n");
697         r.left += 3;
698         do {
699             if (*text == '\t') {
700                 if (strCnt) {
701                     DrawTextW (hdc, text - strCnt, strCnt, &r, align|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
702                     strCnt = 0;
703                 }
704                 if (align==DT_RIGHT) {
705                     break;
706                 }
707                 align = (align==DT_LEFT ? DT_CENTER : DT_RIGHT);
708             } else {
709                 strCnt++;
710             }
711         } while(*text++);
712 
713         if (strCnt) DrawTextW (hdc, text - strCnt, -1, &r, align|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
714 	SetBkMode(hdc, oldbkmode);
715     }
716 }
717 
718 
719 /***********************************************************************
720  * DrawStatusText  [COMCTL32.@]
721  * DrawStatusTextA [COMCTL32.5]
722  *
723  * Draws text with borders, like in a status bar.
724  *
725  * PARAMS
726  *     hdc   [I] handle to the window's display context
727  *     lprc  [I] pointer to a rectangle
728  *     text  [I] pointer to the text
729  *     style [I] drawing style
730  *
731  * RETURNS
732  *     No return value.
733  */
734 
735 void WINAPI DrawStatusTextA (HDC hdc, LPCRECT lprc, LPCSTR text, UINT style)
736 {
737     INT len;
738     LPWSTR textW = NULL;
739 
740     if ( text ) {
741 	if ( (len = MultiByteToWideChar( CP_ACP, 0, text, -1, NULL, 0 )) ) {
742 	    if ( (textW = Alloc( len * sizeof(WCHAR) )) )
743 		MultiByteToWideChar( CP_ACP, 0, text, -1, textW, len );
744 	}
745     }
746     DrawStatusTextW( hdc, lprc, textW, style );
747     Free( textW );
748 }
749 
750 
751 /***********************************************************************
752  * CreateStatusWindow  [COMCTL32.@]
753  * CreateStatusWindowA [COMCTL32.6]
754  *
755  * Creates a status bar
756  *
757  * PARAMS
758  *     style  [I] window style
759  *     text   [I] pointer to the window text
760  *     parent [I] handle to the parent window
761  *     wid    [I] control id of the status bar
762  *
763  * RETURNS
764  *     Success: handle to the status window
765  *     Failure: 0
766  */
767 
768 HWND WINAPI
769 CreateStatusWindowA (LONG style, LPCSTR text, HWND parent, UINT wid)
770 {
771     return CreateWindowA(STATUSCLASSNAMEA, text, style,
772 			   CW_USEDEFAULT, CW_USEDEFAULT,
773 			   CW_USEDEFAULT, CW_USEDEFAULT,
774 			   parent, (HMENU)(DWORD_PTR)wid, 0, 0);
775 }
776 
777 
778 /***********************************************************************
779  * CreateStatusWindowW [COMCTL32.@]
780  *
781  * Creates a status bar control
782  *
783  * PARAMS
784  *     style  [I] window style
785  *     text   [I] pointer to the window text
786  *     parent [I] handle to the parent window
787  *     wid    [I] control id of the status bar
788  *
789  * RETURNS
790  *     Success: handle to the status window
791  *     Failure: 0
792  */
793 
794 HWND WINAPI
795 CreateStatusWindowW (LONG style, LPCWSTR text, HWND parent, UINT wid)
796 {
797     return CreateWindowW(STATUSCLASSNAMEW, text, style,
798 			   CW_USEDEFAULT, CW_USEDEFAULT,
799 			   CW_USEDEFAULT, CW_USEDEFAULT,
800 			   parent, (HMENU)(DWORD_PTR)wid, 0, 0);
801 }
802 
803 
804 /***********************************************************************
805  * CreateUpDownControl [COMCTL32.16]
806  *
807  * Creates an up-down control
808  *
809  * PARAMS
810  *     style  [I] window styles
811  *     x      [I] horizontal position of the control
812  *     y      [I] vertical position of the control
813  *     cx     [I] with of the control
814  *     cy     [I] height of the control
815  *     parent [I] handle to the parent window
816  *     id     [I] the control's identifier
817  *     inst   [I] handle to the application's module instance
818  *     buddy  [I] handle to the buddy window, can be NULL
819  *     maxVal [I] upper limit of the control
820  *     minVal [I] lower limit of the control
821  *     curVal [I] current value of the control
822  *
823  * RETURNS
824  *     Success: handle to the updown control
825  *     Failure: 0
826  */
827 
828 HWND WINAPI
829 CreateUpDownControl (DWORD style, INT x, INT y, INT cx, INT cy,
830 		     HWND parent, INT id, HINSTANCE inst,
831 		     HWND buddy, INT maxVal, INT minVal, INT curVal)
832 {
833     HWND hUD =
834 	CreateWindowW (UPDOWN_CLASSW, 0, style, x, y, cx, cy,
835 			 parent, (HMENU)(DWORD_PTR)id, inst, 0);
836     if (hUD) {
837 	SendMessageW (hUD, UDM_SETBUDDY, (WPARAM)buddy, 0);
838 	SendMessageW (hUD, UDM_SETRANGE, 0, MAKELONG(maxVal, minVal));
839 	SendMessageW (hUD, UDM_SETPOS, 0, MAKELONG(curVal, 0));
840     }
841 
842     return hUD;
843 }
844 
845 
846 /***********************************************************************
847  * InitCommonControls [COMCTL32.17]
848  *
849  * Registers the common controls.
850  *
851  * PARAMS
852  *     No parameters.
853  *
854  * RETURNS
855  *     No return values.
856  *
857  * NOTES
858  *     This function is just a dummy - all the controls are registered at
859  *     the DLL initialization time. See InitCommonContolsEx for details.
860  */
861 
862 VOID WINAPI
863 InitCommonControls (void)
864 {
865 }
866 
867 
868 /***********************************************************************
869  * InitCommonControlsEx [COMCTL32.@]
870  *
871  * Registers the common controls.
872  *
873  * PARAMS
874  *     lpInitCtrls [I] pointer to an INITCOMMONCONTROLS structure.
875  *
876  * RETURNS
877  *     Success: TRUE
878  *     Failure: FALSE
879  *
880  * NOTES
881  *     Probably all versions of comctl32 initializes the Win95 controls in DllMain
882  *     during DLL initialization. Starting from comctl32 v5.82 all the controls
883  *     are initialized there. We follow this behaviour and this function is just
884  *     a dummy.
885  *
886  *     Note: when writing programs under Windows, if you don't call any function
887  *     from comctl32 the linker may not link this DLL. If InitCommonControlsEx
888  *     was the only comctl32 function you were calling and you remove it you may
889  *     have a false impression that InitCommonControlsEx actually did something.
890  */
891 
892 BOOL WINAPI
893 InitCommonControlsEx (const INITCOMMONCONTROLSEX *lpInitCtrls)
894 {
895     if (!lpInitCtrls || lpInitCtrls->dwSize != sizeof(INITCOMMONCONTROLSEX))
896         return FALSE;
897 
898     TRACE("(0x%08x)\n", lpInitCtrls->dwICC);
899     return TRUE;
900 }
901 
902 
903 /***********************************************************************
904  * CreateToolbarEx [COMCTL32.@]
905  *
906  * Creates a toolbar window.
907  *
908  * PARAMS
909  *     hwnd
910  *     style
911  *     wID
912  *     nBitmaps
913  *     hBMInst
914  *     wBMID
915  *     lpButtons
916  *     iNumButtons
917  *     dxButton
918  *     dyButton
919  *     dxBitmap
920  *     dyBitmap
921  *     uStructSize
922  *
923  * RETURNS
924  *     Success: handle to the tool bar control
925  *     Failure: 0
926  */
927 
928 HWND WINAPI
929 CreateToolbarEx (HWND hwnd, DWORD style, UINT wID, INT nBitmaps,
930                  HINSTANCE hBMInst, UINT_PTR wBMID, LPCTBBUTTON lpButtons,
931                  INT iNumButtons, INT dxButton, INT dyButton,
932                  INT dxBitmap, INT dyBitmap, UINT uStructSize)
933 {
934     HWND hwndTB;
935 
936     hwndTB =
937         CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, style|WS_CHILD, 0,0,100,30,
938                         hwnd, (HMENU)(DWORD_PTR)wID, COMCTL32_hModule, NULL);
939     if(hwndTB) {
940 	TBADDBITMAP tbab;
941 
942         SendMessageW (hwndTB, TB_BUTTONSTRUCTSIZE, uStructSize, 0);
943 
944        /* set bitmap and button size */
945        /*If CreateToolbarEx receives 0, windows sets default values*/
946        if (dxBitmap < 0)
947            dxBitmap = 16;
948        if (dyBitmap < 0)
949            dyBitmap = 16;
950        if (dxBitmap == 0 || dyBitmap == 0)
951            dxBitmap = dyBitmap = 16;
952        SendMessageW(hwndTB, TB_SETBITMAPSIZE, 0, MAKELPARAM(dxBitmap, dyBitmap));
953 
954        if (dxButton < 0)
955            dxButton = dxBitmap;
956        if (dyButton < 0)
957            dyButton = dyBitmap;
958        /* TB_SETBUTTONSIZE -> TB_SETBITMAPSIZE bug introduced for Windows compatibility */
959        if (dxButton != 0 && dyButton != 0)
960             SendMessageW(hwndTB, TB_SETBITMAPSIZE, 0, MAKELPARAM(dxButton, dyButton));
961 
962 
963 	/* add bitmaps */
964 	if (nBitmaps > 0 || hBMInst == HINST_COMMCTRL)
965 	{
966 	    tbab.hInst = hBMInst;
967 	    tbab.nID   = wBMID;
968 
969             SendMessageW (hwndTB, TB_ADDBITMAP, nBitmaps, (LPARAM)&tbab);
970 	}
971 	/* add buttons */
972 	if(iNumButtons > 0)
973         SendMessageW (hwndTB, TB_ADDBUTTONSW, iNumButtons, (LPARAM)lpButtons);
974     }
975 
976     return hwndTB;
977 }
978 
979 
980 /***********************************************************************
981  * CreateMappedBitmap [COMCTL32.8]
982  *
983  * Loads a bitmap resource using a colour map.
984  *
985  * PARAMS
986  *     hInstance  [I] Handle to the module containing the bitmap.
987  *     idBitmap   [I] The bitmap resource ID.
988  *     wFlags     [I] CMB_MASKED for using bitmap as a mask or 0 for normal.
989  *     lpColorMap [I] Colour information needed for the bitmap or NULL (uses system colours).
990  *     iNumMaps   [I] Number of COLORMAP's pointed to by lpColorMap.
991  *
992  * RETURNS
993  *     Success: handle to the new bitmap
994  *     Failure: 0
995  */
996 
997 HBITMAP WINAPI
998 CreateMappedBitmap (HINSTANCE hInstance, INT_PTR idBitmap, UINT wFlags,
999 		    LPCOLORMAP lpColorMap, INT iNumMaps)
1000 {
1001     HGLOBAL hglb;
1002     HRSRC hRsrc;
1003     const BITMAPINFOHEADER *lpBitmap;
1004     LPBITMAPINFOHEADER lpBitmapInfo;
1005     UINT nSize, nColorTableSize, iColor;
1006     RGBQUAD *pColorTable;
1007     INT i, iMaps, nWidth, nHeight;
1008     HDC hdcScreen;
1009     HBITMAP hbm;
1010     LPCOLORMAP sysColorMap;
1011     COLORREF cRef;
1012     COLORMAP internalColorMap[4] =
1013 	{{0x000000, 0}, {0x808080, 0}, {0xC0C0C0, 0}, {0xFFFFFF, 0}};
1014 
1015     /* initialize pointer to colortable and default color table */
1016     if (lpColorMap) {
1017 	iMaps = iNumMaps;
1018 	sysColorMap = lpColorMap;
1019     }
1020     else {
1021 	internalColorMap[0].to = GetSysColor (COLOR_BTNTEXT);
1022 	internalColorMap[1].to = GetSysColor (COLOR_BTNSHADOW);
1023 	internalColorMap[2].to = GetSysColor (COLOR_BTNFACE);
1024 	internalColorMap[3].to = GetSysColor (COLOR_BTNHIGHLIGHT);
1025 	iMaps = 4;
1026 	sysColorMap = internalColorMap;
1027     }
1028 
1029     hRsrc = FindResourceW (hInstance, (LPWSTR)idBitmap, (LPWSTR)RT_BITMAP);
1030     if (hRsrc == 0)
1031 	return 0;
1032     hglb = LoadResource (hInstance, hRsrc);
1033     if (hglb == 0)
1034 	return 0;
1035     lpBitmap = LockResource (hglb);
1036     if (lpBitmap == NULL)
1037 	return 0;
1038 
1039     if (lpBitmap->biSize >= sizeof(BITMAPINFOHEADER) && lpBitmap->biClrUsed)
1040         nColorTableSize = lpBitmap->biClrUsed;
1041     else if (lpBitmap->biBitCount <= 8)
1042         nColorTableSize = (1 << lpBitmap->biBitCount);
1043     else
1044         nColorTableSize = 0;
1045     nSize = lpBitmap->biSize;
1046     if (nSize == sizeof(BITMAPINFOHEADER) && lpBitmap->biCompression == BI_BITFIELDS)
1047         nSize += 3 * sizeof(DWORD);
1048     nSize += nColorTableSize * sizeof(RGBQUAD);
1049     lpBitmapInfo = GlobalAlloc (GMEM_FIXED, nSize);
1050     if (lpBitmapInfo == NULL)
1051 	return 0;
1052     RtlMoveMemory (lpBitmapInfo, lpBitmap, nSize);
1053 
1054     pColorTable = (RGBQUAD*)(((LPBYTE)lpBitmapInfo) + lpBitmapInfo->biSize);
1055 
1056     for (iColor = 0; iColor < nColorTableSize; iColor++) {
1057 	for (i = 0; i < iMaps; i++) {
1058             cRef = RGB(pColorTable[iColor].rgbRed,
1059                        pColorTable[iColor].rgbGreen,
1060                        pColorTable[iColor].rgbBlue);
1061 	    if ( cRef  == sysColorMap[i].from) {
1062 #if 0
1063 		if (wFlags & CBS_MASKED) {
1064 		    if (sysColorMap[i].to != COLOR_BTNTEXT)
1065 			pColorTable[iColor] = RGB(255, 255, 255);
1066 		}
1067 		else
1068 #endif
1069                     pColorTable[iColor].rgbBlue  = GetBValue(sysColorMap[i].to);
1070                     pColorTable[iColor].rgbGreen = GetGValue(sysColorMap[i].to);
1071                     pColorTable[iColor].rgbRed   = GetRValue(sysColorMap[i].to);
1072 		break;
1073 	    }
1074 	}
1075     }
1076     nWidth  = lpBitmapInfo->biWidth;
1077     nHeight = lpBitmapInfo->biHeight;
1078     hdcScreen = GetDC (NULL);
1079     hbm = CreateCompatibleBitmap (hdcScreen, nWidth, nHeight);
1080     if (hbm) {
1081 	HDC hdcDst = CreateCompatibleDC (hdcScreen);
1082 	HBITMAP hbmOld = SelectObject (hdcDst, hbm);
1083 	const BYTE *lpBits = (const BYTE *)lpBitmap + nSize;
1084 	StretchDIBits (hdcDst, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight,
1085 		         lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS,
1086 		         SRCCOPY);
1087 	SelectObject (hdcDst, hbmOld);
1088 	DeleteDC (hdcDst);
1089     }
1090     ReleaseDC (NULL, hdcScreen);
1091     GlobalFree (lpBitmapInfo);
1092     FreeResource (hglb);
1093 
1094     return hbm;
1095 }
1096 
1097 
1098 /***********************************************************************
1099  * CreateToolbar [COMCTL32.7]
1100  *
1101  * Creates a toolbar control.
1102  *
1103  * PARAMS
1104  *     hwnd
1105  *     style
1106  *     wID
1107  *     nBitmaps
1108  *     hBMInst
1109  *     wBMID
1110  *     lpButtons
1111  *     iNumButtons
1112  *
1113  * RETURNS
1114  *     Success: handle to the tool bar control
1115  *     Failure: 0
1116  *
1117  * NOTES
1118  *     Do not use this function anymore. Use CreateToolbarEx instead.
1119  */
1120 
1121 HWND WINAPI
1122 CreateToolbar (HWND hwnd, DWORD style, UINT wID, INT nBitmaps,
1123 	       HINSTANCE hBMInst, UINT wBMID,
1124 	       LPCTBBUTTON lpButtons,INT iNumButtons)
1125 {
1126     return CreateToolbarEx (hwnd, style | CCS_NODIVIDER, wID, nBitmaps,
1127 			    hBMInst, wBMID, lpButtons,
1128 			    iNumButtons, 0, 0, 0, 0, CCSIZEOF_STRUCT(TBBUTTON, dwData));
1129 }
1130 
1131 
1132 /***********************************************************************
1133  * DllGetVersion [COMCTL32.@]
1134  *
1135  * Retrieves version information of the 'COMCTL32.DLL'
1136  *
1137  * PARAMS
1138  *     pdvi [O] pointer to version information structure.
1139  *
1140  * RETURNS
1141  *     Success: S_OK
1142  *     Failure: E_INVALIDARG
1143  *
1144  * NOTES
1145  *     Returns version of a comctl32.dll from IE4.01 SP1.
1146  */
1147 
1148 HRESULT WINAPI DllGetVersion (DLLVERSIONINFO *pdvi)
1149 {
1150     if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) {
1151         WARN("wrong DLLVERSIONINFO size from app\n");
1152 	return E_INVALIDARG;
1153     }
1154 
1155     pdvi->dwMajorVersion = COMCTL32_VERSION;
1156     pdvi->dwMinorVersion = COMCTL32_VERSION_MINOR;
1157     pdvi->dwBuildNumber = 2919;
1158     pdvi->dwPlatformID = 6304;
1159 
1160     TRACE("%u.%u.%u.%u\n",
1161 	   pdvi->dwMajorVersion, pdvi->dwMinorVersion,
1162 	   pdvi->dwBuildNumber, pdvi->dwPlatformID);
1163 
1164     return S_OK;
1165 }
1166 
1167 /***********************************************************************
1168  *		DllInstall (COMCTL32.@)
1169  *
1170  * Installs the ComCtl32 DLL.
1171  *
1172  * RETURNS
1173  *     Success: S_OK
1174  *     Failure: A HRESULT error
1175  */
1176 HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
1177 {
1178     TRACE("(%u, %s): stub\n", bInstall, debugstr_w(cmdline));
1179     return S_OK;
1180 }
1181 
1182 /***********************************************************************
1183  * _TrackMouseEvent [COMCTL32.@]
1184  *
1185  * Requests notification of mouse events
1186  *
1187  * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
1188  * to the hwnd specified in the ptme structure.  After the event message
1189  * is posted to the hwnd, the entry in the queue is removed.
1190  *
1191  * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
1192  * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
1193  * immediately and the TME_LEAVE flag being ignored.
1194  *
1195  * PARAMS
1196  *     ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
1197  *
1198  * RETURNS
1199  *     Success: non-zero
1200  *     Failure: zero
1201  *
1202  * IMPLEMENTATION moved to USER32.TrackMouseEvent
1203  *
1204  */
1205 
1206 BOOL WINAPI
1207 _TrackMouseEvent (TRACKMOUSEEVENT *ptme)
1208 {
1209     return TrackMouseEvent (ptme);
1210 }
1211 
1212 /*************************************************************************
1213  * GetMUILanguage [COMCTL32.@]
1214  *
1215  * Returns the user interface language in use by the current process.
1216  *
1217  * RETURNS
1218  *      Language ID in use by the current process.
1219  */
1220 LANGID WINAPI GetMUILanguage (VOID)
1221 {
1222     return COMCTL32_uiLang;
1223 }
1224 
1225 
1226 /*************************************************************************
1227  * InitMUILanguage [COMCTL32.@]
1228  *
1229  * Sets the user interface language to be used by the current process.
1230  *
1231  * RETURNS
1232  *      Nothing.
1233  */
1234 VOID WINAPI InitMUILanguage (LANGID uiLang)
1235 {
1236    COMCTL32_uiLang = uiLang;
1237 }
1238 
1239 
1240 /***********************************************************************
1241  * SetWindowSubclass [COMCTL32.410]
1242  *
1243  * Starts a window subclass
1244  *
1245  * PARAMS
1246  *     hWnd [in] handle to window subclass.
1247  *     pfnSubclass [in] Pointer to new window procedure.
1248  *     uIDSubclass [in] Unique identifier of subclass together with pfnSubclass.
1249  *     dwRef [in] Reference data to pass to window procedure.
1250  *
1251  * RETURNS
1252  *     Success: non-zero
1253  *     Failure: zero
1254  *
1255  * BUGS
1256  *     If an application manually subclasses a window after subclassing it with
1257  *     this API and then with this API again, then none of the previous
1258  *     subclasses get called or the original window procedure.
1259  */
1260 
1261 BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
1262                         UINT_PTR uIDSubclass, DWORD_PTR dwRef)
1263 {
1264    LPSUBCLASS_INFO stack;
1265    LPSUBCLASSPROCS proc;
1266 
1267    TRACE ("(%p, %p, %lx, %lx)\n", hWnd, pfnSubclass, uIDSubclass, dwRef);
1268 
1269    /* Since the window procedure that we set here has two additional arguments,
1270     * we can't simply set it as the new window procedure of the window. So we
1271     * set our own window procedure and then calculate the other two arguments
1272     * from there. */
1273 
1274    /* See if we have been called for this window */
1275    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1276    if (!stack) {
1277       /* allocate stack */
1278       stack = Alloc (sizeof(SUBCLASS_INFO));
1279       if (!stack) {
1280          ERR ("Failed to allocate our Subclassing stack\n");
1281          return FALSE;
1282       }
1283       SetPropW (hWnd, COMCTL32_wSubclass, stack);
1284 
1285       /* set window procedure to our own and save the current one */
1286       if (IsWindowUnicode (hWnd))
1287          stack->origproc = (WNDPROC)SetWindowLongPtrW (hWnd, GWLP_WNDPROC,
1288                                                    (DWORD_PTR)COMCTL32_SubclassProc);
1289       else
1290          stack->origproc = (WNDPROC)SetWindowLongPtrA (hWnd, GWLP_WNDPROC,
1291                                                    (DWORD_PTR)COMCTL32_SubclassProc);
1292    }
1293    else {
1294       /* Check to see if we have called this function with the same uIDSubClass
1295        * and pfnSubclass */
1296       proc = stack->SubclassProcs;
1297       while (proc) {
1298          if ((proc->id == uIDSubclass) &&
1299             (proc->subproc == pfnSubclass)) {
1300             proc->ref = dwRef;
1301             return TRUE;
1302          }
1303          proc = proc->next;
1304       }
1305    }
1306 
1307    proc = Alloc(sizeof(SUBCLASSPROCS));
1308    if (!proc) {
1309       ERR ("Failed to allocate subclass entry in stack\n");
1310       if (IsWindowUnicode (hWnd))
1311          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1312       else
1313          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1314       Free (stack);
1315       RemovePropW( hWnd, COMCTL32_wSubclass );
1316       return FALSE;
1317    }
1318 
1319    proc->subproc = pfnSubclass;
1320    proc->ref = dwRef;
1321    proc->id = uIDSubclass;
1322    proc->next = stack->SubclassProcs;
1323    stack->SubclassProcs = proc;
1324 
1325    return TRUE;
1326 }
1327 
1328 
1329 /***********************************************************************
1330  * GetWindowSubclass [COMCTL32.411]
1331  *
1332  * Gets the Reference data from a subclass.
1333  *
1334  * PARAMS
1335  *     hWnd [in] Handle to the window which we are subclassing
1336  *     pfnSubclass [in] Pointer to the subclass procedure
1337  *     uID [in] Unique identifier of the subclassing procedure
1338  *     pdwRef [out] Pointer to the reference data
1339  *
1340  * RETURNS
1341  *     Success: Non-zero
1342  *     Failure: 0
1343  */
1344 
1345 BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
1346                               UINT_PTR uID, DWORD_PTR *pdwRef)
1347 {
1348    const SUBCLASS_INFO *stack;
1349    const SUBCLASSPROCS *proc;
1350 
1351    TRACE ("(%p, %p, %lx, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
1352 
1353    /* See if we have been called for this window */
1354    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1355    if (!stack)
1356       return FALSE;
1357 
1358    proc = stack->SubclassProcs;
1359    while (proc) {
1360       if ((proc->id == uID) &&
1361          (proc->subproc == pfnSubclass)) {
1362          *pdwRef = proc->ref;
1363          return TRUE;
1364       }
1365       proc = proc->next;
1366    }
1367 
1368    return FALSE;
1369 }
1370 
1371 
1372 /***********************************************************************
1373  * RemoveWindowSubclass [COMCTL32.412]
1374  *
1375  * Removes a window subclass.
1376  *
1377  * PARAMS
1378  *     hWnd [in] Handle to the window which we are subclassing
1379  *     pfnSubclass [in] Pointer to the subclass procedure
1380  *     uID [in] Unique identifier of this subclass
1381  *
1382  * RETURNS
1383  *     Success: non-zero
1384  *     Failure: zero
1385  */
1386 
1387 BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
1388 {
1389    LPSUBCLASS_INFO stack;
1390    LPSUBCLASSPROCS prevproc = NULL;
1391    LPSUBCLASSPROCS proc;
1392    BOOL ret = FALSE;
1393 
1394    TRACE ("(%p, %p, %lx)\n", hWnd, pfnSubclass, uID);
1395 
1396    /* Find the Subclass to remove */
1397    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1398    if (!stack)
1399       return FALSE;
1400 
1401    proc = stack->SubclassProcs;
1402    while (proc) {
1403       if ((proc->id == uID) &&
1404          (proc->subproc == pfnSubclass)) {
1405 
1406          if (!prevproc)
1407             stack->SubclassProcs = proc->next;
1408          else
1409             prevproc->next = proc->next;
1410 
1411          if (stack->stackpos == proc)
1412             stack->stackpos = stack->stackpos->next;
1413 
1414          Free (proc);
1415          ret = TRUE;
1416          break;
1417       }
1418       prevproc = proc;
1419       proc = proc->next;
1420    }
1421 
1422    if (!stack->SubclassProcs && !stack->running) {
1423       TRACE("Last Subclass removed, cleaning up\n");
1424       /* clean up our heap and reset the original window procedure */
1425       if (IsWindowUnicode (hWnd))
1426          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1427       else
1428          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1429       Free (stack);
1430       RemovePropW( hWnd, COMCTL32_wSubclass );
1431    }
1432 
1433    return ret;
1434 }
1435 
1436 /***********************************************************************
1437  * COMCTL32_SubclassProc (internal)
1438  *
1439  * Window procedure for all subclassed windows.
1440  * Saves the current subclassing stack position to support nested messages
1441  */
1442 static LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1443 {
1444    LPSUBCLASS_INFO stack;
1445    LPSUBCLASSPROCS proc;
1446    LRESULT ret;
1447 
1448    TRACE ("(%p, 0x%08x, 0x%08lx, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
1449 
1450    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1451    if (!stack) {
1452       ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
1453       return 0;
1454    }
1455 
1456    /* Save our old stackpos to properly handle nested messages */
1457    proc = stack->stackpos;
1458    stack->stackpos = stack->SubclassProcs;
1459    stack->running++;
1460    ret = DefSubclassProc(hWnd, uMsg, wParam, lParam);
1461    stack->running--;
1462    stack->stackpos = proc;
1463 
1464    if (!stack->SubclassProcs && !stack->running) {
1465       TRACE("Last Subclass removed, cleaning up\n");
1466       /* clean up our heap and reset the original window procedure */
1467       if (IsWindowUnicode (hWnd))
1468          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1469       else
1470          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1471       Free (stack);
1472       RemovePropW( hWnd, COMCTL32_wSubclass );
1473    }
1474    return ret;
1475 }
1476 
1477 /***********************************************************************
1478  * DefSubclassProc [COMCTL32.413]
1479  *
1480  * Calls the next window procedure (i.e. the one before this subclass)
1481  *
1482  * PARAMS
1483  *     hWnd [in] The window that we're subclassing
1484  *     uMsg [in] Message
1485  *     wParam [in] WPARAM
1486  *     lParam [in] LPARAM
1487  *
1488  * RETURNS
1489  *     Success: non-zero
1490  *     Failure: zero
1491  */
1492 
1493 LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1494 {
1495    LPSUBCLASS_INFO stack;
1496    LRESULT ret;
1497 
1498    TRACE ("(%p, 0x%08x, 0x%08lx, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
1499 
1500    /* retrieve our little stack from the Properties */
1501    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1502    if (!stack) {
1503       ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
1504       return 0;
1505    }
1506 
1507    /* If we are at the end of stack then we have to call the original
1508     * window procedure */
1509    if (!stack->stackpos) {
1510       if (IsWindowUnicode (hWnd))
1511          ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
1512       else
1513          ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
1514    } else {
1515       const SUBCLASSPROCS *proc = stack->stackpos;
1516       stack->stackpos = stack->stackpos->next;
1517       /* call the Subclass procedure from the stack */
1518       ret = proc->subproc (hWnd, uMsg, wParam, lParam,
1519             proc->id, proc->ref);
1520    }
1521 
1522    return ret;
1523 }
1524 
1525 
1526 /***********************************************************************
1527  * COMCTL32_CreateToolTip [NOT AN API]
1528  *
1529  * Creates a tooltip for the control specified in hwnd and does all
1530  * necessary setup and notifications.
1531  *
1532  * PARAMS
1533  *     hwndOwner [I] Handle to the window that will own the tool tip.
1534  *
1535  * RETURNS
1536  *     Success: Handle of tool tip window.
1537  *     Failure: NULL
1538  */
1539 
1540 HWND
1541 COMCTL32_CreateToolTip(HWND hwndOwner)
1542 {
1543     HWND hwndToolTip;
1544 
1545     hwndToolTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
1546 				  CW_USEDEFAULT, CW_USEDEFAULT,
1547 				  CW_USEDEFAULT, CW_USEDEFAULT, hwndOwner,
1548 				  0, 0, 0);
1549 
1550     /* Send NM_TOOLTIPSCREATED notification */
1551     if (hwndToolTip)
1552     {
1553 	NMTOOLTIPSCREATED nmttc;
1554         /* true owner can be different if hwndOwner is a child window */
1555         HWND hwndTrueOwner = GetWindow(hwndToolTip, GW_OWNER);
1556         nmttc.hdr.hwndFrom = hwndTrueOwner;
1557         nmttc.hdr.idFrom = GetWindowLongPtrW(hwndTrueOwner, GWLP_ID);
1558 	nmttc.hdr.code = NM_TOOLTIPSCREATED;
1559 	nmttc.hwndToolTips = hwndToolTip;
1560 
1561         SendMessageW(GetParent(hwndTrueOwner), WM_NOTIFY,
1562                      GetWindowLongPtrW(hwndTrueOwner, GWLP_ID), (LPARAM)&nmttc);
1563     }
1564 
1565     return hwndToolTip;
1566 }
1567 
1568 
1569 /***********************************************************************
1570  * COMCTL32_RefreshSysColors [NOT AN API]
1571  *
1572  * Invoked on any control recognizing a WM_SYSCOLORCHANGE message to
1573  * refresh the color values in the color structure
1574  *
1575  * PARAMS
1576  *     none
1577  *
1578  * RETURNS
1579  *     none
1580  */
1581 
1582 VOID
1583 COMCTL32_RefreshSysColors(void)
1584 {
1585     comctl32_color.clrBtnHighlight = GetSysColor (COLOR_BTNHIGHLIGHT);
1586     comctl32_color.clrBtnShadow = GetSysColor (COLOR_BTNSHADOW);
1587     comctl32_color.clrBtnText = GetSysColor (COLOR_BTNTEXT);
1588     comctl32_color.clrBtnFace = GetSysColor (COLOR_BTNFACE);
1589     comctl32_color.clrHighlight = GetSysColor (COLOR_HIGHLIGHT);
1590     comctl32_color.clrHighlightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
1591     comctl32_color.clrHotTrackingColor = GetSysColor (COLOR_HOTLIGHT);
1592     comctl32_color.clr3dHilight = GetSysColor (COLOR_3DHILIGHT);
1593     comctl32_color.clr3dShadow = GetSysColor (COLOR_3DSHADOW);
1594     comctl32_color.clr3dDkShadow = GetSysColor (COLOR_3DDKSHADOW);
1595     comctl32_color.clr3dFace = GetSysColor (COLOR_3DFACE);
1596     comctl32_color.clrWindow = GetSysColor (COLOR_WINDOW);
1597     comctl32_color.clrWindowText = GetSysColor (COLOR_WINDOWTEXT);
1598     comctl32_color.clrGrayText = GetSysColor (COLOR_GRAYTEXT);
1599     comctl32_color.clrActiveCaption = GetSysColor (COLOR_ACTIVECAPTION);
1600     comctl32_color.clrInfoBk = GetSysColor (COLOR_INFOBK);
1601     comctl32_color.clrInfoText = GetSysColor (COLOR_INFOTEXT);
1602 }
1603 
1604 /***********************************************************************
1605  * COMCTL32_DrawInsertMark [NOT AN API]
1606  *
1607  * Draws an insertion mark (which looks similar to an 'I').
1608  *
1609  * PARAMS
1610  *     hDC           [I] Device context to draw onto.
1611  *     lpRect        [I] Co-ordinates of insertion mark.
1612  *     clrInsertMark [I] Colour of the insertion mark.
1613  *     bHorizontal   [I] True if insert mark should be drawn horizontally,
1614  *                       vertical otherwise.
1615  *
1616  * RETURNS
1617  *     none
1618  *
1619  * NOTES
1620  *     Draws up to but not including the bottom co-ordinate when drawing
1621  *     vertically or the right co-ordinate when horizontal.
1622  */
1623 void COMCTL32_DrawInsertMark(HDC hDC, const RECT *lpRect, COLORREF clrInsertMark, BOOL bHorizontal)
1624 {
1625     HPEN hPen = CreatePen(PS_SOLID, 1, clrInsertMark);
1626     HPEN hOldPen;
1627     static const DWORD adwPolyPoints[] = {4,4,4};
1628     LONG lCentre = (bHorizontal ?
1629         lpRect->top + (lpRect->bottom - lpRect->top)/2 :
1630         lpRect->left + (lpRect->right - lpRect->left)/2);
1631     LONG l1 = (bHorizontal ? lpRect->left : lpRect->top);
1632     LONG l2 = (bHorizontal ? lpRect->right : lpRect->bottom);
1633     const POINT aptInsertMark[] =
1634     {
1635         /* top (V) or left (H) arrow */
1636         {lCentre    , l1 + 2},
1637         {lCentre - 2, l1    },
1638         {lCentre + 3, l1    },
1639         {lCentre + 1, l1 + 2},
1640         /* middle line */
1641         {lCentre    , l2 - 2},
1642         {lCentre    , l1 - 1},
1643         {lCentre + 1, l1 - 1},
1644         {lCentre + 1, l2 - 2},
1645         /* bottom (V) or right (H) arrow */
1646         {lCentre    , l2 - 3},
1647         {lCentre - 2, l2 - 1},
1648         {lCentre + 3, l2 - 1},
1649         {lCentre + 1, l2 - 3},
1650     };
1651     hOldPen = SelectObject(hDC, hPen);
1652     PolyPolyline(hDC, aptInsertMark, adwPolyPoints, ARRAY_SIZE(adwPolyPoints));
1653     SelectObject(hDC, hOldPen);
1654     DeleteObject(hPen);
1655 }
1656 
1657 /***********************************************************************
1658  * COMCTL32_EnsureBitmapSize [internal]
1659  *
1660  * If needed, enlarge the bitmap so that the width is at least cxMinWidth and
1661  * the height is at least cyMinHeight. If the bitmap already has these
1662  * dimensions nothing changes.
1663  *
1664  * PARAMS
1665  *     hBitmap       [I/O] Bitmap to modify. The handle may change
1666  *     cxMinWidth    [I]   If the width of the bitmap is smaller, then it will
1667  *                         be enlarged to this value
1668  *     cyMinHeight   [I]   If the height of the bitmap is smaller, then it will
1669  *                         be enlarged to this value
1670  *     cyBackground  [I]   The color with which the new area will be filled
1671  *
1672  * RETURNS
1673  *     none
1674  */
1675 void COMCTL32_EnsureBitmapSize(HBITMAP *pBitmap, int cxMinWidth, int cyMinHeight, COLORREF crBackground)
1676 {
1677     int cxNew, cyNew;
1678     BITMAP bmp;
1679     HBITMAP hNewBitmap;
1680     HBITMAP hNewDCBitmap, hOldDCBitmap;
1681     HBRUSH hNewDCBrush;
1682     HDC hdcNew, hdcOld;
1683 
1684     if (!GetObjectW(*pBitmap, sizeof(BITMAP), &bmp))
1685         return;
1686     cxNew = (cxMinWidth > bmp.bmWidth ? cxMinWidth : bmp.bmWidth);
1687     cyNew = (cyMinHeight > bmp.bmHeight ? cyMinHeight : bmp.bmHeight);
1688     if (cxNew == bmp.bmWidth && cyNew == bmp.bmHeight)
1689         return;
1690 
1691     hdcNew = CreateCompatibleDC(NULL);
1692     hNewBitmap = CreateBitmap(cxNew, cyNew, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
1693     hNewDCBitmap = SelectObject(hdcNew, hNewBitmap);
1694     hNewDCBrush = SelectObject(hdcNew, CreateSolidBrush(crBackground));
1695 
1696     hdcOld = CreateCompatibleDC(NULL);
1697     hOldDCBitmap = SelectObject(hdcOld, *pBitmap);
1698 
1699     BitBlt(hdcNew, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcOld, 0, 0, SRCCOPY);
1700     if (bmp.bmWidth < cxMinWidth)
1701         PatBlt(hdcNew, bmp.bmWidth, 0, cxNew, bmp.bmHeight, PATCOPY);
1702     if (bmp.bmHeight < cyMinHeight)
1703         PatBlt(hdcNew, 0, bmp.bmHeight, bmp.bmWidth, cyNew, PATCOPY);
1704     if (bmp.bmWidth < cxMinWidth && bmp.bmHeight < cyMinHeight)
1705         PatBlt(hdcNew, bmp.bmWidth, bmp.bmHeight, cxNew, cyNew, PATCOPY);
1706 
1707     SelectObject(hdcNew, hNewDCBitmap);
1708     DeleteObject(SelectObject(hdcNew, hNewDCBrush));
1709     DeleteDC(hdcNew);
1710     SelectObject(hdcOld, hOldDCBitmap);
1711     DeleteDC(hdcOld);
1712 
1713     DeleteObject(*pBitmap);
1714     *pBitmap = hNewBitmap;
1715     return;
1716 }
1717 
1718 void COMCTL32_GetFontMetrics(HFONT hFont, TEXTMETRICW *ptm)
1719 {
1720     HDC hdc = GetDC(NULL);
1721     HFONT hOldFont;
1722 
1723     hOldFont = SelectObject(hdc, hFont);
1724     GetTextMetricsW(hdc, ptm);
1725     SelectObject(hdc, hOldFont);
1726     ReleaseDC(NULL, hdc);
1727 }
1728 
1729 #ifndef OCM__BASE      /* avoid including olectl.h */
1730 #define OCM__BASE (WM_USER+0x1c00)
1731 #endif
1732 
1733 /***********************************************************************
1734  * COMCTL32_IsReflectedMessage [internal]
1735  *
1736  * Some parents reflect notify messages - for some messages sent by the child,
1737  * they send it back with the message code increased by OCM__BASE (0x2000).
1738  * This allows better subclassing of controls. We don't need to handle such
1739  * messages but we don't want to print ERRs for them, so this helper function
1740  * identifies them.
1741  *
1742  * Some of the codes are in the CCM_FIRST..CCM_LAST range, but there is no
1743  * collision with defined CCM_ codes.
1744  */
1745 BOOL COMCTL32_IsReflectedMessage(UINT uMsg)
1746 {
1747     switch (uMsg)
1748     {
1749         case OCM__BASE + WM_COMMAND:
1750         case OCM__BASE + WM_CTLCOLORBTN:
1751         case OCM__BASE + WM_CTLCOLOREDIT:
1752         case OCM__BASE + WM_CTLCOLORDLG:
1753         case OCM__BASE + WM_CTLCOLORLISTBOX:
1754         case OCM__BASE + WM_CTLCOLORMSGBOX:
1755         case OCM__BASE + WM_CTLCOLORSCROLLBAR:
1756         case OCM__BASE + WM_CTLCOLORSTATIC:
1757         case OCM__BASE + WM_DRAWITEM:
1758         case OCM__BASE + WM_MEASUREITEM:
1759         case OCM__BASE + WM_DELETEITEM:
1760         case OCM__BASE + WM_VKEYTOITEM:
1761         case OCM__BASE + WM_CHARTOITEM:
1762         case OCM__BASE + WM_COMPAREITEM:
1763         case OCM__BASE + WM_HSCROLL:
1764         case OCM__BASE + WM_VSCROLL:
1765         case OCM__BASE + WM_PARENTNOTIFY:
1766         case OCM__BASE + WM_NOTIFY:
1767             return TRUE;
1768         default:
1769             return FALSE;
1770     }
1771 }
1772 
1773 /***********************************************************************
1774  * MirrorIcon [COMCTL32.414]
1775  *
1776  * Mirrors an icon so that it will appear correctly on a mirrored DC.
1777  *
1778  * PARAMS
1779  *     phicon1 [I/O] Icon.
1780  *     phicon2 [I/O] Icon.
1781  *
1782  * RETURNS
1783  *     Success: TRUE.
1784  *     Failure: FALSE.
1785  */
1786 BOOL WINAPI MirrorIcon(HICON *phicon1, HICON *phicon2)
1787 {
1788     FIXME("(%p, %p): stub\n", phicon1, phicon2);
1789     return FALSE;
1790 }
1791 
1792 static inline BOOL IsDelimiter(WCHAR c)
1793 {
1794     switch(c)
1795     {
1796 	case '/':
1797 	case '\\':
1798 	case '.':
1799 	case ' ':
1800 	    return TRUE;
1801     }
1802     return FALSE;
1803 }
1804 
1805 static int CALLBACK PathWordBreakProc(LPCWSTR lpch, int ichCurrent, int cch, int code)
1806 {
1807     if (code == WB_ISDELIMITER)
1808         return IsDelimiter(lpch[ichCurrent]);
1809     else
1810     {
1811         int dir = (code == WB_LEFT) ? -1 : 1;
1812         for(; 0 <= ichCurrent && ichCurrent < cch; ichCurrent += dir)
1813             if (IsDelimiter(lpch[ichCurrent])) return ichCurrent;
1814     }
1815     return ichCurrent;
1816 }
1817 
1818 /***********************************************************************
1819  * SetPathWordBreakProc [COMCTL32.384]
1820  *
1821  * Sets the word break procedure for an edit control to one that understands
1822  * paths so that the user can jump over directories.
1823  *
1824  * PARAMS
1825  *     hwnd [I] Handle to edit control.
1826  *     bSet [I] If this is TRUE then the word break proc is set, otherwise it is removed.
1827  *
1828  * RETURNS
1829  *     Result from EM_SETWORDBREAKPROC message.
1830  */
1831 LRESULT WINAPI SetPathWordBreakProc(HWND hwnd, BOOL bSet)
1832 {
1833     return SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0,
1834         (LPARAM)(bSet ? PathWordBreakProc : NULL));
1835 }
1836 
1837 /***********************************************************************
1838  * DrawShadowText [COMCTL32.@]
1839  *
1840  * Draw text with shadow.
1841  */
1842 int WINAPI DrawShadowText(HDC hdc, LPCWSTR pszText, UINT cch, RECT *prc, DWORD dwFlags,
1843                           COLORREF crText, COLORREF crShadow, int ixOffset, int iyOffset)
1844 {
1845     COLORREF crOldText;
1846     RECT rcText;
1847     INT iRet, x, y, x2, y2;
1848     BYTE *pBits;
1849     HBITMAP hbm, hbmOld;
1850     BITMAPINFO bi;
1851     HDC hdcMem;
1852     HFONT hOldFont;
1853     BLENDFUNCTION bf;
1854 
1855     /* Create 32 bit DIB section for the shadow */
1856     ZeroMemory(&bi, sizeof(bi));
1857     bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
1858     bi.bmiHeader.biWidth = prc->right - prc->left + 4;
1859     bi.bmiHeader.biHeight = prc->bottom - prc->top + 5; // bottom-up DIB
1860     bi.bmiHeader.biPlanes = 1;
1861     bi.bmiHeader.biBitCount = 32;
1862     bi.bmiHeader.biCompression = BI_RGB;
1863     hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (PVOID*)&pBits, NULL, 0);
1864     if(!hbm)
1865     {
1866         ERR("CreateDIBSection failed\n");
1867         return 0;
1868     }
1869 
1870     /* Create memory device context for new DIB section and select it */
1871     hdcMem = CreateCompatibleDC(hdc);
1872     if(!hdcMem)
1873     {
1874         ERR("CreateCompatibleDC failed\n");
1875         DeleteObject(hbm);
1876         return 0;
1877     }
1878 
1879     hbmOld = (HBITMAP)SelectObject(hdcMem, hbm);
1880 
1881     /* Draw text on our helper bitmap */
1882     hOldFont = (HFONT)SelectObject(hdcMem, GetCurrentObject(hdc, OBJ_FONT));
1883     SetTextColor(hdcMem, RGB(16, 16, 16));
1884     SetBkColor(hdcMem, RGB(0, 0, 0));
1885     SetBkMode(hdcMem, TRANSPARENT);
1886     SetRect(&rcText, 0, 0, prc->right - prc->left, prc->bottom - prc->top);
1887     DrawTextW(hdcMem, pszText, cch, &rcText, dwFlags);
1888     SelectObject(hdcMem, hOldFont);
1889 
1890     /* Flush GDI so data pointed by pBits is valid */
1891     GdiFlush();
1892 
1893     /* Set alpha of pixels (forget about colors for now. They will be changed in next loop).
1894        We copy text image 4*5 times and each time alpha is added */
1895     for (x = 0; x < bi.bmiHeader.biWidth; ++x)
1896         for (y = 0; y < bi.bmiHeader.biHeight; ++y)
1897         {
1898             BYTE *pDest = &pBits[(y * bi.bmiHeader.biWidth + x) * 4];
1899             UINT Alpha = 0;
1900 
1901             for (x2 = x - 4 + 1; x2 <= x; ++x2)
1902                 for (y2 = y; y2 < y + 5; ++y2)
1903                 {
1904                     if (x2 >= 0 && x2 < bi.bmiHeader.biWidth && y2 >= 0 && y2 < bi.bmiHeader.biHeight)
1905                     {
1906                         BYTE *pSrc = &pBits[(y2 * bi.bmiHeader.biWidth + x2) * 4];
1907                         Alpha += pSrc[0];
1908                     }
1909                 }
1910 
1911             if (Alpha > 255)
1912                 Alpha = 255;
1913             pDest[3] = Alpha;
1914         }
1915 
1916     /* Now set the color of each pixel to shadow color * alpha (see GdiAlphaBlend) */
1917     for (x = 0; x < bi.bmiHeader.biWidth; ++x)
1918         for (y = 0; y < bi.bmiHeader.biHeight; ++y)
1919         {
1920             BYTE *pDest = &pBits[(y * bi.bmiHeader.biWidth + x) * 4];
1921             pDest[0] = GetBValue(crShadow) * pDest[3] / 255;
1922             pDest[1] = GetGValue(crShadow) * pDest[3] / 255;
1923             pDest[2] = GetRValue(crShadow) * pDest[3] / 255;
1924         }
1925 
1926     /* Fix ixOffset of the shadow (tested on Win) */
1927     ixOffset -= 3;
1928     iyOffset -= 3;
1929 
1930     /* Alpha blend helper image to destination DC */
1931     bf.BlendOp = AC_SRC_OVER;
1932     bf.BlendFlags = 0;
1933     bf.SourceConstantAlpha = 255;
1934     bf.AlphaFormat = AC_SRC_ALPHA;
1935     GdiAlphaBlend(hdc, prc->left + ixOffset, prc->top + iyOffset, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, hdcMem, 0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, bf);
1936 
1937     /* Delete the helper bitmap */
1938     SelectObject(hdcMem, hbmOld);
1939     DeleteObject(hbm);
1940     DeleteDC(hdcMem);
1941 
1942     /* Finally draw the text over shadow */
1943     crOldText = SetTextColor(hdc, crText);
1944     SetBkMode(hdc, TRANSPARENT);
1945     iRet = DrawTextW(hdc, pszText, cch, prc, dwFlags);
1946     SetTextColor(hdc, crOldText);
1947 
1948     return iRet;
1949 }
1950 
1951 /***********************************************************************
1952  * LoadIconWithScaleDown [COMCTL32.@]
1953  */
1954 HRESULT WINAPI LoadIconWithScaleDown(HINSTANCE hinst, const WCHAR *name, int cx, int cy, HICON *icon)
1955 {
1956     TRACE("(%p, %s, %d, %d, %p)\n", hinst, debugstr_w(name), cx, cy, icon);
1957 
1958     *icon = NULL;
1959 
1960     if (!name)
1961         return E_INVALIDARG;
1962 
1963     *icon = LoadImageW(hinst, name, IMAGE_ICON, cx, cy,
1964                        (hinst || IS_INTRESOURCE(name)) ? 0 : LR_LOADFROMFILE);
1965     if (!*icon)
1966         return HRESULT_FROM_WIN32(GetLastError());
1967 
1968     return S_OK;
1969 }
1970 
1971 /***********************************************************************
1972  * LoadIconMetric [COMCTL32.@]
1973  */
1974 HRESULT WINAPI LoadIconMetric(HINSTANCE hinst, const WCHAR *name, int size, HICON *icon)
1975 {
1976     int cx, cy;
1977 
1978     TRACE("(%p, %s, %d, %p)\n", hinst, debugstr_w(name), size, icon);
1979 
1980     if (size == LIM_SMALL)
1981     {
1982         cx = GetSystemMetrics(SM_CXSMICON);
1983         cy = GetSystemMetrics(SM_CYSMICON);
1984     }
1985     else if (size == LIM_LARGE)
1986     {
1987         cx = GetSystemMetrics(SM_CXICON);
1988         cy = GetSystemMetrics(SM_CYICON);
1989     }
1990     else
1991     {
1992         *icon = NULL;
1993         return E_INVALIDARG;
1994     }
1995 
1996     return LoadIconWithScaleDown(hinst, name, cx, cy, icon);
1997 }
1998