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