From: Zebediah Figura zfigura@codeweavers.com
Based on a patch by Michael Müller.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34319 --- dlls/shell32/shell32.rc | 1 + dlls/shell32/shlview_cmenu.c | 67 ++++++++++++++++++++++++++++++++-- dlls/shell32/tests/shlfolder.c | 16 +++++--- 3 files changed, 74 insertions(+), 10 deletions(-)
diff --git a/dlls/shell32/shell32.rc b/dlls/shell32/shell32.rc index f47698524bb..970bd9a59c6 100644 --- a/dlls/shell32/shell32.rc +++ b/dlls/shell32/shell32.rc @@ -95,6 +95,7 @@ BEGIN BEGIN MENUITEM "C&ut", FCIDM_SHVIEW_CUT MENUITEM "&Copy", FCIDM_SHVIEW_COPY + MENUITEM "&Paste", FCIDM_SHVIEW_INSERT MENUITEM SEPARATOR MENUITEM "Create &Link", FCIDM_SHVIEW_CREATELINK MENUITEM "&Delete", FCIDM_SHVIEW_DELETE diff --git a/dlls/shell32/shlview_cmenu.c b/dlls/shell32/shlview_cmenu.c index 729f543ef51..cb7ce331897 100644 --- a/dlls/shell32/shlview_cmenu.c +++ b/dlls/shell32/shlview_cmenu.c @@ -157,6 +157,35 @@ static ULONG WINAPI ContextMenu_Release(IContextMenu3 *iface) return ref; }
+static BOOL can_paste(const ITEMIDLIST *dst_pidl) +{ + IDataObject *data; + FORMATETC format; + + if (!(_ILIsFolder(dst_pidl) || _ILIsDrive(dst_pidl))) + return FALSE; + + if (FAILED(OleGetClipboard(&data))) + return FALSE; + + InitFormatEtc(format, RegisterClipboardFormatW(CFSTR_SHELLIDLISTW), TYMED_HGLOBAL); + if (SUCCEEDED(IDataObject_QueryGetData(data, &format))) + { + IDataObject_Release(data); + return TRUE; + } + + InitFormatEtc(format, CF_HDROP, TYMED_HGLOBAL); + if (SUCCEEDED(IDataObject_QueryGetData(data, &format))) + { + IDataObject_Release(data); + return TRUE; + } + + IDataObject_Release(data); + return FALSE; +} + static UINT max_menu_id(HMENU hmenu, UINT offset, UINT last) { int i; @@ -252,6 +281,11 @@ static HRESULT WINAPI ItemMenu_QueryContextMenu( EnableMenuItem(hmenu, FCIDM_SHVIEW_RENAME - FCIDM_BASE + idCmdFirst, enable); }
+ /* It's legal to paste into more than one pidl at once. In that case + * the first is used and the rest are ignored. */ + if (!can_paste(This->apidl[0])) + RemoveMenu(hmenu, FCIDM_SHVIEW_INSERT - FCIDM_BASE + idCmdFirst, MF_BYCOMMAND); + return MAKE_HRESULT(SEVERITY_SUCCESS, 0, uIDMax-idCmdFirst); } return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); @@ -311,7 +345,8 @@ static void DoCopyOrCut(ContextMenu *This, HWND hwnd, BOOL cut) } }
-static HRESULT paste_pidls(ContextMenu *menu, const ITEMIDLIST *src_parent, ITEMIDLIST **pidls, unsigned int count) +static HRESULT paste_pidls(IShellFolder *dst_folder, + const ITEMIDLIST *src_parent, ITEMIDLIST **pidls, unsigned int count) { IShellFolder *src_folder; HRESULT hr = S_OK; @@ -326,7 +361,7 @@ static HRESULT paste_pidls(ContextMenu *menu, const ITEMIDLIST *src_parent, ITEM { ISFHelper *psfhlpdst = NULL, *psfhlpsrc = NULL;
- hr = IShellFolder_QueryInterface(menu->parent, &IID_ISFHelper, (void **)&psfhlpdst); + hr = IShellFolder_QueryInterface(dst_folder, &IID_ISFHelper, (void **)&psfhlpdst); if (SUCCEEDED(hr)) hr = IShellFolder_QueryInterface(src_folder, &IID_ISFHelper, (void **)&psfhlpsrc);
@@ -357,6 +392,7 @@ static HRESULT get_data_format(IDataObject *data, UINT cf, STGMEDIUM *medium)
static HRESULT do_paste(ContextMenu *menu, HWND hwnd) { + IShellFolder *dst_folder; IDataObject *data; HRESULT hr; STGMEDIUM medium; @@ -364,6 +400,21 @@ static HRESULT do_paste(ContextMenu *menu, HWND hwnd) if (FAILED(hr = OleGetClipboard(&data))) return hr;
+ if (menu->cidl) + { + if (FAILED(hr = IShellFolder_BindToObject(menu->parent, menu->apidl[0], + NULL, &IID_IShellFolder, (void **)&dst_folder))) + { + WARN("Failed to get destination folder, hr %#lx.\n", hr); + return hr; + } + } + else + { + dst_folder = menu->parent; + IShellFolder_AddRef(dst_folder); + } + if (SUCCEEDED(get_data_format(data, RegisterClipboardFormatW(CFSTR_SHELLIDLISTW), &medium))) { CIDA *cida = GlobalLock(medium.hGlobal); @@ -375,7 +426,7 @@ static HRESULT do_paste(ContextMenu *menu, HWND hwnd) pidls = _ILCopyCidaToaPidl(&pidl, cida); if (pidls) { - hr = paste_pidls(menu, pidl, pidls, cida->cidl); + hr = paste_pidls(dst_folder, pidl, pidls, cida->cidl); _ILFreeaPidl(pidls, cida->cidl); SHFree(pidl); } @@ -400,7 +451,7 @@ static HRESULT do_paste(ContextMenu *menu, HWND hwnd) ITEMIDLIST *dst_pidl; int ret;
- if (FAILED(hr = IShellFolder_QueryInterface(menu->parent, &IID_IPersistFolder2, (void **)&dst_persist))) + if (FAILED(hr = IShellFolder_QueryInterface(dst_folder, &IID_IPersistFolder2, (void **)&dst_persist))) { WARN("Failed to get IPersistFolder2, hr %#lx.\n", hr); IDataObject_Release(data); @@ -928,6 +979,9 @@ static HRESULT WINAPI ItemMenu_InvokeCommand( TRACE("Verb FCIDM_SHVIEW_CUT\n"); DoCopyOrCut(This, lpcmi->hwnd, TRUE); break; + case FCIDM_SHVIEW_INSERT: + do_paste(This, lpcmi->hwnd); + break; case FCIDM_SHVIEW_PROPERTIES: TRACE("Verb FCIDM_SHVIEW_PROPERTIES\n"); DoOpenProperties(This, lpcmi->hwnd); @@ -946,6 +1000,8 @@ static HRESULT WINAPI ItemMenu_InvokeCommand( DoCopyOrCut(This, lpcmi->hwnd, FALSE); else if (strcmp(lpcmi->lpVerb,"cut")==0) DoCopyOrCut(This, lpcmi->hwnd, TRUE); + else if (!strcmp(lpcmi->lpVerb, "paste")) + do_paste(This, lpcmi->hwnd); else if (strcmp(lpcmi->lpVerb,"properties")==0) DoOpenProperties(This, lpcmi->hwnd); else { @@ -988,6 +1044,9 @@ static HRESULT WINAPI ItemMenu_GetCommandString(IContextMenu3 *iface, UINT_PTR c case FCIDM_SHVIEW_DELETE: cmdW = L"delete"; break; + case FCIDM_SHVIEW_INSERT: + cmdW = L"paste"; + break; case FCIDM_SHVIEW_PROPERTIES: cmdW = L"properties"; break; diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c index 39953dd9dda..25e75c90c83 100644 --- a/dlls/shell32/tests/shlfolder.c +++ b/dlls/shell32/tests/shlfolder.c @@ -5706,10 +5706,12 @@ static void test_copy_paste(void)
invoke_info.lpVerb = "paste"; hr = IContextMenu_InvokeCommand(dst_menu, &invoke_info); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
ret = MoveFileExW(L"testcopy_dst/testcopy_src", L"testcopy_src", 0); todo_wine ok(ret, "Got error %lu.\n", GetLastError()); + if (!ret && GetLastError() == ERROR_ALREADY_EXISTS) + RemoveDirectoryW(L"testcopy_dst/testcopy_src");
/* Copy. */
@@ -5747,13 +5749,13 @@ static void test_copy_paste(void)
invoke_info.lpVerb = "paste"; hr = IContextMenu_InvokeCommand(dst_menu, &invoke_info); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
ret = GetFileAttributesW(L"testcopy_src"); ok(ret != INVALID_FILE_ATTRIBUTES, "Got %#x.\n", ret);
ret = RemoveDirectoryW(L"testcopy_dst/testcopy_src"); - todo_wine ok(ret, "Got error %lu.\n", GetLastError()); + ok(ret, "Got error %lu.\n", GetLastError());
/* Manually change the drop effect back to "cut". */
@@ -5773,10 +5775,12 @@ static void test_copy_paste(void)
invoke_info.lpVerb = "paste"; hr = IContextMenu_InvokeCommand(dst_menu, &invoke_info); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
ret = MoveFileExW(L"testcopy_dst/testcopy_src", L"testcopy_src", 0); todo_wine ok(ret, "Got error %lu.\n", GetLastError()); + if (!ret && GetLastError() == ERROR_ALREADY_EXISTS) + RemoveDirectoryW(L"testcopy_dst/testcopy_src");
/* Paste into a background menu. */
@@ -5818,10 +5822,10 @@ static void test_copy_paste(void)
invoke_info.lpVerb = "paste"; hr = IContextMenu_InvokeCommand(dst_menu, &invoke_info); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr);
ret = RemoveDirectoryW(L"testcopy_dst2/testcopy_src"); - todo_wine ok(ret, "Got error %lu.\n", GetLastError()); + ok(ret, "Got error %lu.\n", GetLastError()); ret = GetFileAttributesW(L"testcopy_dst/testcopy_src"); ok(ret == INVALID_FILE_ATTRIBUTES, "Got %#x.\n", ret);