xref: /reactos/dll/win32/comctl32/commctrl.c (revision 0707475f)
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 
GetManifestPath(BOOL create,BOOL bV6)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 
CreateComctl32ActCtx(BOOL bV6)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 
RegisterControls(BOOL bV6)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 
UnregisterControls(BOOL bV6)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 
InitializeClasses()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 
UninitializeClasses()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  */
RegisterClassNameW(LPCWSTR className)296 BOOLEAN WINAPI RegisterClassNameW(LPCWSTR className)
297 {
298     InitializeClasses();
299     return TRUE;
300 }
301 
302 #endif /* __REACTOS__ */
303 
304 #ifndef __REACTOS__
unregister_versioned_classes(void)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 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)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
MenuHelp(UINT uMsg,WPARAM wParam,LPARAM lParam,HMENU hMainMenu,HINSTANCE hInst,HWND hwndStatus,UINT * lpwIDs)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
ShowHideMenuCtl(HWND hwnd,UINT_PTR uFlags,LPINT lpInfo)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
GetEffectiveClientRect(HWND hwnd,LPRECT lpRect,const INT * lpInfo)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 
DrawStatusTextW(HDC hdc,LPCRECT lprc,LPCWSTR text,UINT style)677 void WINAPI DrawStatusTextW (HDC hdc, LPCRECT lprc, LPCWSTR text, UINT style)
678 {
679     RECT r = *lprc;
680     UINT border = BDR_SUNKENOUTER;
681     COLORREF oldbkcolor;
682 
683     if (style & SBT_POPOUT)
684         border = BDR_RAISEDOUTER;
685     else if (style & SBT_NOBORDERS)
686         border = 0;
687 
688     oldbkcolor = SetBkColor (hdc, comctl32_color.clrBtnFace);
689     DrawEdge (hdc, &r, border, BF_MIDDLE|BF_RECT|BF_ADJUST);
690 
691     /* now draw text */
692     if (text) {
693         int oldbkmode = SetBkMode (hdc, TRANSPARENT);
694         COLORREF oldtextcolor;
695         UINT align = DT_LEFT;
696         int strCnt = 0;
697 
698         oldtextcolor = SetTextColor (hdc, comctl32_color.clrBtnText);
699         if (style & SBT_RTLREADING)
700             FIXME("Unsupported RTL style!\n");
701         r.left += 3;
702         do {
703             if (*text == '\t') {
704                 if (strCnt) {
705                     DrawTextW (hdc, text - strCnt, strCnt, &r, align|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
706                     strCnt = 0;
707                 }
708                 if (align==DT_RIGHT) {
709                     break;
710                 }
711                 align = (align==DT_LEFT ? DT_CENTER : DT_RIGHT);
712             } else {
713                 strCnt++;
714             }
715         } while(*text++);
716 
717         if (strCnt) DrawTextW (hdc, text - strCnt, -1, &r, align|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
718         SetBkMode (hdc, oldbkmode);
719         SetTextColor (hdc, oldtextcolor);
720     }
721 
722     SetBkColor (hdc, oldbkcolor);
723 }
724 
725 
726 /***********************************************************************
727  * DrawStatusText  [COMCTL32.@]
728  * DrawStatusTextA [COMCTL32.5]
729  *
730  * Draws text with borders, like in a status bar.
731  *
732  * PARAMS
733  *     hdc   [I] handle to the window's display context
734  *     lprc  [I] pointer to a rectangle
735  *     text  [I] pointer to the text
736  *     style [I] drawing style
737  *
738  * RETURNS
739  *     No return value.
740  */
741 
DrawStatusTextA(HDC hdc,LPCRECT lprc,LPCSTR text,UINT style)742 void WINAPI DrawStatusTextA (HDC hdc, LPCRECT lprc, LPCSTR text, UINT style)
743 {
744     INT len;
745     LPWSTR textW = NULL;
746 
747     if ( text ) {
748 	if ( (len = MultiByteToWideChar( CP_ACP, 0, text, -1, NULL, 0 )) ) {
749 	    if ( (textW = Alloc( len * sizeof(WCHAR) )) )
750 		MultiByteToWideChar( CP_ACP, 0, text, -1, textW, len );
751 	}
752     }
753     DrawStatusTextW( hdc, lprc, textW, style );
754     Free( textW );
755 }
756 
757 
758 /***********************************************************************
759  * CreateStatusWindow  [COMCTL32.@]
760  * CreateStatusWindowA [COMCTL32.6]
761  *
762  * Creates a status bar
763  *
764  * PARAMS
765  *     style  [I] window style
766  *     text   [I] pointer to the window text
767  *     parent [I] handle to the parent window
768  *     wid    [I] control id of the status bar
769  *
770  * RETURNS
771  *     Success: handle to the status window
772  *     Failure: 0
773  */
774 
775 HWND WINAPI
CreateStatusWindowA(LONG style,LPCSTR text,HWND parent,UINT wid)776 CreateStatusWindowA (LONG style, LPCSTR text, HWND parent, UINT wid)
777 {
778     return CreateWindowA(STATUSCLASSNAMEA, text, style,
779 			   CW_USEDEFAULT, CW_USEDEFAULT,
780 			   CW_USEDEFAULT, CW_USEDEFAULT,
781 			   parent, (HMENU)(DWORD_PTR)wid, 0, 0);
782 }
783 
784 
785 /***********************************************************************
786  * CreateStatusWindowW [COMCTL32.@]
787  *
788  * Creates a status bar control
789  *
790  * PARAMS
791  *     style  [I] window style
792  *     text   [I] pointer to the window text
793  *     parent [I] handle to the parent window
794  *     wid    [I] control id of the status bar
795  *
796  * RETURNS
797  *     Success: handle to the status window
798  *     Failure: 0
799  */
800 
801 HWND WINAPI
CreateStatusWindowW(LONG style,LPCWSTR text,HWND parent,UINT wid)802 CreateStatusWindowW (LONG style, LPCWSTR text, HWND parent, UINT wid)
803 {
804     return CreateWindowW(STATUSCLASSNAMEW, text, style,
805 			   CW_USEDEFAULT, CW_USEDEFAULT,
806 			   CW_USEDEFAULT, CW_USEDEFAULT,
807 			   parent, (HMENU)(DWORD_PTR)wid, 0, 0);
808 }
809 
810 
811 /***********************************************************************
812  * CreateUpDownControl [COMCTL32.16]
813  *
814  * Creates an up-down control
815  *
816  * PARAMS
817  *     style  [I] window styles
818  *     x      [I] horizontal position of the control
819  *     y      [I] vertical position of the control
820  *     cx     [I] with of the control
821  *     cy     [I] height of the control
822  *     parent [I] handle to the parent window
823  *     id     [I] the control's identifier
824  *     inst   [I] handle to the application's module instance
825  *     buddy  [I] handle to the buddy window, can be NULL
826  *     maxVal [I] upper limit of the control
827  *     minVal [I] lower limit of the control
828  *     curVal [I] current value of the control
829  *
830  * RETURNS
831  *     Success: handle to the updown control
832  *     Failure: 0
833  */
834 
835 HWND WINAPI
CreateUpDownControl(DWORD style,INT x,INT y,INT cx,INT cy,HWND parent,INT id,HINSTANCE inst,HWND buddy,INT maxVal,INT minVal,INT curVal)836 CreateUpDownControl (DWORD style, INT x, INT y, INT cx, INT cy,
837 		     HWND parent, INT id, HINSTANCE inst,
838 		     HWND buddy, INT maxVal, INT minVal, INT curVal)
839 {
840     HWND hUD =
841 	CreateWindowW (UPDOWN_CLASSW, 0, style, x, y, cx, cy,
842 			 parent, (HMENU)(DWORD_PTR)id, inst, 0);
843     if (hUD) {
844 	SendMessageW (hUD, UDM_SETBUDDY, (WPARAM)buddy, 0);
845 	SendMessageW (hUD, UDM_SETRANGE, 0, MAKELONG(maxVal, minVal));
846 	SendMessageW (hUD, UDM_SETPOS, 0, MAKELONG(curVal, 0));
847     }
848 
849     return hUD;
850 }
851 
852 
853 /***********************************************************************
854  * InitCommonControls [COMCTL32.17]
855  *
856  * Registers the common controls.
857  *
858  * PARAMS
859  *     No parameters.
860  *
861  * RETURNS
862  *     No return values.
863  *
864  * NOTES
865  *     This function is just a dummy - all the controls are registered at
866  *     the DLL initialization time. See InitCommonControlsEx for details.
867  */
868 
869 VOID WINAPI
InitCommonControls(void)870 InitCommonControls (void)
871 {
872 }
873 
874 
875 /***********************************************************************
876  * InitCommonControlsEx [COMCTL32.@]
877  *
878  * Registers the common controls.
879  *
880  * PARAMS
881  *     lpInitCtrls [I] pointer to an INITCOMMONCONTROLS structure.
882  *
883  * RETURNS
884  *     Success: TRUE
885  *     Failure: FALSE
886  *
887  * NOTES
888  *     Probably all versions of comctl32 initializes the Win95 controls in DllMain
889  *     during DLL initialization. Starting from comctl32 v5.82 all the controls
890  *     are initialized there. We follow this behaviour and this function is just
891  *     a dummy.
892  *
893  *     Note: when writing programs under Windows, if you don't call any function
894  *     from comctl32 the linker may not link this DLL. If InitCommonControlsEx
895  *     was the only comctl32 function you were calling and you remove it you may
896  *     have a false impression that InitCommonControlsEx actually did something.
897  */
898 
899 BOOL WINAPI
InitCommonControlsEx(const INITCOMMONCONTROLSEX * lpInitCtrls)900 InitCommonControlsEx (const INITCOMMONCONTROLSEX *lpInitCtrls)
901 {
902     if (!lpInitCtrls || lpInitCtrls->dwSize != sizeof(INITCOMMONCONTROLSEX))
903         return FALSE;
904 
905     TRACE("(0x%08x)\n", lpInitCtrls->dwICC);
906     return TRUE;
907 }
908 
909 
910 /***********************************************************************
911  * CreateToolbarEx [COMCTL32.@]
912  *
913  * Creates a toolbar window.
914  *
915  * PARAMS
916  *     hwnd
917  *     style
918  *     wID
919  *     nBitmaps
920  *     hBMInst
921  *     wBMID
922  *     lpButtons
923  *     iNumButtons
924  *     dxButton
925  *     dyButton
926  *     dxBitmap
927  *     dyBitmap
928  *     uStructSize
929  *
930  * RETURNS
931  *     Success: handle to the tool bar control
932  *     Failure: 0
933  */
934 
935 HWND WINAPI
CreateToolbarEx(HWND hwnd,DWORD style,UINT wID,INT nBitmaps,HINSTANCE hBMInst,UINT_PTR wBMID,LPCTBBUTTON lpButtons,INT iNumButtons,INT dxButton,INT dyButton,INT dxBitmap,INT dyBitmap,UINT uStructSize)936 CreateToolbarEx (HWND hwnd, DWORD style, UINT wID, INT nBitmaps,
937                  HINSTANCE hBMInst, UINT_PTR wBMID, LPCTBBUTTON lpButtons,
938                  INT iNumButtons, INT dxButton, INT dyButton,
939                  INT dxBitmap, INT dyBitmap, UINT uStructSize)
940 {
941     HWND hwndTB;
942 
943     hwndTB =
944         CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, style|WS_CHILD, 0,0,100,30,
945                         hwnd, (HMENU)(DWORD_PTR)wID, COMCTL32_hModule, NULL);
946     if(hwndTB) {
947 	TBADDBITMAP tbab;
948 
949         SendMessageW (hwndTB, TB_BUTTONSTRUCTSIZE, uStructSize, 0);
950 
951        /* set bitmap and button size */
952        /*If CreateToolbarEx receives 0, windows sets default values*/
953        if (dxBitmap < 0)
954            dxBitmap = 16;
955        if (dyBitmap < 0)
956            dyBitmap = 16;
957        if (dxBitmap == 0 || dyBitmap == 0)
958            dxBitmap = dyBitmap = 16;
959        SendMessageW(hwndTB, TB_SETBITMAPSIZE, 0, MAKELPARAM(dxBitmap, dyBitmap));
960 
961        if (dxButton < 0)
962            dxButton = dxBitmap;
963        if (dyButton < 0)
964            dyButton = dyBitmap;
965        /* TB_SETBUTTONSIZE -> TB_SETBITMAPSIZE bug introduced for Windows compatibility */
966        if (dxButton != 0 && dyButton != 0)
967             SendMessageW(hwndTB, TB_SETBITMAPSIZE, 0, MAKELPARAM(dxButton, dyButton));
968 
969 
970 	/* add bitmaps */
971 	if (nBitmaps > 0 || hBMInst == HINST_COMMCTRL)
972 	{
973 	    tbab.hInst = hBMInst;
974 	    tbab.nID   = wBMID;
975 
976             SendMessageW (hwndTB, TB_ADDBITMAP, nBitmaps, (LPARAM)&tbab);
977 	}
978 	/* add buttons */
979 	if(iNumButtons > 0)
980         SendMessageW (hwndTB, TB_ADDBUTTONSW, iNumButtons, (LPARAM)lpButtons);
981     }
982 
983     return hwndTB;
984 }
985 
986 
987 /***********************************************************************
988  * CreateMappedBitmap [COMCTL32.8]
989  *
990  * Loads a bitmap resource using a colour map.
991  *
992  * PARAMS
993  *     hInstance  [I] Handle to the module containing the bitmap.
994  *     idBitmap   [I] The bitmap resource ID.
995  *     wFlags     [I] CMB_MASKED for using bitmap as a mask or 0 for normal.
996  *     lpColorMap [I] Colour information needed for the bitmap or NULL (uses system colours).
997  *     iNumMaps   [I] Number of COLORMAP's pointed to by lpColorMap.
998  *
999  * RETURNS
1000  *     Success: handle to the new bitmap
1001  *     Failure: 0
1002  */
1003 
1004 HBITMAP WINAPI
CreateMappedBitmap(HINSTANCE hInstance,INT_PTR idBitmap,UINT wFlags,LPCOLORMAP lpColorMap,INT iNumMaps)1005 CreateMappedBitmap (HINSTANCE hInstance, INT_PTR idBitmap, UINT wFlags,
1006 		    LPCOLORMAP lpColorMap, INT iNumMaps)
1007 {
1008     HGLOBAL hglb;
1009     HRSRC hRsrc;
1010     const BITMAPINFOHEADER *lpBitmap;
1011     LPBITMAPINFOHEADER lpBitmapInfo;
1012     UINT nSize, nColorTableSize, iColor;
1013     RGBQUAD *pColorTable;
1014     INT i, iMaps, nWidth, nHeight;
1015     HDC hdcScreen;
1016     HBITMAP hbm;
1017     LPCOLORMAP sysColorMap;
1018     COLORREF cRef;
1019     COLORMAP internalColorMap[4] =
1020 	{{0x000000, 0}, {0x808080, 0}, {0xC0C0C0, 0}, {0xFFFFFF, 0}};
1021 
1022     /* initialize pointer to colortable and default color table */
1023     if (lpColorMap) {
1024 	iMaps = iNumMaps;
1025 	sysColorMap = lpColorMap;
1026     }
1027     else {
1028 	internalColorMap[0].to = GetSysColor (COLOR_BTNTEXT);
1029 	internalColorMap[1].to = GetSysColor (COLOR_BTNSHADOW);
1030 	internalColorMap[2].to = GetSysColor (COLOR_BTNFACE);
1031 	internalColorMap[3].to = GetSysColor (COLOR_BTNHIGHLIGHT);
1032 	iMaps = 4;
1033 	sysColorMap = internalColorMap;
1034     }
1035 
1036     hRsrc = FindResourceW (hInstance, (LPWSTR)idBitmap, (LPWSTR)RT_BITMAP);
1037     if (hRsrc == 0)
1038 	return 0;
1039     hglb = LoadResource (hInstance, hRsrc);
1040     if (hglb == 0)
1041 	return 0;
1042     lpBitmap = LockResource (hglb);
1043     if (lpBitmap == NULL)
1044 	return 0;
1045 
1046     if (lpBitmap->biSize >= sizeof(BITMAPINFOHEADER) && lpBitmap->biClrUsed)
1047         nColorTableSize = lpBitmap->biClrUsed;
1048     else if (lpBitmap->biBitCount <= 8)
1049         nColorTableSize = (1 << lpBitmap->biBitCount);
1050     else
1051         nColorTableSize = 0;
1052     nSize = lpBitmap->biSize;
1053     if (nSize == sizeof(BITMAPINFOHEADER) && lpBitmap->biCompression == BI_BITFIELDS)
1054         nSize += 3 * sizeof(DWORD);
1055     nSize += nColorTableSize * sizeof(RGBQUAD);
1056     lpBitmapInfo = GlobalAlloc (GMEM_FIXED, nSize);
1057     if (lpBitmapInfo == NULL)
1058 	return 0;
1059     RtlMoveMemory (lpBitmapInfo, lpBitmap, nSize);
1060 
1061     pColorTable = (RGBQUAD*)(((LPBYTE)lpBitmapInfo) + lpBitmapInfo->biSize);
1062 
1063     for (iColor = 0; iColor < nColorTableSize; iColor++) {
1064 	for (i = 0; i < iMaps; i++) {
1065             cRef = RGB(pColorTable[iColor].rgbRed,
1066                        pColorTable[iColor].rgbGreen,
1067                        pColorTable[iColor].rgbBlue);
1068 	    if ( cRef  == sysColorMap[i].from) {
1069 #if 0
1070 		if (wFlags & CBS_MASKED) {
1071 		    if (sysColorMap[i].to != COLOR_BTNTEXT)
1072 			pColorTable[iColor] = RGB(255, 255, 255);
1073 		}
1074 		else
1075 #endif
1076                     pColorTable[iColor].rgbBlue  = GetBValue(sysColorMap[i].to);
1077                     pColorTable[iColor].rgbGreen = GetGValue(sysColorMap[i].to);
1078                     pColorTable[iColor].rgbRed   = GetRValue(sysColorMap[i].to);
1079 		break;
1080 	    }
1081 	}
1082     }
1083     nWidth  = lpBitmapInfo->biWidth;
1084     nHeight = lpBitmapInfo->biHeight;
1085     hdcScreen = GetDC (NULL);
1086     hbm = CreateCompatibleBitmap (hdcScreen, nWidth, nHeight);
1087     if (hbm) {
1088 	HDC hdcDst = CreateCompatibleDC (hdcScreen);
1089 	HBITMAP hbmOld = SelectObject (hdcDst, hbm);
1090 	const BYTE *lpBits = (const BYTE *)lpBitmap + nSize;
1091 	StretchDIBits (hdcDst, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight,
1092 		         lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS,
1093 		         SRCCOPY);
1094 	SelectObject (hdcDst, hbmOld);
1095 	DeleteDC (hdcDst);
1096     }
1097     ReleaseDC (NULL, hdcScreen);
1098     GlobalFree (lpBitmapInfo);
1099     FreeResource (hglb);
1100 
1101     return hbm;
1102 }
1103 
1104 
1105 /***********************************************************************
1106  * CreateToolbar [COMCTL32.7]
1107  *
1108  * Creates a toolbar control.
1109  *
1110  * PARAMS
1111  *     hwnd
1112  *     style
1113  *     wID
1114  *     nBitmaps
1115  *     hBMInst
1116  *     wBMID
1117  *     lpButtons
1118  *     iNumButtons
1119  *
1120  * RETURNS
1121  *     Success: handle to the tool bar control
1122  *     Failure: 0
1123  *
1124  * NOTES
1125  *     Do not use this function anymore. Use CreateToolbarEx instead.
1126  */
1127 
1128 HWND WINAPI
CreateToolbar(HWND hwnd,DWORD style,UINT wID,INT nBitmaps,HINSTANCE hBMInst,UINT wBMID,LPCTBBUTTON lpButtons,INT iNumButtons)1129 CreateToolbar (HWND hwnd, DWORD style, UINT wID, INT nBitmaps,
1130 	       HINSTANCE hBMInst, UINT wBMID,
1131 	       LPCTBBUTTON lpButtons,INT iNumButtons)
1132 {
1133     return CreateToolbarEx (hwnd, style | CCS_NODIVIDER, wID, nBitmaps,
1134 			    hBMInst, wBMID, lpButtons,
1135 			    iNumButtons, 0, 0, 0, 0, CCSIZEOF_STRUCT(TBBUTTON, dwData));
1136 }
1137 
1138 
1139 /***********************************************************************
1140  * DllGetVersion [COMCTL32.@]
1141  *
1142  * Retrieves version information of the 'COMCTL32.DLL'
1143  *
1144  * PARAMS
1145  *     pdvi [O] pointer to version information structure.
1146  *
1147  * RETURNS
1148  *     Success: S_OK
1149  *     Failure: E_INVALIDARG
1150  *
1151  * NOTES
1152  *     Returns version of a comctl32.dll from IE4.01 SP1.
1153  */
1154 
DllGetVersion(DLLVERSIONINFO * pdvi)1155 HRESULT WINAPI DllGetVersion (DLLVERSIONINFO *pdvi)
1156 {
1157     if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) {
1158         WARN("wrong DLLVERSIONINFO size from app\n");
1159 	return E_INVALIDARG;
1160     }
1161 
1162     pdvi->dwMajorVersion = COMCTL32_VERSION;
1163     pdvi->dwMinorVersion = COMCTL32_VERSION_MINOR;
1164     pdvi->dwBuildNumber = 2919;
1165     pdvi->dwPlatformID = 6304;
1166 
1167     TRACE("%u.%u.%u.%u\n",
1168 	   pdvi->dwMajorVersion, pdvi->dwMinorVersion,
1169 	   pdvi->dwBuildNumber, pdvi->dwPlatformID);
1170 
1171     return S_OK;
1172 }
1173 
1174 /***********************************************************************
1175  *		DllInstall (COMCTL32.@)
1176  *
1177  * Installs the ComCtl32 DLL.
1178  *
1179  * RETURNS
1180  *     Success: S_OK
1181  *     Failure: A HRESULT error
1182  */
DllInstall(BOOL bInstall,LPCWSTR cmdline)1183 HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
1184 {
1185     TRACE("(%u, %s): stub\n", bInstall, debugstr_w(cmdline));
1186     return S_OK;
1187 }
1188 
1189 /***********************************************************************
1190  * _TrackMouseEvent [COMCTL32.@]
1191  *
1192  * Requests notification of mouse events
1193  *
1194  * During mouse tracking WM_MOUSEHOVER or WM_MOUSELEAVE events are posted
1195  * to the hwnd specified in the ptme structure.  After the event message
1196  * is posted to the hwnd, the entry in the queue is removed.
1197  *
1198  * If the current hwnd isn't ptme->hwndTrack the TME_HOVER flag is completely
1199  * ignored. The TME_LEAVE flag results in a WM_MOUSELEAVE message being posted
1200  * immediately and the TME_LEAVE flag being ignored.
1201  *
1202  * PARAMS
1203  *     ptme [I,O] pointer to TRACKMOUSEEVENT information structure.
1204  *
1205  * RETURNS
1206  *     Success: non-zero
1207  *     Failure: zero
1208  *
1209  * IMPLEMENTATION moved to USER32.TrackMouseEvent
1210  *
1211  */
1212 
1213 BOOL WINAPI
_TrackMouseEvent(TRACKMOUSEEVENT * ptme)1214 _TrackMouseEvent (TRACKMOUSEEVENT *ptme)
1215 {
1216     return TrackMouseEvent (ptme);
1217 }
1218 
1219 /*************************************************************************
1220  * GetMUILanguage [COMCTL32.@]
1221  *
1222  * Returns the user interface language in use by the current process.
1223  *
1224  * RETURNS
1225  *      Language ID in use by the current process.
1226  */
GetMUILanguage(VOID)1227 LANGID WINAPI GetMUILanguage (VOID)
1228 {
1229     return COMCTL32_uiLang;
1230 }
1231 
1232 
1233 /*************************************************************************
1234  * InitMUILanguage [COMCTL32.@]
1235  *
1236  * Sets the user interface language to be used by the current process.
1237  *
1238  * RETURNS
1239  *      Nothing.
1240  */
InitMUILanguage(LANGID uiLang)1241 VOID WINAPI InitMUILanguage (LANGID uiLang)
1242 {
1243    COMCTL32_uiLang = uiLang;
1244 }
1245 
1246 
1247 /***********************************************************************
1248  * SetWindowSubclass [COMCTL32.410]
1249  *
1250  * Starts a window subclass
1251  *
1252  * PARAMS
1253  *     hWnd [in] handle to window subclass.
1254  *     pfnSubclass [in] Pointer to new window procedure.
1255  *     uIDSubclass [in] Unique identifier of subclass together with pfnSubclass.
1256  *     dwRef [in] Reference data to pass to window procedure.
1257  *
1258  * RETURNS
1259  *     Success: non-zero
1260  *     Failure: zero
1261  *
1262  * BUGS
1263  *     If an application manually subclasses a window after subclassing it with
1264  *     this API and then with this API again, then none of the previous
1265  *     subclasses get called or the original window procedure.
1266  */
1267 
SetWindowSubclass(HWND hWnd,SUBCLASSPROC pfnSubclass,UINT_PTR uIDSubclass,DWORD_PTR dwRef)1268 BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
1269                         UINT_PTR uIDSubclass, DWORD_PTR dwRef)
1270 {
1271    LPSUBCLASS_INFO stack;
1272    LPSUBCLASSPROCS proc;
1273 
1274    TRACE ("(%p, %p, %lx, %lx)\n", hWnd, pfnSubclass, uIDSubclass, dwRef);
1275 
1276     if (!hWnd || !pfnSubclass)
1277         return FALSE;
1278 
1279    /* Since the window procedure that we set here has two additional arguments,
1280     * we can't simply set it as the new window procedure of the window. So we
1281     * set our own window procedure and then calculate the other two arguments
1282     * from there. */
1283 
1284    /* See if we have been called for this window */
1285    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1286    if (!stack) {
1287       /* allocate stack */
1288       stack = Alloc (sizeof(SUBCLASS_INFO));
1289       if (!stack) {
1290          ERR ("Failed to allocate our Subclassing stack\n");
1291          return FALSE;
1292       }
1293       SetPropW (hWnd, COMCTL32_wSubclass, stack);
1294 
1295       /* set window procedure to our own and save the current one */
1296       if (IsWindowUnicode (hWnd))
1297          stack->origproc = (WNDPROC)SetWindowLongPtrW (hWnd, GWLP_WNDPROC,
1298                                                    (DWORD_PTR)COMCTL32_SubclassProc);
1299       else
1300          stack->origproc = (WNDPROC)SetWindowLongPtrA (hWnd, GWLP_WNDPROC,
1301                                                    (DWORD_PTR)COMCTL32_SubclassProc);
1302    }
1303    else {
1304       /* Check to see if we have called this function with the same uIDSubClass
1305        * and pfnSubclass */
1306       proc = stack->SubclassProcs;
1307       while (proc) {
1308          if ((proc->id == uIDSubclass) &&
1309             (proc->subproc == pfnSubclass)) {
1310             proc->ref = dwRef;
1311             return TRUE;
1312          }
1313          proc = proc->next;
1314       }
1315    }
1316 
1317    proc = Alloc(sizeof(SUBCLASSPROCS));
1318    if (!proc) {
1319       ERR ("Failed to allocate subclass entry in stack\n");
1320       if (IsWindowUnicode (hWnd))
1321          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1322       else
1323          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1324       Free (stack);
1325       RemovePropW( hWnd, COMCTL32_wSubclass );
1326       return FALSE;
1327    }
1328 
1329    proc->subproc = pfnSubclass;
1330    proc->ref = dwRef;
1331    proc->id = uIDSubclass;
1332    proc->next = stack->SubclassProcs;
1333    stack->SubclassProcs = proc;
1334 
1335    return TRUE;
1336 }
1337 
1338 
1339 /***********************************************************************
1340  * GetWindowSubclass [COMCTL32.411]
1341  *
1342  * Gets the Reference data from a subclass.
1343  *
1344  * PARAMS
1345  *     hWnd [in] Handle to the window which we are subclassing
1346  *     pfnSubclass [in] Pointer to the subclass procedure
1347  *     uID [in] Unique identifier of the subclassing procedure
1348  *     pdwRef [out] Pointer to the reference data
1349  *
1350  * RETURNS
1351  *     Success: Non-zero
1352  *     Failure: 0
1353  */
1354 
GetWindowSubclass(HWND hWnd,SUBCLASSPROC pfnSubclass,UINT_PTR uID,DWORD_PTR * pdwRef)1355 BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
1356                               UINT_PTR uID, DWORD_PTR *pdwRef)
1357 {
1358    const SUBCLASS_INFO *stack;
1359    const SUBCLASSPROCS *proc;
1360 
1361    TRACE ("(%p, %p, %lx, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
1362 
1363    /* See if we have been called for this window */
1364    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1365    if (!stack)
1366       return FALSE;
1367 
1368    proc = stack->SubclassProcs;
1369    while (proc) {
1370       if ((proc->id == uID) &&
1371          (proc->subproc == pfnSubclass)) {
1372          *pdwRef = proc->ref;
1373          return TRUE;
1374       }
1375       proc = proc->next;
1376    }
1377 
1378    return FALSE;
1379 }
1380 
1381 
1382 /***********************************************************************
1383  * RemoveWindowSubclass [COMCTL32.412]
1384  *
1385  * Removes a window subclass.
1386  *
1387  * PARAMS
1388  *     hWnd [in] Handle to the window which we are subclassing
1389  *     pfnSubclass [in] Pointer to the subclass procedure
1390  *     uID [in] Unique identifier of this subclass
1391  *
1392  * RETURNS
1393  *     Success: non-zero
1394  *     Failure: zero
1395  */
1396 
RemoveWindowSubclass(HWND hWnd,SUBCLASSPROC pfnSubclass,UINT_PTR uID)1397 BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
1398 {
1399    LPSUBCLASS_INFO stack;
1400    LPSUBCLASSPROCS prevproc = NULL;
1401    LPSUBCLASSPROCS proc;
1402    BOOL ret = FALSE;
1403 
1404    TRACE ("(%p, %p, %lx)\n", hWnd, pfnSubclass, uID);
1405 
1406    /* Find the Subclass to remove */
1407    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1408    if (!stack)
1409       return FALSE;
1410 
1411    proc = stack->SubclassProcs;
1412    while (proc) {
1413       if ((proc->id == uID) &&
1414          (proc->subproc == pfnSubclass)) {
1415 
1416          if (!prevproc)
1417             stack->SubclassProcs = proc->next;
1418          else
1419             prevproc->next = proc->next;
1420 
1421          if (stack->stackpos == proc)
1422             stack->stackpos = stack->stackpos->next;
1423 
1424          Free (proc);
1425          ret = TRUE;
1426          break;
1427       }
1428       prevproc = proc;
1429       proc = proc->next;
1430    }
1431 
1432    if (!stack->SubclassProcs && !stack->running) {
1433       TRACE("Last Subclass removed, cleaning up\n");
1434       /* clean up our heap and reset the original window procedure */
1435       if (IsWindowUnicode (hWnd))
1436          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1437       else
1438          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1439       Free (stack);
1440       RemovePropW( hWnd, COMCTL32_wSubclass );
1441    }
1442 
1443    return ret;
1444 }
1445 
1446 /***********************************************************************
1447  * COMCTL32_SubclassProc (internal)
1448  *
1449  * Window procedure for all subclassed windows.
1450  * Saves the current subclassing stack position to support nested messages
1451  */
COMCTL32_SubclassProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1452 static LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1453 {
1454    LPSUBCLASS_INFO stack;
1455    LPSUBCLASSPROCS proc;
1456    LRESULT ret;
1457 
1458    TRACE ("(%p, 0x%08x, 0x%08lx, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
1459 
1460    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1461    if (!stack) {
1462       ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
1463       return 0;
1464    }
1465 
1466    /* Save our old stackpos to properly handle nested messages */
1467    proc = stack->stackpos;
1468    stack->stackpos = stack->SubclassProcs;
1469    stack->running++;
1470    ret = DefSubclassProc(hWnd, uMsg, wParam, lParam);
1471    stack->running--;
1472    stack->stackpos = proc;
1473 
1474    if (!stack->SubclassProcs && !stack->running) {
1475       TRACE("Last Subclass removed, cleaning up\n");
1476       /* clean up our heap and reset the original window procedure */
1477       if (IsWindowUnicode (hWnd))
1478          SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1479       else
1480          SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc);
1481       Free (stack);
1482       RemovePropW( hWnd, COMCTL32_wSubclass );
1483    }
1484    return ret;
1485 }
1486 
1487 /***********************************************************************
1488  * DefSubclassProc [COMCTL32.413]
1489  *
1490  * Calls the next window procedure (i.e. the one before this subclass)
1491  *
1492  * PARAMS
1493  *     hWnd [in] The window that we're subclassing
1494  *     uMsg [in] Message
1495  *     wParam [in] WPARAM
1496  *     lParam [in] LPARAM
1497  *
1498  * RETURNS
1499  *     Success: non-zero
1500  *     Failure: zero
1501  */
1502 
DefSubclassProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1503 LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1504 {
1505    LPSUBCLASS_INFO stack;
1506    LRESULT ret;
1507 
1508    TRACE ("(%p, 0x%08x, 0x%08lx, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
1509 
1510    /* retrieve our little stack from the Properties */
1511    stack = GetPropW (hWnd, COMCTL32_wSubclass);
1512    if (!stack) {
1513       ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
1514       return 0;
1515    }
1516 
1517    /* If we are at the end of stack then we have to call the original
1518     * window procedure */
1519    if (!stack->stackpos) {
1520       if (IsWindowUnicode (hWnd))
1521          ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
1522       else
1523          ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
1524    } else {
1525       const SUBCLASSPROCS *proc = stack->stackpos;
1526       stack->stackpos = stack->stackpos->next;
1527       /* call the Subclass procedure from the stack */
1528       ret = proc->subproc (hWnd, uMsg, wParam, lParam,
1529             proc->id, proc->ref);
1530    }
1531 
1532    return ret;
1533 }
1534 
1535 
1536 /***********************************************************************
1537  * COMCTL32_CreateToolTip [NOT AN API]
1538  *
1539  * Creates a tooltip for the control specified in hwnd and does all
1540  * necessary setup and notifications.
1541  *
1542  * PARAMS
1543  *     hwndOwner [I] Handle to the window that will own the tool tip.
1544  *
1545  * RETURNS
1546  *     Success: Handle of tool tip window.
1547  *     Failure: NULL
1548  */
1549 
1550 HWND
COMCTL32_CreateToolTip(HWND hwndOwner)1551 COMCTL32_CreateToolTip(HWND hwndOwner)
1552 {
1553     HWND hwndToolTip;
1554 
1555     hwndToolTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
1556 				  CW_USEDEFAULT, CW_USEDEFAULT,
1557 				  CW_USEDEFAULT, CW_USEDEFAULT, hwndOwner,
1558 				  0, 0, 0);
1559 
1560     /* Send NM_TOOLTIPSCREATED notification */
1561     if (hwndToolTip)
1562     {
1563 	NMTOOLTIPSCREATED nmttc;
1564         /* true owner can be different if hwndOwner is a child window */
1565         HWND hwndTrueOwner = GetWindow(hwndToolTip, GW_OWNER);
1566         nmttc.hdr.hwndFrom = hwndTrueOwner;
1567         nmttc.hdr.idFrom = GetWindowLongPtrW(hwndTrueOwner, GWLP_ID);
1568 	nmttc.hdr.code = NM_TOOLTIPSCREATED;
1569 	nmttc.hwndToolTips = hwndToolTip;
1570 
1571         SendMessageW(GetParent(hwndTrueOwner), WM_NOTIFY,
1572                      GetWindowLongPtrW(hwndTrueOwner, GWLP_ID), (LPARAM)&nmttc);
1573     }
1574 
1575     return hwndToolTip;
1576 }
1577 
1578 
1579 /***********************************************************************
1580  * COMCTL32_RefreshSysColors [NOT AN API]
1581  *
1582  * Invoked on any control recognizing a WM_SYSCOLORCHANGE message to
1583  * refresh the color values in the color structure
1584  *
1585  * PARAMS
1586  *     none
1587  *
1588  * RETURNS
1589  *     none
1590  */
1591 
1592 VOID
COMCTL32_RefreshSysColors(void)1593 COMCTL32_RefreshSysColors(void)
1594 {
1595     comctl32_color.clrBtnHighlight = GetSysColor (COLOR_BTNHIGHLIGHT);
1596     comctl32_color.clrBtnShadow = GetSysColor (COLOR_BTNSHADOW);
1597     comctl32_color.clrBtnText = GetSysColor (COLOR_BTNTEXT);
1598     comctl32_color.clrBtnFace = GetSysColor (COLOR_BTNFACE);
1599     comctl32_color.clrHighlight = GetSysColor (COLOR_HIGHLIGHT);
1600     comctl32_color.clrHighlightText = GetSysColor (COLOR_HIGHLIGHTTEXT);
1601     comctl32_color.clrHotTrackingColor = GetSysColor (COLOR_HOTLIGHT);
1602     comctl32_color.clr3dHilight = GetSysColor (COLOR_3DHILIGHT);
1603     comctl32_color.clr3dShadow = GetSysColor (COLOR_3DSHADOW);
1604     comctl32_color.clr3dDkShadow = GetSysColor (COLOR_3DDKSHADOW);
1605     comctl32_color.clr3dFace = GetSysColor (COLOR_3DFACE);
1606     comctl32_color.clrWindow = GetSysColor (COLOR_WINDOW);
1607     comctl32_color.clrWindowText = GetSysColor (COLOR_WINDOWTEXT);
1608     comctl32_color.clrGrayText = GetSysColor (COLOR_GRAYTEXT);
1609     comctl32_color.clrActiveCaption = GetSysColor (COLOR_ACTIVECAPTION);
1610     comctl32_color.clrInfoBk = GetSysColor (COLOR_INFOBK);
1611     comctl32_color.clrInfoText = GetSysColor (COLOR_INFOTEXT);
1612 }
1613 
1614 /***********************************************************************
1615  * COMCTL32_DrawInsertMark [NOT AN API]
1616  *
1617  * Draws an insertion mark (which looks similar to an 'I').
1618  *
1619  * PARAMS
1620  *     hDC           [I] Device context to draw onto.
1621  *     lpRect        [I] Co-ordinates of insertion mark.
1622  *     clrInsertMark [I] Colour of the insertion mark.
1623  *     bHorizontal   [I] True if insert mark should be drawn horizontally,
1624  *                       vertical otherwise.
1625  *
1626  * RETURNS
1627  *     none
1628  *
1629  * NOTES
1630  *     Draws up to but not including the bottom co-ordinate when drawing
1631  *     vertically or the right co-ordinate when horizontal.
1632  */
COMCTL32_DrawInsertMark(HDC hDC,const RECT * lpRect,COLORREF clrInsertMark,BOOL bHorizontal)1633 void COMCTL32_DrawInsertMark(HDC hDC, const RECT *lpRect, COLORREF clrInsertMark, BOOL bHorizontal)
1634 {
1635     HPEN hPen = CreatePen(PS_SOLID, 1, clrInsertMark);
1636     HPEN hOldPen;
1637     static const DWORD adwPolyPoints[] = {4,4,4};
1638     LONG lCentre = (bHorizontal ?
1639         lpRect->top + (lpRect->bottom - lpRect->top)/2 :
1640         lpRect->left + (lpRect->right - lpRect->left)/2);
1641     LONG l1 = (bHorizontal ? lpRect->left : lpRect->top);
1642     LONG l2 = (bHorizontal ? lpRect->right : lpRect->bottom);
1643     const POINT aptInsertMark[] =
1644     {
1645         /* top (V) or left (H) arrow */
1646         {lCentre    , l1 + 2},
1647         {lCentre - 2, l1    },
1648         {lCentre + 3, l1    },
1649         {lCentre + 1, l1 + 2},
1650         /* middle line */
1651         {lCentre    , l2 - 2},
1652         {lCentre    , l1 - 1},
1653         {lCentre + 1, l1 - 1},
1654         {lCentre + 1, l2 - 2},
1655         /* bottom (V) or right (H) arrow */
1656         {lCentre    , l2 - 3},
1657         {lCentre - 2, l2 - 1},
1658         {lCentre + 3, l2 - 1},
1659         {lCentre + 1, l2 - 3},
1660     };
1661     hOldPen = SelectObject(hDC, hPen);
1662     PolyPolyline(hDC, aptInsertMark, adwPolyPoints, ARRAY_SIZE(adwPolyPoints));
1663     SelectObject(hDC, hOldPen);
1664     DeleteObject(hPen);
1665 }
1666 
1667 /***********************************************************************
1668  * COMCTL32_EnsureBitmapSize [internal]
1669  *
1670  * If needed, enlarge the bitmap so that the width is at least cxMinWidth and
1671  * the height is at least cyMinHeight. If the bitmap already has these
1672  * dimensions nothing changes.
1673  *
1674  * PARAMS
1675  *     hBitmap       [I/O] Bitmap to modify. The handle may change
1676  *     cxMinWidth    [I]   If the width of the bitmap is smaller, then it will
1677  *                         be enlarged to this value
1678  *     cyMinHeight   [I]   If the height of the bitmap is smaller, then it will
1679  *                         be enlarged to this value
1680  *     cyBackground  [I]   The color with which the new area will be filled
1681  *
1682  * RETURNS
1683  *     none
1684  */
COMCTL32_EnsureBitmapSize(HBITMAP * pBitmap,int cxMinWidth,int cyMinHeight,COLORREF crBackground)1685 void COMCTL32_EnsureBitmapSize(HBITMAP *pBitmap, int cxMinWidth, int cyMinHeight, COLORREF crBackground)
1686 {
1687     int cxNew, cyNew;
1688     BITMAP bmp;
1689     HBITMAP hNewBitmap;
1690     HBITMAP hNewDCBitmap, hOldDCBitmap;
1691     HBRUSH hNewDCBrush;
1692     HDC hdcNew, hdcOld;
1693 
1694     if (!GetObjectW(*pBitmap, sizeof(BITMAP), &bmp))
1695         return;
1696     cxNew = (cxMinWidth > bmp.bmWidth ? cxMinWidth : bmp.bmWidth);
1697     cyNew = (cyMinHeight > bmp.bmHeight ? cyMinHeight : bmp.bmHeight);
1698     if (cxNew == bmp.bmWidth && cyNew == bmp.bmHeight)
1699         return;
1700 
1701     hdcNew = CreateCompatibleDC(NULL);
1702     hNewBitmap = CreateBitmap(cxNew, cyNew, bmp.bmPlanes, bmp.bmBitsPixel, NULL);
1703     hNewDCBitmap = SelectObject(hdcNew, hNewBitmap);
1704     hNewDCBrush = SelectObject(hdcNew, CreateSolidBrush(crBackground));
1705 
1706     hdcOld = CreateCompatibleDC(NULL);
1707     hOldDCBitmap = SelectObject(hdcOld, *pBitmap);
1708 
1709     BitBlt(hdcNew, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcOld, 0, 0, SRCCOPY);
1710     if (bmp.bmWidth < cxMinWidth)
1711         PatBlt(hdcNew, bmp.bmWidth, 0, cxNew, bmp.bmHeight, PATCOPY);
1712     if (bmp.bmHeight < cyMinHeight)
1713         PatBlt(hdcNew, 0, bmp.bmHeight, bmp.bmWidth, cyNew, PATCOPY);
1714     if (bmp.bmWidth < cxMinWidth && bmp.bmHeight < cyMinHeight)
1715         PatBlt(hdcNew, bmp.bmWidth, bmp.bmHeight, cxNew, cyNew, PATCOPY);
1716 
1717     SelectObject(hdcNew, hNewDCBitmap);
1718     DeleteObject(SelectObject(hdcNew, hNewDCBrush));
1719     DeleteDC(hdcNew);
1720     SelectObject(hdcOld, hOldDCBitmap);
1721     DeleteDC(hdcOld);
1722 
1723     DeleteObject(*pBitmap);
1724     *pBitmap = hNewBitmap;
1725     return;
1726 }
1727 
COMCTL32_GetFontMetrics(HFONT hFont,TEXTMETRICW * ptm)1728 void COMCTL32_GetFontMetrics(HFONT hFont, TEXTMETRICW *ptm)
1729 {
1730     HDC hdc = GetDC(NULL);
1731     HFONT hOldFont;
1732 
1733     hOldFont = SelectObject(hdc, hFont);
1734     GetTextMetricsW(hdc, ptm);
1735     SelectObject(hdc, hOldFont);
1736     ReleaseDC(NULL, hdc);
1737 }
1738 
1739 #ifndef OCM__BASE      /* avoid including olectl.h */
1740 #define OCM__BASE (WM_USER+0x1c00)
1741 #endif
1742 
1743 /***********************************************************************
1744  * COMCTL32_IsReflectedMessage [internal]
1745  *
1746  * Some parents reflect notify messages - for some messages sent by the child,
1747  * they send it back with the message code increased by OCM__BASE (0x2000).
1748  * This allows better subclassing of controls. We don't need to handle such
1749  * messages but we don't want to print ERRs for them, so this helper function
1750  * identifies them.
1751  *
1752  * Some of the codes are in the CCM_FIRST..CCM_LAST range, but there is no
1753  * collision with defined CCM_ codes.
1754  */
COMCTL32_IsReflectedMessage(UINT uMsg)1755 BOOL COMCTL32_IsReflectedMessage(UINT uMsg)
1756 {
1757     switch (uMsg)
1758     {
1759         case OCM__BASE + WM_COMMAND:
1760         case OCM__BASE + WM_CTLCOLORBTN:
1761         case OCM__BASE + WM_CTLCOLOREDIT:
1762         case OCM__BASE + WM_CTLCOLORDLG:
1763         case OCM__BASE + WM_CTLCOLORLISTBOX:
1764         case OCM__BASE + WM_CTLCOLORMSGBOX:
1765         case OCM__BASE + WM_CTLCOLORSCROLLBAR:
1766         case OCM__BASE + WM_CTLCOLORSTATIC:
1767         case OCM__BASE + WM_DRAWITEM:
1768         case OCM__BASE + WM_MEASUREITEM:
1769         case OCM__BASE + WM_DELETEITEM:
1770         case OCM__BASE + WM_VKEYTOITEM:
1771         case OCM__BASE + WM_CHARTOITEM:
1772         case OCM__BASE + WM_COMPAREITEM:
1773         case OCM__BASE + WM_HSCROLL:
1774         case OCM__BASE + WM_VSCROLL:
1775         case OCM__BASE + WM_PARENTNOTIFY:
1776         case OCM__BASE + WM_NOTIFY:
1777             return TRUE;
1778         default:
1779             return FALSE;
1780     }
1781 }
1782 
1783 /***********************************************************************
1784  * MirrorIcon [COMCTL32.414]
1785  *
1786  * Mirrors an icon so that it will appear correctly on a mirrored DC.
1787  *
1788  * PARAMS
1789  *     phicon1 [I/O] Icon.
1790  *     phicon2 [I/O] Icon.
1791  *
1792  * RETURNS
1793  *     Success: TRUE.
1794  *     Failure: FALSE.
1795  */
MirrorIcon(HICON * phicon1,HICON * phicon2)1796 BOOL WINAPI MirrorIcon(HICON *phicon1, HICON *phicon2)
1797 {
1798     FIXME("(%p, %p): stub\n", phicon1, phicon2);
1799     return FALSE;
1800 }
1801 
IsDelimiter(WCHAR c)1802 static inline BOOL IsDelimiter(WCHAR c)
1803 {
1804     switch(c)
1805     {
1806 	case '/':
1807 	case '\\':
1808 	case '.':
1809 	case ' ':
1810 	    return TRUE;
1811     }
1812     return FALSE;
1813 }
1814 
PathWordBreakProc(LPCWSTR lpch,int ichCurrent,int cch,int code)1815 static int CALLBACK PathWordBreakProc(LPCWSTR lpch, int ichCurrent, int cch, int code)
1816 {
1817     if (code == WB_ISDELIMITER)
1818         return IsDelimiter(lpch[ichCurrent]);
1819     else
1820     {
1821         int dir = (code == WB_LEFT) ? -1 : 1;
1822         for(; 0 <= ichCurrent && ichCurrent < cch; ichCurrent += dir)
1823             if (IsDelimiter(lpch[ichCurrent])) return ichCurrent;
1824     }
1825     return ichCurrent;
1826 }
1827 
1828 /***********************************************************************
1829  * SetPathWordBreakProc [COMCTL32.384]
1830  *
1831  * Sets the word break procedure for an edit control to one that understands
1832  * paths so that the user can jump over directories.
1833  *
1834  * PARAMS
1835  *     hwnd [I] Handle to edit control.
1836  *     bSet [I] If this is TRUE then the word break proc is set, otherwise it is removed.
1837  *
1838  * RETURNS
1839  *     Result from EM_SETWORDBREAKPROC message.
1840  */
SetPathWordBreakProc(HWND hwnd,BOOL bSet)1841 LRESULT WINAPI SetPathWordBreakProc(HWND hwnd, BOOL bSet)
1842 {
1843     return SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0,
1844         (LPARAM)(bSet ? PathWordBreakProc : NULL));
1845 }
1846 
1847 /***********************************************************************
1848  * DrawShadowText [COMCTL32.@]
1849  *
1850  * Draw text with shadow.
1851  */
DrawShadowText(HDC hdc,LPCWSTR pszText,UINT cch,RECT * prc,DWORD dwFlags,COLORREF crText,COLORREF crShadow,int ixOffset,int iyOffset)1852 int WINAPI DrawShadowText(HDC hdc, LPCWSTR pszText, UINT cch, RECT *prc, DWORD dwFlags,
1853                           COLORREF crText, COLORREF crShadow, int ixOffset, int iyOffset)
1854 {
1855     COLORREF crOldText;
1856     RECT rcText;
1857     INT iRet, x, y, x2, y2;
1858     BYTE *pBits;
1859     HBITMAP hbm, hbmOld;
1860     BITMAPINFO bi;
1861     HDC hdcMem;
1862     HFONT hOldFont;
1863     BLENDFUNCTION bf;
1864 
1865     /* Create 32 bit DIB section for the shadow */
1866     ZeroMemory(&bi, sizeof(bi));
1867     bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
1868     bi.bmiHeader.biWidth = prc->right - prc->left + 4;
1869     bi.bmiHeader.biHeight = prc->bottom - prc->top + 5; // bottom-up DIB
1870     bi.bmiHeader.biPlanes = 1;
1871     bi.bmiHeader.biBitCount = 32;
1872     bi.bmiHeader.biCompression = BI_RGB;
1873     hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (PVOID*)&pBits, NULL, 0);
1874     if(!hbm)
1875     {
1876         ERR("CreateDIBSection failed\n");
1877         return 0;
1878     }
1879 
1880     /* Create memory device context for new DIB section and select it */
1881     hdcMem = CreateCompatibleDC(hdc);
1882     if(!hdcMem)
1883     {
1884         ERR("CreateCompatibleDC failed\n");
1885         DeleteObject(hbm);
1886         return 0;
1887     }
1888 
1889     hbmOld = (HBITMAP)SelectObject(hdcMem, hbm);
1890 
1891     /* Draw text on our helper bitmap */
1892     hOldFont = (HFONT)SelectObject(hdcMem, GetCurrentObject(hdc, OBJ_FONT));
1893     SetTextColor(hdcMem, RGB(16, 16, 16));
1894     SetBkColor(hdcMem, RGB(0, 0, 0));
1895     SetBkMode(hdcMem, TRANSPARENT);
1896     SetRect(&rcText, 0, 0, prc->right - prc->left, prc->bottom - prc->top);
1897     DrawTextW(hdcMem, pszText, cch, &rcText, dwFlags);
1898     SelectObject(hdcMem, hOldFont);
1899 
1900     /* Flush GDI so data pointed by pBits is valid */
1901     GdiFlush();
1902 
1903     /* Set alpha of pixels (forget about colors for now. They will be changed in next loop).
1904        We copy text image 4*5 times and each time alpha is added */
1905     for (x = 0; x < bi.bmiHeader.biWidth; ++x)
1906         for (y = 0; y < bi.bmiHeader.biHeight; ++y)
1907         {
1908             BYTE *pDest = &pBits[(y * bi.bmiHeader.biWidth + x) * 4];
1909             UINT Alpha = 0;
1910 
1911             for (x2 = x - 4 + 1; x2 <= x; ++x2)
1912                 for (y2 = y; y2 < y + 5; ++y2)
1913                 {
1914                     if (x2 >= 0 && x2 < bi.bmiHeader.biWidth && y2 >= 0 && y2 < bi.bmiHeader.biHeight)
1915                     {
1916                         BYTE *pSrc = &pBits[(y2 * bi.bmiHeader.biWidth + x2) * 4];
1917                         Alpha += pSrc[0];
1918                     }
1919                 }
1920 
1921             if (Alpha > 255)
1922                 Alpha = 255;
1923             pDest[3] = Alpha;
1924         }
1925 
1926     /* Now set the color of each pixel to shadow color * alpha (see GdiAlphaBlend) */
1927     for (x = 0; x < bi.bmiHeader.biWidth; ++x)
1928         for (y = 0; y < bi.bmiHeader.biHeight; ++y)
1929         {
1930             BYTE *pDest = &pBits[(y * bi.bmiHeader.biWidth + x) * 4];
1931             pDest[0] = GetBValue(crShadow) * pDest[3] / 255;
1932             pDest[1] = GetGValue(crShadow) * pDest[3] / 255;
1933             pDest[2] = GetRValue(crShadow) * pDest[3] / 255;
1934         }
1935 
1936     /* Fix ixOffset of the shadow (tested on Win) */
1937     ixOffset -= 3;
1938     iyOffset -= 3;
1939 
1940     /* Alpha blend helper image to destination DC */
1941     bf.BlendOp = AC_SRC_OVER;
1942     bf.BlendFlags = 0;
1943     bf.SourceConstantAlpha = 255;
1944     bf.AlphaFormat = AC_SRC_ALPHA;
1945     GdiAlphaBlend(hdc, prc->left + ixOffset, prc->top + iyOffset, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, hdcMem, 0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, bf);
1946 
1947     /* Delete the helper bitmap */
1948     SelectObject(hdcMem, hbmOld);
1949     DeleteObject(hbm);
1950     DeleteDC(hdcMem);
1951 
1952     /* Finally draw the text over shadow */
1953     crOldText = SetTextColor(hdc, crText);
1954     SetBkMode(hdc, TRANSPARENT);
1955     iRet = DrawTextW(hdc, pszText, cch, prc, dwFlags);
1956     SetTextColor(hdc, crOldText);
1957 
1958     return iRet;
1959 }
1960 
1961 /***********************************************************************
1962  * LoadIconWithScaleDown [COMCTL32.@]
1963  */
LoadIconWithScaleDown(HINSTANCE hinst,const WCHAR * name,int cx,int cy,HICON * icon)1964 HRESULT WINAPI LoadIconWithScaleDown(HINSTANCE hinst, const WCHAR *name, int cx, int cy, HICON *icon)
1965 {
1966     TRACE("(%p, %s, %d, %d, %p)\n", hinst, debugstr_w(name), cx, cy, icon);
1967 
1968     *icon = NULL;
1969 
1970     if (!name)
1971         return E_INVALIDARG;
1972 
1973     *icon = LoadImageW(hinst, name, IMAGE_ICON, cx, cy,
1974                        (hinst || IS_INTRESOURCE(name)) ? 0 : LR_LOADFROMFILE);
1975     if (!*icon)
1976         return HRESULT_FROM_WIN32(GetLastError());
1977 
1978     return S_OK;
1979 }
1980 
1981 /***********************************************************************
1982  * LoadIconMetric [COMCTL32.@]
1983  */
LoadIconMetric(HINSTANCE hinst,const WCHAR * name,int size,HICON * icon)1984 HRESULT WINAPI LoadIconMetric(HINSTANCE hinst, const WCHAR *name, int size, HICON *icon)
1985 {
1986     int cx, cy;
1987 
1988     TRACE("(%p, %s, %d, %p)\n", hinst, debugstr_w(name), size, icon);
1989 
1990     if (size == LIM_SMALL)
1991     {
1992         cx = GetSystemMetrics(SM_CXSMICON);
1993         cy = GetSystemMetrics(SM_CYSMICON);
1994     }
1995     else if (size == LIM_LARGE)
1996     {
1997         cx = GetSystemMetrics(SM_CXICON);
1998         cy = GetSystemMetrics(SM_CYICON);
1999     }
2000     else
2001     {
2002         *icon = NULL;
2003         return E_INVALIDARG;
2004     }
2005 
2006     return LoadIconWithScaleDown(hinst, name, cx, cy, icon);
2007 }
2008