win32u: Move NtUserGetAncestor implementation from user32.
[wine.git] / dlls / user32 / focus.c
blobde959cd6fb0c76256da98bcd6d8e560d144ca5f0
1 /*
2 * Focus and activation functions
4 * Copyright 1993 David Metcalfe
5 * Copyright 1995 Alex Korobka
6 * Copyright 1994, 2002 Alexandre Julliard
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.
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.
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
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "win.h"
30 #include "imm.h"
31 #include "user_private.h"
32 #include "wine/server.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(win);
38 /*****************************************************************
39 * set_focus_window
41 * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
43 static HWND set_focus_window( HWND hwnd )
45 HWND previous = 0, ime_default;
46 BOOL ret;
48 SERVER_START_REQ( set_focus_window )
50 req->handle = wine_server_user_handle( hwnd );
51 if ((ret = !wine_server_call_err( req )))
52 previous = wine_server_ptr_handle( reply->previous );
54 SERVER_END_REQ;
55 if (!ret) return 0;
56 if (previous == hwnd) return previous;
58 if (previous)
60 SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
62 ime_default = ImmGetDefaultIMEWnd( previous );
63 if (ime_default)
64 SendMessageW( ime_default, WM_IME_INTERNAL, IME_INTERNAL_DEACTIVATE, (LPARAM)previous );
66 if (hwnd != GetFocus()) return previous; /* changed by the message */
68 if (IsWindow(hwnd))
70 USER_Driver->pSetFocus(hwnd);
72 ime_default = ImmGetDefaultIMEWnd( hwnd );
73 if (ime_default)
74 SendMessageW( ime_default, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE, (LPARAM)hwnd );
76 if (previous)
77 NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 );
79 SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
81 return previous;
85 /*******************************************************************
86 * set_active_window
88 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
90 HWND previous = GetActiveWindow();
91 BOOL ret;
92 DWORD old_thread, new_thread;
93 CBTACTIVATESTRUCT cbt;
95 if (previous == hwnd)
97 if (prev) *prev = hwnd;
98 return TRUE;
101 /* call CBT hook chain */
102 cbt.fMouse = mouse;
103 cbt.hWndActive = previous;
104 if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
106 if (IsWindow(previous))
108 SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
109 SendMessageW( previous, WM_ACTIVATE,
110 MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
113 SERVER_START_REQ( set_active_window )
115 req->handle = wine_server_user_handle( hwnd );
116 if ((ret = !wine_server_call_err( req )))
117 previous = wine_server_ptr_handle( reply->previous );
119 SERVER_END_REQ;
120 if (!ret) return FALSE;
121 if (prev) *prev = previous;
122 if (previous == hwnd) return TRUE;
124 if (hwnd)
126 /* send palette messages */
127 if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
128 SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
129 SMTO_ABORTIFHUNG, 2000, NULL );
130 if (!IsWindow(hwnd)) return FALSE;
133 old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
134 new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
136 if (old_thread != new_thread)
138 HWND *list, *phwnd;
140 if ((list = WIN_ListChildren( GetDesktopWindow() )))
142 if (old_thread)
144 for (phwnd = list; *phwnd; phwnd++)
146 if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
147 SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
150 if (new_thread)
152 for (phwnd = list; *phwnd; phwnd++)
154 if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
155 SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
158 HeapFree( GetProcessHeap(), 0, list );
162 if (IsWindow(hwnd))
164 SendMessageW( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous );
165 SendMessageW( hwnd, WM_ACTIVATE,
166 MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
167 (LPARAM)previous );
168 if (NtUserGetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow())
169 PostMessageW( GetDesktopWindow(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
172 /* now change focus if necessary */
173 if (focus)
175 GUITHREADINFO info;
177 info.cbSize = sizeof(info);
178 NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info );
179 /* Do not change focus if the window is no more active */
180 if (hwnd == info.hwndActive)
182 if (!info.hwndFocus || !hwnd || NtUserGetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
183 set_focus_window( hwnd );
187 return TRUE;
191 /*******************************************************************
192 * set_foreground_window
194 static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
196 BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
197 HWND previous = 0;
199 SERVER_START_REQ( set_foreground_window )
201 req->handle = wine_server_user_handle( hwnd );
202 if ((ret = !wine_server_call_err( req )))
204 previous = wine_server_ptr_handle( reply->previous );
205 send_msg_old = reply->send_msg_old;
206 send_msg_new = reply->send_msg_new;
209 SERVER_END_REQ;
211 if (ret && previous != hwnd)
213 if (send_msg_old) /* old window belongs to other thread */
214 SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
215 else if (send_msg_new) /* old window belongs to us but new one to other thread */
216 ret = set_active_window( 0, NULL, mouse, TRUE );
218 if (send_msg_new) /* new window belongs to other thread */
219 SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
220 else /* new window belongs to us */
221 ret = set_active_window( hwnd, NULL, mouse, TRUE );
223 return ret;
227 /*******************************************************************
228 * FOCUS_MouseActivate
230 * Activate a window as a result of a mouse click
232 BOOL FOCUS_MouseActivate( HWND hwnd )
234 return set_foreground_window( hwnd, TRUE );
238 /*******************************************************************
239 * SetActiveWindow (USER32.@)
241 HWND WINAPI SetActiveWindow( HWND hwnd )
243 HWND prev;
245 TRACE( "%p\n", hwnd );
247 if (hwnd)
249 LONG style;
251 hwnd = WIN_GetFullHandle( hwnd );
252 if (!IsWindow( hwnd ))
254 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
255 return 0;
258 style = GetWindowLongW( hwnd, GWL_STYLE );
259 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
260 return GetActiveWindow(); /* Windows doesn't seem to return an error here */
263 if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
264 return prev;
268 /*****************************************************************
269 * SetFocus (USER32.@)
271 HWND WINAPI SetFocus( HWND hwnd )
273 HWND hwndTop = hwnd;
274 HWND previous = GetFocus();
276 TRACE( "%p prev %p\n", hwnd, previous );
278 if (hwnd)
280 /* Check if we can set the focus to this window */
281 hwnd = WIN_GetFullHandle( hwnd );
282 if (!IsWindow( hwnd ))
284 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
285 return 0;
287 if (hwnd == previous) return previous; /* nothing to do */
288 for (;;)
290 HWND parent;
291 LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
292 if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
293 if (!(style & WS_CHILD)) break;
294 parent = NtUserGetAncestor( hwndTop, GA_PARENT );
295 if (!parent || parent == GetDesktopWindow())
297 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
298 break;
300 if (parent == get_hwnd_message_parent()) return 0;
301 hwndTop = parent;
304 /* call hooks */
305 if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
307 /* activate hwndTop if needed. */
308 if (hwndTop != GetActiveWindow())
310 if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
311 if (!IsWindow( hwnd )) return 0; /* Abort if window destroyed */
313 /* Do not change focus if the window is no longer active */
314 if (hwndTop != GetActiveWindow()) return 0;
317 else /* NULL hwnd passed in */
319 if (!previous) return 0; /* nothing to do */
320 if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
323 /* change focus and send messages */
324 return set_focus_window( hwnd );
328 /*******************************************************************
329 * SetForegroundWindow (USER32.@)
331 BOOL WINAPI SetForegroundWindow( HWND hwnd )
333 TRACE( "%p\n", hwnd );
335 hwnd = WIN_GetFullHandle( hwnd );
336 return set_foreground_window( hwnd, FALSE );
340 /*******************************************************************
341 * GetActiveWindow (USER32.@)
343 HWND WINAPI GetActiveWindow(void)
345 GUITHREADINFO info;
346 info.cbSize = sizeof(info);
347 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
351 /*****************************************************************
352 * GetFocus (USER32.@)
354 HWND WINAPI GetFocus(void)
356 GUITHREADINFO info;
357 info.cbSize = sizeof(info);
358 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
362 /***********************************************************************
363 * SetShellWindowEx (USER32.@)
364 * hwndShell = Progman[Program Manager]
365 * |-> SHELLDLL_DefView
366 * hwndListView = | |-> SysListView32
367 * | | |-> tooltips_class32
368 * | |
369 * | |-> SysHeader32
371 * |-> ProxyTarget
373 BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
375 BOOL ret;
377 if (GetShellWindow())
378 return FALSE;
380 if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
381 return FALSE;
383 if (hwndListView != hwndShell)
384 if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
385 return FALSE;
387 if (hwndListView && hwndListView!=hwndShell)
388 SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
390 SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
392 SERVER_START_REQ(set_global_windows)
394 req->flags = SET_GLOBAL_SHELL_WINDOWS;
395 req->shell_window = wine_server_user_handle( hwndShell );
396 req->shell_listview = wine_server_user_handle( hwndListView );
397 ret = !wine_server_call_err(req);
399 SERVER_END_REQ;
401 return ret;
405 /*******************************************************************
406 * SetShellWindow (USER32.@)
408 BOOL WINAPI SetShellWindow(HWND hwndShell)
410 return SetShellWindowEx(hwndShell, hwndShell);
414 /*******************************************************************
415 * GetShellWindow (USER32.@)
417 HWND WINAPI GetShellWindow(void)
419 HWND hwndShell = 0;
421 SERVER_START_REQ(set_global_windows)
423 req->flags = 0;
424 if (!wine_server_call_err(req))
425 hwndShell = wine_server_ptr_handle( reply->old_shell_window );
427 SERVER_END_REQ;
429 return hwndShell;
433 /***********************************************************************
434 * SetProgmanWindow (USER32.@)
436 HWND WINAPI SetProgmanWindow ( HWND hwnd )
438 SERVER_START_REQ(set_global_windows)
440 req->flags = SET_GLOBAL_PROGMAN_WINDOW;
441 req->progman_window = wine_server_user_handle( hwnd );
442 if (wine_server_call_err( req )) hwnd = 0;
444 SERVER_END_REQ;
445 return hwnd;
449 /***********************************************************************
450 * GetProgmanWindow (USER32.@)
452 HWND WINAPI GetProgmanWindow(void)
454 HWND ret = 0;
456 SERVER_START_REQ(set_global_windows)
458 req->flags = 0;
459 if (!wine_server_call_err(req))
460 ret = wine_server_ptr_handle( reply->old_progman_window );
462 SERVER_END_REQ;
463 return ret;
467 /***********************************************************************
468 * SetTaskmanWindow (USER32.@)
469 * NOTES
470 * hwnd = MSTaskSwWClass
471 * |-> SysTabControl32
473 HWND WINAPI SetTaskmanWindow ( HWND hwnd )
475 SERVER_START_REQ(set_global_windows)
477 req->flags = SET_GLOBAL_TASKMAN_WINDOW;
478 req->taskman_window = wine_server_user_handle( hwnd );
479 if (wine_server_call_err( req )) hwnd = 0;
481 SERVER_END_REQ;
482 return hwnd;
485 /***********************************************************************
486 * GetTaskmanWindow (USER32.@)
488 HWND WINAPI GetTaskmanWindow(void)
490 HWND ret = 0;
492 SERVER_START_REQ(set_global_windows)
494 req->flags = 0;
495 if (!wine_server_call_err(req))
496 ret = wine_server_ptr_handle( reply->old_taskman_window );
498 SERVER_END_REQ;
499 return ret;