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