diff --git a/dll/shellext/netshell/shfldr_netconnect.cpp b/dll/shellext/netshell/shfldr_netconnect.cpp index 38e743fb533..dfd1a0d8927 100644 --- a/dll/shellext/netshell/shfldr_netconnect.cpp +++ b/dll/shellext/netshell/shfldr_netconnect.cpp @@ -641,14 +641,6 @@ HRESULT WINAPI CNetConUiObject::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { CmdId = IDS_NET_PROPERTIES; } - else if ((UINT_PTR)lpcmi->lpVerb == FCIDM_SHVIEW_RENAME) // DefView accelerator - { - CmdId = IDS_NET_RENAME; - } - else if ((UINT_PTR)lpcmi->lpVerb == FCIDM_SHVIEW_PROPERTIES) // DefView accelerator - { - CmdId = IDS_NET_PROPERTIES; - } else if (!IS_INTRESOURCE(lpcmi->lpVerb) || LOWORD(lpcmi->lpVerb) > 7) { FIXME("Got invalid command\n"); diff --git a/dll/win32/browseui/shellfind/CFindFolder.cpp b/dll/win32/browseui/shellfind/CFindFolder.cpp index b84fad26800..fb072923a19 100644 --- a/dll/win32/browseui/shellfind/CFindFolder.cpp +++ b/dll/win32/browseui/shellfind/CFindFolder.cpp @@ -10,6 +10,32 @@ WINE_DEFAULT_DEBUG_CHANNEL(shellfind); +#ifndef _SHELL32_ + +static HRESULT WINAPI DisplayNameOfW(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, + _In_ DWORD dwFlags, _Out_ LPWSTR pszBuf, _In_ UINT cchBuf) +{ + *pszBuf = UNICODE_NULL; + STRRET sr; + HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr); + return FAILED(hr) ? hr : StrRetToBufW(&sr, pidl, pszBuf, cchBuf); +} + +static HRESULT +GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, + _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax) +{ + HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax); + if (FAILED(hr)) + { + WCHAR buf[MAX_PATH]; + hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf)); + if (SUCCEEDED(hr)) + hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL; + } + return hr; +} + static HRESULT SHELL32_CoCreateInitSF(LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti, LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut) { @@ -37,6 +63,8 @@ static HRESULT SHELL32_CoCreateInitSF(LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TAR return pShellFolder->QueryInterface(riid, ppvOut); } +#endif // _SHELL32_ + static void WINAPI _InsertMenuItemW( HMENU hMenu, UINT indexMenu, @@ -76,6 +104,28 @@ static void WINAPI _InsertMenuItemW( InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii); } +static HRESULT QueryActiveShellView(IUnknown *pUnkSite, REFGUID rService, IShellView **ppSV) +{ + CComPtr pSB; + HRESULT hr = IUnknown_QueryService(pUnkSite, rService, IID_PPV_ARG(IShellBrowser, &pSB)); + return SUCCEEDED(hr) ? pSB->QueryActiveShellView(ppSV) : hr; +} + +static HRESULT BeginRenameOfShellViewSelection(IUnknown *pUnkSite) +{ + CComPtr pSV; + HRESULT hr = QueryActiveShellView(pUnkSite, SID_SShellBrowser, &pSV); + if (FAILED(hr)) + return hr; + CComPtr pFV2; + if (SUCCEEDED(hr = pSV->QueryInterface(IID_PPV_ARG(IFolderView2, &pFV2)))) + return pFV2->DoRename(); + CComPtr pSV2; + if (SUCCEEDED(hr = pSV->QueryInterface(IID_PPV_ARG(IShellView2, &pSV2)))) + return pSV2->HandleRename(NULL); + return hr; +} + struct FolderViewColumns { int iResource; @@ -894,22 +944,27 @@ STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl if (FAILED(hr = pFolder->GetAttributesOf(1, &pidlChild, rgfInOut))) break; } - *rgfInOut &= ~SFGAO_CANRENAME; // FIXME: Handle SetNameOf return hr; } class CFindFolderContextMenu : public IContextMenu, + public IObjectWithSite, public CComObjectRootEx { CComPtr m_pInner; CComPtr m_shellFolderView; + IUnknown *m_pUnkSite = NULL; + UINT m_cidl; UINT m_MyFirstId = 0; static const UINT ADDITIONAL_MENU_ITEMS = 2; - //// *** IContextMenu methods *** + // *** IContextMenu *** STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { + if (m_cidl > 1) + uFlags &= ~CMF_CANRENAME; + m_MyFirstId = 0; if (idCmdLast - idCmdFirst > ADDITIONAL_MENU_ITEMS) { @@ -927,12 +982,7 @@ class CFindFolderContextMenu : STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { - if (!IS_INTRESOURCE(lpcmi->lpVerb)) - { - return m_pInner->InvokeCommand(lpcmi); - } - - UINT idCmd = LOWORD(lpcmi->lpVerb); + WORD idCmd = IS_INTRESOURCE(lpcmi->lpVerb) ? LOWORD(lpcmi->lpVerb) : 0; if (m_MyFirstId && idCmd >= m_MyFirstId && idCmd < m_MyFirstId + ADDITIONAL_MENU_ITEMS) { PCUITEMID_CHILD *apidl; @@ -945,12 +995,27 @@ class CFindFolderContextMenu : { CComHeapPtr folderPidl(ILCreateFromPathW(_ILGetPath(apidl[i]))); if (!folderPidl) - return E_OUTOFMEMORY; + { + hResult = E_OUTOFMEMORY; + break; + } LPCITEMIDLIST child = _ILGetFSPidl(apidl[i]); SHOpenFolderAndSelectItems(folderPidl, 1, &child, 0); } LocalFree(apidl); // Yes, LocalFree - return S_OK; + return hResult; + } + + // TODO: Use GetDfmCmd instead after moving all of this to shell32 + CHAR szVerb[42]; + PCSTR pszVerb = !idCmd ? lpcmi->lpVerb : NULL; + if (!pszVerb && SUCCEEDED(GetCommandStringA(m_pInner, LOWORD(lpcmi->lpVerb), GCS_VERBA, szVerb, _countof(szVerb)))) + pszVerb = szVerb; + + if (pszVerb && !lstrcmpiA(pszVerb, "rename")) + { + // Note: We can't invoke m_pInner (CDefaultContextMenu::DoRename) because the pidl is incorrect and SelectItem will fail. + return BeginRenameOfShellViewSelection(m_pUnkSite); } // FIXME: We can't block FCIDM_SHVIEW_REFRESH here, add items on SFVM_LISTREFRESHED instead @@ -962,8 +1027,27 @@ class CFindFolderContextMenu : return m_pInner->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); } + // *** IObjectWithSite *** + STDMETHODIMP SetSite(IUnknown *pUnkSite) override + { + IUnknown_Set(&m_pUnkSite, pUnkSite); + IUnknown_SetSite(m_pInner, pUnkSite); + return S_OK; + } + + STDMETHODIMP GetSite(REFIID riid, void **ppvSite) override + { + *ppvSite = NULL; + return m_pUnkSite ? m_pUnkSite->QueryInterface(riid, ppvSite) : E_FAIL; + } + public: - static HRESULT Create(IShellFolderView *pShellFolderView, IContextMenu *pInnerContextMenu, IContextMenu **pContextMenu) + ~CFindFolderContextMenu() + { + SetSite(NULL); + } + + static HRESULT Create(IShellFolderView *pShellFolderView, UINT cidl, IContextMenu *pInnerContextMenu, IContextMenu **pContextMenu) { CComObject *pObj; HRESULT hResult = CComObject::CreateInstance(&pObj); @@ -971,11 +1055,13 @@ public: return hResult; pObj->m_shellFolderView = pShellFolderView; pObj->m_pInner = pInnerContextMenu; + pObj->m_cidl = cidl; return pObj->QueryInterface(IID_PPV_ARG(IContextMenu, pContextMenu)); } BEGIN_COM_MAP(CFindFolderContextMenu) COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) + COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) END_COM_MAP() }; @@ -1034,7 +1120,7 @@ STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHI hr = pShellFolder->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, (void**)&pContextMenu); if (FAILED_UNEXPECTEDLY(hr)) return hr; - return CFindFolderContextMenu::Create(m_shellFolderView, pContextMenu, (IContextMenu **)ppvOut); + return CFindFolderContextMenu::Create(m_shellFolderView, cidl, pContextMenu, (IContextMenu **)ppvOut); } CComPtr pFolder; @@ -1056,8 +1142,34 @@ STDMETHODIMP CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, STDMETHODIMP CFindFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut) { - UNIMPLEMENTED; - return E_NOTIMPL; + if (!pPidlOut) + return E_INVALIDARG; // Our pidls are special, the caller must get it from us and not from parsing + *pPidlOut = NULL; + + HRESULT hr; + CComPtr pFolder; + PCUITEMID_CHILD pidlChild; + if (FAILED_UNEXPECTEDLY(hr = GetFSFolderAndChild(pidl, &pFolder, &pidlChild))) + return hr; + + PCWSTR pszDir = _ILGetPath(pidl); + PWSTR pszFull = (PWSTR)SHAlloc((wcslen(pszDir) + MAX_PATH) * sizeof(*pszDir)); + if (!pszFull) + return E_OUTOFMEMORY; + + PITEMID_CHILD pidlRawNew = NULL; + hr = pFolder->SetNameOf(hwndOwner, pidlChild, lpName, dwFlags, &pidlRawNew); + if (SUCCEEDED(hr) && pidlRawNew) + { + WCHAR szFileName[MAX_PATH]; + hr = DisplayNameOfW(pFolder, pidlRawNew, SHGDN_FORPARSING | SHGDN_INFOLDER, szFileName, _countof(szFileName)); + ILFree(pidlRawNew); + PathCombineW(pszFull, pszDir, szFileName); + if (SUCCEEDED(hr)) + hr = ((*pPidlOut = _ILCreate(pszFull)) != NULL) ? S_OK : E_OUTOFMEMORY; + } + SHFree(pszFull); + return hr; } //// *** IShellFolderViewCB method *** diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index f865e7d28d3..7fdf61c5c65 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -344,13 +344,14 @@ public: HRESULT FillArrangeAsMenu(HMENU hmenuArrange); HRESULT CheckViewMode(HMENU hmenuView); LRESULT DoColumnContextMenu(LRESULT lParam); + HRESULT SelectAndPositionItem(int Idx, UINT fSVSI, POINT *ppt); UINT GetSelections(); SFGAOF GetSelectionAttributes(SFGAOF Query); HRESULT OpenSelectedItems(PCSTR pszVerb = NULL); void OnDeactivate(); void DoActivate(UINT uState); HRESULT drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); - HRESULT InvokeContextMenuCommand(CComPtr& pCM, LPCSTR lpVerb, POINT* pt = NULL); + HRESULT InvokeContextMenuCommand(CComPtr& pCM, LPCSTR lpVerb, POINT* pt = NULL, bool TryMapVerb = false); LRESULT OnExplorerCommand(UINT uCommand, BOOL bUseSelection); FOLDERVIEWMODE GetDefaultViewMode(); HRESULT GetDefaultViewStream(DWORD Stgm, IStream **ppStream); @@ -391,7 +392,7 @@ public: // *** IShellView2 methods *** STDMETHOD(GetView)(SHELLVIEWID *view_guid, ULONG view_type) override; STDMETHOD(CreateViewWindow2)(LPSV2CVW2_PARAMS view_params) override; - STDMETHOD(HandleRename)(LPCITEMIDLIST new_pidl) override; + STDMETHOD(HandleRename)(LPCITEMIDLIST pidl) override; STDMETHOD(SelectAndPositionItem)(LPCITEMIDLIST item, UINT flags, POINT *point) override; // *** IShellView3 methods *** @@ -2141,7 +2142,7 @@ SFGAOF CDefView::GetSelectionAttributes(SFGAOF Query) return SUCCEEDED(m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &Attr)) ? (Attr & Query) : 0; } -HRESULT CDefView::InvokeContextMenuCommand(CComPtr& pCM, LPCSTR lpVerb, POINT* pt) +HRESULT CDefView::InvokeContextMenuCommand(CComPtr& pCM, LPCSTR lpVerb, POINT* pt, bool TryMapVerb) { CMINVOKECOMMANDINFOEX cmi; @@ -2151,6 +2152,24 @@ HRESULT CDefView::InvokeContextMenuCommand(CComPtr& pCM, LPCSTR lp cmi.lpVerb = lpVerb; cmi.nShow = SW_SHOW; + WCHAR szverbW[sizeof("properties")]; + static const WORD verbmap[] = { FCIDM_SHVIEW_DELETE, FCIDM_SHVIEW_RENAME, + FCIDM_SHVIEW_PROPERTIES, FCIDM_SHVIEW_CREATELINK, + FCIDM_SHVIEW_CUT, FCIDM_SHVIEW_COPY, FCIDM_SHVIEW_INSERT }; + for (SIZE_T i = 0; TryMapVerb && i < _countof(verbmap); ++i) + { + if (cmi.lpVerb != MAKEINTRESOURCEA(verbmap[i])) + continue; + if (PCSTR pszverbA = MapFcidmCmdToVerb((UINT_PTR)cmi.lpVerb)) + { + // Map our internal commands to canonical verbs so non-shell32 menus can understand us + SHAnsiToUnicode(pszverbA, szverbW, _countof(szverbW)); + cmi.lpVerb = pszverbA; + cmi.lpVerbW = szverbW; + break; + } + } + if (GetKeyState(VK_SHIFT) < 0) cmi.fMask |= CMIC_MASK_SHIFT_DOWN; @@ -2388,7 +2407,7 @@ LRESULT CDefView::OnExplorerCommand(UINT uCommand, BOOL bUseSelection) } // FIXME: We should probably use the objects position? - InvokeContextMenuCommand(pCM, MAKEINTRESOURCEA(uCommand), NULL); + InvokeContextMenuCommand(pCM, MAKEINTRESOURCEA(uCommand), NULL, true); return 0; } @@ -3790,21 +3809,13 @@ HRESULT STDMETHODCALLTYPE CDefView::SelectItem(int iItem, DWORD dwFlags) HRESULT STDMETHODCALLTYPE CDefView::SelectAndPositionItems(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, POINT *apt, DWORD dwFlags) { ASSERT(m_ListView); - - /* Reset the selection */ - m_ListView.SetItemState(-1, 0, LVIS_SELECTED); - - int lvIndex; + m_ListView.SetItemState(-1, 0, LVIS_SELECTED); // Reset the selection for (UINT i = 0 ; i < cidl; i++) { - lvIndex = LV_FindItemByPidl(apidl[i]); + int lvIndex = LV_FindItemByPidl(apidl[i]); if (lvIndex != -1) - { - SelectItem(lvIndex, dwFlags); - m_ListView.SetItemPosition(lvIndex, &apt[i]); - } + SelectAndPositionItem(lvIndex, dwFlags, apt ? &apt[i] : NULL); } - return S_OK; } @@ -3918,16 +3929,35 @@ HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShell return S_OK; } -HRESULT STDMETHODCALLTYPE CDefView::HandleRename(LPCITEMIDLIST new_pidl) +HRESULT STDMETHODCALLTYPE CDefView::HandleRename(LPCITEMIDLIST pidl) { - FIXME("(%p)->(%p) stub\n", this, new_pidl); - return E_NOTIMPL; + if (!pidl) + { + int idx = m_ListView.GetNextItem(-1, LVNI_SELECTED | LVNI_FOCUSED); + if (idx < 0) + idx = m_ListView.GetNextItem(-1, LVNI_SELECTED); + pidl = _PidlByItem(idx); + } + if (ILFindLastID(pidl) != pidl) + return E_INVALIDARG; + return SelectItem(pidl, SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT); +} + +HRESULT CDefView::SelectAndPositionItem(int Idx, UINT fSVSI, POINT *ppt) +{ + if (Idx == -1) + return fSVSI == SVSI_DESELECTOTHERS ? SelectItem(-2, fSVSI) : E_INVALIDARG; + if (ppt) + m_ListView.SetItemPosition(Idx, ppt); + return SelectItem(Idx, fSVSI); // After SetItemPosition for SVSI_ENSUREVISIBLE } HRESULT STDMETHODCALLTYPE CDefView::SelectAndPositionItem(LPCITEMIDLIST item, UINT flags, POINT *point) { - FIXME("(%p)->(%p, %u, %p) stub\n", this, item, flags, point); - return E_NOTIMPL; + if (!item) + return SelectAndPositionItem(-1, flags, point); + int idx = LV_FindItemByPidl(item); + return idx != -1 ? SelectAndPositionItem(idx, flags, point) : S_FALSE; } // IShellFolderView implementation diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index 554aeedbc01..2f361901745 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -180,6 +180,16 @@ static const struct _StaticInvokeCommandMap_ { "moveto", FCIDM_SHVIEW_MOVETO }, }; +PCSTR MapFcidmCmdToVerb(_In_ UINT_PTR CmdId) +{ + for (SIZE_T i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) + { + if (g_StaticInvokeCmdMap[i].IntVerb == CmdId && CmdId) + return g_StaticInvokeCmdMap[i].szStringVerb; + } + return NULL; +} + UINT MapVerbToDfmCmd(_In_ LPCSTR verba) { for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index 3d0bda0ec5e..28930d528d2 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -205,6 +205,8 @@ DCMA_InsertMenuItems( HRESULT SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg); +PCSTR +MapFcidmCmdToVerb(_In_ UINT_PTR CmdId); UINT MapVerbToDfmCmd(_In_ LPCSTR verba); UINT