Reimplemented Get/SetActiveWindow, Get/SetFocus and
[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 "winbase.h"
24 #include "winuser.h"
25 #include "winerror.h"
26 #include "win.h"
27 #include "hook.h"
28 #include "message.h"
29 #include "user.h"
30 #include "wine/server.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(win);
34
35
36 /*****************************************************************
37  *              set_focus_window
38  *
39  * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
40  */
41 static HWND set_focus_window( HWND hwnd )
42 {
43     HWND previous = 0;
44     BOOL ret;
45
46     SERVER_START_REQ( set_focus_window )
47     {
48         req->handle = hwnd;
49         if ((ret = !wine_server_call_err( req ))) previous = reply->previous;
50     }
51     SERVER_END_REQ;
52     if (!ret) return 0;
53     if (previous == hwnd) return previous;
54
55     if (previous)
56     {
57         SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
58         if (hwnd != GetFocus()) return previous;  /* changed by the message */
59     }
60     if (IsWindow(hwnd))
61     {
62         if (USER_Driver.pSetFocus) USER_Driver.pSetFocus(hwnd);
63         SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
64     }
65     return previous;
66 }
67
68
69 /*******************************************************************
70  *              set_active_window
71  */
72 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
73 {
74     HWND previous = GetActiveWindow();
75     BOOL ret;
76     DWORD old_thread, new_thread;
77
78     if (previous == hwnd)
79     {
80         if (prev) *prev = hwnd;
81         return TRUE;
82     }
83
84     /* call CBT hook chain */
85     if (HOOK_IsHooked( WH_CBT ))
86     {
87         CBTACTIVATESTRUCT cbt;
88         cbt.fMouse     = mouse;
89         cbt.hWndActive = previous;
90         if (HOOK_CallHooksW( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt )) return FALSE;
91     }
92
93     if (IsWindow(previous))
94     {
95         SendMessageW( previous, WM_NCACTIVATE, FALSE, 0 );
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             SendMessageW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0 );
115
116         if (!(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED))
117             SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
118
119         if (!IsWindow(hwnd)) return FALSE;
120     }
121
122     old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
123     new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
124
125     if (old_thread != new_thread)
126     {
127         HWND *list, *phwnd;
128
129         if ((list = WIN_ListChildren( GetDesktopWindow() )))
130         {
131             if (old_thread)
132             {
133                 for (phwnd = list; *phwnd; phwnd++)
134                 {
135                     if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
136                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
137                 }
138             }
139             if (new_thread)
140             {
141                 for (phwnd = list; *phwnd; phwnd++)
142                 {
143                     if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
144                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
145                 }
146             }
147             HeapFree( GetProcessHeap(), 0, list );
148         }
149     }
150
151     if (IsWindow(hwnd))
152     {
153         SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), 0 );
154         SendMessageW( hwnd, WM_ACTIVATE,
155                       MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
156                       (LPARAM)previous );
157     }
158
159     /* now change focus if necessary */
160     if (focus)
161     {
162         HWND curfocus = GetFocus();
163         if (!curfocus || !hwnd || GetAncestor( curfocus, GA_ROOT ) != hwnd)
164             set_focus_window( hwnd );
165     }
166
167     return TRUE;
168 }
169
170
171 /*******************************************************************
172  *              set_foreground_window
173  */
174 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
175 {
176     BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
177     HWND previous = 0;
178
179     SERVER_START_REQ( set_foreground_window )
180     {
181         req->handle = hwnd;
182         if ((ret = !wine_server_call_err( req )))
183         {
184             previous = reply->previous;
185             send_msg_old = reply->send_msg_old;
186             send_msg_new = reply->send_msg_new;
187         }
188     }
189     SERVER_END_REQ;
190
191     if (ret)
192     {
193         if (send_msg_old)  /* old window belongs to other thread */
194             SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
195         else if (send_msg_new)  /* old window belongs to us but new one to other thread */
196             ret = set_active_window( 0, NULL, mouse, TRUE );
197
198         if (send_msg_new)  /* new window belongs to other thread */
199             SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, hwnd, 0 );
200         else  /* new window belongs to us */
201             ret = set_active_window( hwnd, NULL, mouse, TRUE );
202     }
203     return ret;
204 }
205
206
207 /*******************************************************************
208  *              FOCUS_MouseActivate
209  *
210  * Activate a window as a result of a mouse click
211  */
212 BOOL FOCUS_MouseActivate( HWND hwnd )
213 {
214     return set_foreground_window( hwnd, TRUE );
215 }
216
217
218 /*******************************************************************
219  *              SetActiveWindow (USER32.@)
220  */
221 HWND WINAPI SetActiveWindow( HWND hwnd )
222 {
223     HWND prev;
224
225     TRACE( "%x\n", hwnd );
226
227     if (hwnd)
228     {
229         LONG style = GetWindowLongW( hwnd, GWL_STYLE );
230
231         if (!(style & WS_VISIBLE) || (style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
232             return GetActiveWindow();  /* Windows doesn't seem to return an error here */
233
234         hwnd = WIN_GetFullHandle( hwnd );
235     }
236
237     if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
238     return prev;
239 }
240
241
242 /*****************************************************************
243  *              SetFocus  (USER32.@)
244  */
245 HWND WINAPI SetFocus( HWND hwnd )
246 {
247     HWND hwndTop = hwnd;
248     HWND previous = GetFocus();
249
250     TRACE( "%x prev %x\n", hwnd, previous );
251
252     if (hwnd)
253     {
254         /* Check if we can set the focus to this window */
255         hwnd = WIN_GetFullHandle( hwnd );
256         if (hwnd == previous) return previous;  /* nothing to do */
257         for (;;)
258         {
259             HWND parent;
260             LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
261             if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
262             parent = GetAncestor( hwndTop, GA_PARENT );
263             if (!parent || parent == GetDesktopWindow()) break;
264             hwndTop = parent;
265         }
266
267         /* call hooks */
268         if (HOOK_CallHooksW( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous )) return 0;
269
270         /* activate hwndTop if needed. */
271         if (hwndTop != GetActiveWindow())
272         {
273             if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
274             if (!IsWindow( hwnd )) return 0;  /* Abort if window destroyed */
275         }
276     }
277     else /* NULL hwnd passed in */
278     {
279         if (!previous) return 0;  /* nothing to do */
280         if( HOOK_CallHooksW( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous ) )
281             return 0;
282     }
283
284     /* change focus and send messages */
285     return set_focus_window( hwnd );
286 }
287
288
289 /*******************************************************************
290  *              SetForegroundWindow  (USER32.@)
291  */
292 BOOL WINAPI SetForegroundWindow( HWND hwnd )
293 {
294     TRACE( "%x\n", hwnd );
295     if (hwnd) hwnd = WIN_GetFullHandle( hwnd );
296     return set_foreground_window( hwnd, FALSE );
297 }
298
299
300 /*******************************************************************
301  *              GetActiveWindow  (USER32.@)
302  */
303 HWND WINAPI GetActiveWindow(void)
304 {
305     HWND ret = 0;
306
307     SERVER_START_REQ( get_thread_input )
308     {
309         req->tid = GetCurrentThreadId();
310         if (!wine_server_call_err( req )) ret = reply->active;
311     }
312     SERVER_END_REQ;
313     return ret;
314 }
315
316
317 /*****************************************************************
318  *              GetFocus  (USER32.@)
319  */
320 HWND WINAPI GetFocus(void)
321 {
322     HWND ret = 0;
323
324     SERVER_START_REQ( get_thread_input )
325     {
326         req->tid = GetCurrentThreadId();
327         if (!wine_server_call_err( req )) ret = reply->focus;
328     }
329     SERVER_END_REQ;
330     return ret;
331 }
332
333
334 /*******************************************************************
335  *              GetForegroundWindow  (USER32.@)
336  */
337 HWND WINAPI GetForegroundWindow(void)
338 {
339     HWND ret = 0;
340
341     SERVER_START_REQ( get_thread_input )
342     {
343         req->tid = 0;
344         if (!wine_server_call_err( req )) ret = reply->foreground;
345     }
346     SERVER_END_REQ;
347     return ret;
348 }