user32/tests: Fix the listbox tests on Win9x and NT4.
[wine/multimedia.git] / dlls / user32 / msgbox.c
blobd9cec530a4117a35560951420ec568039970a4a7
1 /*
2 * Message boxes
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
22 #include <stdarg.h>
23 #include <string.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winternl.h"
29 #include "dlgs.h"
30 #include "user_private.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dialog);
34 WINE_DECLARE_DEBUG_CHANNEL(msgbox);
36 #define MSGBOX_IDICON 1088
37 #define MSGBOX_IDTEXT 100
38 #define IDS_ERROR 2
40 struct ThreadWindows
42 UINT numHandles;
43 UINT numAllocs;
44 HWND *handles;
47 static BOOL CALLBACK MSGBOX_EnumProc(HWND hwnd, LPARAM lParam)
49 struct ThreadWindows *threadWindows = (struct ThreadWindows *)lParam;
51 if (!EnableWindow(hwnd, FALSE))
53 if(threadWindows->numHandles >= threadWindows->numAllocs)
55 threadWindows->handles = HeapReAlloc(GetProcessHeap(), 0, threadWindows->handles,
56 (threadWindows->numAllocs*2)*sizeof(HWND));
57 threadWindows->numAllocs *= 2;
59 threadWindows->handles[threadWindows->numHandles++]=hwnd;
61 return TRUE;
64 static HFONT MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMSW lpmb)
66 HFONT hFont = 0, hPrevFont = 0;
67 RECT rect;
68 HWND hItem;
69 HDC hdc;
70 int i, buttons;
71 int bspace, bw, bh, theight, tleft, wwidth, wheight, wleft, wtop, bpos;
72 int borheight, borwidth, iheight, ileft, iwidth, twidth, tiheight;
73 NONCLIENTMETRICSW nclm;
74 HMONITOR monitor = 0;
75 MONITORINFO mon_info;
76 LPCWSTR lpszText;
77 WCHAR *buffer = NULL;
78 const WCHAR *ptr;
80 /* Index the order the buttons need to appear to an ID* constant */
81 static const int buttonOrder[10] = { 6, 7, 1, 3, 4, 2, 5, 10, 11, 9 };
83 nclm.cbSize = sizeof(nclm);
84 SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, 0, &nclm, 0);
85 hFont = CreateFontIndirectW (&nclm.lfMessageFont);
86 /* set button font */
87 for (i=1; i < 12; i++)
88 /* No button 8 (Close) */
89 if (i != 8) {
90 SendDlgItemMessageW (hwnd, i, WM_SETFONT, (WPARAM)hFont, 0);
92 /* set text font */
93 SendDlgItemMessageW (hwnd, MSGBOX_IDTEXT, WM_SETFONT, (WPARAM)hFont, 0);
95 if (HIWORD(lpmb->lpszCaption)) {
96 SetWindowTextW(hwnd, lpmb->lpszCaption);
97 } else {
98 UINT len = LoadStringW( lpmb->hInstance, LOWORD(lpmb->lpszCaption), (LPWSTR)&ptr, 0 );
99 if (!len) len = LoadStringW( user32_module, IDS_ERROR, (LPWSTR)&ptr, 0 );
100 buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
101 if (buffer)
103 memcpy( buffer, ptr, len * sizeof(WCHAR) );
104 buffer[len] = 0;
105 SetWindowTextW( hwnd, buffer );
106 HeapFree( GetProcessHeap(), 0, buffer );
107 buffer = NULL;
110 if (HIWORD(lpmb->lpszText)) {
111 lpszText = lpmb->lpszText;
112 } else {
113 UINT len = LoadStringW( lpmb->hInstance, LOWORD(lpmb->lpszText), (LPWSTR)&ptr, 0 );
114 lpszText = buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
115 if (buffer)
117 memcpy( buffer, ptr, len * sizeof(WCHAR) );
118 buffer[len] = 0;
122 TRACE_(msgbox)("%s\n", debugstr_w(lpszText));
123 SetWindowTextW(GetDlgItem(hwnd, MSGBOX_IDTEXT), lpszText);
125 /* Hide not selected buttons */
126 switch(lpmb->dwStyle & MB_TYPEMASK) {
127 case MB_OK:
128 ShowWindow(GetDlgItem(hwnd, IDCANCEL), SW_HIDE);
129 /* fall through */
130 case MB_OKCANCEL:
131 ShowWindow(GetDlgItem(hwnd, IDABORT), SW_HIDE);
132 ShowWindow(GetDlgItem(hwnd, IDRETRY), SW_HIDE);
133 ShowWindow(GetDlgItem(hwnd, IDIGNORE), SW_HIDE);
134 ShowWindow(GetDlgItem(hwnd, IDYES), SW_HIDE);
135 ShowWindow(GetDlgItem(hwnd, IDNO), SW_HIDE);
136 ShowWindow(GetDlgItem(hwnd, IDTRYAGAIN), SW_HIDE);
137 ShowWindow(GetDlgItem(hwnd, IDCONTINUE), SW_HIDE);
138 break;
139 case MB_ABORTRETRYIGNORE:
140 ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE);
141 ShowWindow(GetDlgItem(hwnd, IDCANCEL), SW_HIDE);
142 ShowWindow(GetDlgItem(hwnd, IDYES), SW_HIDE);
143 ShowWindow(GetDlgItem(hwnd, IDNO), SW_HIDE);
144 ShowWindow(GetDlgItem(hwnd, IDCONTINUE), SW_HIDE);
145 ShowWindow(GetDlgItem(hwnd, IDTRYAGAIN), SW_HIDE);
146 break;
147 case MB_YESNO:
148 ShowWindow(GetDlgItem(hwnd, IDCANCEL), SW_HIDE);
149 /* fall through */
150 case MB_YESNOCANCEL:
151 ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE);
152 ShowWindow(GetDlgItem(hwnd, IDABORT), SW_HIDE);
153 ShowWindow(GetDlgItem(hwnd, IDRETRY), SW_HIDE);
154 ShowWindow(GetDlgItem(hwnd, IDIGNORE), SW_HIDE);
155 ShowWindow(GetDlgItem(hwnd, IDCONTINUE), SW_HIDE);
156 ShowWindow(GetDlgItem(hwnd, IDTRYAGAIN), SW_HIDE);
157 break;
158 case MB_RETRYCANCEL:
159 ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE);
160 ShowWindow(GetDlgItem(hwnd, IDABORT), SW_HIDE);
161 ShowWindow(GetDlgItem(hwnd, IDIGNORE), SW_HIDE);
162 ShowWindow(GetDlgItem(hwnd, IDYES), SW_HIDE);
163 ShowWindow(GetDlgItem(hwnd, IDNO), SW_HIDE);
164 ShowWindow(GetDlgItem(hwnd, IDCONTINUE), SW_HIDE);
165 ShowWindow(GetDlgItem(hwnd, IDTRYAGAIN), SW_HIDE);
166 break;
167 case MB_CANCELTRYCONTINUE:
168 ShowWindow(GetDlgItem(hwnd, IDOK), SW_HIDE);
169 ShowWindow(GetDlgItem(hwnd, IDABORT), SW_HIDE);
170 ShowWindow(GetDlgItem(hwnd, IDIGNORE), SW_HIDE);
171 ShowWindow(GetDlgItem(hwnd, IDYES), SW_HIDE);
172 ShowWindow(GetDlgItem(hwnd, IDNO), SW_HIDE);
173 ShowWindow(GetDlgItem(hwnd, IDRETRY), SW_HIDE);
175 /* Set the icon */
176 switch(lpmb->dwStyle & MB_ICONMASK) {
177 case MB_ICONEXCLAMATION:
178 SendDlgItemMessageW(hwnd, stc1, STM_SETICON,
179 (WPARAM)LoadIconW(0, (LPWSTR)IDI_EXCLAMATION), 0);
180 break;
181 case MB_ICONQUESTION:
182 SendDlgItemMessageW(hwnd, stc1, STM_SETICON,
183 (WPARAM)LoadIconW(0, (LPWSTR)IDI_QUESTION), 0);
184 break;
185 case MB_ICONASTERISK:
186 SendDlgItemMessageW(hwnd, stc1, STM_SETICON,
187 (WPARAM)LoadIconW(0, (LPWSTR)IDI_ASTERISK), 0);
188 break;
189 case MB_ICONHAND:
190 SendDlgItemMessageW(hwnd, stc1, STM_SETICON,
191 (WPARAM)LoadIconW(0, (LPWSTR)IDI_HAND), 0);
192 break;
193 case MB_USERICON:
194 SendDlgItemMessageW(hwnd, stc1, STM_SETICON,
195 (WPARAM)LoadIconW(lpmb->hInstance, lpmb->lpszIcon), 0);
196 break;
197 default:
198 /* By default, Windows 95/98/NT do not associate an icon to message boxes.
199 * So wine should do the same.
201 break;
204 /* Hide Help button unless MB_HELP supplied */
205 if (!(lpmb->dwStyle & MB_HELP)) {
206 ShowWindow(GetDlgItem(hwnd, IDHELP), SW_HIDE);
209 /* Position everything */
210 GetWindowRect(hwnd, &rect);
211 borheight = rect.bottom - rect.top;
212 borwidth = rect.right - rect.left;
213 GetClientRect(hwnd, &rect);
214 borheight -= rect.bottom - rect.top;
215 borwidth -= rect.right - rect.left;
217 /* Get the icon height */
218 GetWindowRect(GetDlgItem(hwnd, MSGBOX_IDICON), &rect);
219 MapWindowPoints(0, hwnd, (LPPOINT)&rect, 2);
220 if (!(lpmb->dwStyle & MB_ICONMASK))
222 rect.bottom = rect.top;
223 rect.right = rect.left;
225 iheight = rect.bottom - rect.top;
226 ileft = rect.left;
227 iwidth = rect.right - ileft;
229 hdc = GetDC(hwnd);
230 if (hFont)
231 hPrevFont = SelectObject(hdc, hFont);
233 /* Get the number of visible buttons and their size */
234 bh = bw = 1; /* Minimum button sizes */
235 for (buttons = 0, i = 1; i < 12; i++)
237 if (i == 8) continue; /* No CLOSE button */
238 hItem = GetDlgItem(hwnd, i);
239 if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE)
241 WCHAR buttonText[1024];
242 int w, h;
243 buttons++;
244 if (GetWindowTextW(hItem, buttonText, 1024))
246 DrawTextW( hdc, buttonText, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT);
247 h = rect.bottom - rect.top;
248 w = rect.right - rect.left;
249 if (h > bh) bh = h;
250 if (w > bw) bw = w ;
254 bw = max(bw, bh * 2);
255 /* Button white space */
256 bh = bh * 2;
257 bw = bw * 2;
258 bspace = bw/3; /* Space between buttons */
260 /* Get the text size */
261 GetClientRect(GetDlgItem(hwnd, MSGBOX_IDTEXT), &rect);
262 rect.top = rect.left = rect.bottom = 0;
263 DrawTextW( hdc, lpszText, -1, &rect,
264 DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT);
265 /* Min text width corresponds to space for the buttons */
266 tleft = ileft;
267 if (iwidth) tleft += ileft + iwidth;
268 twidth = max((bw + bspace) * buttons + bspace - tleft, rect.right);
269 theight = rect.bottom;
271 if (hFont)
272 SelectObject(hdc, hPrevFont);
273 ReleaseDC(hwnd, hdc);
275 tiheight = 16 + max(iheight, theight);
276 wwidth = tleft + twidth + ileft + borwidth;
277 wheight = 8 + tiheight + bh + borheight;
279 /* Message boxes are always desktop centered, so query desktop size and center window */
280 monitor = MonitorFromWindow(lpmb->hwndOwner ? lpmb->hwndOwner : GetActiveWindow(), MONITOR_DEFAULTTOPRIMARY);
281 mon_info.cbSize = sizeof(mon_info);
282 GetMonitorInfoW(monitor, &mon_info);
283 wleft = (mon_info.rcWork.left + mon_info.rcWork.right - wwidth) / 2;
284 wtop = (mon_info.rcWork.top + mon_info.rcWork.bottom - wheight) / 2;
286 /* Resize and center the window */
287 SetWindowPos(hwnd, 0, wleft, wtop, wwidth, wheight,
288 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
290 /* Position the icon */
291 SetWindowPos(GetDlgItem(hwnd, MSGBOX_IDICON), 0, ileft, (tiheight - iheight) / 2, 0, 0,
292 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
294 /* Position the text */
295 SetWindowPos(GetDlgItem(hwnd, MSGBOX_IDTEXT), 0, tleft, (tiheight - theight) / 2, twidth, theight,
296 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
298 /* Position the buttons */
299 bpos = (wwidth - (bw + bspace) * buttons + bspace) / 2;
300 for (buttons = i = 0; i < (sizeof(buttonOrder) / sizeof(buttonOrder[0])); i++) {
302 /* Convert the button order to ID* value to order for the buttons */
303 hItem = GetDlgItem(hwnd, buttonOrder[i]);
304 if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE) {
305 if (buttons++ == ((lpmb->dwStyle & MB_DEFMASK) >> 8)) {
306 SetFocus(hItem);
307 SendMessageW( hItem, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE );
309 SetWindowPos(hItem, 0, bpos, tiheight, bw, bh,
310 SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
311 bpos += bw + bspace;
315 /*handle modal message boxes*/
316 if (((lpmb->dwStyle & MB_TASKMODAL) && (lpmb->hwndOwner==NULL)) || (lpmb->dwStyle & MB_SYSTEMMODAL))
317 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
319 HeapFree( GetProcessHeap(), 0, buffer );
320 return hFont;
324 /**************************************************************************
325 * MSGBOX_DlgProc
327 * Dialog procedure for message boxes.
329 static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message,
330 WPARAM wParam, LPARAM lParam )
332 HFONT hFont;
334 switch(message) {
335 case WM_INITDIALOG:
337 LPMSGBOXPARAMSW mbp = (LPMSGBOXPARAMSW)lParam;
338 SetWindowContextHelpId(hwnd, mbp->dwContextHelpId);
339 hFont = MSGBOX_OnInit(hwnd, mbp);
340 SetPropA(hwnd, "WINE_MSGBOX_HFONT", (HANDLE)hFont);
341 SetPropA(hwnd, "WINE_MSGBOX_HELPCALLBACK", (HANDLE)mbp->lpfnMsgBoxCallback);
342 break;
345 case WM_COMMAND:
346 switch (LOWORD(wParam))
348 case IDOK:
349 case IDCANCEL:
350 case IDABORT:
351 case IDRETRY:
352 case IDIGNORE:
353 case IDYES:
354 case IDNO:
355 case IDTRYAGAIN:
356 case IDCONTINUE:
357 hFont = GetPropA(hwnd, "WINE_MSGBOX_HFONT");
358 EndDialog(hwnd, wParam);
359 if (hFont)
360 DeleteObject(hFont);
361 break;
362 case IDHELP:
363 FIXME("Help button not supported yet\n");
364 break;
366 break;
368 case WM_HELP:
370 MSGBOXCALLBACK callback = (MSGBOXCALLBACK)GetPropA(hwnd, "WINE_MSGBOX_HELPCALLBACK");
371 HELPINFO hi;
373 memcpy(&hi, (void *)lParam, sizeof(hi));
374 hi.dwContextId = GetWindowContextHelpId(hwnd);
376 if (callback)
377 callback(&hi);
378 else
379 SendMessageW(GetWindow(hwnd, GW_OWNER), WM_HELP, 0, (LPARAM)&hi);
380 break;
383 default:
384 /* Ok. Ignore all the other messages */
385 TRACE("Message number 0x%04x is being ignored.\n", message);
386 break;
388 return 0;
392 /**************************************************************************
393 * MessageBoxA (USER32.@)
395 INT WINAPI MessageBoxA(HWND hWnd, LPCSTR text, LPCSTR title, UINT type)
397 return MessageBoxExA(hWnd, text, title, type, LANG_NEUTRAL);
401 /**************************************************************************
402 * MessageBoxW (USER32.@)
404 INT WINAPI MessageBoxW( HWND hwnd, LPCWSTR text, LPCWSTR title, UINT type )
406 return MessageBoxExW(hwnd, text, title, type, LANG_NEUTRAL);
410 /**************************************************************************
411 * MessageBoxExA (USER32.@)
413 INT WINAPI MessageBoxExA( HWND hWnd, LPCSTR text, LPCSTR title,
414 UINT type, WORD langid )
416 MSGBOXPARAMSA msgbox;
418 msgbox.cbSize = sizeof(msgbox);
419 msgbox.hwndOwner = hWnd;
420 msgbox.hInstance = 0;
421 msgbox.lpszText = text;
422 msgbox.lpszCaption = title;
423 msgbox.dwStyle = type;
424 msgbox.lpszIcon = NULL;
425 msgbox.dwContextHelpId = 0;
426 msgbox.lpfnMsgBoxCallback = NULL;
427 msgbox.dwLanguageId = langid;
429 return MessageBoxIndirectA(&msgbox);
432 /**************************************************************************
433 * MessageBoxExW (USER32.@)
435 INT WINAPI MessageBoxExW( HWND hWnd, LPCWSTR text, LPCWSTR title,
436 UINT type, WORD langid )
438 MSGBOXPARAMSW msgbox;
440 msgbox.cbSize = sizeof(msgbox);
441 msgbox.hwndOwner = hWnd;
442 msgbox.hInstance = 0;
443 msgbox.lpszText = text;
444 msgbox.lpszCaption = title;
445 msgbox.dwStyle = type;
446 msgbox.lpszIcon = NULL;
447 msgbox.dwContextHelpId = 0;
448 msgbox.lpfnMsgBoxCallback = NULL;
449 msgbox.dwLanguageId = langid;
451 return MessageBoxIndirectW(&msgbox);
454 /**************************************************************************
455 * MessageBoxIndirectA (USER32.@)
457 INT WINAPI MessageBoxIndirectA( LPMSGBOXPARAMSA msgbox )
459 MSGBOXPARAMSW msgboxW;
460 UNICODE_STRING textW, captionW, iconW;
461 int ret;
463 if (HIWORD(msgbox->lpszText))
464 RtlCreateUnicodeStringFromAsciiz(&textW, msgbox->lpszText);
465 else
466 textW.Buffer = (LPWSTR)msgbox->lpszText;
467 if (HIWORD(msgbox->lpszCaption))
468 RtlCreateUnicodeStringFromAsciiz(&captionW, msgbox->lpszCaption);
469 else
470 captionW.Buffer = (LPWSTR)msgbox->lpszCaption;
472 if (msgbox->dwStyle & MB_USERICON)
474 if (HIWORD(msgbox->lpszIcon))
475 RtlCreateUnicodeStringFromAsciiz(&iconW, msgbox->lpszIcon);
476 else
477 iconW.Buffer = (LPWSTR)msgbox->lpszIcon;
479 else
480 iconW.Buffer = NULL;
482 msgboxW.cbSize = sizeof(msgboxW);
483 msgboxW.hwndOwner = msgbox->hwndOwner;
484 msgboxW.hInstance = msgbox->hInstance;
485 msgboxW.lpszText = textW.Buffer;
486 msgboxW.lpszCaption = captionW.Buffer;
487 msgboxW.dwStyle = msgbox->dwStyle;
488 msgboxW.lpszIcon = iconW.Buffer;
489 msgboxW.dwContextHelpId = msgbox->dwContextHelpId;
490 msgboxW.lpfnMsgBoxCallback = msgbox->lpfnMsgBoxCallback;
491 msgboxW.dwLanguageId = msgbox->dwLanguageId;
493 ret = MessageBoxIndirectW(&msgboxW);
495 if (HIWORD(textW.Buffer)) RtlFreeUnicodeString(&textW);
496 if (HIWORD(captionW.Buffer)) RtlFreeUnicodeString(&captionW);
497 if (HIWORD(iconW.Buffer)) RtlFreeUnicodeString(&iconW);
498 return ret;
501 /**************************************************************************
502 * MessageBoxIndirectW (USER32.@)
504 INT WINAPI MessageBoxIndirectW( LPMSGBOXPARAMSW msgbox )
506 LPVOID tmplate;
507 HRSRC hRes;
508 int ret;
509 UINT i;
510 struct ThreadWindows threadWindows;
511 static const WCHAR msg_box_res_nameW[] = { 'M','S','G','B','O','X',0 };
513 if (!(hRes = FindResourceExW(user32_module, (LPWSTR)RT_DIALOG,
514 msg_box_res_nameW, msgbox->dwLanguageId)))
515 return 0;
516 if (!(tmplate = LoadResource(user32_module, hRes)))
517 return 0;
519 if ((msgbox->dwStyle & MB_TASKMODAL) && (msgbox->hwndOwner==NULL))
521 threadWindows.numHandles = 0;
522 threadWindows.numAllocs = 10;
523 threadWindows.handles = HeapAlloc(GetProcessHeap(), 0, 10*sizeof(HWND));
524 EnumThreadWindows(GetCurrentThreadId(), MSGBOX_EnumProc, (LPARAM)&threadWindows);
527 ret=DialogBoxIndirectParamW(msgbox->hInstance, tmplate,
528 msgbox->hwndOwner, MSGBOX_DlgProc, (LPARAM)msgbox);
530 if ((msgbox->dwStyle & MB_TASKMODAL) && (msgbox->hwndOwner==NULL))
532 for (i = 0; i < threadWindows.numHandles; i++)
533 EnableWindow(threadWindows.handles[i], TRUE);
534 HeapFree(GetProcessHeap(), 0, threadWindows.handles);
536 return ret;