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 #if 0 79 CComPtr<IShellItemArray> array; 80 HRESULT hr = SHCreateShellItemArrayFromDataObject(pdto, IID_PPV_ARG(IShellItemArray, &array)); 81 if (SUCCEEDED(hr)) 82 { 83 for (i = 0, array->GetCount(&count); i < count && SUCCEEDED(hr); ++i) 84 { 85 CComPtr<IShellItem> item; 86 hr = array->GetItemAt(i, &item); 87 if (SUCCEEDED(hr)) 88 { 89 CComHeapPtr<WCHAR> path; 90 hr = item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path); 91 if (SUCCEEDED(hr)) 92 { 93 AppendToPathList(paths, path, i); 94 } 95 } 96 } 97 } 98 #else 99 FIXME("Implement and use SHCreateShellItemArrayFromDataObject\n"); 100 CDataObjectHIDA pCIDA(pdto); 101 HRESULT hr = pCIDA.hr(); 102 if (SUCCEEDED(hr)) 103 { 104 for (i = 0, count = pCIDA->cidl; i < count && SUCCEEDED(hr); ++i) 105 { 106 PCUIDLIST_ABSOLUTE folder = HIDA_GetPIDLFolder(pCIDA); 107 PCUIDLIST_RELATIVE item = HIDA_GetPIDLItem(pCIDA, i); 108 CComHeapPtr<ITEMIDLIST> full; 109 hr = SHILCombine(folder, item, &full); 110 if (SUCCEEDED(hr)) 111 { 112 PCUITEMID_CHILD child; 113 CComPtr<IShellFolder> sf; 114 hr = SHBindToParent(full, IID_PPV_ARG(IShellFolder, &sf), &child); 115 if (SUCCEEDED(hr)) 116 { 117 STRRET strret; 118 hr = sf->GetDisplayNameOf(child, SHGDN_FORPARSING, &strret); 119 if (SUCCEEDED(hr)) 120 { 121 CComHeapPtr<WCHAR> path; 122 hr = StrRetToStrW(&strret, child, &path); 123 if (SUCCEEDED(hr)) 124 { 125 AppendToPathList(paths, path, i); 126 } 127 } 128 } 129 } 130 } 131 } 132 else 133 { 134 FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 135 STGMEDIUM stgm; 136 hr = pdto->GetData(&fmte, &stgm); 137 if (SUCCEEDED(hr)) 138 { 139 for (i = 0, count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0); i < count && SUCCEEDED(hr); ++i) 140 { 141 WCHAR path[MAX_PATH]; 142 if (DragQueryFileW((HDROP)stgm.hGlobal, i, path, _countof(path))) 143 { 144 AppendToPathList(paths, path, i); 145 } 146 } 147 ReleaseStgMedium(&stgm); 148 } 149 } 150 #endif 151 152 if (SUCCEEDED(hr)) 153 { 154 DWORD err = SetClipboardFromString(paths); 155 hr = HRESULT_FROM_WIN32(err); 156 } 157 158 return hr; 159 } 160 161 STDMETHODIMP 162 CCopyAsPathMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 163 { 164 MENUITEMINFOW mii; 165 166 TRACE("CCopyAsPathMenu::QueryContextMenu(%p %p %u %u %u %u)\n", this, 167 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 168 169 if ((uFlags & CMF_NOVERBS) || !(uFlags & CMF_EXTENDEDVERBS)) 170 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdFirst); 171 172 // Insert "Copy as path" 173 CStringW strText(MAKEINTRESOURCEW(IDS_COPYASPATHMENU)); 174 ZeroMemory(&mii, sizeof(mii)); 175 mii.cbSize = sizeof(mii); 176 mii.fMask = MIIM_ID | MIIM_TYPE; 177 mii.fType = MFT_STRING; 178 mii.wID = idCmdFirst + IDC_COPYASPATH; 179 mii.dwTypeData = strText.GetBuffer(); 180 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 181 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, mii.wID - idCmdFirst + 1); 182 183 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdFirst); 184 } 185 186 STDMETHODIMP 187 CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 188 { 189 TRACE("CCopyAsPathMenu::InvokeCommand(%p %p)\n", this, lpcmi); 190 191 if (IS_INTRESOURCE(lpcmi->lpVerb) && LOWORD(lpcmi->lpVerb) == IDC_COPYASPATH) 192 return DoCopyAsPath(m_pDataObject); 193 194 return E_FAIL; 195 } 196 197 STDMETHODIMP 198 CCopyAsPathMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) 199 { 200 FIXME("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this, 201 idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); 202 203 return E_NOTIMPL; 204 } 205 206 STDMETHODIMP 207 CCopyAsPathMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 208 { 209 m_pDataObject = pdtobj; 210 return S_OK; 211 } 212