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