gdi32: Let user32 specify the device rectangle when setting the visible region.
[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 = wine_server_user_handle( hwnd );
52         if ((ret = !wine_server_call_err( req )))
53             previous = wine_server_ptr_handle( reply->previous );
54     }
55     SERVER_END_REQ;
56     if (!ret) return 0;
57     if (previous == hwnd) return previous;
58
59     if (previous)
60     {
61         SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
62         if (hwnd != GetFocus()) return previous;  /* changed by the message */
63     }
64     if (IsWindow(hwnd))
65     {
66         USER_Driver->pSetFocus(hwnd);
67         SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
68     }
69     return previous;
70 }
71
72
73 /*******************************************************************
74  *              set_active_window
75  */
76 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
77 {
78     HWND previous = GetActiveWindow();
79     BOOL ret;
80     DWORD old_thread, new_thread;
81     CBTACTIVATESTRUCT cbt;
82
83     if (previous == hwnd)
84     {
85         if (prev) *prev = hwnd;
86         return TRUE;
87     }
88
89     /* call CBT hook chain */
90     cbt.fMouse     = mouse;
91     cbt.hWndActive = previous;
92     if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
93
94     if (IsWindow(previous))
95     {
96         SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
97         SendMessageW( previous, WM_ACTIVATE,
98                       MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
99     }
100
101     SERVER_START_REQ( set_active_window )
102     {
103         req->handle = wine_server_user_handle( hwnd );
104         if ((ret = !wine_server_call_err( req )))
105             previous = wine_server_ptr_handle( 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         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()), (LPARAM)previous );
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         GUITHREADINFO info;
162
163         info.cbSize = sizeof(info);
164         GetGUIThreadInfo( GetCurrentThreadId(), &info );
165         /* Do not change focus if the window is no more active */
166         if (hwnd == info.hwndActive)
167         {
168             if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
169                 set_focus_window( hwnd );
170         }
171     }
172
173     return TRUE;
174 }
175
176
177 /*******************************************************************
178  *              set_foreground_window
179  */
180 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
181 {
182     BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
183     HWND previous = 0;
184
185     SERVER_START_REQ( set_foreground_window )
186     {
187         req->handle = wine_server_user_handle( hwnd );
188         if ((ret = !wine_server_call_err( req )))
189         {
190             previous = wine_server_ptr_handle( reply->previous );
191             send_msg_old = reply->send_msg_old;
192             send_msg_new = reply->send_msg_new;
193         }
194     }
195     SERVER_END_REQ;
196
197     if (ret && previous != hwnd)
198     {
199         if (send_msg_old)  /* old window belongs to other thread */
200             SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
201         else if (send_msg_new)  /* old window belongs to us but new one to other thread */
202             ret = set_active_window( 0, NULL, mouse, TRUE );
203
204         if (send_msg_new)  /* new window belongs to other thread */
205             SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
206         else  /* new window belongs to us */
207             ret = set_active_window( hwnd, NULL, mouse, TRUE );
208     }
209     return ret;
210 }
211
212
213 /*******************************************************************
214  *              FOCUS_MouseActivate
215  *
216  * Activate a window as a result of a mouse click
217  */
218 BOOL FOCUS_MouseActivate( HWND hwnd )
219 {
220     return set_foreground_window( hwnd, TRUE );
221 }
222
223
224 /*******************************************************************
225  *              SetActiveWindow (USER32.@)
226  */
227 HWND WINAPI SetActiveWindow( HWND hwnd )
228 {
229     HWND prev;
230
231     TRACE( "%p\n", hwnd );
232
233     if (hwnd)
234     {
235         LONG style;
236
237         hwnd = WIN_GetFullHandle( hwnd );
238         if (!IsWindow( hwnd ))
239         {
240             SetLastError( ERROR_INVALID_WINDOW_HANDLE );
241             return 0;
242         }
243
244         style = GetWindowLongW( hwnd, GWL_STYLE );
245         if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
246             return GetActiveWindow();  /* Windows doesn't seem to return an error here */
247     }
248
249     if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
250     return prev;
251 }
252
253
254 /*****************************************************************
255  *              SetFocus  (USER32.@)
256  */
257 HWND WINAPI SetFocus( HWND hwnd )
258 {
259     HWND hwndTop = hwnd;
260     HWND previous = GetFocus();
261
262     TRACE( "%p prev %p\n", hwnd, previous );
263
264     if (hwnd)
265     {
266         /* Check if we can set the focus to this window */
267         hwnd = WIN_GetFullHandle( hwnd );
268         if (!IsWindow( hwnd ))
269         {
270             SetLastError( ERROR_INVALID_WINDOW_HANDLE );
271             return 0;
272         }
273         if (hwnd == previous) return previous;  /* nothing to do */
274         for (;;)
275         {
276             HWND parent;
277             LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
278             if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
279             parent = GetAncestor( hwndTop, GA_PARENT );
280             if (!parent || parent == GetDesktopWindow())
281             {
282                 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
283                 break;
284             }
285             if (parent == get_hwnd_message_parent()) return 0;
286             hwndTop = parent;
287         }
288
289         /* call hooks */
290         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
291
292         /* activate hwndTop if needed. */
293         if (hwndTop != GetActiveWindow())
294         {
295             if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
296             if (!IsWindow( hwnd )) return 0;  /* Abort if window destroyed */
297
298             /* Do not change focus if the window is no longer active */
299             if (hwndTop != GetActiveWindow()) return 0;
300         }
301     }
302     else /* NULL hwnd passed in */
303     {
304         if (!previous) return 0;  /* nothing to do */
305         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
306     }
307
308     /* change focus and send messages */
309     return set_focus_window( hwnd );
310 }
311
312
313 /*******************************************************************
314  *              SetForegroundWindow  (USER32.@)
315  */
316 BOOL WINAPI SetForegroundWindow( HWND hwnd )
317 {
318     TRACE( "%p\n", hwnd );
319
320     hwnd = WIN_GetFullHandle( hwnd );
321     return set_foreground_window( hwnd, FALSE );
322 }
323
324
325 /*******************************************************************
326  *              GetActiveWindow  (USER32.@)
327  */
328 HWND WINAPI GetActiveWindow(void)
329 {
330     HWND ret = 0;
331
332     SERVER_START_REQ( get_thread_input )
333     {
334         req->tid = GetCurrentThreadId();
335         if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->active );
336     }
337     SERVER_END_REQ;
338     return ret;
339 }
340
341
342 /*****************************************************************
343  *              GetFocus  (USER32.@)
344  */
345 HWND WINAPI GetFocus(void)
346 {
347     HWND ret = 0;
348
349     SERVER_START_REQ( get_thread_input )
350     {
351         req->tid = GetCurrentThreadId();
352         if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->focus );
353     }
354     SERVER_END_REQ;
355     return ret;
356 }
357
358
359 /*******************************************************************
360  *              GetForegroundWindow  (USER32.@)
361  */
362 HWND WINAPI GetForegroundWindow(void)
363 {
364     HWND ret = 0;
365
366     SERVER_START_REQ( get_thread_input )
367     {
368         req->tid = 0;
369         if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground );
370     }
371     SERVER_END_REQ;
372     return ret;
373 }
374
375
376 /***********************************************************************
377 *               SetShellWindowEx (USER32.@)
378 * hwndShell =    Progman[Program Manager]
379 *                |-> SHELLDLL_DefView
380 * hwndListView = |   |-> SysListView32
381 *                |   |   |-> tooltips_class32
382 *                |   |
383 *                |   |-> SysHeader32
384 *                |
385 *                |-> ProxyTarget
386 */
387 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
388 {
389     BOOL ret;
390
391     if (GetShellWindow())
392         return FALSE;
393
394     if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
395         return FALSE;
396
397     if (hwndListView != hwndShell)
398         if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
399             return FALSE;
400
401     if (hwndListView && hwndListView!=hwndShell)
402         SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
403
404     SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
405
406     SERVER_START_REQ(set_global_windows)
407     {
408         req->flags          = SET_GLOBAL_SHELL_WINDOWS;
409         req->shell_window   = wine_server_user_handle( hwndShell );
410         req->shell_listview = wine_server_user_handle( hwndListView );
411         ret = !wine_server_call_err(req);
412     }
413     SERVER_END_REQ;
414
415     return ret;
416 }
417
418
419 /*******************************************************************
420 *               SetShellWindow (USER32.@)
421 */
422 BOOL WINAPI SetShellWindow(HWND hwndShell)
423 {
424     return SetShellWindowEx(hwndShell, hwndShell);
425 }
426
427
428 /*******************************************************************
429 *               GetShellWindow (USER32.@)
430 */
431 HWND WINAPI GetShellWindow(void)
432 {
433     HWND hwndShell = 0;
434
435     SERVER_START_REQ(set_global_windows)
436     {
437         req->flags = 0;
438         if (!wine_server_call_err(req))
439             hwndShell = wine_server_ptr_handle( reply->old_shell_window );
440     }
441     SERVER_END_REQ;
442
443     return hwndShell;
444 }
445
446
447 /***********************************************************************
448  *              SetProgmanWindow (USER32.@)
449  */
450 HWND WINAPI SetProgmanWindow ( HWND hwnd )
451 {
452     SERVER_START_REQ(set_global_windows)
453     {
454         req->flags          = SET_GLOBAL_PROGMAN_WINDOW;
455         req->progman_window = wine_server_user_handle( hwnd );
456         if (wine_server_call_err( req )) hwnd = 0;
457     }
458     SERVER_END_REQ;
459     return hwnd;
460 }
461
462
463 /***********************************************************************
464  *              GetProgmanWindow (USER32.@)
465  */
466 HWND WINAPI GetProgmanWindow(void)
467 {
468     HWND ret = 0;
469
470     SERVER_START_REQ(set_global_windows)
471     {
472         req->flags = 0;
473         if (!wine_server_call_err(req))
474             ret = wine_server_ptr_handle( reply->old_progman_window );
475     }
476     SERVER_END_REQ;
477     return ret;
478 }
479
480
481 /***********************************************************************
482  *              SetTaskmanWindow (USER32.@)
483  * NOTES
484  *   hwnd = MSTaskSwWClass
485  *          |-> SysTabControl32
486  */
487 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
488 {
489     SERVER_START_REQ(set_global_windows)
490     {
491         req->flags          = SET_GLOBAL_TASKMAN_WINDOW;
492         req->taskman_window = wine_server_user_handle( hwnd );
493         if (wine_server_call_err( req )) hwnd = 0;
494     }
495     SERVER_END_REQ;
496     return hwnd;
497 }
498
499 /***********************************************************************
500  *              GetTaskmanWindow (USER32.@)
501  */
502 HWND WINAPI GetTaskmanWindow(void)
503 {
504     HWND ret = 0;
505
506     SERVER_START_REQ(set_global_windows)
507     {
508         req->flags = 0;
509         if (!wine_server_call_err(req))
510             ret = wine_server_ptr_handle( reply->old_taskman_window );
511     }
512     SERVER_END_REQ;
513     return ret;
514 }