diff --git a/src/Winfile.vcxproj b/src/Winfile.vcxproj index c6ac905c..89b83a84 100644 --- a/src/Winfile.vcxproj +++ b/src/Winfile.vcxproj @@ -297,6 +297,7 @@ + @@ -367,6 +368,7 @@ + diff --git a/src/Winfile.vcxproj.filters b/src/Winfile.vcxproj.filters index aaa8f825..d3bb1061 100644 --- a/src/Winfile.vcxproj.filters +++ b/src/Winfile.vcxproj.filters @@ -37,6 +37,7 @@ + @@ -62,6 +63,7 @@ + diff --git a/src/explorermenu.c b/src/explorermenu.c new file mode 100644 index 00000000..223bfa55 --- /dev/null +++ b/src/explorermenu.c @@ -0,0 +1,113 @@ +#include + +// +// How to host an IContextMenu, part 1 - Initial foray +// https://devblogs.microsoft.com/oldnewthing/20040920-00/?p=37823 +// +// How to host an IContextMenu, part 2 - Displaying the context menu +// https://devblogs.microsoft.com/oldnewthing/20040922-00/?p=37793 +// +// How to host an IContextMenu, part 3 - Invocation location +// https://devblogs.microsoft.com/oldnewthing/20040923-00/?p=37773 +// +// How to host an IContextMenu, part 4 - Key context +// https://devblogs.microsoft.com/oldnewthing/20040924-00/?p=37753 +// +// How to host an IContextMenu, part 5 - Handling menu messages +// https://devblogs.microsoft.com/oldnewthing/20040927-00/?p=37733 +// + +IContextMenu2 *pExplorerCm2; +IContextMenu3 *pExplorerCm3; + +static HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv) +{ + *ppv = NULL; + HRESULT hr; + LPITEMIDLIST pidl; + SFGAOF sfgao; + if (SUCCEEDED(hr = SHParseDisplayName(pszPath, NULL, &pidl, 0, &sfgao))) + { + IShellFolder *psf; + LPCITEMIDLIST pidlChild; + if (SUCCEEDED(hr = SHBindToParent(pidl, &IID_IShellFolder, (void **)&psf, &pidlChild))) + { + hr = psf->lpVtbl->GetUIObjectOf(psf, hwnd, 1, &pidlChild, riid, NULL, ppv); + psf->lpVtbl->Release(psf); + } + CoTaskMemFree(pidl); + } + return hr; +} + +// Since we are combining the WinFile menu with the explorer menu +// all of the WinFile command IDs must be below this range +#define EXPLORER_QCM_FIRST 0x1001 +#define EXPLORER_QCM_LAST 0x7FFF + +int ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, HMENU hMenuWinFile, UINT xPos, UINT yPos) +{ + POINT pt = { (long)xPos, (long)yPos }; + if (pt.x == -1 && pt.y == -1) + { + pt.x = pt.y = 0; + ClientToScreen(hwnd, &pt); + } + + int ret = 0; + IContextMenu *pcm; + if (SUCCEEDED(GetUIObjectOfFile(hwnd, pFileName, &IID_IContextMenu, (void **)&pcm))) + { + HMENU hmenu = CreatePopupMenu(); + if (hmenu) + { + if (SUCCEEDED(pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, EXPLORER_QCM_FIRST, EXPLORER_QCM_LAST, CMF_NORMAL))) + { + InsertMenu(hMenuWinFile, (UINT)-1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hmenu, L"Explorer"); + pcm->lpVtbl->QueryInterface(pcm, &IID_IContextMenu2, (void **)&pExplorerCm2); + pcm->lpVtbl->QueryInterface(pcm, &IID_IContextMenu3, (void **)&pExplorerCm3); + int iCmd = TrackPopupMenuEx(hMenuWinFile, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL); + if (pExplorerCm2) + { + pExplorerCm2->lpVtbl->Release(pExplorerCm2); + pExplorerCm2 = NULL; + } + if (pExplorerCm3) + { + pExplorerCm3->lpVtbl->Release(pExplorerCm3); + pExplorerCm3 = NULL; + } + if (iCmd > 0 && iCmd <= EXPLORER_QCM_FIRST) + { + // The command selected was in the caller's range, they will have to execute it + ret = iCmd; + } + else if (iCmd > 0) + { + CMINVOKECOMMANDINFOEX info = { 0 }; + info.cbSize = sizeof(info); + info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_PTINVOKE; + if (GetKeyState(VK_CONTROL) < 0) + { + info.fMask |= CMIC_MASK_CONTROL_DOWN; + } + if (GetKeyState(VK_SHIFT) < 0) + { + info.fMask |= CMIC_MASK_SHIFT_DOWN; + } + info.hwnd = hwnd; + int i = iCmd - EXPLORER_QCM_FIRST; + info.lpVerb = MAKEINTRESOURCEA(i); + info.lpVerbW = MAKEINTRESOURCEW(i); + info.nShow = SW_SHOWNORMAL; + info.ptInvoke.x = xPos; + info.ptInvoke.y = yPos; + HRESULT hr = pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&info); + } + } + DestroyMenu(hmenu); + } + pcm->lpVtbl->Release(pcm); + } + return ret; +} diff --git a/src/explorermenu.h b/src/explorermenu.h new file mode 100644 index 00000000..9bc6dddb --- /dev/null +++ b/src/explorermenu.h @@ -0,0 +1,6 @@ +#include + +int ShowExplorerContextMenu(HWND hwnd, LPCWSTR pFileName, HMENU hMenuWinFile, UINT xPos, UINT yPos); + +extern IContextMenu2 *pExplorerCm2; +extern IContextMenu3 *pExplorerCm3; diff --git a/src/wfdir.c b/src/wfdir.c index e1fb64c1..70ce6834 100644 --- a/src/wfdir.c +++ b/src/wfdir.c @@ -16,6 +16,7 @@ #include #include "wfdrop.h" +#include "explorermenu.h" WCHAR szAttr[] = L"RHSACE"; @@ -429,6 +430,19 @@ DirWndProc( static HWND hwndOwnerDraw = NULL; + // Process messages for the explorer context menu + if (pExplorerCm3) { + LRESULT lres; + if (SUCCEEDED(pExplorerCm3->lpVtbl->HandleMenuMsg2(pExplorerCm3, uMsg, wParam, lParam, &lres))) { + return lres; + } + } + else if (pExplorerCm2) { + if (SUCCEEDED(pExplorerCm2->lpVtbl->HandleMenuMsg(pExplorerCm2, uMsg, wParam, lParam))) { + return 0; + } + } + #ifdef PROGMANHSCROLL // // If the window is different and uMsg == WM_DRAWITEM _OR_ diff --git a/src/wfdlgs.c b/src/wfdlgs.c index b315b12e..3f726077 100644 --- a/src/wfdlgs.c +++ b/src/wfdlgs.c @@ -14,6 +14,7 @@ #include #include "lfn.h" #include "wfcopy.h" +#include "explorermenu.h" VOID MDIClientSizeChange(HWND hwndActive, INT iFlags); @@ -840,6 +841,7 @@ ActivateCommonContextMenu(HWND hwnd, HWND hwndLB, LPARAM lParam) { DWORD cmd, item; POINT pt; + WCHAR *pSelectedFile = NULL; HMENU hMenu = GetSubMenu(LoadMenu(hAppInstance, TEXT("CTXMENU")), 0); @@ -881,14 +883,20 @@ ActivateCommonContextMenu(HWND hwnd, HWND hwndLB, LPARAM lParam) SendMessage(hwndLB, LB_SETSEL, (WPARAM)TRUE, (LPARAM)item); BOOL bDir = FALSE; - SendMessage(hwnd, FS_GETSELECTION, 5, (LPARAM)&bDir); + pSelectedFile = (WCHAR *)SendMessage(hwnd, FS_GETSELECTION, 5, (LPARAM)&bDir); if (bDir) EnableMenuItem(hMenu, IDM_EDIT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } } } - cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hwnd, NULL); + if (pSelectedFile != NULL) { + cmd = ShowExplorerContextMenu(hwnd, pSelectedFile, hMenu, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + else + { + cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hwnd, NULL); + } if (cmd != 0) PostMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(cmd, 0, 0));