diff --git a/dll/win32/shimgvw/shimgvw.c b/dll/win32/shimgvw/shimgvw.c index ba8dd000e81..392a0a463be 100644 --- a/dll/win32/shimgvw/shimgvw.c +++ b/dll/win32/shimgvw/shimgvw.c @@ -33,6 +33,7 @@ HWND g_hwndFullscreen = NULL; SHIMGVW_FILENODE * g_pCurrentFile = NULL; GpImage * g_pImage = NULL; SHIMGVW_SETTINGS g_Settings; +UINT g_ImageId; static const UINT s_ZoomSteps[] = { @@ -402,6 +403,10 @@ Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName) Preview_ResetZoom(pData); Preview_UpdateTitle(pData, szOpenFileName); + + ++g_ImageId; + EnableCommandIfVerbExists(g_ImageId, g_hMainWnd, IDC_PRINT, L"print", pData->m_szFile); + EnableCommandIfVerbExists(g_ImageId, g_hMainWnd, IDC_MODIFY, L"edit", pData->m_szFile); } static VOID @@ -557,15 +562,17 @@ Preview_pSaveImageAs(PPREVIEW_DATA pData) static VOID Preview_pPrintImage(PPREVIEW_DATA pData) { - /* FIXME */ + ShellExecuteVerb(g_hMainWnd, L"print", pData->m_szFile, FALSE); } static VOID Preview_UpdateUI(PPREVIEW_DATA pData) { BOOL bEnable = (g_pImage != NULL); - PostMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_SAVEAS, bEnable); - PostMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_PRINT, bEnable); + SendMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_SAVEAS, bEnable); + // These will be validated and enabled later by EnableCommandIfVerbExists + SendMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_PRINT, FALSE); + SendMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_MODIFY, FALSE); } static VOID @@ -1397,26 +1404,8 @@ Preview_Delete(PPREVIEW_DATA pData) static VOID Preview_Edit(HWND hwnd) { - SHELLEXECUTEINFOW sei; - PPREVIEW_DATA pData = Preview_GetData(hwnd); - - if (!pData->m_szFile[0]) - return; - - ZeroMemory(&sei, sizeof(sei)); - sei.cbSize = sizeof(sei); - sei.lpVerb = L"edit"; - sei.lpFile = pData->m_szFile; - sei.nShow = SW_SHOWNORMAL; - if (!ShellExecuteExW(&sei)) - { - DPRINT1("Preview_Edit: ShellExecuteExW() failed with code %ld\n", GetLastError()); - } - else - { - // Destroy the window to quit the application - DestroyWindow(hwnd); - } + PPREVIEW_DATA pData = Preview_GetData(g_hMainWnd); + ShellExecuteVerb(pData->m_hwnd, L"edit", pData->m_szFile, TRUE); } static VOID @@ -1715,6 +1704,13 @@ PreviewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; } + case WM_UPDATECOMMANDSTATE: + { + PPREVIEW_DATA pData = Preview_GetData(g_hMainWnd); + if (g_ImageId == lParam) + SendMessage(pData->m_hwndToolBar, TB_ENABLEBUTTON, LOWORD(wParam), HIWORD(wParam)); + break; + } default: { return DefWindowProcW(hwnd, uMsg, wParam, lParam); @@ -1738,6 +1734,7 @@ ImageView_Main(HWND hwnd, LPCWSTR szFileName) INITCOMMONCONTROLSEX Icc = { .dwSize = sizeof(Icc), .dwICC = ICC_WIN95_CLASSES }; InitCommonControlsEx(&Icc); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); // Give UI higher priority than background threads /* Initialize COM */ hrCoInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); diff --git a/dll/win32/shimgvw/shimgvw.h b/dll/win32/shimgvw/shimgvw.h index 279d08cd244..cdb6f975fa2 100644 --- a/dll/win32/shimgvw/shimgvw.h +++ b/dll/win32/shimgvw/shimgvw.h @@ -30,6 +30,8 @@ #include "resource.h" +#define WM_UPDATECOMMANDSTATE (WM_APP + 0) + extern HINSTANCE g_hInstance; extern GpImage *g_pImage; @@ -72,6 +74,8 @@ void Anime_Pause(PANIME pAnime); BOOL Anime_OnTimer(PANIME pAnime, WPARAM wParam); void DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam); +void EnableCommandIfVerbExists(UINT ImageId, HWND hwnd, UINT CmdId, PCWSTR Verb, PCWSTR File); +void ShellExecuteVerb(HWND hwnd, PCWSTR Verb, PCWSTR File, BOOL Quit); void DisplayHelp(HWND hwnd); static inline LPVOID QuickAlloc(SIZE_T cbSize, BOOL bZero) diff --git a/dll/win32/shimgvw/util.c b/dll/win32/shimgvw/util.c index 290ad083fad..5a3b8d46294 100644 --- a/dll/win32/shimgvw/util.c +++ b/dll/win32/shimgvw/util.c @@ -47,11 +47,10 @@ static void ModifyShellContextMenu(IContextMenu *pCM, HMENU hMenu, UINT CmdIdFirst, PCWSTR Assoc) { HRESULT hr; - UINT id, i; - for (i = 0; i < GetMenuItemCount(hMenu); ++i) + for (UINT i = 0, c = GetMenuItemCount(hMenu); i < c; ++i) { WCHAR buf[200]; - id = GetMenuItemIdByPos(hMenu, i); + UINT id = GetMenuItemIdByPos(hMenu, i); if (id == (UINT)-1) continue; @@ -131,8 +130,8 @@ die: g_pContextMenu = NULL; } -void -DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam) +HRESULT +GetUIObjectOfPath(HWND hwnd, PCWSTR File, REFIID riid, void **ppv) { HRESULT hr; IShellFolder *pSF; @@ -140,19 +139,110 @@ DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam) PIDLIST_ABSOLUTE pidl = ILCreateFromPath(File); if (pidl && SUCCEEDED(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pSF), &pidlItem))) { - IContextMenu *pCM; - hr = IShellFolder_GetUIObjectOf(pSF, hwnd, 1, &pidlItem, &IID_IContextMenu, NULL, (void**)&pCM); - if (SUCCEEDED(hr)) - { - DoShellContextMenu(hwnd, pCM, File, lParam); - IContextMenu_Release(pCM); - } + hr = IShellFolder_GetUIObjectOf(pSF, hwnd, 1, &pidlItem, riid, NULL, ppv); IShellFolder_Release(pSF); } SHFree(pidl); + return hr; } -void DisplayHelp(HWND hwnd) +void +DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam) +{ + IContextMenu *pCM; + HRESULT hr = GetUIObjectOfPath(hwnd, File, IID_PPV_ARG(IContextMenu, &pCM)); + if (SUCCEEDED(hr)) + { + DoShellContextMenu(hwnd, pCM, File, lParam); + IContextMenu_Release(pCM); + } +} + +typedef struct _ENABLECOMMANDDATA +{ + HWND hwnd; + PCWSTR Verb; + UINT CmdId; + UINT ImageId; + WCHAR File[ANYSIZE_ARRAY]; +} ENABLECOMMANDDATA; + +static DWORD CALLBACK +EnableCommandIfVerbExistsProc(LPVOID ThreadParam) +{ + enum { first = 1, last = 0x7fff }; + ENABLECOMMANDDATA *pData = ThreadParam; + IContextMenu *pCM; + HRESULT hr = GetUIObjectOfPath(pData->hwnd, pData->File, IID_PPV_ARG(IContextMenu, &pCM)); + if (SUCCEEDED(hr)) + { + HMENU hMenu = CreatePopupMenu(); + hr = IContextMenu_QueryContextMenu(pCM, hMenu, 0, first, last, CMF_NORMAL); + if (SUCCEEDED(hr)) + { + for (UINT i = 0, c = GetMenuItemCount(hMenu); i < c; ++i) + { + WCHAR buf[200]; + UINT id = GetMenuItemIdByPos(hMenu, i); + if (id == (UINT)-1) + continue; + + *buf = UNICODE_NULL; + hr = IContextMenu_GetCommandString(pCM, id - first, GCS_VERBW, NULL, (char*)buf, _countof(buf)); + if (SUCCEEDED(hr) && !lstrcmpiW(buf, pData->Verb)) + { + PostMessageW(pData->hwnd, WM_UPDATECOMMANDSTATE, MAKELONG(pData->CmdId, TRUE), pData->ImageId); + break; + } + } + } + DestroyMenu(hMenu); + IContextMenu_Release(pCM); + } + SHFree(pData); + return 0; +} + +void +EnableCommandIfVerbExists(UINT ImageId, HWND hwnd, UINT CmdId, PCWSTR Verb, PCWSTR File) +{ + const SIZE_T cch = lstrlenW(File) + 1; + ENABLECOMMANDDATA *pData = SHAlloc(FIELD_OFFSET(ENABLECOMMANDDATA, File[cch])); + if (pData) + { + pData->hwnd = hwnd; + pData->Verb = Verb; // Note: This assumes the string is valid for the lifetime of the thread. + pData->CmdId = CmdId; + pData->ImageId = ImageId; + CopyMemory(pData->File, File, cch * sizeof(*File)); + SHCreateThread(EnableCommandIfVerbExistsProc, pData, CTF_COINIT | CTF_INSIST, NULL); + } +} + +void +ShellExecuteVerb(HWND hwnd, PCWSTR Verb, PCWSTR File, BOOL Quit) +{ + SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_INVOKEIDLIST | SEE_MASK_ASYNCOK }; + if (!*File) + return; + + sei.hwnd = hwnd; + sei.lpVerb = Verb; + sei.lpFile = File; + sei.nShow = SW_SHOW; + if (!ShellExecuteExW(&sei)) + { + DPRINT1("ShellExecuteExW(%ls, %ls) failed with code %ld\n", Verb, File, GetLastError()); + } + else if (Quit) + { + // Destroy the window to quit the application + DestroyWindow(hwnd); + } +} + +void +DisplayHelp(HWND hwnd) { SHELL_ErrorBox(hwnd, ERROR_NOT_SUPPORTED); }