1 /* 2 * PROJECT: ReactOS shell32 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Copy as Path Menu implementation 5 * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks@proton.me> 6 * Copyright 2024 Thamatip Chitpong <thamatip.chitpong@reactos.org> 7 */ 8 9 #include "precomp.h" 10 11 WINE_DEFAULT_DEBUG_CHANNEL(shell); 12 13 #define IDC_COPYASPATH 0 14 15 CCopyAsPathMenu::CCopyAsPathMenu() 16 { 17 } 18 19 CCopyAsPathMenu::~CCopyAsPathMenu() 20 { 21 } 22 23 static DWORD 24 SetClipboard(UINT cf, const void* data, SIZE_T size) 25 { 26 BOOL succ = FALSE; 27 HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size); 28 if (handle) 29 { 30 LPVOID clipdata = GlobalLock(handle); 31 if (clipdata) 32 { 33 CopyMemory(clipdata, data, size); 34 GlobalUnlock(handle); 35 if (OpenClipboard(NULL)) 36 { 37 EmptyClipboard(); 38 succ = SetClipboardData(cf, handle) != NULL; 39 CloseClipboard(); 40 } 41 } 42 if (!succ) 43 { 44 GlobalFree(handle); 45 } 46 } 47 return succ ? ERROR_SUCCESS : GetLastError(); 48 } 49 50 static DWORD 51 SetClipboardFromString(LPCWSTR str) 52 { 53 SIZE_T cch = lstrlenW(str) + 1, size = cch * sizeof(WCHAR); 54 if (size > cch) 55 return SetClipboard(CF_UNICODETEXT, str, size); 56 else 57 return ERROR_BUFFER_OVERFLOW; 58 } 59 60 static void 61 AppendToPathList(CStringW &paths, LPCWSTR path, DWORD index) 62 { 63 if (index) 64 paths += L"\r\n"; 65 LPCWSTR quote = StrChrW(path, L' '); 66 if (quote) 67 paths += L'\"'; 68 paths += path; 69 if (quote) 70 paths += L'\"'; 71 } 72 73 HRESULT 74 CCopyAsPathMenu::DoCopyAsPath(IDataObject *pdto) 75 { 76 CStringW paths; 77 DWORD i, count; 78 CComPtr<IShellItemArray> array; 79 HRESULT hr = SHCreateShellItemArrayFromDataObject(pdto, IID_PPV_ARG(IShellItemArray, &array)); 80 if (SUCCEEDED(hr)) 81 { 82 for (i = 0, array->GetCount(&count); i < count && SUCCEEDED(hr); ++i) 83 { 84 CComPtr<IShellItem> item; 85 hr = array->GetItemAt(i, &item); 86 if (SUCCEEDED(hr)) 87 { 88 CComHeapPtr<WCHAR> path; 89 hr = item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path); 90 if (SUCCEEDED(hr)) 91 { 92 AppendToPathList(paths, path, i); 93 } 94 } 95 } 96 } 97 if (SUCCEEDED(hr)) 98 { 99 DWORD err = SetClipboardFromString(paths); 100 hr = HRESULT_FROM_WIN32(err); 101 } 102 103 return hr; 104 } 105 106 STDMETHODIMP 107 CCopyAsPathMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 108 { 109 MENUITEMINFOW mii; 110 111 TRACE("CCopyAsPathMenu::QueryContextMenu(%p %p %u %u %u %u)\n", this, 112 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 113 114 if ((uFlags & CMF_NOVERBS) || !(uFlags & CMF_EXTENDEDVERBS)) 115 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); 116 117 // Insert "Copy as path" 118 CStringW strText(MAKEINTRESOURCEW(IDS_COPYASPATHMENU)); 119 ZeroMemory(&mii, sizeof(mii)); 120 mii.cbSize = sizeof(mii); 121 mii.fMask = MIIM_ID | MIIM_TYPE; 122 mii.fType = MFT_STRING; 123 mii.wID = idCmdFirst + IDC_COPYASPATH; 124 mii.dwTypeData = strText.GetBuffer(); 125 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 126 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, mii.wID - idCmdFirst + 1); 127 128 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); 129 } 130 131 STDMETHODIMP 132 CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 133 { 134 TRACE("CCopyAsPathMenu::InvokeCommand(%p %p)\n", this, lpcmi); 135 136 if (IS_INTRESOURCE(lpcmi->lpVerb) && LOWORD(lpcmi->lpVerb) == IDC_COPYASPATH) 137 return DoCopyAsPath(m_pDataObject); 138 139 return E_FAIL; 140 } 141 142 STDMETHODIMP 143 CCopyAsPathMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) 144 { 145 FIXME("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this, 146 idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); 147 148 return E_NOTIMPL; 149 } 150 151 STDMETHODIMP 152 CCopyAsPathMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 153 { 154 m_pDataObject = pdtobj; 155 return S_OK; 156 } 157