4 * Copyright 1995 Bernd Schmidt
5 * Copyright 2004 Ivan Leo Puoti, Juan Lang
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "user_private.h"
31 #include "resources.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(dialog
);
35 WINE_DECLARE_DEBUG_CHANNEL(msgbox
);
44 static BOOL CALLBACK
MSGBOX_EnumProc(HWND hwnd
, LPARAM lParam
)
46 struct ThreadWindows
*threadWindows
= (struct ThreadWindows
*)lParam
;
48 if (!EnableWindow(hwnd
, FALSE
))
50 if(threadWindows
->numHandles
>= threadWindows
->numAllocs
)
52 threadWindows
->handles
= HeapReAlloc(GetProcessHeap(), 0, threadWindows
->handles
,
53 (threadWindows
->numAllocs
*2)*sizeof(HWND
));
54 threadWindows
->numAllocs
*= 2;
56 threadWindows
->handles
[threadWindows
->numHandles
++]=hwnd
;
61 static void MSGBOX_OnInit(HWND hwnd
, LPMSGBOXPARAMSW lpmb
)
68 int bspace
, bw
, bh
, theight
, tleft
, wwidth
, wheight
, wleft
, wtop
, bpos
;
69 int borheight
, borwidth
, iheight
, ileft
, iwidth
, twidth
, tiheight
;
70 NONCLIENTMETRICSW nclm
;
77 /* Index the order the buttons need to appear to an ID* constant */
78 static const int buttonOrder
[10] = { IDYES
, IDNO
, IDOK
, IDABORT
, IDRETRY
,
79 IDCANCEL
, IDIGNORE
, IDTRYAGAIN
,
82 nclm
.cbSize
= sizeof(nclm
);
83 SystemParametersInfoW (SPI_GETNONCLIENTMETRICS
, 0, &nclm
, 0);
85 if (!IS_INTRESOURCE(lpmb
->lpszCaption
)) {
86 SetWindowTextW(hwnd
, lpmb
->lpszCaption
);
88 UINT len
= LoadStringW( lpmb
->hInstance
, LOWORD(lpmb
->lpszCaption
), (LPWSTR
)&ptr
, 0 );
89 if (!len
) len
= LoadStringW( user32_module
, IDS_ERROR
, (LPWSTR
)&ptr
, 0 );
90 buffer
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
93 memcpy( buffer
, ptr
, len
* sizeof(WCHAR
) );
95 SetWindowTextW( hwnd
, buffer
);
96 HeapFree( GetProcessHeap(), 0, buffer
);
100 if (IS_INTRESOURCE(lpmb
->lpszText
)) {
101 UINT len
= LoadStringW( lpmb
->hInstance
, LOWORD(lpmb
->lpszText
), (LPWSTR
)&ptr
, 0 );
102 lpszText
= buffer
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
105 memcpy( buffer
, ptr
, len
* sizeof(WCHAR
) );
109 lpszText
= lpmb
->lpszText
;
112 /* handle modal message boxes */
113 if (lpmb
->dwStyle
& MB_TASKMODAL
&& lpmb
->hwndOwner
== NULL
)
114 NtUserSetWindowPos( hwnd
, lpmb
->dwStyle
& MB_TOPMOST
? HWND_TOPMOST
: HWND_TOP
, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
);
115 else if (lpmb
->dwStyle
& MB_SYSTEMMODAL
)
116 NtUserSetWindowPos( hwnd
, HWND_TOPMOST
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
| SWP_FRAMECHANGED
);
117 else if (lpmb
->dwStyle
& MB_TOPMOST
)
118 NtUserSetWindowPos( hwnd
, HWND_TOPMOST
, 0, 0, 0, 0, SWP_NOMOVE
| SWP_NOSIZE
);
120 TRACE_(msgbox
)("%s\n", debugstr_w(lpszText
));
121 SetWindowTextW(GetDlgItem(hwnd
, MSGBOX_IDTEXT
), lpszText
);
123 /* Remove not selected buttons and assign the WS_GROUP style to the first button */
125 switch(lpmb
->dwStyle
& MB_TYPEMASK
) {
127 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCANCEL
));
130 hItem
= GetDlgItem(hwnd
, IDOK
);
131 NtUserDestroyWindow(GetDlgItem(hwnd
, IDABORT
));
132 NtUserDestroyWindow(GetDlgItem(hwnd
, IDRETRY
));
133 NtUserDestroyWindow(GetDlgItem(hwnd
, IDIGNORE
));
134 NtUserDestroyWindow(GetDlgItem(hwnd
, IDYES
));
135 NtUserDestroyWindow(GetDlgItem(hwnd
, IDNO
));
136 NtUserDestroyWindow(GetDlgItem(hwnd
, IDTRYAGAIN
));
137 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCONTINUE
));
139 case MB_ABORTRETRYIGNORE
:
140 hItem
= GetDlgItem(hwnd
, IDABORT
);
141 NtUserDestroyWindow(GetDlgItem(hwnd
, IDOK
));
142 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCANCEL
));
143 NtUserDestroyWindow(GetDlgItem(hwnd
, IDYES
));
144 NtUserDestroyWindow(GetDlgItem(hwnd
, IDNO
));
145 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCONTINUE
));
146 NtUserDestroyWindow(GetDlgItem(hwnd
, IDTRYAGAIN
));
149 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCANCEL
));
152 hItem
= GetDlgItem(hwnd
, IDYES
);
153 NtUserDestroyWindow(GetDlgItem(hwnd
, IDOK
));
154 NtUserDestroyWindow(GetDlgItem(hwnd
, IDABORT
));
155 NtUserDestroyWindow(GetDlgItem(hwnd
, IDRETRY
));
156 NtUserDestroyWindow(GetDlgItem(hwnd
, IDIGNORE
));
157 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCONTINUE
));
158 NtUserDestroyWindow(GetDlgItem(hwnd
, IDTRYAGAIN
));
161 hItem
= GetDlgItem(hwnd
, IDRETRY
);
162 NtUserDestroyWindow(GetDlgItem(hwnd
, IDOK
));
163 NtUserDestroyWindow(GetDlgItem(hwnd
, IDABORT
));
164 NtUserDestroyWindow(GetDlgItem(hwnd
, IDIGNORE
));
165 NtUserDestroyWindow(GetDlgItem(hwnd
, IDYES
));
166 NtUserDestroyWindow(GetDlgItem(hwnd
, IDNO
));
167 NtUserDestroyWindow(GetDlgItem(hwnd
, IDCONTINUE
));
168 NtUserDestroyWindow(GetDlgItem(hwnd
, IDTRYAGAIN
));
170 case MB_CANCELTRYCONTINUE
:
171 hItem
= GetDlgItem(hwnd
, IDCANCEL
);
172 NtUserDestroyWindow(GetDlgItem(hwnd
, IDOK
));
173 NtUserDestroyWindow(GetDlgItem(hwnd
, IDABORT
));
174 NtUserDestroyWindow(GetDlgItem(hwnd
, IDIGNORE
));
175 NtUserDestroyWindow(GetDlgItem(hwnd
, IDYES
));
176 NtUserDestroyWindow(GetDlgItem(hwnd
, IDNO
));
177 NtUserDestroyWindow(GetDlgItem(hwnd
, IDRETRY
));
180 if (hItem
) SetWindowLongW(hItem
, GWL_STYLE
, GetWindowLongW(hItem
, GWL_STYLE
) | WS_GROUP
);
183 switch(lpmb
->dwStyle
& MB_ICONMASK
) {
184 case MB_ICONEXCLAMATION
:
185 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
,
186 (WPARAM
)LoadIconW(0, (LPWSTR
)IDI_EXCLAMATION
), 0);
188 case MB_ICONQUESTION
:
189 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
,
190 (WPARAM
)LoadIconW(0, (LPWSTR
)IDI_QUESTION
), 0);
192 case MB_ICONASTERISK
:
193 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
,
194 (WPARAM
)LoadIconW(0, (LPWSTR
)IDI_ASTERISK
), 0);
197 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
,
198 (WPARAM
)LoadIconW(0, (LPWSTR
)IDI_HAND
), 0);
201 SendDlgItemMessageW(hwnd
, MSGBOX_IDICON
, STM_SETICON
,
202 (WPARAM
)LoadIconW(lpmb
->hInstance
, lpmb
->lpszIcon
), 0);
205 /* By default, Windows 95/98/NT do not associate an icon to message boxes.
206 * So wine should do the same.
211 /* Remove Help button unless MB_HELP supplied */
212 if (!(lpmb
->dwStyle
& MB_HELP
)) {
213 NtUserDestroyWindow(GetDlgItem(hwnd
, IDHELP
));
216 /* Position everything */
217 GetWindowRect(hwnd
, &rect
);
218 borheight
= rect
.bottom
- rect
.top
;
219 borwidth
= rect
.right
- rect
.left
;
220 GetClientRect(hwnd
, &rect
);
221 borheight
-= rect
.bottom
- rect
.top
;
222 borwidth
-= rect
.right
- rect
.left
;
224 /* Get the icon height */
225 GetWindowRect(GetDlgItem(hwnd
, MSGBOX_IDICON
), &rect
);
226 MapWindowPoints(0, hwnd
, (LPPOINT
)&rect
, 2);
227 if (!(lpmb
->dwStyle
& MB_ICONMASK
))
229 rect
.bottom
= rect
.top
;
230 rect
.right
= rect
.left
;
232 iheight
= rect
.bottom
- rect
.top
;
234 iwidth
= rect
.right
- ileft
;
236 hdc
= NtUserGetDC(hwnd
);
237 hPrevFont
= SelectObject( hdc
, (HFONT
)SendMessageW( hwnd
, WM_GETFONT
, 0, 0 ));
239 /* Get the number of visible buttons and their size */
240 bh
= bw
= 1; /* Minimum button sizes */
241 for (buttons
= 0, i
= IDOK
; i
<= IDCONTINUE
; i
++)
243 if (i
== IDCLOSE
) continue; /* No CLOSE button */
244 hItem
= GetDlgItem(hwnd
, i
);
245 if (GetWindowLongW(hItem
, GWL_STYLE
) & WS_VISIBLE
)
247 WCHAR buttonText
[1024];
250 if (GetWindowTextW(hItem
, buttonText
, 1024))
252 DrawTextW( hdc
, buttonText
, -1, &rect
, DT_LEFT
| DT_EXPANDTABS
| DT_CALCRECT
);
253 h
= rect
.bottom
- rect
.top
;
254 w
= rect
.right
- rect
.left
;
260 bw
= max(bw
, bh
* 2);
261 /* Button white space */
264 bspace
= bw
/3; /* Space between buttons */
266 /* Get the text size */
267 GetClientRect(GetDlgItem(hwnd
, MSGBOX_IDTEXT
), &rect
);
268 rect
.top
= rect
.left
= rect
.bottom
= 0;
269 DrawTextW(hdc
, lpszText
, -1, &rect
,
270 DT_LEFT
| DT_EXPANDTABS
| DT_WORDBREAK
| DT_CALCRECT
| DT_NOPREFIX
);
271 /* Min text width corresponds to space for the buttons */
273 if (iwidth
) tleft
+= ileft
+ iwidth
;
274 twidth
= max((bw
+ bspace
) * buttons
+ bspace
- tleft
, rect
.right
);
275 theight
= rect
.bottom
;
277 SelectObject(hdc
, hPrevFont
);
278 NtUserReleaseDC( hwnd
, hdc
);
280 tiheight
= 16 + max(iheight
, theight
);
281 wwidth
= tleft
+ twidth
+ ileft
+ borwidth
;
282 wheight
= 8 + tiheight
+ bh
+ borheight
;
284 /* Message boxes are always desktop centered, so query desktop size and center window */
285 monitor
= MonitorFromWindow(lpmb
->hwndOwner
? lpmb
->hwndOwner
: GetActiveWindow(), MONITOR_DEFAULTTOPRIMARY
);
286 mon_info
.cbSize
= sizeof(mon_info
);
287 GetMonitorInfoW(monitor
, &mon_info
);
288 wleft
= (mon_info
.rcWork
.left
+ mon_info
.rcWork
.right
- wwidth
) / 2;
289 wtop
= (mon_info
.rcWork
.top
+ mon_info
.rcWork
.bottom
- wheight
) / 2;
291 /* Resize and center the window */
292 NtUserSetWindowPos( hwnd
, 0, wleft
, wtop
, wwidth
, wheight
,
293 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
295 /* Position the icon */
296 NtUserSetWindowPos( GetDlgItem(hwnd
, MSGBOX_IDICON
), 0, ileft
, (tiheight
- iheight
) / 2, 0, 0,
297 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
299 /* Position the text */
300 NtUserSetWindowPos( GetDlgItem(hwnd
, MSGBOX_IDTEXT
), 0, tleft
, (tiheight
- theight
) / 2, twidth
, theight
,
301 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
303 /* Position the buttons */
304 bpos
= (wwidth
- (bw
+ bspace
) * buttons
+ bspace
) / 2;
305 for (buttons
= i
= 0; i
< ARRAY_SIZE(buttonOrder
); i
++) {
307 /* Convert the button order to ID* value to order for the buttons */
308 hItem
= GetDlgItem(hwnd
, buttonOrder
[i
]);
309 if (GetWindowLongW(hItem
, GWL_STYLE
) & WS_VISIBLE
) {
310 if (buttons
++ == ((lpmb
->dwStyle
& MB_DEFMASK
) >> 8)) {
311 NtUserSetFocus(hItem
);
312 SendMessageW( hItem
, BM_SETSTYLE
, BS_DEFPUSHBUTTON
, TRUE
);
314 NtUserSetWindowPos( hItem
, 0, bpos
, tiheight
, bw
, bh
,
315 SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOREDRAW
);
320 HeapFree( GetProcessHeap(), 0, buffer
);
324 /**************************************************************************
327 * Dialog procedure for message boxes.
329 static INT_PTR CALLBACK
MSGBOX_DlgProc( HWND hwnd
, UINT message
,
330 WPARAM wParam
, LPARAM lParam
)
335 LPMSGBOXPARAMSW mbp
= (LPMSGBOXPARAMSW
)lParam
;
336 SetWindowContextHelpId(hwnd
, mbp
->dwContextHelpId
);
337 MSGBOX_OnInit(hwnd
, mbp
);
338 SetPropA(hwnd
, "WINE_MSGBOX_HELPCALLBACK", mbp
->lpfnMsgBoxCallback
);
343 switch (LOWORD(wParam
))
354 EndDialog(hwnd
, wParam
);
357 FIXME("Help button not supported yet\n");
364 MSGBOXCALLBACK callback
= (MSGBOXCALLBACK
)GetPropA(hwnd
, "WINE_MSGBOX_HELPCALLBACK");
367 memcpy(&hi
, (void *)lParam
, sizeof(hi
));
368 hi
.dwContextId
= GetWindowContextHelpId(hwnd
);
373 SendMessageW(GetWindow(hwnd
, GW_OWNER
), WM_HELP
, 0, (LPARAM
)&hi
);
378 /* Ok. Ignore all the other messages */
379 TRACE("Message number 0x%04x is being ignored.\n", message
);
386 /**************************************************************************
387 * MessageBoxA (USER32.@)
389 INT WINAPI
MessageBoxA(HWND hWnd
, LPCSTR text
, LPCSTR title
, UINT type
)
391 return MessageBoxExA(hWnd
, text
, title
, type
, LANG_NEUTRAL
);
395 /**************************************************************************
396 * MessageBoxW (USER32.@)
398 INT WINAPI
MessageBoxW( HWND hwnd
, LPCWSTR text
, LPCWSTR title
, UINT type
)
400 return MessageBoxExW(hwnd
, text
, title
, type
, LANG_NEUTRAL
);
404 /**************************************************************************
405 * MessageBoxExA (USER32.@)
407 INT WINAPI
MessageBoxExA( HWND hWnd
, LPCSTR text
, LPCSTR title
,
408 UINT type
, WORD langid
)
410 MSGBOXPARAMSA msgbox
;
412 msgbox
.cbSize
= sizeof(msgbox
);
413 msgbox
.hwndOwner
= hWnd
;
414 msgbox
.hInstance
= 0;
415 msgbox
.lpszText
= text
;
416 msgbox
.lpszCaption
= title
;
417 msgbox
.dwStyle
= type
;
418 msgbox
.lpszIcon
= NULL
;
419 msgbox
.dwContextHelpId
= 0;
420 msgbox
.lpfnMsgBoxCallback
= NULL
;
421 msgbox
.dwLanguageId
= langid
;
423 return MessageBoxIndirectA(&msgbox
);
426 /**************************************************************************
427 * MessageBoxExW (USER32.@)
429 INT WINAPI
MessageBoxExW( HWND hWnd
, LPCWSTR text
, LPCWSTR title
,
430 UINT type
, WORD langid
)
432 MSGBOXPARAMSW msgbox
;
434 msgbox
.cbSize
= sizeof(msgbox
);
435 msgbox
.hwndOwner
= hWnd
;
436 msgbox
.hInstance
= 0;
437 msgbox
.lpszText
= text
;
438 msgbox
.lpszCaption
= title
;
439 msgbox
.dwStyle
= type
;
440 msgbox
.lpszIcon
= NULL
;
441 msgbox
.dwContextHelpId
= 0;
442 msgbox
.lpfnMsgBoxCallback
= NULL
;
443 msgbox
.dwLanguageId
= langid
;
445 return MessageBoxIndirectW(&msgbox
);
448 /**************************************************************************
449 * MessageBoxTimeoutA (USER32.@)
451 INT WINAPI
MessageBoxTimeoutA( HWND hWnd
, LPCSTR text
, LPCSTR title
,
452 UINT type
, WORD langid
, DWORD timeout
)
454 FIXME("timeout not supported (%lu)\n", timeout
);
455 return MessageBoxExA( hWnd
, text
, title
, type
, langid
);
458 /**************************************************************************
459 * MessageBoxTimeoutW (USER32.@)
461 INT WINAPI
MessageBoxTimeoutW( HWND hWnd
, LPCWSTR text
, LPCWSTR title
,
462 UINT type
, WORD langid
, DWORD timeout
)
464 FIXME("timeout not supported (%lu)\n", timeout
);
465 return MessageBoxExW( hWnd
, text
, title
, type
, langid
);
468 /**************************************************************************
469 * MessageBoxIndirectA (USER32.@)
471 INT WINAPI
MessageBoxIndirectA( LPMSGBOXPARAMSA msgbox
)
473 MSGBOXPARAMSW msgboxW
;
474 UNICODE_STRING textW
, captionW
, iconW
;
477 if (IS_INTRESOURCE(msgbox
->lpszText
))
478 textW
.Buffer
= (LPWSTR
)msgbox
->lpszText
;
480 RtlCreateUnicodeStringFromAsciiz(&textW
, msgbox
->lpszText
);
481 if (IS_INTRESOURCE(msgbox
->lpszCaption
))
482 captionW
.Buffer
= (LPWSTR
)msgbox
->lpszCaption
;
484 RtlCreateUnicodeStringFromAsciiz(&captionW
, msgbox
->lpszCaption
);
486 if (msgbox
->dwStyle
& MB_USERICON
)
488 if (IS_INTRESOURCE(msgbox
->lpszIcon
))
489 iconW
.Buffer
= (LPWSTR
)msgbox
->lpszIcon
;
491 RtlCreateUnicodeStringFromAsciiz(&iconW
, msgbox
->lpszIcon
);
496 msgboxW
.cbSize
= sizeof(msgboxW
);
497 msgboxW
.hwndOwner
= msgbox
->hwndOwner
;
498 msgboxW
.hInstance
= msgbox
->hInstance
;
499 msgboxW
.lpszText
= textW
.Buffer
;
500 msgboxW
.lpszCaption
= captionW
.Buffer
;
501 msgboxW
.dwStyle
= msgbox
->dwStyle
;
502 msgboxW
.lpszIcon
= iconW
.Buffer
;
503 msgboxW
.dwContextHelpId
= msgbox
->dwContextHelpId
;
504 msgboxW
.lpfnMsgBoxCallback
= msgbox
->lpfnMsgBoxCallback
;
505 msgboxW
.dwLanguageId
= msgbox
->dwLanguageId
;
507 ret
= MessageBoxIndirectW(&msgboxW
);
509 if (!IS_INTRESOURCE(textW
.Buffer
)) RtlFreeUnicodeString(&textW
);
510 if (!IS_INTRESOURCE(captionW
.Buffer
)) RtlFreeUnicodeString(&captionW
);
511 if (!IS_INTRESOURCE(iconW
.Buffer
)) RtlFreeUnicodeString(&iconW
);
515 /**************************************************************************
516 * MessageBoxIndirectW (USER32.@)
518 INT WINAPI
MessageBoxIndirectW( LPMSGBOXPARAMSW msgbox
)
524 struct ThreadWindows threadWindows
;
526 if (!(hRes
= FindResourceExW(user32_module
, (LPWSTR
)RT_DIALOG
, L
"MSGBOX", msgbox
->dwLanguageId
)))
528 if (!msgbox
->dwLanguageId
||
529 !(hRes
= FindResourceExW(user32_module
, (LPWSTR
)RT_DIALOG
, L
"MSGBOX", LANG_NEUTRAL
)))
532 if (!(tmplate
= LoadResource(user32_module
, hRes
)))
535 if ((msgbox
->dwStyle
& MB_TASKMODAL
) && (msgbox
->hwndOwner
==NULL
))
537 threadWindows
.numHandles
= 0;
538 threadWindows
.numAllocs
= 10;
539 threadWindows
.handles
= HeapAlloc(GetProcessHeap(), 0, 10*sizeof(HWND
));
540 EnumThreadWindows(GetCurrentThreadId(), MSGBOX_EnumProc
, (LPARAM
)&threadWindows
);
543 ret
=DialogBoxIndirectParamW(msgbox
->hInstance
, tmplate
,
544 msgbox
->hwndOwner
, MSGBOX_DlgProc
, (LPARAM
)msgbox
);
546 if ((msgbox
->dwStyle
& MB_TASKMODAL
) && (msgbox
->hwndOwner
==NULL
))
548 for (i
= 0; i
< threadWindows
.numHandles
; i
++)
549 EnableWindow(threadWindows
.handles
[i
], TRUE
);
550 HeapFree(GetProcessHeap(), 0, threadWindows
.handles
);