diff --git a/dll/win32/shell32/CCopyAsPathMenu.cpp b/dll/win32/shell32/CCopyAsPathMenu.cpp new file mode 100644 index 00000000000..55bac2f4909 --- /dev/null +++ b/dll/win32/shell32/CCopyAsPathMenu.cpp @@ -0,0 +1,152 @@ +/* + * PROJECT: ReactOS shell32 + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Copy as Path Menu implementation + * COPYRIGHT: Copyright 2024 Whindmar Saksit + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +static DWORD +SetClipboard(UINT cf, const void* data, SIZE_T size) +{ + BOOL succ = FALSE; + HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size); + if (handle) + { + LPVOID clipdata = GlobalLock(handle); + if (clipdata) + { + CopyMemory(clipdata, data, size); + GlobalUnlock(handle); + if (OpenClipboard(NULL)) + { + EmptyClipboard(); + succ = SetClipboardData(cf, handle) != NULL; + CloseClipboard(); + } + } + if (!succ) + { + GlobalFree(handle); + } + } + return succ ? ERROR_SUCCESS : GetLastError(); +} + +static DWORD +SetClipboardFromString(LPCWSTR str) +{ + SIZE_T cch = lstrlenW(str) + 1, size = cch * sizeof(WCHAR); + if (size > cch) + return SetClipboard(CF_UNICODETEXT, str, size); + else + return ERROR_BUFFER_OVERFLOW; +} + +static void +AppendToPathList(CStringW &paths, LPCWSTR path, DWORD index) +{ + if (index) + paths += L"\r\n"; + LPCWSTR quote = StrChrW(path, L' '); + if (quote) + paths += L'\"'; + paths += path; + if (quote) + paths += L'\"'; +} + +STDMETHODIMP +CCopyAsPathMenu::Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) +{ + CStringW paths; + DWORD i, count; +#if 0 + CComPtr array; + HRESULT hr = SHCreateShellItemArrayFromDataObject(pdto, IID_PPV_ARG(IShellItemArray, &array)); + if (SUCCEEDED(hr)) + { + for (i = 0, array->GetCount(&count); i < count && SUCCEEDED(hr); ++i) + { + CComPtr item; + hr = array->GetItemAt(i, &item); + if (SUCCEEDED(hr)) + { + CComHeapPtr path; + hr = item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path); + if (SUCCEEDED(hr)) + { + AppendToPathList(paths, path, i); + } + } + } + } +#else + FIXME("Implement and use SHCreateShellItemArrayFromDataObject\n"); + CDataObjectHIDA pCIDA(pdto); + HRESULT hr = pCIDA.hr(); + if (SUCCEEDED(hr)) + { + for (i = 0, count = pCIDA->cidl; i < count && SUCCEEDED(hr); ++i) + { + PCUIDLIST_ABSOLUTE folder = HIDA_GetPIDLFolder(pCIDA); + PCUIDLIST_RELATIVE item = HIDA_GetPIDLItem(pCIDA, i); + CComHeapPtr full; + hr = SHILCombine(folder, item, &full); + if (SUCCEEDED(hr)) + { + PCUITEMID_CHILD child; + CComPtr sf; + hr = SHBindToParent(full, IID_PPV_ARG(IShellFolder, &sf), &child); + if (SUCCEEDED(hr)) + { + STRRET strret; + hr = sf->GetDisplayNameOf(child, SHGDN_FORPARSING, &strret); + if (SUCCEEDED(hr)) + { + CComHeapPtr path; + hr = StrRetToStrW(&strret, child, &path); + if (SUCCEEDED(hr)) + { + AppendToPathList(paths, path, i); + } + } + } + } + } + } + else + { + FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM stgm; + hr = pdto->GetData(&fmte, &stgm); + if (SUCCEEDED(hr)) + { + for (i = 0, count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0); i < count && SUCCEEDED(hr); ++i) + { + WCHAR path[MAX_PATH]; + if (DragQueryFileW((HDROP)stgm.hGlobal, i, path, _countof(path))) + { + AppendToPathList(paths, path, i); + } + } + ReleaseStgMedium(&stgm); + } + } +#endif + + if (SUCCEEDED(hr)) + { + DWORD err = SetClipboardFromString(paths); + hr = HRESULT_FROM_WIN32(err); + } + + if (SUCCEEDED(hr)) + *pdwEffect &= DROPEFFECT_COPY; + else + *pdwEffect &= DROPEFFECT_NONE; + return hr; +} diff --git a/dll/win32/shell32/CCopyAsPathMenu.h b/dll/win32/shell32/CCopyAsPathMenu.h new file mode 100644 index 00000000000..f11efd20a90 --- /dev/null +++ b/dll/win32/shell32/CCopyAsPathMenu.h @@ -0,0 +1,41 @@ +/* + * PROJECT: ReactOS shell32 + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Copy as Path Menu implementation + * COPYRIGHT: Copyright 2024 Whindmar Saksit + */ + +#pragma once + +class CCopyAsPathMenu : + public CComCoClass, + public CComObjectRootEx, + public IDropTarget +{ +public: + DECLARE_REGISTRY_RESOURCEID(IDR_COPYASPATHMENU) + DECLARE_NOT_AGGREGATABLE(CCopyAsPathMenu) + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + BEGIN_COM_MAP(CCopyAsPathMenu) + COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) + END_COM_MAP() + + // IDropTarget + STDMETHODIMP DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) + { + *pdwEffect &= DROPEFFECT_COPY; + return S_OK; + } + STDMETHODIMP DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) + { + *pdwEffect &= DROPEFFECT_COPY; + return S_OK; + } + STDMETHODIMP DragLeave() + { + return S_OK; + } + STDMETHODIMP Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect); +}; diff --git a/dll/win32/shell32/CCopyMoveToMenu.cpp b/dll/win32/shell32/CCopyMoveToMenu.cpp index f07328cee7d..859b05eb5b7 100644 --- a/dll/win32/shell32/CCopyMoveToMenu.cpp +++ b/dll/win32/shell32/CCopyMoveToMenu.cpp @@ -436,145 +436,3 @@ CCopyMoveToMenu::GetSite(REFIID riid, void **ppvSite) return m_pSite->QueryInterface(riid, ppvSite); } - -static DWORD -SetClipboard(UINT cf, const void* data, SIZE_T size) -{ - BOOL succ = FALSE; - HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size); - if (handle) - { - LPVOID clipdata = GlobalLock(handle); - if (clipdata) - { - CopyMemory(clipdata, data, size); - GlobalUnlock(handle); - if (OpenClipboard(NULL)) - { - EmptyClipboard(); - succ = SetClipboardData(cf, handle) != NULL; - CloseClipboard(); - } - } - if (!succ) - { - GlobalFree(handle); - } - } - return succ ? ERROR_SUCCESS : GetLastError(); -} - -static DWORD -SetClipboardFromString(LPCWSTR str) -{ - SIZE_T cch = lstrlenW(str) + 1, size = cch * sizeof(WCHAR); - if (size > cch) - return SetClipboard(CF_UNICODETEXT, str, size); - else - return ERROR_BUFFER_OVERFLOW; -} - -static void -AppendToPathList(CStringW &paths, LPCWSTR path, DWORD index) -{ - if (index) - paths += L"\r\n"; - LPCWSTR quote = StrChrW(path, L' '); - if (quote) - paths += L'\"'; - paths += path; - if (quote) - paths += L'\"'; -} - -STDMETHODIMP -CCopyAsPathMenu::Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) -{ - CStringW paths; - DWORD i, count; -#if 0 - CComPtr array; - HRESULT hr = SHCreateShellItemArrayFromDataObject(pdto, IID_PPV_ARG(IShellItemArray, &array)); - if (SUCCEEDED(hr)) - { - for (i = 0, array->GetCount(&count); i < count && SUCCEEDED(hr); ++i) - { - CComPtr item; - hr = array->GetItemAt(i, &item); - if (SUCCEEDED(hr)) - { - CComHeapPtr path; - hr = item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path); - if (SUCCEEDED(hr)) - { - AppendToPathList(paths, path, i); - } - } - } - } -#else - FIXME("Implement and use SHCreateShellItemArrayFromDataObject\n"); - CDataObjectHIDA pCIDA(pdto); - HRESULT hr = pCIDA.hr(); - if (SUCCEEDED(hr)) - { - for (i = 0, count = pCIDA->cidl; i < count && SUCCEEDED(hr); ++i) - { - PCUIDLIST_ABSOLUTE folder = HIDA_GetPIDLFolder(pCIDA); - PCUIDLIST_RELATIVE item = HIDA_GetPIDLItem(pCIDA, i); - CComHeapPtr full; - hr = SHILCombine(folder, item, &full); - if (SUCCEEDED(hr)) - { - PCUITEMID_CHILD child; - CComPtr sf; - hr = SHBindToParent(full, IID_PPV_ARG(IShellFolder, &sf), &child); - if (SUCCEEDED(hr)) - { - STRRET strret; - hr = sf->GetDisplayNameOf(child, SHGDN_FORPARSING, &strret); - if (SUCCEEDED(hr)) - { - CComHeapPtr path; - hr = StrRetToStrW(&strret, child, &path); - if (SUCCEEDED(hr)) - { - AppendToPathList(paths, path, i); - } - } - } - } - } - } - else - { - FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; - STGMEDIUM stgm; - hr = pdto->GetData(&fmte, &stgm); - if (SUCCEEDED(hr)) - { - for (i = 0, count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0); i < count && SUCCEEDED(hr); ++i) - { - WCHAR path[MAX_PATH]; - if (DragQueryFileW((HDROP)stgm.hGlobal, i, path, _countof(path))) - { - AppendToPathList(paths, path, i); - } - } - ReleaseStgMedium(&stgm); - } - } -#endif - - if (SUCCEEDED(hr)) - { - DWORD err = SetClipboardFromString(paths); - hr = HRESULT_FROM_WIN32(err); - } - - if (SUCCEEDED(hr)) - *pdwEffect &= DROPEFFECT_COPY; - else - *pdwEffect &= DROPEFFECT_NONE; - return hr; -} diff --git a/dll/win32/shell32/CCopyMoveToMenu.h b/dll/win32/shell32/CCopyMoveToMenu.h index 224352fc6cb..3b9de7ad91a 100644 --- a/dll/win32/shell32/CCopyMoveToMenu.h +++ b/dll/win32/shell32/CCopyMoveToMenu.h @@ -109,35 +109,3 @@ public: UINT GetFileOp() const override { return FO_MOVE; } LPCSTR GetVerb() const override { return "moveto"; } }; - -class CCopyAsPathMenu - : public CComCoClass - , public CComObjectRootEx - , public IDropTarget -{ -public: - DECLARE_REGISTRY_RESOURCEID(IDR_COPYASPATHMENU) - DECLARE_NOT_AGGREGATABLE(CCopyAsPathMenu) - DECLARE_PROTECT_FINAL_CONSTRUCT() - - BEGIN_COM_MAP(CCopyAsPathMenu) - COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) - END_COM_MAP() - - // IDropTarget - STDMETHODIMP DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) - { - *pdwEffect &= DROPEFFECT_COPY; - return S_OK; - } - STDMETHODIMP DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) - { - *pdwEffect &= DROPEFFECT_COPY; - return S_OK; - } - STDMETHODIMP DragLeave() - { - return S_OK; - } - STDMETHODIMP Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect); -}; diff --git a/dll/win32/shell32/CMakeLists.txt b/dll/win32/shell32/CMakeLists.txt index 264b116f5a2..f90babb9762 100644 --- a/dll/win32/shell32/CMakeLists.txt +++ b/dll/win32/shell32/CMakeLists.txt @@ -74,6 +74,7 @@ list(APPEND SOURCE CNewMenu.cpp CSendToMenu.cpp CCopyMoveToMenu.cpp + CCopyAsPathMenu.cpp CShellDispatch.cpp CFolder.cpp CFolderItems.cpp diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index 19c4255fc0f..af2af91776f 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -96,6 +96,7 @@ #include "CNewMenu.h" #include "CSendToMenu.h" #include "CCopyMoveToMenu.h" +#include "CCopyAsPathMenu.h" #include "dialogs/filedefext.h" #include "dialogs/drvdefext.h" #include "CQueryAssociations.h"