Better function parameter checking.
[wine] / dlls / user / focus.c
1 /*
2  * Focus and activation functions
3  *
4  * Copyright 1993 David Metcalfe
5  * Copyright 1995 Alex Korobka
6  * Copyright 1994, 2002 Alexandre Julliard
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "win.h"
33 #include "message.h"
34 #include "user.h"
35 #include "wine/server.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(win);
39
40
41 /*****************************************************************
42  *              set_focus_window
43  *
44  * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
45  */
46 static HWND set_focus_window( HWND hwnd )
47 {
48     HWND previous = 0;
49     BOOL ret;
50
51     SERVER_START_REQ( set_focus_window )
52     {
53         req->handle = hwnd;
54         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
55     }
56     SERVER_END_REQ;
57     if (!ret) return 0;
58     if (previous == hwnd) return previous;
59
60     if (previous)
61     {
62         SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
63         if (hwnd != GetFocus()) return previous;  /* changed by the message */
64     }
65     if (IsWindow(hwnd))
66     {
67         if (USER_Driver.pSetFocus) USER_Driver.pSetFocus(hwnd);
68         SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
69     }
70     return previous;
71 }
72
73
74 /*******************************************************************
75  *              set_active_window
76  */
77 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
78 {
79     HWND previous = GetActiveWindow();
80     BOOL ret;
81     DWORD old_thread, new_thread;
82     CBTACTIVATESTRUCT cbt;
83
84     if (previous == hwnd)
85     {
86         if (prev) *prev = hwnd;
87         return TRUE;
88     }
89
90     /* call CBT hook chain */
91     cbt.fMouse     = mouse;
92     cbt.hWndActive = previous;
93     if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
94
95     if (IsWindow(previous))
96     {
97         SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
98         SendMessageW( previous, WM_ACTIVATE,
99                       MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
100     }
101
102     SERVER_START_REQ( set_active_window )
103     {
104         req->handle = hwnd;
105         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
106     }
107     SERVER_END_REQ;
108     if (!ret) return FALSE;
109     if (prev) *prev = previous;
110     if (previous == hwnd) return TRUE;
111
112     if (hwnd)
113     {
114         /* send palette messages */
115         if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
116             SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
117                                  SMTO_ABORTIFHUNG, 2000, NULL );
118
119         if (!(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED))
120             SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
121
122         if (!IsWindow(hwnd)) return FALSE;
123     }
124
125     old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
126     new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
127
128     if (old_thread != new_thread)
129     {
130         HWND *list, *phwnd;
131
132         if ((list = WIN_ListChildren( GetDesktopWindow() )))
133         {
134             if (old_thread)
135             {
136                 for (phwnd = list; *phwnd; phwnd++)
137                 {
138                     if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
139                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
140                 }
141             }
142             if (new_thread)
143             {
144                 for (phwnd = list; *phwnd; phwnd++)
145                 {
146                     if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
147                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
148                 }
149             }
150             HeapFree( GetProcessHeap(), 0, list );
151         }
152     }
153
154     if (IsWindow(hwnd))
155     {
156         SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous );
157         SendMessageW( hwnd, WM_ACTIVATE,
158                       MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
159                       (LPARAM)previous );
160     }
161
162     /* now change focus if necessary */
163     if (focus)
164     {
165         HWND curfocus = GetFocus();
166         if (!curfocus || !hwnd || GetAncestor( curfocus, GA_ROOT ) != hwnd)
167             set_focus_window( hwnd );
168     }
169
170     return TRUE;
171 }
172
173
174 /*******************************************************************
175  *              set_foreground_window
176  */
177 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
178 {
179     BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
180     HWND previous = 0;
181
182     SERVER_START_REQ( set_foreground_window )
183     {
184         req->handle = hwnd;
185         if ((ret = !wine_server_call_err( req )))
186         {
187             previous = reply->previous;
188             send_msg_old = reply->send_msg_old;
189             send_msg_new = reply->send_msg_new;
190         }
191     }
192     SERVER_END_REQ;
193
194     if (ret)
195     {
196         if (send_msg_old)  /* old window belongs to other thread */
197             SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
198         else if (send_msg_new)  /* old window belongs to us but new one to other thread */
199             ret = set_active_window( 0, NULL, mouse, TRUE );
200
201         if (send_msg_new)  /* new window belongs to other thread */
202             SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
203         else  /* new window belongs to us */
204             ret = set_active_window( hwnd, NULL, mouse, TRUE );
205     }
206     return ret;
207 }
208
209
210 /*******************************************************************
211  *              FOCUS_MouseActivate
212  *
213  * Activate a window as a result of a mouse click
214  */
215 BOOL FOCUS_MouseActivate( HWND hwnd )
216 {
217     return set_foreground_window( hwnd, TRUE );
218 }
219
220
221 /*******************************************************************
222  *              SetActiveWindow (USER32.@)
223  */
224 HWND WINAPI SetActiveWindow( HWND hwnd )
225 {
226     HWND prev;
227
228     TRACE( "%p\n", hwnd );
229
230     if (hwnd)
231     {
232         LONG style = GetWindowLongW( hwnd, GWL_STYLE );
233
234         if (!(style & WS_VISIBLE) || (style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
235             return GetActiveWindow();  /* Windows doesn't seem to return an error here */
236
237         hwnd = WIN_GetFullHandle( hwnd );
238     }
239
240     if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
241     return prev;
242 }
243
244
245 /*****************************************************************
246  *              SetFocus  (USER32.@)
247  */
248 HWND WINAPI SetFocus( HWND hwnd )
249 {
250     HWND hwndTop = hwnd;
251     HWND previous = GetFocus();
252
253     TRACE( "%p prev %p\n", hwnd, previous );
254
255     if (hwnd)
256     {
257         /* Check if we can set the focus to this window */
258         hwnd = WIN_GetFullHandle( hwnd );
259         if (hwnd == previous) return previous;  /* nothing to do */
260         for (;;)
261         {
262             HWND parent;
263             LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
264             if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
265             parent = GetAncestor( hwndTop, GA_PARENT );
266             if (!parent || parent == GetDesktopWindow()) break;
267             hwndTop = parent;
268         }
269
270         /* call hooks */
271         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
272
273         /* activate hwndTop if needed. */
274         if (hwndTop != GetActiveWindow())
275         {
276             if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
277             if (!IsWindow( hwnd )) return 0;  /* Abort if window destroyed */
278         }
279     }
280     else /* NULL hwnd passed in */
281     {
282         if (!previous) return 0;  /* nothing to do */
283         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
284     }
285
286     /* change focus and send messages */
287     return set_focus_window( hwnd );
288 }
289
290
291 /*******************************************************************
292  *              SetForegroundWindow  (USER32.@)
293  */
294 BOOL WINAPI SetForegroundWindow( HWND hwnd )
295 {
296     TRACE( "%p\n", hwnd );
297     if (hwnd) hwnd = WIN_GetFullHandle( hwnd );
298     return set_foreground_window( hwnd, FALSE );
299 }
300
301
302 /*******************************************************************
303  *              GetActiveWindow  (USER32.@)
304  */
305 HWND WINAPI GetActiveWindow(void)
306 {
307     HWND ret = 0;
308
309     SERVER_START_REQ( get_thread_input )
310     {
311         req->tid = GetCurrentThreadId();
312         if (!wine_server_call_err( req )) ret = reply->active;
313     }
314     SERVER_END_REQ;
315     return ret;
316 }
317
318
319 /*****************************************************************
320  *              GetFocus  (USER32.@)
321  */
322 HWND WINAPI GetFocus(void)
323 {
324     HWND ret = 0;
325
326     SERVER_START_REQ( get_thread_input )
327     {
328         req->tid = GetCurrentThreadId();
329         if (!wine_server_call_err( req )) ret = reply->focus;
330     }
331     SERVER_END_REQ;
332     return ret;
333 }
334
335
336 /*******************************************************************
337  *              GetForegroundWindow  (USER32.@)
338  */
339 HWND WINAPI GetForegroundWindow(void)
340 {
341     HWND ret = 0;
342
343     SERVER_START_REQ( get_thread_input )
344     {
345         req->tid = 0;
346         if (!wine_server_call_err( req )) ret = reply->foreground;
347     }
348     SERVER_END_REQ;
349     return ret;
350 }
351
352
353 /***********************************************************************
354 *               SetShellWindowEx (USER32.@)
355 * hwndShell =    Progman[Program Manager]
356 *                |-> SHELLDLL_DefView
357 * hwndListView = |   |-> SysListView32
358 *                |   |   |-> tooltips_class32
359 *                |   |
360 *                |   |-> SysHeader32
361 *                |
362 *                |-> ProxyTarget
363 */
364 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
365 {
366     BOOL ret;
367
368     if (GetShellWindow())
369         return FALSE;
370
371     if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
372         return FALSE;
373
374     if (hwndListView != hwndShell)
375         if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
376             return FALSE;
377
378     if (hwndListView && hwndListView!=hwndShell)
379         SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
380
381     SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
382
383     SERVER_START_REQ(set_global_windows)
384     {
385         req->flags          = SET_GLOBAL_SHELL_WINDOWS;
386         req->shell_window   = hwndShell;
387         req->shell_listview = hwndListView;
388         ret = !wine_server_call_err(req);
389     }
390     SERVER_END_REQ;
391
392     return ret;
393 }
394
395
396 /*******************************************************************
397 *               SetShellWindow (USER32.@)
398 */
399 BOOL WINAPI SetShellWindow(HWND hwndShell)
400 {
401     return SetShellWindowEx(hwndShell, hwndShell);
402 }
403
404
405 /*******************************************************************
406 *               GetShellWindow (USER32.@)
407 */
408 HWND WINAPI GetShellWindow(void)
409 {
410     HWND hwndShell = 0;
411
412     SERVER_START_REQ(set_global_windows)
413     {
414         req->flags = 0;
415         if (!wine_server_call_err(req))
416             hwndShell = reply->old_shell_window;
417     }
418     SERVER_END_REQ;
419
420     return hwndShell;
421 }
422
423
424 /***********************************************************************
425  *              SetProgmanWindow (USER32.@)
426  */
427 HWND WINAPI SetProgmanWindow ( HWND hwnd )
428 {
429     SERVER_START_REQ(set_global_windows)
430     {
431         req->flags          = SET_GLOBAL_PROGMAN_WINDOW;
432         req->progman_window = hwnd;
433         if (wine_server_call_err( req )) hwnd = 0;
434     }
435     SERVER_END_REQ;
436     return hwnd;
437 }
438
439
440 /***********************************************************************
441  *              GetProgmanWindow (USER32.@)
442  */
443 HWND WINAPI GetProgmanWindow(void)
444 {
445     HWND ret = 0;
446
447     SERVER_START_REQ(set_global_windows)
448     {
449         req->flags = 0;
450         if (!wine_server_call_err(req)) ret = reply->old_progman_window;
451     }
452     SERVER_END_REQ;
453     return ret;
454 }
455
456
457 /***********************************************************************
458  *              SetTaskmanWindow (USER32.@)
459  * NOTES
460  *   hwnd = MSTaskSwWClass
461  *          |-> SysTabControl32
462  */
463 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
464 {
465     SERVER_START_REQ(set_global_windows)
466     {
467         req->flags          = SET_GLOBAL_TASKMAN_WINDOW;
468         req->taskman_window = hwnd;
469         if (wine_server_call_err( req )) hwnd = 0;
470     }
471     SERVER_END_REQ;
472     return hwnd;
473 }
474
475 /***********************************************************************
476  *              GetTaskmanWindow (USER32.@)
477  */
478 HWND WINAPI GetTaskmanWindow(void)
479 {
480     HWND ret = 0;
481
482     SERVER_START_REQ(set_global_windows)
483     {
484         req->flags = 0;
485         if (!wine_server_call_err(req)) ret = reply->old_taskman_window;
486     }
487     SERVER_END_REQ;
488     return ret;
489 }