501 lines
14 KiB
C
501 lines
14 KiB
C
/*
|
|
* Focus and activation functions
|
|
*
|
|
* Copyright 1993 David Metcalfe
|
|
* Copyright 1995 Alex Korobka
|
|
* Copyright 1994, 2002 Alexandre Julliard
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "win.h"
|
|
#include "imm.h"
|
|
#include "user_private.h"
|
|
#include "wine/server.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(win);
|
|
|
|
|
|
/*****************************************************************
|
|
* set_focus_window
|
|
*
|
|
* Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
|
|
*/
|
|
static HWND set_focus_window( HWND hwnd )
|
|
{
|
|
HWND previous = 0, ime_default;
|
|
BOOL ret;
|
|
|
|
SERVER_START_REQ( set_focus_window )
|
|
{
|
|
req->handle = wine_server_user_handle( hwnd );
|
|
if ((ret = !wine_server_call_err( req )))
|
|
previous = wine_server_ptr_handle( reply->previous );
|
|
}
|
|
SERVER_END_REQ;
|
|
if (!ret) return 0;
|
|
if (previous == hwnd) return previous;
|
|
|
|
if (previous)
|
|
{
|
|
SendMessageW( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
|
|
|
|
ime_default = ImmGetDefaultIMEWnd( previous );
|
|
if (ime_default)
|
|
SendMessageW( ime_default, WM_IME_INTERNAL, IME_INTERNAL_DEACTIVATE, (LPARAM)previous );
|
|
|
|
if (hwnd != GetFocus()) return previous; /* changed by the message */
|
|
}
|
|
if (IsWindow(hwnd))
|
|
{
|
|
USER_Driver->pSetFocus(hwnd);
|
|
|
|
ime_default = ImmGetDefaultIMEWnd( hwnd );
|
|
if (ime_default)
|
|
SendMessageW( ime_default, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE, (LPARAM)hwnd );
|
|
|
|
if (previous)
|
|
NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 );
|
|
|
|
SendMessageW( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
|
|
}
|
|
return previous;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* set_active_window
|
|
*/
|
|
static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
|
|
{
|
|
HWND previous = GetActiveWindow();
|
|
BOOL ret;
|
|
DWORD old_thread, new_thread;
|
|
CBTACTIVATESTRUCT cbt;
|
|
|
|
if (previous == hwnd)
|
|
{
|
|
if (prev) *prev = hwnd;
|
|
return TRUE;
|
|
}
|
|
|
|
/* call CBT hook chain */
|
|
cbt.fMouse = mouse;
|
|
cbt.hWndActive = previous;
|
|
if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE;
|
|
|
|
if (IsWindow(previous))
|
|
{
|
|
SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
|
|
SendMessageW( previous, WM_ACTIVATE,
|
|
MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd );
|
|
}
|
|
|
|
SERVER_START_REQ( set_active_window )
|
|
{
|
|
req->handle = wine_server_user_handle( hwnd );
|
|
if ((ret = !wine_server_call_err( req )))
|
|
previous = wine_server_ptr_handle( reply->previous );
|
|
}
|
|
SERVER_END_REQ;
|
|
if (!ret) return FALSE;
|
|
if (prev) *prev = previous;
|
|
if (previous == hwnd) return TRUE;
|
|
|
|
if (hwnd)
|
|
{
|
|
/* send palette messages */
|
|
if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
|
|
SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
|
|
SMTO_ABORTIFHUNG, 2000, NULL );
|
|
if (!IsWindow(hwnd)) return FALSE;
|
|
}
|
|
|
|
old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0;
|
|
new_thread = hwnd ? GetWindowThreadProcessId( hwnd, NULL ) : 0;
|
|
|
|
if (old_thread != new_thread)
|
|
{
|
|
HWND *list, *phwnd;
|
|
|
|
if ((list = WIN_ListChildren( GetDesktopWindow() )))
|
|
{
|
|
if (old_thread)
|
|
{
|
|
for (phwnd = list; *phwnd; phwnd++)
|
|
{
|
|
if (GetWindowThreadProcessId( *phwnd, NULL ) == old_thread)
|
|
SendMessageW( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
|
|
}
|
|
}
|
|
if (new_thread)
|
|
{
|
|
for (phwnd = list; *phwnd; phwnd++)
|
|
{
|
|
if (GetWindowThreadProcessId( *phwnd, NULL ) == new_thread)
|
|
SendMessageW( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
|
|
}
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, list );
|
|
}
|
|
}
|
|
|
|
if (IsWindow(hwnd))
|
|
{
|
|
SendMessageW( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous );
|
|
SendMessageW( hwnd, WM_ACTIVATE,
|
|
MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ),
|
|
(LPARAM)previous );
|
|
if (GetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow())
|
|
PostMessageW( GetDesktopWindow(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
|
|
}
|
|
|
|
/* now change focus if necessary */
|
|
if (focus)
|
|
{
|
|
GUITHREADINFO info;
|
|
|
|
info.cbSize = sizeof(info);
|
|
NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info );
|
|
/* Do not change focus if the window is no more active */
|
|
if (hwnd == info.hwndActive)
|
|
{
|
|
if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
|
|
set_focus_window( hwnd );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* set_foreground_window
|
|
*/
|
|
static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
|
|
{
|
|
BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
|
|
HWND previous = 0;
|
|
|
|
SERVER_START_REQ( set_foreground_window )
|
|
{
|
|
req->handle = wine_server_user_handle( hwnd );
|
|
if ((ret = !wine_server_call_err( req )))
|
|
{
|
|
previous = wine_server_ptr_handle( reply->previous );
|
|
send_msg_old = reply->send_msg_old;
|
|
send_msg_new = reply->send_msg_new;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
if (ret && previous != hwnd)
|
|
{
|
|
if (send_msg_old) /* old window belongs to other thread */
|
|
SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
|
|
else if (send_msg_new) /* old window belongs to us but new one to other thread */
|
|
ret = set_active_window( 0, NULL, mouse, TRUE );
|
|
|
|
if (send_msg_new) /* new window belongs to other thread */
|
|
SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
|
|
else /* new window belongs to us */
|
|
ret = set_active_window( hwnd, NULL, mouse, TRUE );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* FOCUS_MouseActivate
|
|
*
|
|
* Activate a window as a result of a mouse click
|
|
*/
|
|
BOOL FOCUS_MouseActivate( HWND hwnd )
|
|
{
|
|
return set_foreground_window( hwnd, TRUE );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* SetActiveWindow (USER32.@)
|
|
*/
|
|
HWND WINAPI SetActiveWindow( HWND hwnd )
|
|
{
|
|
HWND prev;
|
|
|
|
TRACE( "%p\n", hwnd );
|
|
|
|
if (hwnd)
|
|
{
|
|
LONG style;
|
|
|
|
hwnd = WIN_GetFullHandle( hwnd );
|
|
if (!IsWindow( hwnd ))
|
|
{
|
|
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
|
|
return 0;
|
|
}
|
|
|
|
style = GetWindowLongW( hwnd, GWL_STYLE );
|
|
if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
|
|
return GetActiveWindow(); /* Windows doesn't seem to return an error here */
|
|
}
|
|
|
|
if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
|
|
return prev;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
* SetFocus (USER32.@)
|
|
*/
|
|
HWND WINAPI SetFocus( HWND hwnd )
|
|
{
|
|
HWND hwndTop = hwnd;
|
|
HWND previous = GetFocus();
|
|
|
|
TRACE( "%p prev %p\n", hwnd, previous );
|
|
|
|
if (hwnd)
|
|
{
|
|
/* Check if we can set the focus to this window */
|
|
hwnd = WIN_GetFullHandle( hwnd );
|
|
if (!IsWindow( hwnd ))
|
|
{
|
|
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
|
|
return 0;
|
|
}
|
|
if (hwnd == previous) return previous; /* nothing to do */
|
|
for (;;)
|
|
{
|
|
HWND parent;
|
|
LONG style = GetWindowLongW( hwndTop, GWL_STYLE );
|
|
if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
|
|
if (!(style & WS_CHILD)) break;
|
|
parent = GetAncestor( hwndTop, GA_PARENT );
|
|
if (!parent || parent == GetDesktopWindow())
|
|
{
|
|
if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
|
|
break;
|
|
}
|
|
if (parent == get_hwnd_message_parent()) return 0;
|
|
hwndTop = parent;
|
|
}
|
|
|
|
/* call hooks */
|
|
if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, TRUE )) return 0;
|
|
|
|
/* activate hwndTop if needed. */
|
|
if (hwndTop != GetActiveWindow())
|
|
{
|
|
if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
|
|
if (!IsWindow( hwnd )) return 0; /* Abort if window destroyed */
|
|
|
|
/* Do not change focus if the window is no longer active */
|
|
if (hwndTop != GetActiveWindow()) return 0;
|
|
}
|
|
}
|
|
else /* NULL hwnd passed in */
|
|
{
|
|
if (!previous) return 0; /* nothing to do */
|
|
if (HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, TRUE )) return 0;
|
|
}
|
|
|
|
/* change focus and send messages */
|
|
return set_focus_window( hwnd );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* SetForegroundWindow (USER32.@)
|
|
*/
|
|
BOOL WINAPI SetForegroundWindow( HWND hwnd )
|
|
{
|
|
TRACE( "%p\n", hwnd );
|
|
|
|
hwnd = WIN_GetFullHandle( hwnd );
|
|
return set_foreground_window( hwnd, FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* GetActiveWindow (USER32.@)
|
|
*/
|
|
HWND WINAPI GetActiveWindow(void)
|
|
{
|
|
GUITHREADINFO info;
|
|
info.cbSize = sizeof(info);
|
|
return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
|
|
}
|
|
|
|
|
|
/*****************************************************************
|
|
* GetFocus (USER32.@)
|
|
*/
|
|
HWND WINAPI GetFocus(void)
|
|
{
|
|
GUITHREADINFO info;
|
|
info.cbSize = sizeof(info);
|
|
return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetShellWindowEx (USER32.@)
|
|
* hwndShell = Progman[Program Manager]
|
|
* |-> SHELLDLL_DefView
|
|
* hwndListView = | |-> SysListView32
|
|
* | | |-> tooltips_class32
|
|
* | |
|
|
* | |-> SysHeader32
|
|
* |
|
|
* |-> ProxyTarget
|
|
*/
|
|
BOOL WINAPI SetShellWindowEx(HWND hwndShell, HWND hwndListView)
|
|
{
|
|
BOOL ret;
|
|
|
|
if (GetShellWindow())
|
|
return FALSE;
|
|
|
|
if (GetWindowLongW(hwndShell, GWL_EXSTYLE) & WS_EX_TOPMOST)
|
|
return FALSE;
|
|
|
|
if (hwndListView != hwndShell)
|
|
if (GetWindowLongW(hwndListView, GWL_EXSTYLE) & WS_EX_TOPMOST)
|
|
return FALSE;
|
|
|
|
if (hwndListView && hwndListView!=hwndShell)
|
|
SetWindowPos(hwndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
|
|
|
|
SetWindowPos(hwndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
|
|
|
|
SERVER_START_REQ(set_global_windows)
|
|
{
|
|
req->flags = SET_GLOBAL_SHELL_WINDOWS;
|
|
req->shell_window = wine_server_user_handle( hwndShell );
|
|
req->shell_listview = wine_server_user_handle( hwndListView );
|
|
ret = !wine_server_call_err(req);
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* SetShellWindow (USER32.@)
|
|
*/
|
|
BOOL WINAPI SetShellWindow(HWND hwndShell)
|
|
{
|
|
return SetShellWindowEx(hwndShell, hwndShell);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* GetShellWindow (USER32.@)
|
|
*/
|
|
HWND WINAPI GetShellWindow(void)
|
|
{
|
|
HWND hwndShell = 0;
|
|
|
|
SERVER_START_REQ(set_global_windows)
|
|
{
|
|
req->flags = 0;
|
|
if (!wine_server_call_err(req))
|
|
hwndShell = wine_server_ptr_handle( reply->old_shell_window );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return hwndShell;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetProgmanWindow (USER32.@)
|
|
*/
|
|
HWND WINAPI SetProgmanWindow ( HWND hwnd )
|
|
{
|
|
SERVER_START_REQ(set_global_windows)
|
|
{
|
|
req->flags = SET_GLOBAL_PROGMAN_WINDOW;
|
|
req->progman_window = wine_server_user_handle( hwnd );
|
|
if (wine_server_call_err( req )) hwnd = 0;
|
|
}
|
|
SERVER_END_REQ;
|
|
return hwnd;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetProgmanWindow (USER32.@)
|
|
*/
|
|
HWND WINAPI GetProgmanWindow(void)
|
|
{
|
|
HWND ret = 0;
|
|
|
|
SERVER_START_REQ(set_global_windows)
|
|
{
|
|
req->flags = 0;
|
|
if (!wine_server_call_err(req))
|
|
ret = wine_server_ptr_handle( reply->old_progman_window );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetTaskmanWindow (USER32.@)
|
|
* NOTES
|
|
* hwnd = MSTaskSwWClass
|
|
* |-> SysTabControl32
|
|
*/
|
|
HWND WINAPI SetTaskmanWindow ( HWND hwnd )
|
|
{
|
|
SERVER_START_REQ(set_global_windows)
|
|
{
|
|
req->flags = SET_GLOBAL_TASKMAN_WINDOW;
|
|
req->taskman_window = wine_server_user_handle( hwnd );
|
|
if (wine_server_call_err( req )) hwnd = 0;
|
|
}
|
|
SERVER_END_REQ;
|
|
return hwnd;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTaskmanWindow (USER32.@)
|
|
*/
|
|
HWND WINAPI GetTaskmanWindow(void)
|
|
{
|
|
HWND ret = 0;
|
|
|
|
SERVER_START_REQ(set_global_windows)
|
|
{
|
|
req->flags = 0;
|
|
if (!wine_server_call_err(req))
|
|
ret = wine_server_ptr_handle( reply->old_taskman_window );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|