4e986bebeb1adfda3ef0f88303441217807499a0
[reactos.git] / dll / win32 / browseui / shellfind / CFindFolder.cpp
1 /*
2 * PROJECT: ReactOS Search Shell Extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Search results folder
5 * COPYRIGHT: Copyright 2019 Brock Mammen
6 */
7
8 #include "CFindFolder.h"
9
10 WINE_DEFAULT_DEBUG_CHANNEL(shellfind);
11
12 // FIXME: Remove this declaration after the function has been fully implemented
13 EXTERN_C HRESULT
14 WINAPI
15 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder,
16 UINT cidl,
17 PCUITEMID_CHILD_ARRAY apidl,
18 DWORD dwFlags);
19
20 struct FolderViewColumns
21 {
22 LPCWSTR wzColumnName;
23 DWORD dwDefaultState;
24 int fmt;
25 int cxChar;
26 };
27
28 static FolderViewColumns g_ColumnDefs[] =
29 {
30 {L"Name", SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
31 {L"In Folder", SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
32 {L"Relevance", SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 0}
33 };
34
35 CFindFolder::CFindFolder() :
36 m_hStopEvent(NULL)
37 {
38 }
39
40 static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath, LPCITEMIDLIST lpcFindDataPidl)
41 {
42 int pathLen = (wcslen(lpszPath) + 1) * sizeof(WCHAR);
43 int cbData = sizeof(WORD) + pathLen + lpcFindDataPidl->mkid.cb;
44 LPITEMIDLIST pidl = (LPITEMIDLIST) SHAlloc(cbData + sizeof(WORD));
45 if (!pidl)
46 return NULL;
47
48 LPBYTE p = (LPBYTE) pidl;
49 *((WORD *) p) = cbData;
50 p += sizeof(WORD);
51
52 memcpy(p, lpszPath, pathLen);
53 p += pathLen;
54
55 memcpy(p, lpcFindDataPidl, lpcFindDataPidl->mkid.cb);
56 p += lpcFindDataPidl->mkid.cb;
57
58 *((WORD *) p) = 0;
59
60 return pidl;
61 }
62
63 static LPCWSTR _ILGetPath(LPCITEMIDLIST pidl)
64 {
65 if (!pidl || !pidl->mkid.cb)
66 return NULL;
67 return (LPCWSTR) pidl->mkid.abID;
68 }
69
70 static LPCITEMIDLIST _ILGetFSPidl(LPCITEMIDLIST pidl)
71 {
72 if (!pidl || !pidl->mkid.cb)
73 return pidl;
74 return (LPCITEMIDLIST) ((LPBYTE) pidl->mkid.abID
75 + ((wcslen((LPCWSTR) pidl->mkid.abID) + 1) * sizeof(WCHAR)));
76 }
77
78 LRESULT CFindFolder::AddItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
79 {
80 if (!lParam)
81 return 0;
82
83 HRESULT hr;
84 LPWSTR path = (LPWSTR) lParam;
85
86 CComPtr<IShellFolder> pShellFolder;
87 hr = SHGetDesktopFolder(&pShellFolder);
88 if (FAILED_UNEXPECTEDLY(hr))
89 {
90 LocalFree(path);
91 return hr;
92 }
93
94 CComHeapPtr<ITEMIDLIST> lpFSPidl;
95 DWORD pchEaten;
96 hr = pShellFolder->ParseDisplayName(NULL, NULL, path, &pchEaten, &lpFSPidl, NULL);
97 if (FAILED_UNEXPECTEDLY(hr))
98 {
99 LocalFree(path);
100 return hr;
101 }
102
103 LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl);
104 CComHeapPtr<ITEMIDLIST> lpSearchPidl(_ILCreate(path, lpLastFSPidl));
105 LocalFree(path);
106 if (!lpSearchPidl)
107 {
108 return E_OUTOFMEMORY;
109 }
110
111 UINT uItemIndex;
112 hr = m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex);
113
114 return hr;
115 }
116
117 LRESULT CFindFolder::UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
118 {
119 LPWSTR status = (LPWSTR) lParam;
120 if (m_shellBrowser)
121 {
122 m_shellBrowser->SetStatusTextSB(status);
123 }
124 LocalFree(status);
125
126 return S_OK;
127 }
128
129 // *** IShellFolder2 methods ***
130 STDMETHODIMP CFindFolder::GetDefaultSearchGUID(GUID *pguid)
131 {
132 UNIMPLEMENTED;
133 return E_NOTIMPL;
134 }
135
136 STDMETHODIMP CFindFolder::EnumSearches(IEnumExtraSearch **ppenum)
137 {
138 UNIMPLEMENTED;
139 return E_NOTIMPL;
140 }
141
142 STDMETHODIMP CFindFolder::GetDefaultColumn(DWORD, ULONG *pSort, ULONG *pDisplay)
143 {
144 if (pSort)
145 *pSort = 0;
146 if (pDisplay)
147 *pDisplay = 0;
148 return S_OK;
149 }
150
151 STDMETHODIMP CFindFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
152 {
153 if (!pcsFlags)
154 return E_INVALIDARG;
155 if (iColumn >= _countof(g_ColumnDefs))
156 return m_pisfInner->GetDefaultColumnState(iColumn - _countof(g_ColumnDefs) + 1, pcsFlags);
157 *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
158 return S_OK;
159 }
160
161 STDMETHODIMP CFindFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
162 {
163 return m_pisfInner->GetDetailsEx(pidl, pscid, pv);
164 }
165
166 STDMETHODIMP CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *pDetails)
167 {
168 if (iColumn >= _countof(g_ColumnDefs))
169 return m_pisfInner->GetDetailsOf(_ILGetFSPidl(pidl), iColumn - _countof(g_ColumnDefs) + 1, pDetails);
170
171 pDetails->cxChar = g_ColumnDefs[iColumn].cxChar;
172 pDetails->fmt = g_ColumnDefs[iColumn].fmt;
173
174 if (!pidl)
175 return SHSetStrRet(&pDetails->str, g_ColumnDefs[iColumn].wzColumnName);
176
177 if (iColumn == 1)
178 {
179 WCHAR path[MAX_PATH];
180 wcscpy(path, _ILGetPath(pidl));
181 PathRemoveFileSpecW(path);
182 return SHSetStrRet(&pDetails->str, path);
183 }
184
185 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
186 }
187
188 STDMETHODIMP CFindFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
189 {
190 UNIMPLEMENTED;
191 return E_NOTIMPL;
192 }
193
194 // *** IShellFolder methods ***
195 STDMETHODIMP CFindFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, ULONG *pchEaten,
196 PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
197 {
198 UNIMPLEMENTED;
199 return E_NOTIMPL;
200 }
201
202 STDMETHODIMP CFindFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
203 {
204 *ppEnumIDList = NULL;
205 return S_FALSE;
206 }
207
208 STDMETHODIMP CFindFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
209 {
210 UNIMPLEMENTED;
211 return E_NOTIMPL;
212 }
213
214 STDMETHODIMP CFindFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
215 {
216 UNIMPLEMENTED;
217 return E_NOTIMPL;
218 }
219
220 STDMETHODIMP CFindFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
221 {
222 return m_pisfInner->CompareIDs(lParam, _ILGetFSPidl(pidl1), _ILGetFSPidl(pidl2));
223 }
224
225 STDMETHODIMP CFindFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
226 {
227 if (riid == IID_IShellView)
228 {
229 SFV_CREATE sfvparams = {};
230 sfvparams.cbSize = sizeof(SFV_CREATE);
231 sfvparams.pshf = this;
232 sfvparams.psfvcb = this;
233 HRESULT hr = SHCreateShellFolderView(&sfvparams, (IShellView **) ppvOut);
234 if (FAILED_UNEXPECTEDLY(hr))
235 {
236 return hr;
237 }
238
239 return ((IShellView * ) * ppvOut)->QueryInterface(IID_PPV_ARG(IShellFolderView, &m_shellFolderView));
240 }
241 return E_NOINTERFACE;
242 }
243
244 STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
245 {
246 CComHeapPtr<PCITEMID_CHILD> aFSPidl;
247 aFSPidl.Allocate(cidl);
248 for (UINT i = 0; i < cidl; i++)
249 {
250 aFSPidl[i] = _ILGetFSPidl(apidl[i]);
251 }
252
253 return m_pisfInner->GetAttributesOf(cidl, aFSPidl, rgfInOut);
254 }
255
256 STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid,
257 UINT *prgfInOut, LPVOID *ppvOut)
258 {
259 if (riid == IID_IDataObject && cidl == 1)
260 {
261 WCHAR path[MAX_PATH];
262 wcscpy(path, (LPCWSTR) apidl[0]->mkid.abID);
263 PathRemoveFileSpecW(path);
264 CComHeapPtr<ITEMIDLIST> rootPidl(ILCreateFromPathW(path));
265 if (!rootPidl)
266 return E_OUTOFMEMORY;
267 PCITEMID_CHILD aFSPidl[1];
268 aFSPidl[0] = _ILGetFSPidl(apidl[0]);
269 return IDataObject_Constructor(hwndOwner, rootPidl, aFSPidl, cidl, (IDataObject **) ppvOut);
270 }
271
272 if (cidl <= 0)
273 {
274 return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
275 }
276
277 PCITEMID_CHILD *aFSPidl = new PCITEMID_CHILD[cidl];
278 for (UINT i = 0; i < cidl; i++)
279 {
280 aFSPidl[i] = _ILGetFSPidl(apidl[i]);
281 }
282
283 if (riid == IID_IContextMenu)
284 {
285 HKEY hKeys[16];
286 UINT cKeys = 0;
287 AddFSClassKeysToArray(aFSPidl[0], hKeys, &cKeys);
288
289 DEFCONTEXTMENU dcm;
290 dcm.hwnd = hwndOwner;
291 dcm.pcmcb = this;
292 dcm.pidlFolder = m_pidl;
293 dcm.psf = this;
294 dcm.cidl = cidl;
295 dcm.apidl = apidl;
296 dcm.cKeys = cKeys;
297 dcm.aKeys = hKeys;
298 dcm.punkAssociationInfo = NULL;
299 HRESULT hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
300 delete[] aFSPidl;
301
302 return hr;
303 }
304
305 HRESULT hr = m_pisfInner->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, ppvOut);
306 delete[] aFSPidl;
307
308 return hr;
309 }
310
311 STDMETHODIMP CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET pName)
312 {
313 return m_pisfInner->GetDisplayNameOf(_ILGetFSPidl(pidl), dwFlags, pName);
314 }
315
316 STDMETHODIMP CFindFolder::SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags,
317 PITEMID_CHILD *pPidlOut)
318 {
319 UNIMPLEMENTED;
320 return E_NOTIMPL;
321 }
322
323 //// *** IShellFolderViewCB method ***
324 STDMETHODIMP CFindFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
325 {
326 switch (uMsg)
327 {
328 case SFVM_DEFVIEWMODE:
329 {
330 FOLDERVIEWMODE *pViewMode = (FOLDERVIEWMODE *) lParam;
331 *pViewMode = FVM_DETAILS;
332 return S_OK;
333 }
334 case SFVM_WINDOWCREATED:
335 {
336 SubclassWindow((HWND) wParam);
337
338 CComPtr<IServiceProvider> pServiceProvider;
339 HRESULT hr = m_shellFolderView->QueryInterface(IID_PPV_ARG(IServiceProvider, &pServiceProvider));
340 if (FAILED_UNEXPECTEDLY(hr))
341 {
342 return hr;
343 }
344 return pServiceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &m_shellBrowser));
345 }
346 }
347 return E_NOTIMPL;
348 }
349
350 //// *** IContextMenuCB method ***
351 STDMETHODIMP CFindFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
352 {
353 switch (uMsg)
354 {
355 case DFM_MERGECONTEXTMENU:
356 {
357 QCMINFO *pqcminfo = (QCMINFO *) lParam;
358 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_SEPARATOR, NULL, 0);
359 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_STRING, L"Open Containing Folder", MFS_ENABLED);
360 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_SEPARATOR, NULL, 0);
361 return S_OK;
362 }
363 case DFM_INVOKECOMMAND:
364 case DFM_INVOKECOMMANDEX:
365 {
366 if (wParam != 1)
367 break;
368
369 PCUITEMID_CHILD *apidl;
370 UINT cidl;
371 HRESULT hr = m_shellFolderView->GetSelectedObjects(&apidl, &cidl);
372 if (FAILED_UNEXPECTEDLY(hr))
373 return hr;
374
375 for (UINT i = 0; i < cidl; i++)
376 {
377 CComHeapPtr<ITEMIDLIST> pidl;
378 DWORD attrs = 0;
379 hr = SHILCreateFromPathW((LPCWSTR) apidl[i]->mkid.abID, &pidl, &attrs);
380 if (SUCCEEDED(hr))
381 {
382 SHOpenFolderAndSelectItems(NULL, 1, &pidl, 0);
383 }
384 }
385
386 return S_OK;
387 }
388 case DFM_GETDEFSTATICID:
389 return S_FALSE;
390 }
391 return Shell_DefaultContextMenuCallBack(m_pisfInner, pdtobj);
392 }
393
394 //// *** IPersistFolder2 methods ***
395 STDMETHODIMP CFindFolder::GetCurFolder(LPITEMIDLIST *pidl)
396 {
397 *pidl = ILClone(m_pidl);
398 return S_OK;
399 }
400
401 // *** IPersistFolder methods ***
402 STDMETHODIMP CFindFolder::Initialize(LPCITEMIDLIST pidl)
403 {
404 m_pidl = ILClone(pidl);
405 if (!m_pidl)
406 return E_OUTOFMEMORY;
407
408 return SHELL32_CoCreateInitSF(m_pidl,
409 NULL,
410 NULL,
411 &CLSID_ShellFSFolder,
412 IID_PPV_ARG(IShellFolder2, &m_pisfInner));
413 }
414
415 // *** IPersist methods ***
416 STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassId)
417 {
418 if (pClassId == NULL)
419 return E_INVALIDARG;
420 memcpy(pClassId, &CLSID_FindFolder, sizeof(CLSID));
421 return S_OK;
422 }