Reactos
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
11WINE_DEFAULT_DEBUG_CHANNEL(shell);
12
13#define IDC_COPYASPATH 0
14
15CCopyAsPathMenu::CCopyAsPathMenu()
16{
17}
18
19CCopyAsPathMenu::~CCopyAsPathMenu()
20{
21}
22
23static DWORD
24SetClipboard(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
50static DWORD
51SetClipboardFromString(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
60static void
61AppendToPathList(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
73HRESULT
74CCopyAsPathMenu::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
106static const CMVERBMAP g_VerbMap[] =
107{
108 { "copyaspath", IDC_COPYASPATH },
109 { NULL }
110};
111
112STDMETHODIMP
113CCopyAsPathMenu::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
137STDMETHODIMP
138CCopyAsPathMenu::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
149STDMETHODIMP
150CCopyAsPathMenu::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
157STDMETHODIMP
158CCopyAsPathMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
159{
160 m_pDataObject = pdtobj;
161 return S_OK;
162}