diff --git a/base/shell/explorer/CMakeLists.txt b/base/shell/explorer/CMakeLists.txt index 2fa8357d2b1..e243c3387fa 100644 --- a/base/shell/explorer/CMakeLists.txt +++ b/base/shell/explorer/CMakeLists.txt @@ -36,5 +36,6 @@ add_executable(explorer ${SOURCE} explorer.rc) target_link_libraries(explorer uuid wine cpprt atl_classes) set_module_type(explorer win32gui UNICODE) add_importlibs(explorer setupapi advapi32 gdi32 user32 comctl32 ole32 oleaut32 shell32 browseui shlwapi shdocvw version uxtheme msvcrt kernel32 ntdll) +add_delay_importlibs(explorer psapi) add_pch(explorer precomp.h SOURCE) add_cd_file(TARGET explorer DESTINATION reactos FOR all) diff --git a/base/shell/explorer/precomp.h b/base/shell/explorer/precomp.h index 38adf881505..0367f253752 100644 --- a/base/shell/explorer/precomp.h +++ b/base/shell/explorer/precomp.h @@ -395,6 +395,7 @@ HRESULT CSysPagerWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv); */ #define TSWM_UPDATETASKBARPOS (WM_USER + 2) +#define TSWM_ACTIVATETASKINDEX (WM_USER + 88) HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv); diff --git a/base/shell/explorer/taskswnd.cpp b/base/shell/explorer/taskswnd.cpp index 381ba1c3f11..e44a733d87d 100644 --- a/base/shell/explorer/taskswnd.cpp +++ b/base/shell/explorer/taskswnd.cpp @@ -11,6 +11,7 @@ #include #include #include +#include /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every 5 seconds */ @@ -101,6 +102,23 @@ SHELL_IsRudeWindow(_In_opt_ HMONITOR hMonitor, _In_ HWND hWnd, _In_ BOOL bDontCh return ::EqualRect(&rcUnion, &rcWnd) && (bDontCheckActive || SHELL_IsRudeWindowActive(hWnd)); } +static BOOL +SHELL_GetImageFileNameByWindow(_In_ HWND hWnd, _Out_cap_(cchPath) LPWSTR pszPath, _In_ DWORD cchPath) +{ + DWORD dwProcessId; + HANDLE hProcess; + BOOL bSuccess; + if (GetWindowThreadProcessId(hWnd, &dwProcessId) == 0) + return FALSE; + // TODO: Switch to PROCESS_QUERY_LIMITED_INFORMATION once NT6 kernel32 support is implemented. + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId); + if (hProcess == NULL) + return FALSE; + bSuccess = GetModuleFileNameExW(hProcess, NULL, pszPath, cchPath) != 0; + CloseHandle(hProcess); + return bSuccess; +} + //////////////////////////////////////////////////////////////// const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass"; @@ -2096,6 +2114,72 @@ public: return TRUE; } + LRESULT OnActivateTaskIndex(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) + { + DWORD dwAccelerators = HIWORD(wParam); + WCHAR szExecutablePath[MAX_PATH]; + + PTASK_ITEM pTaskItem = FindTaskItemByIndex(LOWORD(wParam)); + if (pTaskItem && ::IsWindow(pTaskItem->hWnd)) + { + // Win+Shift: New instance + if ((dwAccelerators & MOD_SHIFT) == MOD_SHIFT) + { + if (!SHELL_GetImageFileNameByWindow(pTaskItem->hWnd, + szExecutablePath, + _countof(szExecutablePath))) + { + return FALSE; + } + // Win+Shift+Ctrl is used for creating a new instance of a task as Administrator + LPCWSTR szVerb = L"open"; + if ((dwAccelerators & MOD_CONTROL) == MOD_CONTROL) + szVerb = L"runas"; + ShellExecuteW(NULL, szVerb, szExecutablePath, NULL, NULL, SW_SHOWNORMAL); + return TRUE; + } + // Win+Alt: Jump list + else if ((dwAccelerators & MOD_ALT) == MOD_ALT) + { + // TODO: Use JumpList once implemented + + // Find TaskItem's position on the screen + RECT rcItem, rcToolbar; + m_TaskBar.GetItemRect(pTaskItem->Index, &rcItem); + m_TaskBar.GetWindowRect(&rcToolbar); + OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top); + + ::SendMessageCallbackW(pTaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(rcItem.left, rcItem.top), + SendAsyncProc, (ULONG_PTR)pTaskItem); + } + // Win+Ctrl: Switch to last window of a task + else if ((dwAccelerators & MOD_CONTROL) == MOD_CONTROL) + { + // TODO: Implement once Superbar is implemented. + return FALSE; + } + // No modifier: just switch to the task + else + { + // TODO: This should launch a pinned task once Superbar is implemented + + BOOL bIsMinimized = ::IsIconic(pTaskItem->hWnd); + BOOL bIsActive = (pTaskItem == m_ActiveTaskItem); + + if (!bIsMinimized && bIsActive) + { + if (!::IsHungAppWindow(pTaskItem->hWnd)) + ::ShowWindowAsync(pTaskItem->hWnd, SW_MINIMIZE); + } + else + { + ::SwitchToThisWindow(pTaskItem->hWnd, TRUE); + } + } + } + return TRUE; + } + LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { BOOL bSettingsChanged = FALSE; @@ -2277,6 +2361,7 @@ public: MESSAGE_HANDLER(WM_COMMAND, OnCommand) MESSAGE_HANDLER(WM_NOTIFY, OnNotify) MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos) + MESSAGE_HANDLER(TSWM_ACTIVATETASKINDEX, OnActivateTaskIndex) MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged) MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) MESSAGE_HANDLER(WM_TIMER, OnTimer) diff --git a/base/shell/explorer/traywnd.cpp b/base/shell/explorer/traywnd.cpp index b5c6fe98980..14efdb846c1 100644 --- a/base/shell/explorer/traywnd.cpp +++ b/base/shell/explorer/traywnd.cpp @@ -44,6 +44,8 @@ HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, ICont #define IDHK_DESKTOP 0x1fe #define IDHK_PAGER 0x1ff +#define IDHK_ACTIVATE_TASK 0x209 + enum { NONE, TILED, CASCADED } g_Arrangement = NONE; struct WINDOWPOSBACKUPDATA @@ -2437,6 +2439,61 @@ ChangePos: MAKELONG(IDHK_SYS_PROPERTIES, MAKEWORD(VK_PAUSE, MOD_WIN)), MAKELONG(IDHK_DESKTOP, MAKEWORD('D', MOD_WIN)), MAKELONG(IDHK_PAGER, MAKEWORD('B', MOD_WIN)), + + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN)), + + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_ALT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_ALT)), + + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_CONTROL)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_CONTROL)), + + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_SHIFT)), + + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), + MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_CONTROL | MOD_SHIFT)), }; if (!SHRestricted(REST_NOWINKEYS)) { @@ -3107,6 +3164,13 @@ HandleTrayContextMenu: LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { + if (wParam == IDHK_ACTIVATE_TASK) + { + // Map the ('1'-'9','0') hotkey to a zero-based task index + DWORD iTask = (HIWORD(lParam) - '0' + 9) % 10; + // Request the task switcher to perform the switch + ::SendMessageW(m_TaskSwitch, TSWM_ACTIVATETASKINDEX, MAKEWPARAM(iTask, LOWORD(lParam)), 0); + } return HandleHotKey(wParam); }