Implemented the SMTO_ABORTIFHUNG flag of SendMessageTimeout.
[wine] / windows / defdlg.c
1 /*
2  * Default dialog procedure
3  *
4  * Copyright 1993, 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wingdi.h"
24 #include "wine/winuser16.h"
25 #include "controls.h"
26 #include "win.h"
27 #include "winproc.h"
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(dialog);
31
32
33 /***********************************************************************
34  *           DEFDLG_GetDlgProc
35  */
36 static WNDPROC DEFDLG_GetDlgProc( HWND hwnd )
37 {
38     WNDPROC ret;
39     WND *wndPtr = WIN_GetPtr( hwnd );
40
41     if (!wndPtr) return 0;
42     if (wndPtr == WND_OTHER_PROCESS)
43     {
44         ERR( "cannot get dlg proc %p from other process\n", hwnd );
45         return 0;
46     }
47     ret = *(WNDPROC *)((char *)wndPtr->wExtra + DWL_DLGPROC);
48     WIN_ReleasePtr( wndPtr );
49     return ret;
50 }
51
52 /***********************************************************************
53  *           DEFDLG_SetFocus
54  *
55  * Set the focus to a control of the dialog, selecting the text if
56  * the control is an edit dialog.
57  */
58 static void DEFDLG_SetFocus( HWND hwndDlg, HWND hwndCtrl )
59 {
60     HWND hwndPrev = GetFocus();
61
62     if (IsChild( hwndDlg, hwndPrev ))
63     {
64         if (SendMessageW( hwndPrev, WM_GETDLGCODE, 0, 0 ) & DLGC_HASSETSEL)
65             SendMessageW( hwndPrev, EM_SETSEL, -1, 0 );
66     }
67     if (SendMessageW( hwndCtrl, WM_GETDLGCODE, 0, 0 ) & DLGC_HASSETSEL)
68         SendMessageW( hwndCtrl, EM_SETSEL, 0, -1 );
69     SetFocus( hwndCtrl );
70 }
71
72
73 /***********************************************************************
74  *           DEFDLG_SaveFocus
75  */
76 static void DEFDLG_SaveFocus( HWND hwnd )
77 {
78     DIALOGINFO *infoPtr;
79     HWND hwndFocus = GetFocus();
80
81     if (!hwndFocus || !IsChild( hwnd, hwndFocus )) return;
82     if (!(infoPtr = DIALOG_get_info( hwnd ))) return;
83     infoPtr->hwndFocus = hwndFocus;
84     /* Remove default button */
85 }
86
87
88 /***********************************************************************
89  *           DEFDLG_RestoreFocus
90  */
91 static void DEFDLG_RestoreFocus( HWND hwnd )
92 {
93     DIALOGINFO *infoPtr;
94
95     if (IsIconic( hwnd )) return;
96     if (!(infoPtr = DIALOG_get_info( hwnd ))) return;
97     if (!IsWindow( infoPtr->hwndFocus )) return;
98     /* Don't set the focus back to controls if EndDialog is already called.*/
99     if (!(infoPtr->flags & DF_END))
100     {
101         DEFDLG_SetFocus( hwnd, infoPtr->hwndFocus );
102         return;
103     }
104     /* This used to set infoPtr->hwndFocus to NULL for no apparent reason,
105        sometimes losing focus when receiving WM_SETFOCUS messages. */
106 }
107
108
109 /***********************************************************************
110  *           DEFDLG_FindDefButton
111  *
112  * Find the current default push-button.
113  */
114 static HWND DEFDLG_FindDefButton( HWND hwndDlg )
115 {
116     HWND hwndChild = GetWindow( hwndDlg, GW_CHILD );
117     while (hwndChild)
118     {
119         if (SendMessageW( hwndChild, WM_GETDLGCODE, 0, 0 ) & DLGC_DEFPUSHBUTTON)
120             break;
121         hwndChild = GetWindow( hwndChild, GW_HWNDNEXT );
122     }
123     return hwndChild;
124 }
125
126
127 /***********************************************************************
128  *           DEFDLG_SetDefButton
129  *
130  * Set the new default button to be hwndNew.
131  */
132 static BOOL DEFDLG_SetDefButton( HWND hwndDlg, DIALOGINFO *dlgInfo,
133                                    HWND hwndNew )
134 {
135     DWORD dlgcode=0; /* initialize just to avoid a warning */
136     if (hwndNew &&
137         !((dlgcode=SendMessageW(hwndNew, WM_GETDLGCODE, 0, 0 ))
138             & (DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON)))
139         return FALSE;  /* Destination is not a push button */
140
141     if (dlgInfo->idResult)  /* There's already a default pushbutton */
142     {
143         HWND hwndOld = GetDlgItem( hwndDlg, dlgInfo->idResult );
144         if (SendMessageA( hwndOld, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON)
145             SendMessageA( hwndOld, BM_SETSTYLE, BS_PUSHBUTTON, TRUE );
146     }
147     if (hwndNew)
148     {
149         if(dlgcode==DLGC_UNDEFPUSHBUTTON)
150             SendMessageA( hwndNew, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE );
151         dlgInfo->idResult = GetDlgCtrlID( hwndNew );
152     }
153     else dlgInfo->idResult = 0;
154     return TRUE;
155 }
156
157
158 /***********************************************************************
159  *           DEFDLG_Proc
160  *
161  * Implementation of DefDlgProc(). Only handle messages that need special
162  * handling for dialogs.
163  */
164 static LRESULT DEFDLG_Proc( HWND hwnd, UINT msg, WPARAM wParam,
165                             LPARAM lParam, DIALOGINFO *dlgInfo )
166 {
167     switch(msg)
168     {
169         case WM_ERASEBKGND:
170         {
171             HBRUSH brush = (HBRUSH)SendMessageW( hwnd, WM_CTLCOLORDLG, wParam, (LPARAM)hwnd );
172             if (!brush) brush = (HBRUSH)DefWindowProcW( hwnd, WM_CTLCOLORDLG, wParam, (LPARAM)hwnd );
173             if (brush)
174             {
175                 RECT rect;
176                 HDC hdc = (HDC)wParam;
177                 GetClientRect( hwnd, &rect );
178                 DPtoLP( hdc, (LPPOINT)&rect, 2 );
179                 FillRect( hdc, &rect, brush );
180             }
181             return 1;
182         }
183         case WM_NCDESTROY:
184             if ((dlgInfo = (DIALOGINFO *)SetWindowLongW( hwnd, DWL_WINE_DIALOGINFO, 0 )))
185             {
186                 /* Free dialog heap (if created) */
187                 if (dlgInfo->hDialogHeap)
188                 {
189                     GlobalUnlock16(dlgInfo->hDialogHeap);
190                     GlobalFree16(dlgInfo->hDialogHeap);
191                 }
192                 if (dlgInfo->hUserFont) DeleteObject( dlgInfo->hUserFont );
193                 if (dlgInfo->hMenu) DestroyMenu( dlgInfo->hMenu );
194                 WINPROC_FreeProc( DEFDLG_GetDlgProc( hwnd ), WIN_PROC_WINDOW );
195                 HeapFree( GetProcessHeap(), 0, dlgInfo );
196             }
197               /* Window clean-up */
198             return DefWindowProcA( hwnd, msg, wParam, lParam );
199
200         case WM_SHOWWINDOW:
201             if (!wParam) DEFDLG_SaveFocus( hwnd );
202             return DefWindowProcA( hwnd, msg, wParam, lParam );
203
204         case WM_ACTIVATE:
205             if (wParam) DEFDLG_RestoreFocus( hwnd );
206             else DEFDLG_SaveFocus( hwnd );
207             return 0;
208
209         case WM_SETFOCUS:
210             DEFDLG_RestoreFocus( hwnd );
211             return 0;
212
213         case DM_SETDEFID:
214             if (dlgInfo && !(dlgInfo->flags & DF_END))
215                 DEFDLG_SetDefButton( hwnd, dlgInfo, wParam ? GetDlgItem( hwnd, wParam ) : 0 );
216             return 1;
217
218         case DM_GETDEFID:
219             if (dlgInfo && !(dlgInfo->flags & DF_END))
220             {
221                 HWND hwndDefId;
222                 if (dlgInfo->idResult)
223                     return MAKELONG( dlgInfo->idResult, DC_HASDEFID );
224                 if ((hwndDefId = DEFDLG_FindDefButton( hwnd )))
225                     return MAKELONG( GetDlgCtrlID( hwndDefId ), DC_HASDEFID);
226             }
227             return 0;
228
229         case WM_NEXTDLGCTL:
230             if (dlgInfo)
231             {
232                 HWND hwndDest = (HWND)wParam;
233                 if (!lParam)
234                     hwndDest = GetNextDlgTabItem(hwnd, GetFocus(), wParam);
235                 if (hwndDest) DEFDLG_SetFocus( hwnd, hwndDest );
236                 DEFDLG_SetDefButton( hwnd, dlgInfo, hwndDest );
237             }
238             return 0;
239
240         case WM_ENTERMENULOOP:
241         case WM_LBUTTONDOWN:
242         case WM_NCLBUTTONDOWN:
243             {
244                 HWND hwndFocus = GetFocus();
245                 if (hwndFocus)
246                 {
247                     /* always make combo box hide its listbox control */
248                     if (!SendMessageA( hwndFocus, CB_SHOWDROPDOWN, FALSE, 0 ))
249                         SendMessageA( GetParent(hwndFocus), CB_SHOWDROPDOWN, FALSE, 0 );
250                 }
251             }
252             return DefWindowProcA( hwnd, msg, wParam, lParam );
253
254         case WM_GETFONT:
255             return dlgInfo ? (LRESULT)dlgInfo->hUserFont : 0;
256
257         case WM_CLOSE:
258             PostMessageA( hwnd, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED),
259                             (LPARAM)GetDlgItem( hwnd, IDCANCEL ) );
260             return 0;
261
262         case WM_NOTIFYFORMAT:
263             return DefWindowProcA( hwnd, msg, wParam, lParam );
264     }
265     return 0;
266 }
267
268 /***********************************************************************
269  *           DEFDLG_Epilog
270  */
271 static LRESULT DEFDLG_Epilog(HWND hwnd, UINT msg, BOOL fResult)
272 {
273     /* see SDK 3.1 */
274
275     if ((msg >= WM_CTLCOLORMSGBOX && msg <= WM_CTLCOLORSTATIC) ||
276          msg == WM_CTLCOLOR || msg == WM_COMPAREITEM ||
277          msg == WM_VKEYTOITEM || msg == WM_CHARTOITEM ||
278          msg == WM_QUERYDRAGICON || msg == WM_INITDIALOG)
279         return fResult;
280
281     return GetWindowLongA( hwnd, DWL_MSGRESULT );
282 }
283
284 /***********************************************************************
285  *              DefDlgProc (USER.308)
286  */
287 LRESULT WINAPI DefDlgProc16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam,
288                              LPARAM lParam )
289 {
290     WNDPROC16 dlgproc;
291     HWND hwnd32 = WIN_Handle32( hwnd );
292     BOOL result = FALSE;
293
294     SetWindowLongW( hwnd32, DWL_MSGRESULT, 0 );
295
296     if ((dlgproc = (WNDPROC16)DEFDLG_GetDlgProc( hwnd32 )))
297     {
298         /* Call dialog procedure */
299         result = CallWindowProc16( dlgproc, hwnd, msg, wParam, lParam );
300         /* 16 bit dlg procs only return BOOL16 */
301         if( WINPROC_GetProcType( (WNDPROC)dlgproc ) == WIN_PROC_16 )
302             result = LOWORD(result);
303     }
304
305     if (!result && IsWindow(hwnd32))
306     {
307         /* callback didn't process this message */
308
309         switch(msg)
310         {
311             case WM_ERASEBKGND:
312             case WM_SHOWWINDOW:
313             case WM_ACTIVATE:
314             case WM_SETFOCUS:
315             case DM_SETDEFID:
316             case DM_GETDEFID:
317             case WM_NEXTDLGCTL:
318             case WM_GETFONT:
319             case WM_CLOSE:
320             case WM_NCDESTROY:
321             case WM_ENTERMENULOOP:
322             case WM_LBUTTONDOWN:
323             case WM_NCLBUTTONDOWN:
324                 return DEFDLG_Proc( hwnd32, msg, (WPARAM)wParam, lParam, DIALOG_get_info(hwnd32) );
325             case WM_INITDIALOG:
326             case WM_VKEYTOITEM:
327             case WM_COMPAREITEM:
328             case WM_CHARTOITEM:
329                 break;
330
331             default:
332                 return DefWindowProc16( hwnd, msg, wParam, lParam );
333         }
334     }
335     return DEFDLG_Epilog( hwnd32, msg, result);
336 }
337
338
339 /***********************************************************************
340  *              DefDlgProcA (USER32.@)
341  */
342 LRESULT WINAPI DefDlgProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
343 {
344     WNDPROC dlgproc;
345     BOOL result = FALSE;
346
347     SetWindowLongW( hwnd, DWL_MSGRESULT, 0 );
348
349     if ((dlgproc = DEFDLG_GetDlgProc( hwnd )))
350     {
351         /* Call dialog procedure */
352         result = CallWindowProcA( dlgproc, hwnd, msg, wParam, lParam );
353         /* 16 bit dlg procs only return BOOL16 */
354         if( WINPROC_GetProcType( dlgproc ) == WIN_PROC_16 )
355             result = LOWORD(result);
356     }
357
358     if (!result && IsWindow(hwnd))
359     {
360         /* callback didn't process this message */
361
362         switch(msg)
363         {
364             case WM_ERASEBKGND:
365             case WM_SHOWWINDOW:
366             case WM_ACTIVATE:
367             case WM_SETFOCUS:
368             case DM_SETDEFID:
369             case DM_GETDEFID:
370             case WM_NEXTDLGCTL:
371             case WM_GETFONT:
372             case WM_CLOSE:
373             case WM_NCDESTROY:
374             case WM_ENTERMENULOOP:
375             case WM_LBUTTONDOWN:
376             case WM_NCLBUTTONDOWN:
377                  return DEFDLG_Proc( hwnd, msg, wParam, lParam, DIALOG_get_info(hwnd) );
378             case WM_INITDIALOG:
379             case WM_VKEYTOITEM:
380             case WM_COMPAREITEM:
381             case WM_CHARTOITEM:
382                  break;
383
384             default:
385                  return DefWindowProcA( hwnd, msg, wParam, lParam );
386         }
387     }
388     return DEFDLG_Epilog(hwnd, msg, result);
389 }
390
391
392 /***********************************************************************
393  *              DefDlgProcW (USER32.@)
394  */
395 LRESULT WINAPI DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
396 {
397     BOOL result = FALSE;
398     WNDPROC dlgproc;
399
400     SetWindowLongW( hwnd, DWL_MSGRESULT, 0 );
401
402     if ((dlgproc = DEFDLG_GetDlgProc( hwnd )))
403     {
404         /* Call dialog procedure */
405         result = CallWindowProcW( dlgproc, hwnd, msg, wParam, lParam );
406         /* 16 bit dlg procs only return BOOL16 */
407         if( WINPROC_GetProcType( dlgproc ) == WIN_PROC_16 )
408             result = LOWORD(result);
409     }
410
411     if (!result && IsWindow(hwnd))
412     {
413         /* callback didn't process this message */
414
415         switch(msg)
416         {
417             case WM_ERASEBKGND:
418             case WM_SHOWWINDOW:
419             case WM_ACTIVATE:
420             case WM_SETFOCUS:
421             case DM_SETDEFID:
422             case DM_GETDEFID:
423             case WM_NEXTDLGCTL:
424             case WM_GETFONT:
425             case WM_CLOSE:
426             case WM_NCDESTROY:
427             case WM_ENTERMENULOOP:
428             case WM_LBUTTONDOWN:
429             case WM_NCLBUTTONDOWN:
430                  return DEFDLG_Proc( hwnd, msg, wParam, lParam, DIALOG_get_info(hwnd) );
431             case WM_INITDIALOG:
432             case WM_VKEYTOITEM:
433             case WM_COMPAREITEM:
434             case WM_CHARTOITEM:
435                  break;
436
437             default:
438                  return DefWindowProcW( hwnd, msg, wParam, lParam );
439         }
440     }
441     return DEFDLG_Epilog(hwnd, msg, result);
442 }