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