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 static const CMVERBMAP g_VerbMap[] = 107 { 108 { "copyaspath", IDC_COPYASPATH }, 109 { NULL } 110 }; 111 112 STDMETHODIMP 113 CCopyAsPathMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 114 { 115 MENUITEMINFOW mii; 116 117 TRACE("CCopyAsPathMenu::QueryContextMenu(%p %p %u %u %u %u)\n", this, 118 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 119 120 if ((uFlags & CMF_NOVERBS) || !(uFlags & CMF_EXTENDEDVERBS)) 121 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); 122 123 // Insert "Copy as path" 124 CStringW strText(MAKEINTRESOURCEW(IDS_COPYASPATHMENU)); 125 ZeroMemory(&mii, sizeof(mii)); 126 mii.cbSize = sizeof(mii); 127 mii.fMask = MIIM_ID | MIIM_TYPE; 128 mii.fType = MFT_STRING; 129 mii.wID = idCmdFirst + IDC_COPYASPATH; 130 mii.dwTypeData = strText.GetBuffer(); 131 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 132 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, mii.wID - idCmdFirst + 1); 133 134 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); 135 } 136 137 STDMETHODIMP 138 CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 139 { 140 TRACE("CCopyAsPathMenu::InvokeCommand(%p %p)\n", this, lpcmi); 141 142 int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_VerbMap); 143 if (CmdId == IDC_COPYASPATH) 144 return DoCopyAsPath(m_pDataObject); 145 146 return E_FAIL; 147 } 148 149 STDMETHODIMP 150 CCopyAsPathMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) 151 { 152 TRACE("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this, 153 idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); 154 return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen, g_VerbMap); 155 } 156 157 STDMETHODIMP 158 CCopyAsPathMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 159 { 160 m_pDataObject = pdtobj; 161 return S_OK; 162 } 163