xref: /reactos/dll/win32/comctl32/commctrl.c (revision 58588b76)
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     if (!hWnd || !pfnSubclass)
1270         return FALSE;
1271 
1272    /* Since the window procedure that we set here has two additional arguments,
1273     * we can't simply set it as the new window procedure of the window. So we
1274     * set our own window procedure and then calculate the other two arguments
1275     * from there. */
1276 
1277    /* See if we have been called for this window */
1278    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1279    if (!stack) {
1280       /* allocate stack */
1281       stack = Alloc (sizeof(SUBCLASS_INFO));
1282       if (!stack) {
1283          ERR ("Failed to allocate our Subclassing stack\n");
1284          return FALSE;
1285       }
1286       SetPropW (hWnd, COMCTL32_wSubclass, stack);
1287 
1288       /* set window procedure to our own and save the current one */
1289       if (IsWindowUnicode (hWnd))
1290          stack->origproc = (WNDPROC)SetWindowLongPtrW (hWnd, GWLP_WNDPROC,
1291                                                    (DWORD_PTR)COMCTL32_SubclassProc);
1292       else
1293          stack->origproc = (WNDPROC)SetWindowLongPtrA (hWnd, GWLP_WNDPROC,
1294                                                    (DWORD_PTR)COMCTL32_SubclassProc);
1295    }
1296    else {
1297       /* Check to see if we have called this function with the same uIDSubClass
1298        * and pfnSubclass */
1299       proc = stack->SubclassProcs;
1300       while (proc) {
1301          if ((proc->id == uIDSubclass) &&
1302             (proc->subproc == pfnSubclass)) {
1303             proc->ref = dwRef;
1304             return TRUE;
1305          }
1306          proc = proc->next;
1307       }
1308    }
1309 
1310    proc = Alloc(sizeof(SUBCLASSPROCS));
1311    if (!proc) {
1312       ERR ("Failed to allocate subclass entry in stack\n");
1313       if (IsWindowUnicode (hWnd))
1314          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1315       else
1316          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1317       Free (stack);
1318       RemovePropW( hWnd, COMCTL32_wSubclass );
1319       return FALSE;
1320    }
1321 
1322    proc->subproc = pfnSubclass;
1323    proc->ref = dwRef;
1324    proc->id = uIDSubclass;
1325    proc->next = stack->SubclassProcs;
1326    stack->SubclassProcs = proc;
1327 
1328    return TRUE;
1329 }
1330 
1331 
1332 /***********************************************************************
1333  * GetWindowSubclass [COMCTL32.411]
1334  *
1335  * Gets the Reference data from a subclass.
1336  *
1337  * PARAMS
1338  *     hWnd [in] Handle to the window which we are subclassing
1339  *     pfnSubclass [in] Pointer to the subclass procedure
1340  *     uID [in] Unique identifier of the subclassing procedure
1341  *     pdwRef [out] Pointer to the reference data
1342  *
1343  * RETURNS
1344  *     Success: Non-zero
1345  *     Failure: 0
1346  */
1347 
1348 BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
1349                               UINT_PTR uID, DWORD_PTR *pdwRef)
1350 {
1351    const SUBCLASS_INFO *stack;
1352    const SUBCLASSPROCS *proc;
1353 
1354    TRACE ("(%p, %p, %lx, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
1355 
1356    /* See if we have been called for this window */
1357    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1358    if (!stack)
1359       return FALSE;
1360 
1361    proc = stack->SubclassProcs;
1362    while (proc) {
1363       if ((proc->id == uID) &&
1364          (proc->subproc == pfnSubclass)) {
1365          *pdwRef = proc->ref;
1366          return TRUE;
1367       }
1368       proc = proc->next;
1369    }
1370 
1371    return FALSE;
1372 }
1373 
1374 
1375 /***********************************************************************
1376  * RemoveWindowSubclass [COMCTL32.412]
1377  *
1378  * Removes a window subclass.
1379  *
1380  * PARAMS
1381  *     hWnd [in] Handle to the window which we are subclassing
1382  *     pfnSubclass [in] Pointer to the subclass procedure
1383  *     uID [in] Unique identifier of this subclass
1384  *
1385  * RETURNS
1386  *     Success: non-zero
1387  *     Failure: zero
1388  */
1389 
1390 BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
1391 {
1392    LPSUBCLASS_INFO stack;
1393    LPSUBCLASSPROCS prevproc = NULL;
1394    LPSUBCLASSPROCS proc;
1395    BOOL ret = FALSE;
1396 
1397    TRACE ("(%p, %p, %lx)\n", hWnd, pfnSubclass, uID);
1398 
1399    /* Find the Subclass to remove */
1400    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1401    if (!stack)
1402       return FALSE;
1403 
1404    proc = stack->SubclassProcs;
1405    while (proc) {
1406       if ((proc->id == uID) &&
1407          (proc->subproc == pfnSubclass)) {
1408 
1409          if (!prevproc)
1410             stack->SubclassProcs = proc->next;
1411          else
1412             prevproc->next = proc->next;
1413 
1414          if (stack->stackpos == proc)
1415             stack->stackpos = stack->stackpos->next;
1416 
1417          Free (proc);
1418          ret = TRUE;
1419          break;
1420       }
1421       prevproc = proc;
1422       proc = proc->next;
1423    }
1424 
1425    if (!stack->SubclassProcs && !stack->running) {
1426       TRACE("Last Subclass removed, cleaning up\n");
1427       /* clean up our heap and reset the original window procedure */
1428       if (IsWindowUnicode (hWnd))
1429          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1430       else
1431          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1432       Free (stack);
1433       RemovePropW( hWnd, COMCTL32_wSubclass );
1434    }
1435 
1436    return ret;
1437 }
1438 
1439 /***********************************************************************
1440  * COMCTL32_SubclassProc (internal)
1441  *
1442  * Window procedure for all subclassed windows.
1443  * Saves the current subclassing stack position to support nested messages
1444  */
1445 static LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1446 {
1447    LPSUBCLASS_INFO stack;
1448    LPSUBCLASSPROCS proc;
1449    LRESULT ret;
1450 
1451    TRACE ("(%p, 0x%08x, 0x%08lx, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
1452 
1453    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1454    if (!stack) {
1455       ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
1456       return 0;
1457    }
1458 
1459    /* Save our old stackpos to properly handle nested messages */
1460    proc = stack->stackpos;
1461    stack->stackpos = stack->SubclassProcs;
1462    stack->running++;
1463    ret = DefSubclassProc(hWnd, uMsg, wParam, lParam);
1464    stack->running--;
1465    stack->stackpos = proc;
1466 
1467    if (!stack->SubclassProcs && !stack->running) {
1468       TRACE("Last Subclass removed, cleaning up\n");
1469       /* clean up our heap and reset the original window procedure */
1470       if (IsWindowUnicode (hWnd))
1471          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1472       else
1473          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1474       Free (stack);
1475       RemovePropW( hWnd, COMCTL32_wSubclass );
1476    }
1477    return ret;
1478 }
1479 
1480 /***********************************************************************
1481  * DefSubclassProc [COMCTL32.413]
1482  *
1483  * Calls the next window procedure (i.e. the one before this subclass)
1484  *
1485  * PARAMS
1486  *     hWnd [in] The window that we're subclassing
1487  *     uMsg [in] Message
1488  *     wParam [in] WPARAM
1489  *     lParam [in] LPARAM
1490  *
1491  * RETURNS
1492  *     Success: non-zero
1493  *     Failure: zero
1494  */
1495 
1496 LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1497 {
1498    LPSUBCLASS_INFO stack;
1499    LRESULT ret;
1500 
1501    TRACE ("(%p, 0x%08x, 0x%08lx, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
1502 
1503    /* retrieve our little stack from the Properties */
1504    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1505    if (!stack) {
1506       ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
1507       return 0;
1508    }
1509 
1510    /* If we are at the end of stack then we have to call the original
1511     * window procedure */
1512    if (!stack->stackpos) {
1513       if (IsWindowUnicode (hWnd))
1514          ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
1515       else
1516          ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
1517    } else {
1518       const SUBCLASSPROCS *proc = stack->stackpos;
1519       stack->stackpos = stack->stackpos->next;
1520       /* call the Subclass procedure from the stack */
1521       ret = proc->subproc (hWnd, uMsg, wParam, lParam,
1522             proc->id, proc->ref);
1523    }
1524 
1525    return ret;
1526 }
1527 
1528 
1529 /***********************************************************************
1530  * COMCTL32_CreateToolTip [NOT AN API]
1531  *
1532  * Creates a tooltip for the control specified in hwnd and does all
1533  * necessary setup and notifications.
1534  *
1535  * PARAMS
1536  *     hwndOwner [I] Handle to the window that will own the tool tip.
1537  *
1538  * RETURNS
1539  *     Success: Handle of tool tip window.
1540  *     Failure: NULL
1541  */
1542 
1543 HWND
1544 COMCTL32_CreateToolTip(HWND hwndOwner)
1545 {
1546     HWND hwndToolTip;
1547 
1548     hwndToolTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
1549 				  CW_USEDEFAULT, CW_USEDEFAULT,
1550 				  CW_USEDEFAULT, CW_USEDEFAULT, hwndOwner,
1551 				  0, 0, 0);
1552 
1553     /* Send NM_TOOLTIPSCREATED notification */
1554     if (hwndToolTip)
1555     {
1556 	NMTOOLTIPSCREATED nmttc;
1557         /* true owner can be different if hwndOwner is a child window */
1558         HWND hwndTrueOwner = GetWindow(hwndToolTip, GW_OWNER);
1559         nmttc.hdr.hwndFrom = hwndTrueOwner;
1560         nmttc.hdr.idFrom = GetWindowLongPtrW(hwndTrueOwner, GWLP_ID);
1561 	nmttc.hdr.code = NM_TOOLTIPSCREATED;
1562 	nmttc.hwndToolTips = hwndToolTip;
1563 
1564         SendMessageW(GetParent(hwndTrueOwner), WM_NOTIFY,
1565                      GetWindowLongPtrW(hwndTrueOwner, GWLP_ID), (LPARAM)&nmttc);
1566     }
1567 
1568     return hwndToolTip;
1569 }
1570 
1571 
1572 /***********************************************************************
1573  * COMCTL32_RefreshSysColors [NOT AN API]
1574  *
1575  * Invoked on any control recognizing a WM_SYSCOLORCHANGE message to
1576  * refresh the color values in the color structure
1577  *
1578  * PARAMS
1579  *     none
1580  *
1581  * RETURNS
1582  *     none
1583  */
1584 
1585 VOID
1586 COMCTL32_RefreshSysColors(void)
1587 {
1588     comctl32_color.clrBtnHighlight = GetSysColor (COLOR_BTNHIGHLIGHT);
1589     comctl32_color.clrBtnShadow = GetSysColor (COLOR_BTNSHADOW);
1590     comctl32_color.clrBtnText = GetSysColor (COLOR_BTNTEXT);
1591     comctl32_color.clrBtnFace = GetSysColor (COLOR_BTNFACE);
1592     comctl32_color.clrHighlight = GetSysColor (COLOR_HIGHLIGHT);
1593     comctl32_color.clrHighlightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
1594     comctl32_color.clrHotTrackingColor = GetSysColor (COLOR_HOTLIGHT);
1595     comctl32_color.clr3dHilight = GetSysColor (COLOR_3DHILIGHT);
1596     comctl32_color.clr3dShadow = GetSysColor (COLOR_3DSHADOW);
1597     comctl32_color.clr3dDkShadow = GetSysColor (COLOR_3DDKSHADOW);
1598     comctl32_color.clr3dFace = GetSysColor (COLOR_3DFACE);
1599     comctl32_color.clrWindow = GetSysColor (COLOR_WINDOW);
1600     comctl32_color.clrWindowText = GetSysColor (COLOR_WINDOWTEXT);
1601     comctl32_color.clrGrayText = GetSysColor (COLOR_GRAYTEXT);
1602     comctl32_color.clrActiveCaption = GetSysColor (COLOR_ACTIVECAPTION);
1603     comctl32_color.clrInfoBk = GetSysColor (COLOR_INFOBK);
1604     comctl32_color.clrInfoText = GetSysColor (COLOR_INFOTEXT);
1605 }
1606 
1607 /***********************************************************************
1608  * COMCTL32_DrawInsertMark [NOT AN API]
1609  *
1610  * Draws an insertion mark (which looks similar to an 'I').
1611  *
1612  * PARAMS
1613  *     hDC           [I] Device context to draw onto.
1614  *     lpRect        [I] Co-ordinates of insertion mark.
1615  *     clrInsertMark [I] Colour of the insertion mark.
1616  *     bHorizontal   [I] True if insert mark should be drawn horizontally,
1617  *                       vertical otherwise.
1618  *
1619  * RETURNS
1620  *     none
1621  *
1622  * NOTES
1623  *     Draws up to but not including the bottom co-ordinate when drawing
1624  *     vertically or the right co-ordinate when horizontal.
1625  */
1626 void COMCTL32_DrawInsertMark(HDC hDC, const RECT *lpRect, COLORREF clrInsertMark, BOOL bHorizontal)
1627 {
1628     HPEN hPen = CreatePen(PS_SOLID, 1, clrInsertMark);
1629     HPEN hOldPen;
1630     static const DWORD adwPolyPoints[] = {4,4,4};
1631     LONG lCentre = (bHorizontal ?
1632         lpRect->top + (lpRect->bottom - lpRect->top)/2 :
1633         lpRect->left + (lpRect->right - lpRect->left)/2);
1634     LONG l1 = (bHorizontal ? lpRect->left : lpRect->top);
1635     LONG l2 = (bHorizontal ? lpRect->right : lpRect->bottom);
1636     const POINT aptInsertMark[] =
1637     {
1638         /* top (V) or left (H) arrow */
1639         {lCentre    , l1 + 2},
1640         {lCentre - 2, l1    },
1641         {lCentre + 3, l1    },
1642         {lCentre + 1, l1 + 2},
1643         /* middle line */
1644         {lCentre    , l2 - 2},
1645         {lCentre    , l1 - 1},
1646         {lCentre + 1, l1 - 1},
1647         {lCentre + 1, l2 - 2},
1648         /* bottom (V) or right (H) arrow */
1649         {lCentre    , l2 - 3},
1650         {lCentre - 2, l2 - 1},
1651         {lCentre + 3, l2 - 1},
1652         {lCentre + 1, l2 - 3},
1653     };
1654     hOldPen = SelectObject(hDC, hPen);
1655     PolyPolyline(hDC, aptInsertMark, adwPolyPoints, ARRAY_SIZE(adwPolyPoints));
1656     SelectObject(hDC, hOldPen);
1657     DeleteObject(hPen);
1658 }
1659 
1660 /***********************************************************************
1661  * COMCTL32_EnsureBitmapSize [internal]
1662  *
1663  * If needed, enlarge the bitmap so that the width is at least cxMinWidth and
1664  * the height is at least cyMinHeight. If the bitmap already has these
1665  * dimensions nothing changes.
1666  *
1667  * PARAMS
1668  *     hBitmap       [I/O] Bitmap to modify. The handle may change
1669  *     cxMinWidth    [I]   If the width of the bitmap is smaller, then it will
1670  *                         be enlarged to this value
1671  *     cyMinHeight   [I]   If the height of the bitmap is smaller, then it will
1672  *                         be enlarged to this value
1673  *     cyBackground  [I]   The color with which the new area will be filled
1674  *
1675  * RETURNS
1676  *     none
1677  */
1678 void COMCTL32_EnsureBitmapSize(HBITMAP *pBitmap, int cxMinWidth, int cyMinHeight, COLORREF crBackground)
1679 {
1680     int cxNew, cyNew;
1681     BITMAP bmp;
1682     HBITMAP hNewBitmap;
1683     HBITMAP hNewDCBitmap, hOldDCBitmap;
1684     HBRUSH hNewDCBrush;
1685     HDC hdcNew, hdcOld;
1686 
1687     if (!GetObjectW(*pBitmap, sizeof(BITMAP), &bmp))
1688         return;
1689     cxNew = (cxMinWidth > bmp.bmWidth ? cxMinWidth : bmp.bmWidth);
1690     cyNew = (cyMinHeight > bmp.bmHeight ? cyMinHeight : bmp.bmHeight);
1691     if (cxNew == bmp.bmWidth && cyNew == bmp.bmHeight)
1692         return;
1693 
1694     hdcNew = CreateCompatibleDC(NULL);
1695     hNewBitmap = CreateBitmap(cxNew, cyNew, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
1696     hNewDCBitmap = SelectObject(hdcNew, hNewBitmap);
1697     hNewDCBrush = SelectObject(hdcNew, CreateSolidBrush(crBackground));
1698 
1699     hdcOld = CreateCompatibleDC(NULL);
1700     hOldDCBitmap = SelectObject(hdcOld, *pBitmap);
1701 
1702     BitBlt(hdcNew, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcOld, 0, 0, SRCCOPY);
1703     if (bmp.bmWidth < cxMinWidth)
1704         PatBlt(hdcNew, bmp.bmWidth, 0, cxNew, bmp.bmHeight, PATCOPY);
1705     if (bmp.bmHeight < cyMinHeight)
1706         PatBlt(hdcNew, 0, bmp.bmHeight, bmp.bmWidth, cyNew, PATCOPY);
1707     if (bmp.bmWidth < cxMinWidth && bmp.bmHeight < cyMinHeight)
1708         PatBlt(hdcNew, bmp.bmWidth, bmp.bmHeight, cxNew, cyNew, PATCOPY);
1709 
1710     SelectObject(hdcNew, hNewDCBitmap);
1711     DeleteObject(SelectObject(hdcNew, hNewDCBrush));
1712     DeleteDC(hdcNew);
1713     SelectObject(hdcOld, hOldDCBitmap);
1714     DeleteDC(hdcOld);
1715 
1716     DeleteObject(*pBitmap);
1717     *pBitmap = hNewBitmap;
1718     return;
1719 }
1720 
1721 void COMCTL32_GetFontMetrics(HFONT hFont, TEXTMETRICW *ptm)
1722 {
1723     HDC hdc = GetDC(NULL);
1724     HFONT hOldFont;
1725 
1726     hOldFont = SelectObject(hdc, hFont);
1727     GetTextMetricsW(hdc, ptm);
1728     SelectObject(hdc, hOldFont);
1729     ReleaseDC(NULL, hdc);
1730 }
1731 
1732 #ifndef OCM__BASE      /* avoid including olectl.h */
1733 #define OCM__BASE (WM_USER+0x1c00)
1734 #endif
1735 
1736 /***********************************************************************
1737  * COMCTL32_IsReflectedMessage [internal]
1738  *
1739  * Some parents reflect notify messages - for some messages sent by the child,
1740  * they send it back with the message code increased by OCM__BASE (0x2000).
1741  * This allows better subclassing of controls. We don't need to handle such
1742  * messages but we don't want to print ERRs for them, so this helper function
1743  * identifies them.
1744  *
1745  * Some of the codes are in the CCM_FIRST..CCM_LAST range, but there is no
1746  * collision with defined CCM_ codes.
1747  */
1748 BOOL COMCTL32_IsReflectedMessage(UINT uMsg)
1749 {
1750     switch (uMsg)
1751     {
1752         case OCM__BASE + WM_COMMAND:
1753         case OCM__BASE + WM_CTLCOLORBTN:
1754         case OCM__BASE + WM_CTLCOLOREDIT:
1755         case OCM__BASE + WM_CTLCOLORDLG:
1756         case OCM__BASE + WM_CTLCOLORLISTBOX:
1757         case OCM__BASE + WM_CTLCOLORMSGBOX:
1758         case OCM__BASE + WM_CTLCOLORSCROLLBAR:
1759         case OCM__BASE + WM_CTLCOLORSTATIC:
1760         case OCM__BASE + WM_DRAWITEM:
1761         case OCM__BASE + WM_MEASUREITEM:
1762         case OCM__BASE + WM_DELETEITEM:
1763         case OCM__BASE + WM_VKEYTOITEM:
1764         case OCM__BASE + WM_CHARTOITEM:
1765         case OCM__BASE + WM_COMPAREITEM:
1766         case OCM__BASE + WM_HSCROLL:
1767         case OCM__BASE + WM_VSCROLL:
1768         case OCM__BASE + WM_PARENTNOTIFY:
1769         case OCM__BASE + WM_NOTIFY:
1770             return TRUE;
1771         default:
1772             return FALSE;
1773     }
1774 }
1775 
1776 /***********************************************************************
1777  * MirrorIcon [COMCTL32.414]
1778  *
1779  * Mirrors an icon so that it will appear correctly on a mirrored DC.
1780  *
1781  * PARAMS
1782  *     phicon1 [I/O] Icon.
1783  *     phicon2 [I/O] Icon.
1784  *
1785  * RETURNS
1786  *     Success: TRUE.
1787  *     Failure: FALSE.
1788  */
1789 BOOL WINAPI MirrorIcon(HICON *phicon1, HICON *phicon2)
1790 {
1791     FIXME("(%p, %p): stub\n", phicon1, phicon2);
1792     return FALSE;
1793 }
1794 
1795 static inline BOOL IsDelimiter(WCHAR c)
1796 {
1797     switch(c)
1798     {
1799 	case '/':
1800 	case '\\':
1801 	case '.':
1802 	case ' ':
1803 	    return TRUE;
1804     }
1805     return FALSE;
1806 }
1807 
1808 static int CALLBACK PathWordBreakProc(LPCWSTR lpch, int ichCurrent, int cch, int code)
1809 {
1810     if (code == WB_ISDELIMITER)
1811         return IsDelimiter(lpch[ichCurrent]);
1812     else
1813     {
1814         int dir = (code == WB_LEFT) ? -1 : 1;
1815         for(; 0 <= ichCurrent && ichCurrent < cch; ichCurrent += dir)
1816             if (IsDelimiter(lpch[ichCurrent])) return ichCurrent;
1817     }
1818     return ichCurrent;
1819 }
1820 
1821 /***********************************************************************
1822  * SetPathWordBreakProc [COMCTL32.384]
1823  *
1824  * Sets the word break procedure for an edit control to one that understands
1825  * paths so that the user can jump over directories.
1826  *
1827  * PARAMS
1828  *     hwnd [I] Handle to edit control.
1829  *     bSet [I] If this is TRUE then the word break proc is set, otherwise it is removed.
1830  *
1831  * RETURNS
1832  *     Result from EM_SETWORDBREAKPROC message.
1833  */
1834 LRESULT WINAPI SetPathWordBreakProc(HWND hwnd, BOOL bSet)
1835 {
1836     return SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0,
1837         (LPARAM)(bSet ? PathWordBreakProc : NULL));
1838 }
1839 
1840 /***********************************************************************
1841  * DrawShadowText [COMCTL32.@]
1842  *
1843  * Draw text with shadow.
1844  */
1845 int WINAPI DrawShadowText(HDC hdc, LPCWSTR pszText, UINT cch, RECT *prc, DWORD dwFlags,
1846                           COLORREF crText, COLORREF crShadow, int ixOffset, int iyOffset)
1847 {
1848     COLORREF crOldText;
1849     RECT rcText;
1850     INT iRet, x, y, x2, y2;
1851     BYTE *pBits;
1852     HBITMAP hbm, hbmOld;
1853     BITMAPINFO bi;
1854     HDC hdcMem;
1855     HFONT hOldFont;
1856     BLENDFUNCTION bf;
1857 
1858     /* Create 32 bit DIB section for the shadow */
1859     ZeroMemory(&bi, sizeof(bi));
1860     bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
1861     bi.bmiHeader.biWidth = prc->right - prc->left + 4;
1862     bi.bmiHeader.biHeight = prc->bottom - prc->top + 5; // bottom-up DIB
1863     bi.bmiHeader.biPlanes = 1;
1864     bi.bmiHeader.biBitCount = 32;
1865     bi.bmiHeader.biCompression = BI_RGB;
1866     hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (PVOID*)&pBits, NULL, 0);
1867     if(!hbm)
1868     {
1869         ERR("CreateDIBSection failed\n");
1870         return 0;
1871     }
1872 
1873     /* Create memory device context for new DIB section and select it */
1874     hdcMem = CreateCompatibleDC(hdc);
1875     if(!hdcMem)
1876     {
1877         ERR("CreateCompatibleDC failed\n");
1878         DeleteObject(hbm);
1879         return 0;
1880     }
1881 
1882     hbmOld = (HBITMAP)SelectObject(hdcMem, hbm);
1883 
1884     /* Draw text on our helper bitmap */
1885     hOldFont = (HFONT)SelectObject(hdcMem, GetCurrentObject(hdc, OBJ_FONT));
1886     SetTextColor(hdcMem, RGB(16, 16, 16));
1887     SetBkColor(hdcMem, RGB(0, 0, 0));
1888     SetBkMode(hdcMem, TRANSPARENT);
1889     SetRect(&rcText, 0, 0, prc->right - prc->left, prc->bottom - prc->top);
1890     DrawTextW(hdcMem, pszText, cch, &rcText, dwFlags);
1891     SelectObject(hdcMem, hOldFont);
1892 
1893     /* Flush GDI so data pointed by pBits is valid */
1894     GdiFlush();
1895 
1896     /* Set alpha of pixels (forget about colors for now. They will be changed in next loop).
1897        We copy text image 4*5 times and each time alpha is added */
1898     for (x = 0; x < bi.bmiHeader.biWidth; ++x)
1899         for (y = 0; y < bi.bmiHeader.biHeight; ++y)
1900         {
1901             BYTE *pDest = &pBits[(y * bi.bmiHeader.biWidth + x) * 4];
1902             UINT Alpha = 0;
1903 
1904             for (x2 = x - 4 + 1; x2 <= x; ++x2)
1905                 for (y2 = y; y2 < y + 5; ++y2)
1906                 {
1907                     if (x2 >= 0 && x2 < bi.bmiHeader.biWidth && y2 >= 0 && y2 < bi.bmiHeader.biHeight)
1908                     {
1909                         BYTE *pSrc = &pBits[(y2 * bi.bmiHeader.biWidth + x2) * 4];
1910                         Alpha += pSrc[0];
1911                     }
1912                 }
1913 
1914             if (Alpha > 255)
1915                 Alpha = 255;
1916             pDest[3] = Alpha;
1917         }
1918 
1919     /* Now set the color of each pixel to shadow color * alpha (see GdiAlphaBlend) */
1920     for (x = 0; x < bi.bmiHeader.biWidth; ++x)
1921         for (y = 0; y < bi.bmiHeader.biHeight; ++y)
1922         {
1923             BYTE *pDest = &pBits[(y * bi.bmiHeader.biWidth + x) * 4];
1924             pDest[0] = GetBValue(crShadow) * pDest[3] / 255;
1925             pDest[1] = GetGValue(crShadow) * pDest[3] / 255;
1926             pDest[2] = GetRValue(crShadow) * pDest[3] / 255;
1927         }
1928 
1929     /* Fix ixOffset of the shadow (tested on Win) */
1930     ixOffset -= 3;
1931     iyOffset -= 3;
1932 
1933     /* Alpha blend helper image to destination DC */
1934     bf.BlendOp = AC_SRC_OVER;
1935     bf.BlendFlags = 0;
1936     bf.SourceConstantAlpha = 255;
1937     bf.AlphaFormat = AC_SRC_ALPHA;
1938     GdiAlphaBlend(hdc, prc->left + ixOffset, prc->top + iyOffset, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, hdcMem, 0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, bf);
1939 
1940     /* Delete the helper bitmap */
1941     SelectObject(hdcMem, hbmOld);
1942     DeleteObject(hbm);
1943     DeleteDC(hdcMem);
1944 
1945     /* Finally draw the text over shadow */
1946     crOldText = SetTextColor(hdc, crText);
1947     SetBkMode(hdc, TRANSPARENT);
1948     iRet = DrawTextW(hdc, pszText, cch, prc, dwFlags);
1949     SetTextColor(hdc, crOldText);
1950 
1951     return iRet;
1952 }
1953 
1954 /***********************************************************************
1955  * LoadIconWithScaleDown [COMCTL32.@]
1956  */
1957 HRESULT WINAPI LoadIconWithScaleDown(HINSTANCE hinst, const WCHAR *name, int cx, int cy, HICON *icon)
1958 {
1959     TRACE("(%p, %s, %d, %d, %p)\n", hinst, debugstr_w(name), cx, cy, icon);
1960 
1961     *icon = NULL;
1962 
1963     if (!name)
1964         return E_INVALIDARG;
1965 
1966     *icon = LoadImageW(hinst, name, IMAGE_ICON, cx, cy,
1967                        (hinst || IS_INTRESOURCE(name)) ? 0 : LR_LOADFROMFILE);
1968     if (!*icon)
1969         return HRESULT_FROM_WIN32(GetLastError());
1970 
1971     return S_OK;
1972 }
1973 
1974 /***********************************************************************
1975  * LoadIconMetric [COMCTL32.@]
1976  */
1977 HRESULT WINAPI LoadIconMetric(HINSTANCE hinst, const WCHAR *name, int size, HICON *icon)
1978 {
1979     int cx, cy;
1980 
1981     TRACE("(%p, %s, %d, %p)\n", hinst, debugstr_w(name), size, icon);
1982 
1983     if (size == LIM_SMALL)
1984     {
1985         cx = GetSystemMetrics(SM_CXSMICON);
1986         cy = GetSystemMetrics(SM_CYSMICON);
1987     }
1988     else if (size == LIM_LARGE)
1989     {
1990         cx = GetSystemMetrics(SM_CXICON);
1991         cy = GetSystemMetrics(SM_CYICON);
1992     }
1993     else
1994     {
1995         *icon = NULL;
1996         return E_INVALIDARG;
1997     }
1998 
1999     return LoadIconWithScaleDown(hinst, name, cx, cy, icon);
2000 }
2001