gdiplus: Added GdipSetPageScale/GdipGetPageScale.
[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
117         if (!GetPropA( hwnd, "__wine_x11_managed" ))
118             SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
119
120         if (!IsWindow(hwnd)) return FALSE;
121     }
122
123     old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
124     new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
125
126     if (old_thread != new_thread)
127     {
128         HWND *list, *phwnd;
129
130         if ((list = WIN_ListChildren( GetDesktopWindow() )))
131         {
132             if (old_thread)
133             {
134                 for (phwnd = list; *phwnd; phwnd++)
135                 {
136                     if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
137                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
138                 }
139             }
140             if (new_thread)
141             {
142                 for (phwnd = list; *phwnd; phwnd++)
143                 {
144                     if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
145                         SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
146                 }
147             }
148             HeapFree( GetProcessHeap(), 0, list );
149         }
150     }
151
152     if (IsWindow(hwnd))
153     {
154         SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous );
155         SendMessageW( hwnd, WM_ACTIVATE,
156                       MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
157                       (LPARAM)previous );
158     }
159
160     /* now change focus if necessary */
161     if (focus)
162     {
163         GUITHREADINFO info;
164
165         GetGUIThreadInfo( GetCurrentThreadId(), &info );
166         /* Do not change focus if the window is no more active */
167         if (hwnd == info.hwndActive)
168         {
169             if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
170                 set_focus_window( hwnd );
171         }
172     }
173
174     return TRUE;
175 }
176
177
178 /*******************************************************************
179  *              set_foreground_window
180  */
181 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
182 {
183     BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
184     HWND previous = 0;
185
186     SERVER_START_REQ( set_foreground_window )
187     {
188         req->handle = hwnd;
189         if ((ret = !wine_server_call_err( req )))
190         {
191             previous = reply->previous;
192             send_msg_old = reply->send_msg_old;
193             send_msg_new = reply->send_msg_new;
194         }
195     }
196     SERVER_END_REQ;
197
198     if (ret)
199     {
200         if (send_msg_old)  /* old window belongs to other thread */
201             SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
202         else if (send_msg_new)  /* old window belongs to us but new one to other thread */
203             ret = set_active_window( 0, NULL, mouse, TRUE );
204
205         if (send_msg_new)  /* new window belongs to other thread */
206             SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
207         else  /* new window belongs to us */
208             ret = set_active_window( hwnd, NULL, mouse, TRUE );
209     }
210     return ret;
211 }
212
213
214 /*******************************************************************
215  *              FOCUS_MouseActivate
216  *
217  * Activate a window as a result of a mouse click
218  */
219 BOOL FOCUS_MouseActivate( HWND hwnd )
220 {
221     return set_foreground_window( hwnd, TRUE );
222 }
223
224
225 /*******************************************************************
226  *              SetActiveWindow (USER32.@)
227  */
228 HWND WINAPI SetActiveWindow( HWND hwnd )
229 {
230     HWND prev;
231
232     TRACE( "%p\n", hwnd );
233
234     if (hwnd)
235     {
236         LONG style = GetWindowLongW( hwnd, GWL_STYLE );
237
238         if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
239             return GetActiveWindow();  /* Windows doesn't seem to return an error here */
240
241         hwnd = WIN_GetFullHandle( hwnd );
242     }
243
244     if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
245     return prev;
246 }
247
248
249 /*****************************************************************
250  *              SetFocus  (USER32.@)
251  */
252 HWND WINAPI SetFocus( HWND hwnd )
253 {
254     HWND hwndTop = hwnd;
255     HWND previous = GetFocus();
256
257     TRACE( "%p prev %p\n", hwnd, previous );
258
259     if (hwnd)
260     {
261         /* Check if we can set the focus to this window */
262         hwnd = WIN_GetFullHandle( hwnd );
263         if (hwnd == previous) return previous;  /* nothing to do */
264         for (;;)
265         {
266             HWND parent;
267             LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
268             if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
269             parent = GetAncestor( hwndTop, GA_PARENT );
270             if (!parent || parent == GetDesktopWindow()) break;
271             hwndTop = parent;
272         }
273
274         /* call hooks */
275         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
276
277         /* activate hwndTop if needed. */
278         if (hwndTop != GetActiveWindow())
279         {
280             if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
281             if (!IsWindow( hwnd )) return 0;  /* Abort if window destroyed */
282         }
283     }
284     else /* NULL hwnd passed in */
285     {
286         if (!previous) return 0;  /* nothing to do */
287         if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
288     }
289
290     /* change focus and send messages */
291     return set_focus_window( hwnd );
292 }
293
294
295 /*******************************************************************
296  *              SetForegroundWindow  (USER32.@)
297  */
298 BOOL WINAPI SetForegroundWindow( HWND hwnd )
299 {
300     TRACE( "%p\n", hwnd );
301
302     hwnd = WIN_GetFullHandle( hwnd );
303     return set_foreground_window( hwnd, FALSE );
304 }
305
306
307 /*******************************************************************
308  *              GetActiveWindow  (USER32.@)
309  */
310 HWND WINAPI GetActiveWindow(void)
311 {
312     HWND ret = 0;
313
314     SERVER_START_REQ( get_thread_input )
315     {
316         req->tid = GetCurrentThreadId();
317         if (!wine_server_call_err( req )) ret = reply->active;
318     }
319     SERVER_END_REQ;
320     return ret;
321 }
322
323
324 /*****************************************************************
325  *              GetFocus  (USER32.@)
326  */
327 HWND WINAPI GetFocus(void)
328 {
329     HWND ret = 0;
330
331     SERVER_START_REQ( get_thread_input )
332     {
333         req->tid = GetCurrentThreadId();
334         if (!wine_server_call_err( req )) ret = reply->focus;
335     }
336     SERVER_END_REQ;
337     return ret;
338 }
339
340
341 /*******************************************************************
342  *              GetForegroundWindow  (USER32.@)
343  */
344 HWND WINAPI GetForegroundWindow(void)
345 {
346     HWND ret = 0;
347
348     SERVER_START_REQ( get_thread_input )
349     {
350         req->tid = 0;
351         if (!wine_server_call_err( req )) ret = reply->foreground;
352     }
353     SERVER_END_REQ;
354     return ret;
355 }
356
357
358 /***********************************************************************
359 *               SetShellWindowEx (USER32.@)
360 * hwndShell =    Progman[Program Manager]
361 *                |-> SHELLDLL_DefView
362 * hwndListView = |   |-> SysListView32
363 *                |   |   |-> tooltips_class32
364 *                |   |
365 *                |   |-> SysHeader32
366 *                |
367 *                |-> ProxyTarget
368 */
369 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
370 {
371     BOOL ret;
372
373     if (GetShellWindow())
374         return FALSE;
375
376     if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
377         return FALSE;
378
379     if (hwndListView != hwndShell)
380         if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
381             return FALSE;
382
383     if (hwndListView && hwndListView!=hwndShell)
384         SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
385
386     SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
387
388     SERVER_START_REQ(set_global_windows)
389     {
390         req->flags          = SET_GLOBAL_SHELL_WINDOWS;
391         req->shell_window   = hwndShell;
392         req->shell_listview = hwndListView;
393         ret = !wine_server_call_err(req);
394     }
395     SERVER_END_REQ;
396
397     return ret;
398 }
399
400
401 /*******************************************************************
402 *               SetShellWindow (USER32.@)
403 */
404 BOOL WINAPI SetShellWindow(HWND hwndShell)
405 {
406     return SetShellWindowEx(hwndShell, hwndShell);
407 }
408
409
410 /*******************************************************************
411 *               GetShellWindow (USER32.@)
412 */
413 HWND WINAPI GetShellWindow(void)
414 {
415     HWND hwndShell = 0;
416
417     SERVER_START_REQ(set_global_windows)
418     {
419         req->flags = 0;
420         if (!wine_server_call_err(req))
421             hwndShell = reply->old_shell_window;
422     }
423     SERVER_END_REQ;
424
425     return hwndShell;
426 }
427
428
429 /***********************************************************************
430  *              SetProgmanWindow (USER32.@)
431  */
432 HWND WINAPI SetProgmanWindow ( HWND hwnd )
433 {
434     SERVER_START_REQ(set_global_windows)
435     {
436         req->flags          = SET_GLOBAL_PROGMAN_WINDOW;
437         req->progman_window = hwnd;
438         if (wine_server_call_err( req )) hwnd = 0;
439     }
440     SERVER_END_REQ;
441     return hwnd;
442 }
443
444
445 /***********************************************************************
446  *              GetProgmanWindow (USER32.@)
447  */
448 HWND WINAPI GetProgmanWindow(void)
449 {
450     HWND ret = 0;
451
452     SERVER_START_REQ(set_global_windows)
453     {
454         req->flags = 0;
455         if (!wine_server_call_err(req)) ret = reply->old_progman_window;
456     }
457     SERVER_END_REQ;
458     return ret;
459 }
460
461
462 /***********************************************************************
463  *              SetTaskmanWindow (USER32.@)
464  * NOTES
465  *   hwnd = MSTaskSwWClass
466  *          |-> SysTabControl32
467  */
468 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
469 {
470     SERVER_START_REQ(set_global_windows)
471     {
472         req->flags          = SET_GLOBAL_TASKMAN_WINDOW;
473         req->taskman_window = hwnd;
474         if (wine_server_call_err( req )) hwnd = 0;
475     }
476     SERVER_END_REQ;
477     return hwnd;
478 }
479
480 /***********************************************************************
481  *              GetTaskmanWindow (USER32.@)
482  */
483 HWND WINAPI GetTaskmanWindow(void)
484 {
485     HWND ret = 0;
486
487     SERVER_START_REQ(set_global_windows)
488     {
489         req->flags = 0;
490         if (!wine_server_call_err(req)) ret = reply->old_taskman_window;
491     }
492     SERVER_END_REQ;
493     return ret;
494 }