[SHELL32] Add line bar to About dialog for consistence
[reactos.git] / dll / win32 / shell32 / shlfolder.cpp
1 /*
2 * Shell Folder stuff
3 *
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2018 Katayama Hirofumi MZ
7 *
8 * IShellFolder2 and related interfaces
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25 #include "precomp.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(shell);
28
29 /***************************************************************************
30 * GetNextElement (internal function)
31 *
32 * Gets a part of a string till the first backslash.
33 *
34 * PARAMETERS
35 * pszNext [IN] string to get the element from
36 * pszOut [IN] pointer to buffer which receives string
37 * dwOut [IN] length of pszOut
38 *
39 * RETURNS
40 * LPSTR pointer to first, not yet parsed char
41 */
42
43 LPCWSTR GetNextElementW (LPCWSTR pszNext, LPWSTR pszOut, DWORD dwOut)
44 {
45 LPCWSTR pszTail = pszNext;
46 DWORD dwCopy;
47
48 TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext), pszOut, dwOut);
49
50 *pszOut = 0x0000;
51
52 if (!pszNext || !*pszNext)
53 return NULL;
54
55 while (*pszTail && (*pszTail != (WCHAR) '\\'))
56 pszTail++;
57
58 dwCopy = pszTail - pszNext + 1;
59 lstrcpynW (pszOut, pszNext, (dwOut < dwCopy) ? dwOut : dwCopy);
60
61 if (*pszTail)
62 pszTail++;
63 else
64 pszTail = NULL;
65
66 TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext), debugstr_w (pszOut), dwOut, pszTail);
67 return pszTail;
68 }
69
70 HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
71 LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes)
72 {
73 HRESULT hr = E_INVALIDARG;
74 LPITEMIDLIST pidlIn = pidlInOut ? *pidlInOut : NULL;
75 LPITEMIDLIST pidlOut = NULL;
76 LPITEMIDLIST pidlTemp = NULL;
77 CComPtr<IShellFolder> psfChild;
78
79 TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlIn, debugstr_w (szNext));
80
81 /* get the shellfolder for the child pidl and let it analyse further */
82 hr = psf->BindToObject(pidlIn, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
83 if (FAILED(hr))
84 return hr;
85
86 hr = psfChild->ParseDisplayName(hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes);
87 if (FAILED(hr))
88 return hr;
89
90 pidlTemp = ILCombine (pidlIn, pidlOut);
91 if (!pidlTemp)
92 {
93 hr = E_OUTOFMEMORY;
94 if (pidlOut)
95 ILFree(pidlOut);
96 return hr;
97 }
98
99 if (pidlOut)
100 ILFree (pidlOut);
101
102 if (pidlIn)
103 ILFree (pidlIn);
104
105 *pidlInOut = pidlTemp;
106
107 TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr);
108 return S_OK;
109 }
110
111 /***********************************************************************
112 * SHELL32_CoCreateInitSF
113 *
114 * Creates a shell folder and initializes it with a pidl and a root folder
115 * via IPersistFolder3 or IPersistFolder.
116 *
117 * NOTES
118 * pathRoot can be NULL for Folders being a drive.
119 * In this case the absolute path is built from pidlChild (eg. C:)
120 */
121 HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
122 LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
123 {
124 HRESULT hr;
125 CComPtr<IShellFolder> pShellFolder;
126
127 hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
128 if (FAILED(hr))
129 return hr;
130
131 LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
132 CComPtr<IPersistFolder> ppf;
133 CComPtr<IPersistFolder3> ppf3;
134
135 if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
136 {
137 ppf3->InitializeEx(NULL, pidlAbsolute, ppfti);
138 }
139 else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
140 {
141 ppf->Initialize(pidlAbsolute);
142 }
143 ILFree (pidlAbsolute);
144
145 return pShellFolder->QueryInterface(riid, ppvOut);
146 }
147
148 HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, const GUID* clsid,
149 int csidl, REFIID riid, LPVOID *ppvOut)
150 {
151 /* fill the PERSIST_FOLDER_TARGET_INFO */
152 PERSIST_FOLDER_TARGET_INFO pfti = {0};
153 pfti.dwAttributes = -1;
154 pfti.csidl = csidl;
155
156 return SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, clsid, riid, ppvOut);
157 }
158
159 HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
160 LPCITEMIDLIST pidl, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
161 {
162 PITEMID_CHILD pidlChild = ILCloneFirst (pidl);
163 if (!pidlChild)
164 return E_FAIL;
165
166 CComPtr<IShellFolder> psf;
167 HRESULT hr = SHELL32_CoCreateInitSF(pidlRoot,
168 ppfti,
169 pidlChild,
170 clsid,
171 IID_PPV_ARG(IShellFolder, &psf));
172 ILFree(pidlChild);
173
174 if (FAILED_UNEXPECTEDLY(hr))
175 return hr;
176
177 if (_ILIsPidlSimple (pidl))
178 return psf->QueryInterface(riid, ppvOut);
179 else
180 return psf->BindToObject(ILGetNext (pidl), NULL, riid, ppvOut);
181 }
182
183 /***********************************************************************
184 * SHELL32_GetDisplayNameOfChild
185 *
186 * Retrieves the display name of a child object of a shellfolder.
187 *
188 * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
189 * - it binds to the child shellfolder [subpidl1]
190 * - asks it for the displayname of [subpidl2][subpidl3]
191 *
192 * Is possible the pidl is a simple pidl. In this case it asks the
193 * subfolder for the displayname of an empty pidl. The subfolder
194 * returns the own displayname eg. "::{guid}". This is used for
195 * virtual folders with the registry key WantsFORPARSING set.
196 */
197 HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf,
198 LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
199 {
200 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
201 if (!pidlFirst)
202 return E_OUTOFMEMORY;
203
204 CComPtr<IShellFolder> psfChild;
205 HRESULT hr = psf->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
206 if (SUCCEEDED (hr))
207 {
208 hr = psfChild->GetDisplayNameOf(ILGetNext (pidl), dwFlags, strRet);
209 }
210 ILFree (pidlFirst);
211
212 return hr;
213 }
214
215 HRESULT SHELL32_CompareChildren(IShellFolder2* psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
216 {
217 PUIDLIST_RELATIVE nextpidl1 = ILGetNext (pidl1);
218 PUIDLIST_RELATIVE nextpidl2 = ILGetNext (pidl2);
219
220 bool isEmpty1 = _ILIsDesktop(nextpidl1);
221 bool isEmpty2 = _ILIsDesktop(nextpidl2);
222 if (isEmpty1 || isEmpty2)
223 return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
224
225 PITEMID_CHILD firstpidl = ILCloneFirst (pidl1);
226 if (!firstpidl)
227 return E_OUTOFMEMORY;
228
229 CComPtr<IShellFolder> psf2;
230 HRESULT hr = psf->BindToObject(firstpidl, 0, IID_PPV_ARG(IShellFolder, &psf2));
231 ILFree(firstpidl);
232 if (FAILED(hr))
233 return MAKE_COMPARE_HRESULT(0);
234
235 return psf2->CompareIDs(lParam, nextpidl1, nextpidl2);
236 }
237
238 HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
239 {
240 SHELLDETAILS sd;
241 WCHAR wszItem1[MAX_PATH], wszItem2[MAX_PATH];
242 HRESULT hres;
243
244 hres = isf->GetDetailsOf(pidl1, lParam, &sd);
245 if (FAILED(hres))
246 return MAKE_COMPARE_HRESULT(1);
247
248 hres = StrRetToBufW(&sd.str, pidl1, wszItem1, MAX_PATH);
249 if (FAILED(hres))
250 return MAKE_COMPARE_HRESULT(1);
251
252 hres = isf->GetDetailsOf(pidl2, lParam, &sd);
253 if (FAILED(hres))
254 return MAKE_COMPARE_HRESULT(1);
255
256 hres = StrRetToBufW(&sd.str, pidl2, wszItem2, MAX_PATH);
257 if (FAILED(hres))
258 return MAKE_COMPARE_HRESULT(1);
259
260 int ret = wcsicmp(wszItem1, wszItem2);
261 if (ret == 0)
262 return SHELL32_CompareChildren(isf, lParam, pidl1, pidl2);
263
264 return MAKE_COMPARE_HRESULT(ret);
265 }
266
267 void AddClassKeyToArray(const WCHAR * szClass, HKEY* array, UINT* cKeys)
268 {
269 if (*cKeys >= 16)
270 return;
271
272 HKEY hkey;
273 LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
274 if (result != ERROR_SUCCESS)
275 return;
276
277 array[*cKeys] = hkey;
278 *cKeys += 1;
279 }
280
281 void AddFSClassKeysToArray(PCUITEMID_CHILD pidl, HKEY* array, UINT* cKeys)
282 {
283 if (_ILIsValue(pidl))
284 {
285 FileStructW* pFileData = _ILGetFileStructW(pidl);
286 LPWSTR extension = PathFindExtension(pFileData->wszName);
287
288 if (extension)
289 {
290 AddClassKeyToArray(extension, array, cKeys);
291
292 WCHAR wszClass[MAX_PATH], wszClass2[MAX_PATH];
293 DWORD dwSize = sizeof(wszClass);
294 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
295 {
296 swprintf(wszClass2, L"%s//%s", extension, wszClass);
297
298 AddClassKeyToArray(wszClass, array, cKeys);
299 AddClassKeyToArray(wszClass2, array, cKeys);
300 }
301
302 swprintf(wszClass2, L"SystemFileAssociations//%s", extension);
303 AddClassKeyToArray(wszClass2, array, cKeys);
304
305 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
306 {
307 swprintf(wszClass2, L"SystemFileAssociations//%s", wszClass);
308 AddClassKeyToArray(wszClass2, array, cKeys);
309 }
310 }
311
312 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
313 AddClassKeyToArray(L"*", array, cKeys);
314 }
315 else if (_ILIsFolder(pidl))
316 {
317 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
318 AddClassKeyToArray(L"Directory", array, cKeys);
319 AddClassKeyToArray(L"Folder", array, cKeys);
320 }
321 else
322 {
323 ERR("Got non FS pidl\n");
324 }
325 }
326
327 HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl)
328 {
329 UINT cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
330 if (!cfShellIDList)
331 return E_FAIL;
332
333 FORMATETC fmt;
334 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
335
336 HRESULT hr = pDataObject->QueryGetData(&fmt);
337 if (FAILED_UNEXPECTEDLY(hr))
338 return hr;
339
340 STGMEDIUM medium;
341 hr = pDataObject->GetData(&fmt, &medium);
342 if (FAILED_UNEXPECTEDLY(hr))
343 return hr;
344
345 /* lock the handle */
346 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
347 if (!lpcida)
348 {
349 ReleaseStgMedium(&medium);
350 return E_FAIL;
351 }
352
353 /* convert the data into pidl */
354 LPITEMIDLIST pidl;
355 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
356 if (!apidl)
357 {
358 ReleaseStgMedium(&medium);
359 return E_OUTOFMEMORY;
360 }
361
362 *ppidlfolder = pidl;
363 *apidlItems = apidl;
364 *pcidl = lpcida->cidl;
365
366 ReleaseStgMedium(&medium);
367 return S_OK;
368 }
369
370 /***********************************************************************
371 * SHCreateLinks
372 *
373 * Undocumented.
374 */
375 HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject,
376 UINT uFlags, LPITEMIDLIST *lppidlLinks)
377 {
378 FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks);
379 return E_NOTIMPL;
380 }
381
382 /***********************************************************************
383 * SHOpenFolderAndSelectItems
384 *
385 * Unimplemented.
386 */
387 EXTERN_C HRESULT
388 WINAPI
389 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder,
390 UINT cidl,
391 PCUITEMID_CHILD_ARRAY apidl,
392 DWORD dwFlags)
393 {
394 ERR("SHOpenFolderAndSelectItems() is hackplemented\n");
395 PCIDLIST_ABSOLUTE pidlItem;
396 if (cidl)
397 {
398 /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */
399 if (ILGetNext(apidl[0]) != NULL)
400 {
401 pidlItem = apidl[0];
402 }
403 else
404 {
405 pidlItem = ILCombine(pidlFolder, apidl[0]);
406 }
407 }
408 else
409 {
410 pidlItem = pidlFolder;
411 }
412
413 CComPtr<IShellFolder> psfDesktop;
414
415 HRESULT hr = SHGetDesktopFolder(&psfDesktop);
416 if (FAILED_UNEXPECTEDLY(hr))
417 return hr;
418
419 STRRET strret;
420 hr = psfDesktop->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &strret);
421 if (FAILED_UNEXPECTEDLY(hr))
422 return hr;
423
424 WCHAR wszBuf[MAX_PATH];
425 hr = StrRetToBufW(&strret, pidlItem, wszBuf, _countof(wszBuf));
426 if (FAILED_UNEXPECTEDLY(hr))
427 return hr;
428
429 WCHAR wszParams[MAX_PATH];
430 wcscpy(wszParams, L"/select,");
431 wcscat(wszParams, wszBuf);
432
433 SHELLEXECUTEINFOW sei;
434 memset(&sei, 0, sizeof sei);
435 sei.cbSize = sizeof sei;
436 sei.fMask = SEE_MASK_WAITFORINPUTIDLE;
437 sei.lpFile = L"explorer.exe";
438 sei.lpParameters = wszParams;
439
440 if (ShellExecuteExW(&sei))
441 return S_OK;
442 else
443 return E_FAIL;
444 }
445
446 /*
447 * for internal use
448 */
449 HRESULT WINAPI
450 Shell_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdtobj)
451 {
452 PIDLIST_ABSOLUTE pidlFolder;
453 PUITEMID_CHILD *apidl;
454 UINT cidl;
455 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
456 if (FAILED_UNEXPECTEDLY(hr))
457 return hr;
458
459 if (cidl > 1)
460 {
461 ERR("SHMultiFileProperties is not yet implemented\n");
462 SHFree(pidlFolder);
463 _ILFreeaPidl(apidl, cidl);
464 return E_FAIL;
465 }
466
467 STRRET strFile;
468 hr = psf->GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strFile);
469 if (SUCCEEDED(hr))
470 {
471 hr = SH_ShowPropertiesDialog(strFile.pOleStr, pidlFolder, apidl);
472 if (FAILED(hr))
473 ERR("SH_ShowPropertiesDialog failed\n");
474 }
475 else
476 {
477 ERR("Failed to get display name\n");
478 }
479
480 SHFree(pidlFolder);
481 _ILFreeaPidl(apidl, cidl);
482
483 return hr;
484 }