user32/tests: Skip tests if function is not implemented.
[wine] / dlls / user32 / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "win.h"
32 #include "user_private.h"
33 #include "wine/server.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(win);
37
38
39 /*****************************************************************
40  *              set_focus_window
41  *
42  * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
43  */
44 static HWND set_focus_window( HWND hwnd )
45 {
46     HWND previous = 0;
47     BOOL ret;
48
49     SERVER_START_REQ( set_focus_window )
50     {
51         req->handle = hwnd;
52         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
53     }
54     SERVER_END_REQ;
55     if (!ret) return 0;
56     if (previous == hwnd) return previous;
57
58     if (previous)
59     {
60         SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
61         if (hwnd != GetFocus()) return previous;  /* changed by the message */
62     }
63     if (IsWindow(hwnd))
64     {
65         USER_Driver->pSetFocus(hwnd);
66         SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
67     }
68     return previous;
69 }
70
71
72 /*******************************************************************
73  *              set_active_window
74  */
75 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
76 {
77     HWND previous = GetActiveWindow();
78     BOOL ret;
79     DWORD old_thread, new_thread;
80     CBTACTIVATESTRUCT cbt;
81
82     if (previous == hwnd)
83     {
84         if (prev) *prev = hwnd;
85         return TRUE;
86     }
87
88     /* call CBT hook chain */
89     cbt.fMouse     = mouse;
90     cbt.hWndActive = previous;
91     if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
92
93     if (IsWindow(previous))
94     {
95         SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
96         SendMessageW( previous, WM_ACTIVATE,
97                       MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
98     }
99
100     SERVER_START_REQ( set_active_window )
101     {
102         req->handle = hwnd;
103         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
104     }
105     SERVER_END_REQ;
106     if (!ret) return FALSE;
107     if (prev) *prev = previous;
108     if (previous == hwnd) return TRUE;
109
110     if (hwnd)
111     {
112         /* send palette messages */
113         if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
114             SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
115                                  SMTO_ABORTIFHUNG, 2000, NULL );
116         if (!IsWindow(hwnd)) return FALSE;
117     }
118
119     old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
120     new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
121
122     if (old_thread != new_thread)
123     {
124         HWND *list, *phwnd;
125
126         if ((list = WIN_ListChildren( GetDesktopWindow() )))
127         {
128             if (old_thread)
129             {
130                 for (phwnd = list; *phwnd; phwnd++)
131                 {
132                     if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
133                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
134                 }
135             }
136             if (new_thread)
137             {
138                 for (phwnd = list; *phwnd; phwnd++)
139                 {
140                     if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
141                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
142                 }
143             }
144             HeapFree( GetProcessHeap(), 0, list );
145         }
146     }
147
148     if (IsWindow(hwnd))
149     {
150         SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous );
151         SendMessageW( hwnd, WM_ACTIVATE,
152                       MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
153                       (LPARAM)previous );
154     }
155
156     /* now change focus if necessary */
157     if (focus)
158     {
159         GUITHREADINFO info;
160
161         GetGUIThreadInfo( GetCurrentThreadId(), &info );
162         /* Do not change focus if the window is no more active */
163         if (hwnd == info.hwndActive)
164         {
165             if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
166                 set_focus_window( hwnd );
167         }
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_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
298     hwnd = WIN_GetFullHandle( hwnd );
299     return set_foreground_window( hwnd, FALSE );
300 }
301
302
303 /*******************************************************************
304  *              GetActiveWindow  (USER32.@)
305  */
306 HWND WINAPI GetActiveWindow(void)
307 {
308     HWND ret = 0;
309
310     SERVER_START_REQ( get_thread_input )
311     {
312         req->tid = GetCurrentThreadId();
313         if (!wine_server_call_err( req )) ret = reply->active;
314     }
315     SERVER_END_REQ;
316     return ret;
317 }
318
319
320 /*****************************************************************
321  *              GetFocus  (USER32.@)
322  */
323 HWND WINAPI GetFocus(void)
324 {
325     HWND ret = 0;
326
327     SERVER_START_REQ( get_thread_input )
328     {
329         req->tid = GetCurrentThreadId();
330         if (!wine_server_call_err( req )) ret = reply->focus;
331     }
332     SERVER_END_REQ;
333     return ret;
334 }
335
336
337 /*******************************************************************
338  *              GetForegroundWindow  (USER32.@)
339  */
340 HWND WINAPI GetForegroundWindow(void)
341 {
342     HWND ret = 0;
343
344     SERVER_START_REQ( get_thread_input )
345     {
346         req->tid = 0;
347         if (!wine_server_call_err( req )) ret = reply->foreground;
348     }
349     SERVER_END_REQ;
350     return ret;
351 }
352
353
354 /***********************************************************************
355 *               SetShellWindowEx (USER32.@)
356 * hwndShell =    Progman[Program Manager]
357 *                |-> SHELLDLL_DefView
358 * hwndListView = |   |-> SysListView32
359 *                |   |   |-> tooltips_class32
360 *                |   |
361 *                |   |-> SysHeader32
362 *                |
363 *                |-> ProxyTarget
364 */
365 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
366 {
367     BOOL ret;
368
369     if (GetShellWindow())
370         return FALSE;
371
372     if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
373         return FALSE;
374
375     if (hwndListView != hwndShell)
376         if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
377             return FALSE;
378
379     if (hwndListView && hwndListView!=hwndShell)
380         SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
381
382     SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
383
384     SERVER_START_REQ(set_global_windows)
385     {
386         req->flags          = SET_GLOBAL_SHELL_WINDOWS;
387         req->shell_window   = hwndShell;
388         req->shell_listview = hwndListView;
389         ret = !wine_server_call_err(req);
390     }
391     SERVER_END_REQ;
392
393     return ret;
394 }
395
396
397 /*******************************************************************
398 *               SetShellWindow (USER32.@)
399 */
400 BOOL WINAPI SetShellWindow(HWND hwndShell)
401 {
402     return SetShellWindowEx(hwndShell, hwndShell);
403 }
404
405
406 /*******************************************************************
407 *               GetShellWindow (USER32.@)
408 */
409 HWND WINAPI GetShellWindow(void)
410 {
411     HWND hwndShell = 0;
412
413     SERVER_START_REQ(set_global_windows)
414     {
415         req->flags = 0;
416         if (!wine_server_call_err(req))
417             hwndShell = reply->old_shell_window;
418     }
419     SERVER_END_REQ;
420
421     return hwndShell;
422 }
423
424
425 /***********************************************************************
426  *              SetProgmanWindow (USER32.@)
427  */
428 HWND WINAPI SetProgmanWindow ( HWND hwnd )
429 {
430     SERVER_START_REQ(set_global_windows)
431     {
432         req->flags          = SET_GLOBAL_PROGMAN_WINDOW;
433         req->progman_window = hwnd;
434         if (wine_server_call_err( req )) hwnd = 0;
435     }
436     SERVER_END_REQ;
437     return hwnd;
438 }
439
440
441 /***********************************************************************
442  *              GetProgmanWindow (USER32.@)
443  */
444 HWND WINAPI GetProgmanWindow(void)
445 {
446     HWND ret = 0;
447
448     SERVER_START_REQ(set_global_windows)
449     {
450         req->flags = 0;
451         if (!wine_server_call_err(req)) ret = reply->old_progman_window;
452     }
453     SERVER_END_REQ;
454     return ret;
455 }
456
457
458 /***********************************************************************
459  *              SetTaskmanWindow (USER32.@)
460  * NOTES
461  *   hwnd = MSTaskSwWClass
462  *          |-> SysTabControl32
463  */
464 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
465 {
466     SERVER_START_REQ(set_global_windows)
467     {
468         req->flags          = SET_GLOBAL_TASKMAN_WINDOW;
469         req->taskman_window = hwnd;
470         if (wine_server_call_err( req )) hwnd = 0;
471     }
472     SERVER_END_REQ;
473     return hwnd;
474 }
475
476 /***********************************************************************
477  *              GetTaskmanWindow (USER32.@)
478  */
479 HWND WINAPI GetTaskmanWindow(void)
480 {
481     HWND ret = 0;
482
483     SERVER_START_REQ(set_global_windows)
484     {
485         req->flags = 0;
486         if (!wine_server_call_err(req)) ret = reply->old_taskman_window;
487     }
488     SERVER_END_REQ;
489     return ret;
490 }