Commit af7f11bb authored by Zebediah Figura's avatar Zebediah Figura Committed by Alexandre Julliard

shell32: Implement Paste in the item menu.

Based on a patch by Michael Müller. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34319
parent 5744ef64
......@@ -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
......
......@@ -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;
......
......@@ -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);
......@@ -5884,7 +5888,7 @@ static void test_copy_paste(void)
invoke_info.lpVerb = "paste";
hr = IContextMenu_InvokeCommand(dst_menu, &invoke_info);
todo_wine ok(hr == S_OK || hr == S_FALSE /* win10 < 1809 */, "Got hr %#lx.\n", hr);
ok(hr == S_OK || hr == S_FALSE /* win10 < 1809 */, "Got hr %#lx.\n", hr);
ret = RemoveDirectoryW(L"testcopy_src");
ok(ret, "Got error %lu.\n", GetLastError());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment