077cc3a7b03e0fa7f1c03b93fdabca98dced666e
[reactos.git] / reactos / dll / win32 / shell32 / folders / CFSFolder.cpp
1
2 /*
3 * file system folder
4 *
5 * Copyright 1997 Marcus Meissner
6 * Copyright 1998, 1999, 2002 Juergen Schmied
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <precomp.h>
24
25 WINE_DEFAULT_DEBUG_CHANNEL (shell);
26
27 static HRESULT getIconLocationForFolder(IShellFolder * psf, LPCITEMIDLIST pidl, UINT uFlags,
28 LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
29 {
30 static const WCHAR shellClassInfo[] = { '.', 'S', 'h', 'e', 'l', 'l', 'C', 'l', 'a', 's', 's', 'I', 'n', 'f', 'o', 0 };
31 static const WCHAR iconFile[] = { 'I', 'c', 'o', 'n', 'F', 'i', 'l', 'e', 0 };
32 static const WCHAR clsid[] = { 'C', 'L', 'S', 'I', 'D', 0 };
33 static const WCHAR clsid2[] = { 'C', 'L', 'S', 'I', 'D', '2', 0 };
34 static const WCHAR iconIndex[] = { 'I', 'c', 'o', 'n', 'I', 'n', 'd', 'e', 'x', 0 };
35 static const WCHAR wszDesktopIni[] = { 'd','e','s','k','t','o','p','.','i','n','i',0 };
36 int icon_idx;
37
38 if (!(uFlags & GIL_DEFAULTICON) && (_ILGetFileAttributes(ILFindLastID(pidl), NULL, 0) & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0 )
39 {
40 WCHAR wszFolderPath[MAX_PATH];
41
42 if (!ILGetDisplayNameExW(psf, pidl, wszFolderPath, 0))
43 return FALSE;
44
45 PathAppendW(wszFolderPath, wszDesktopIni);
46
47 if (PathFileExistsW(wszFolderPath))
48 {
49 WCHAR wszPath[MAX_PATH];
50 WCHAR wszCLSIDValue[CHARS_IN_GUID];
51
52 if (GetPrivateProfileStringW(shellClassInfo, iconFile, NULL, wszPath, MAX_PATH, wszFolderPath))
53 {
54 ExpandEnvironmentStringsW(wszPath, szIconFile, cchMax);
55
56 *piIndex = GetPrivateProfileIntW(shellClassInfo, iconIndex, 0, wszFolderPath);
57 return S_OK;
58 }
59 else if (GetPrivateProfileStringW(shellClassInfo, clsid, NULL, wszCLSIDValue, CHARS_IN_GUID, wszFolderPath) &&
60 HCR_GetIconW(wszCLSIDValue, szIconFile, NULL, cchMax, &icon_idx))
61 {
62 *piIndex = icon_idx;
63 return S_OK;
64 }
65 else if (GetPrivateProfileStringW(shellClassInfo, clsid2, NULL, wszCLSIDValue, CHARS_IN_GUID, wszFolderPath) &&
66 HCR_GetIconW(wszCLSIDValue, szIconFile, NULL, cchMax, &icon_idx))
67 {
68 *piIndex = icon_idx;
69 return S_OK;
70 }
71 }
72 }
73
74 static const WCHAR folder[] = { 'F', 'o', 'l', 'd', 'e', 'r', 0 };
75
76 if (!HCR_GetIconW(folder, szIconFile, NULL, cchMax, &icon_idx))
77 {
78 lstrcpynW(szIconFile, swShell32Name, cchMax);
79 icon_idx = -IDI_SHELL_FOLDER;
80 }
81
82 if (uFlags & GIL_OPENICON)
83 *piIndex = icon_idx < 0 ? icon_idx - 1 : icon_idx + 1;
84 else
85 *piIndex = icon_idx;
86
87 return S_OK;
88 }
89
90 HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
91 {
92 CComPtr<IDefaultExtractIconInit> initIcon;
93 HRESULT hr;
94 int icon_idx = 0;
95 UINT flags = 0; // FIXME: Use it!
96 CHAR sTemp[MAX_PATH] = "";
97 WCHAR wTemp[MAX_PATH] = L"";
98
99 hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
100 if (FAILED(hr))
101 return hr;
102
103 if (_ILIsFolder (pidl))
104 {
105 if (SUCCEEDED(getIconLocationForFolder(psf,
106 pidl, 0, wTemp, _countof(wTemp),
107 &icon_idx,
108 &flags)))
109 {
110 initIcon->SetNormalIcon(wTemp, icon_idx);
111 // FIXME: if/when getIconLocationForFolder does something for
112 // GIL_FORSHORTCUT, code below should be uncommented. and
113 // the following line removed.
114 initIcon->SetShortcutIcon(wTemp, icon_idx);
115 }
116 if (SUCCEEDED(getIconLocationForFolder(psf,
117 pidl, GIL_DEFAULTICON, wTemp, _countof(wTemp),
118 &icon_idx,
119 &flags)))
120 {
121 initIcon->SetDefaultIcon(wTemp, icon_idx);
122 }
123 // if (SUCCEEDED(getIconLocationForFolder(psf,
124 // pidl, GIL_FORSHORTCUT, wTemp, _countof(wTemp),
125 // &icon_idx,
126 // &flags)))
127 // {
128 // initIcon->SetShortcutIcon(wTemp, icon_idx);
129 // }
130 if (SUCCEEDED(getIconLocationForFolder(psf,
131 pidl, GIL_OPENICON, wTemp, _countof(wTemp),
132 &icon_idx,
133 &flags)))
134 {
135 initIcon->SetOpenIcon(wTemp, icon_idx);
136 }
137 }
138 else
139 {
140 BOOL found = FALSE;
141
142 if (_ILGetExtension(pidl, sTemp, _countof(sTemp)))
143 {
144 if (HCR_MapTypeToValueA(sTemp, sTemp, _countof(sTemp), TRUE)
145 && HCR_GetIconA(sTemp, sTemp, NULL, _countof(sTemp), &icon_idx))
146 {
147 if (!lstrcmpA("%1", sTemp)) /* icon is in the file */
148 {
149 ILGetDisplayNameExW(psf, pidl, wTemp, 0);
150 icon_idx = 0;
151 }
152 else
153 {
154 MultiByteToWideChar(CP_ACP, 0, sTemp, -1, wTemp, _countof(wTemp));
155 }
156
157 found = TRUE;
158 }
159 else if (!lstrcmpiA(sTemp, "lnkfile"))
160 {
161 /* extract icon from shell shortcut */
162 CComPtr<IShellLinkW> psl;
163 CComPtr<IExtractIconW> pei;
164
165 HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_NULL_PPV_ARG(IShellLinkW, &psl));
166 if (SUCCEEDED(hr))
167 {
168 hr = psl->GetIconLocation(wTemp, _countof(wTemp), &icon_idx);
169 if (FAILED(hr) || !*wTemp)
170 {
171 /* The icon was not found directly, try to retrieve it from the shell link target */
172 hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei));
173 if (FAILED(hr) || !pei)
174 TRACE("No IExtractIconW interface!\n");
175 else
176 hr = pei->GetIconLocation(GIL_FORSHELL, wTemp, _countof(wTemp), &icon_idx, &flags);
177 }
178
179 if (SUCCEEDED(hr) && *wTemp)
180 found = TRUE;
181 }
182 }
183 }
184
185 /* FIXME: We should normally use the correct icon format according to 'flags' */
186 if (!found)
187 /* default icon */
188 initIcon->SetNormalIcon(swShell32Name, 0);
189 else
190 initIcon->SetNormalIcon(wTemp, icon_idx);
191 }
192
193 return initIcon->QueryInterface(iid, ppvOut);
194 }
195
196 /*
197 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is
198 returned by Next. When the enumerator is created, it can do numerous additional operations
199 including formatting a drive, reconnecting a network share drive, and requesting a disk
200 be inserted in a removable drive.
201 */
202
203 /***********************************************************************
204 * IShellFolder implementation
205 */
206
207 class CFileSysEnum :
208 public CEnumIDListBase
209 {
210 private:
211 public:
212 CFileSysEnum();
213 ~CFileSysEnum();
214 HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags);
215
216 BEGIN_COM_MAP(CFileSysEnum)
217 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
218 END_COM_MAP()
219 };
220
221 CFileSysEnum::CFileSysEnum()
222 {
223 }
224
225 CFileSysEnum::~CFileSysEnum()
226 {
227 }
228
229 HRESULT WINAPI CFileSysEnum::Initialize(LPWSTR lpszPath, DWORD dwFlags)
230 {
231 WIN32_FIND_DATAW stffile;
232 HANDLE hFile;
233 WCHAR szPath[MAX_PATH];
234 BOOL succeeded = TRUE;
235 static const WCHAR stars[] = { '*','.','*',0 };
236 static const WCHAR dot[] = { '.',0 };
237 static const WCHAR dotdot[] = { '.','.',0 };
238
239 TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(lpszPath), dwFlags);
240
241 if(!lpszPath || !lpszPath[0]) return FALSE;
242
243 wcscpy(szPath, lpszPath);
244 PathAddBackslashW(szPath);
245 wcscat(szPath,stars);
246
247 hFile = FindFirstFileW(szPath,&stffile);
248 if ( hFile != INVALID_HANDLE_VALUE )
249 {
250 BOOL findFinished = FALSE;
251
252 do
253 {
254 if ( !(stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
255 || (dwFlags & SHCONTF_INCLUDEHIDDEN) )
256 {
257 LPITEMIDLIST pidl = NULL;
258
259 if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
260 dwFlags & SHCONTF_FOLDERS &&
261 strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot))
262 {
263 pidl = _ILCreateFromFindDataW(&stffile);
264 succeeded = succeeded && AddToEnumList(pidl);
265 }
266 else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
267 && dwFlags & SHCONTF_NONFOLDERS)
268 {
269 pidl = _ILCreateFromFindDataW(&stffile);
270 succeeded = succeeded && AddToEnumList(pidl);
271 }
272 }
273 if (succeeded)
274 {
275 if (!FindNextFileW(hFile, &stffile))
276 {
277 if (GetLastError() == ERROR_NO_MORE_FILES)
278 findFinished = TRUE;
279 else
280 succeeded = FALSE;
281 }
282 }
283 } while (succeeded && !findFinished);
284 FindClose(hFile);
285 }
286
287 return succeeded;
288 }
289
290 CFSFolder::CFSFolder()
291 {
292 pclsid = (CLSID *)&CLSID_ShellFSFolder;
293 sPathTarget = NULL;
294 pidlRoot = NULL;
295 m_bGroupPolicyActive = 0;
296 }
297
298 CFSFolder::~CFSFolder()
299 {
300 TRACE("-- destroying IShellFolder(%p)\n", this);
301
302 SHFree(pidlRoot);
303 SHFree(sPathTarget);
304 }
305
306
307 static const shvheader GenericSFHeader[] = {
308 {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
309 {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 0},
310 {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
311 {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
312 {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12},
313 {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}
314 };
315
316 #define GENERICSHELLVIEWCOLUMNS 6
317
318 /**************************************************************************
319 * SHELL32_CreatePidlFromBindCtx [internal]
320 *
321 * If the caller bound File System Bind Data, assume it is the
322 * find data for the path.
323 * This allows binding of paths that don't exist.
324 */
325 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
326 {
327 IFileSystemBindData *fsbd = NULL;
328 LPITEMIDLIST pidl = NULL;
329 IUnknown *param = NULL;
330 WIN32_FIND_DATAW wfd;
331 HRESULT r;
332
333 TRACE("%p %s\n", pbc, debugstr_w(path));
334
335 if (!pbc)
336 return NULL;
337
338 /* see if the caller bound File System Bind Data */
339 r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, &param);
340 if (FAILED(r))
341 return NULL;
342
343 r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd));
344 if (SUCCEEDED(r))
345 {
346 r = fsbd->GetFindData(&wfd);
347 if (SUCCEEDED(r))
348 {
349 lstrcpynW(&wfd.cFileName[0], path, MAX_PATH);
350 pidl = _ILCreateFromFindDataW(&wfd);
351 }
352 fsbd->Release();
353 }
354
355 return pidl;
356 }
357
358 void SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, CLSID* pclsidFolder)
359 {
360 WCHAR wszCLSIDValue[CHARS_IN_GUID];
361 WCHAR wszDesktopIni[MAX_PATH];
362 StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir);
363 StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini");
364
365 if (GetPrivateProfileStringW(L".ShellClassInfo",
366 L"CLSID",
367 L"",
368 wszCLSIDValue,
369 CHARS_IN_GUID,
370 wszDesktopIni))
371 {
372 CLSIDFromString (wszCLSIDValue, pclsidFolder);
373 }
374 }
375
376
377 static const DWORD dwSupportedAttr=
378 SFGAO_CANCOPY | /*0x00000001 */
379 SFGAO_CANMOVE | /*0x00000002 */
380 SFGAO_CANLINK | /*0x00000004 */
381 SFGAO_CANRENAME | /*0x00000010 */
382 SFGAO_CANDELETE | /*0x00000020 */
383 SFGAO_HASPROPSHEET | /*0x00000040 */
384 SFGAO_DROPTARGET | /*0x00000100 */
385 SFGAO_LINK | /*0x00010000 */
386 SFGAO_READONLY | /*0x00040000 */
387 SFGAO_HIDDEN | /*0x00080000 */
388 SFGAO_FILESYSANCESTOR | /*0x10000000 */
389 SFGAO_FOLDER | /*0x20000000 */
390 SFGAO_FILESYSTEM | /*0x40000000 */
391 SFGAO_HASSUBFOLDER; /*0x80000000 */
392
393 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
394 {
395 DWORD dwFileAttributes, dwShellAttributes;
396
397 if (!_ILIsFolder(pidl) && !_ILIsValue(pidl))
398 {
399 ERR("Got wrong type of pidl!\n");
400 *pdwAttributes &= SFGAO_CANLINK;
401 return S_OK;
402 }
403
404 if (*pdwAttributes & ~dwSupportedAttr)
405 {
406 WARN ("attributes 0x%08x not implemented\n", (*pdwAttributes & ~dwSupportedAttr));
407 *pdwAttributes &= dwSupportedAttr;
408 }
409
410 dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0);
411
412 /* Set common attributes */
413 dwShellAttributes = *pdwAttributes;
414 dwShellAttributes |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANDELETE |
415 SFGAO_CANRENAME | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANCOPY;
416
417 if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
418 {
419 dwShellAttributes |= (SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
420 }
421 else
422 dwShellAttributes &= ~(SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
423
424 if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
425 dwShellAttributes |= SFGAO_HIDDEN;
426 else
427 dwShellAttributes &= ~SFGAO_HIDDEN;
428
429 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
430 dwShellAttributes |= SFGAO_READONLY;
431 else
432 dwShellAttributes &= ~SFGAO_READONLY;
433
434 if (SFGAO_LINK & *pdwAttributes)
435 {
436 char ext[MAX_PATH];
437
438 if (!_ILGetExtension(pidl, ext, MAX_PATH) || lstrcmpiA(ext, "lnk"))
439 dwShellAttributes &= ~SFGAO_LINK;
440 }
441
442 if (SFGAO_HASSUBFOLDER & *pdwAttributes)
443 {
444 CComPtr<IShellFolder> psf2;
445 if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
446 {
447 CComPtr<IEnumIDList> pEnumIL;
448 if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL)))
449 {
450 if (pEnumIL->Skip(1) != S_OK)
451 dwShellAttributes &= ~SFGAO_HASSUBFOLDER;
452 }
453 }
454 }
455
456 *pdwAttributes &= dwShellAttributes;
457
458 TRACE ("-- 0x%08x\n", *pdwAttributes);
459 return S_OK;
460 }
461
462 /**************************************************************************
463 * CFSFolder::ParseDisplayName {SHELL32}
464 *
465 * Parse a display name.
466 *
467 * PARAMS
468 * hwndOwner [in] Parent window for any message's
469 * pbc [in] optional FileSystemBindData context
470 * lpszDisplayName [in] Unicode displayname.
471 * pchEaten [out] (unicode) characters processed
472 * ppidl [out] complex pidl to item
473 * pdwAttributes [out] items attributes
474 *
475 * NOTES
476 * Every folder tries to parse only its own (the leftmost) pidl and creates a
477 * subfolder to evaluate the remaining parts.
478 * Now we can parse into namespaces implemented by shell extensions
479 *
480 * Behaviour on win98: lpszDisplayName=NULL -> crash
481 * lpszDisplayName="" -> returns mycoputer-pidl
482 *
483 * FIXME
484 * pdwAttributes is not set
485 * pchEaten is not set like in windows
486 */
487 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
488 LPBC pbc,
489 LPOLESTR lpszDisplayName,
490 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
491 DWORD *pdwAttributes)
492 {
493 HRESULT hr = E_INVALIDARG;
494 LPCWSTR szNext = NULL;
495 WCHAR szElement[MAX_PATH];
496 WCHAR szPath[MAX_PATH];
497 LPITEMIDLIST pidlTemp = NULL;
498 DWORD len;
499
500 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
501 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
502 pchEaten, ppidl, pdwAttributes);
503
504 if (!ppidl)
505 return E_INVALIDARG;
506
507 if (!lpszDisplayName)
508 {
509 *ppidl = NULL;
510 return E_INVALIDARG;
511 }
512
513 *ppidl = NULL;
514
515 if (pchEaten)
516 *pchEaten = 0; /* strange but like the original */
517
518 if (*lpszDisplayName)
519 {
520 /* get the next element */
521 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
522
523 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement);
524 if (pidlTemp != NULL)
525 {
526 /* We are creating an id list without ensuring that the items exist.
527 If we have a remaining path, this must be a folder.
528 We have to do it now because it is set as a file by default */
529 if (szNext)
530 {
531 pidlTemp->mkid.abID[0] = PT_FOLDER;
532 }
533 hr = S_OK;
534 }
535 else
536 {
537 /* build the full pathname to the element */
538 lstrcpynW(szPath, sPathTarget, MAX_PATH - 1);
539 PathAddBackslashW(szPath);
540 len = wcslen(szPath);
541 lstrcpynW(szPath + len, szElement, MAX_PATH - len);
542
543 /* get the pidl */
544 hr = _ILCreateFromPathW(szPath, &pidlTemp);
545 }
546
547 if (SUCCEEDED(hr))
548 {
549 if (szNext && *szNext)
550 {
551 /* try to analyse the next element */
552 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
553 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
554 }
555 else
556 {
557 /* it's the last element */
558 if (pdwAttributes && *pdwAttributes)
559 hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes);
560 }
561 }
562 }
563
564 if (SUCCEEDED(hr))
565 *ppidl = pidlTemp;
566 else
567 *ppidl = NULL;
568
569 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
570
571 return hr;
572 }
573
574 /**************************************************************************
575 * CFSFolder::EnumObjects
576 * PARAMETERS
577 * HWND hwndOwner, //[in ] Parent Window
578 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
579 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
580 */
581 HRESULT WINAPI CFSFolder::EnumObjects(
582 HWND hwndOwner,
583 DWORD dwFlags,
584 LPENUMIDLIST *ppEnumIDList)
585 {
586 return ShellObjectCreatorInit<CFileSysEnum>(sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
587 }
588
589 /**************************************************************************
590 * CFSFolder::BindToObject
591 * PARAMETERS
592 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
593 * LPBC pbc, //[in ] optional FileSystemBindData context
594 * REFIID riid, //[in ] Initial Interface
595 * LPVOID* ppvObject //[out] Interface*
596 */
597 HRESULT WINAPI CFSFolder::BindToObject(
598 PCUIDLIST_RELATIVE pidl,
599 LPBC pbc,
600 REFIID riid,
601 LPVOID * ppvOut)
602 {
603 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
604 shdebugstr_guid(&riid), ppvOut);
605
606 CComPtr<IShellFolder> pSF;
607 HRESULT hr;
608
609 if (!pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb)
610 return E_INVALIDARG;
611
612 if (_ILIsValue(pidl))
613 {
614 ERR("Binding to file is unimplemented\n");
615 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
616 }
617 if (!_ILIsFolder(pidl))
618 {
619 ERR("Got an unknown type of pidl!\n");
620 return E_FAIL;
621 }
622
623 *ppvOut = NULL;
624
625 /* Get the pidl data */
626 FileStruct* pData = &_ILGetDataPointer(pidl)->u.file;
627 FileStructW* pDataW = _ILGetFileStructW(pidl);
628
629 /* Create the target folder info */
630 PERSIST_FOLDER_TARGET_INFO pfti = {0};
631 pfti.dwAttributes = -1;
632 pfti.csidl = -1;
633 PathCombineW(pfti.szTargetParsingName, sPathTarget, pDataW->wszName);
634
635 /* Get the CLSID to bind to */
636 CLSID clsidFolder = CLSID_ShellFSFolder;
637 if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
638 SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, &clsidFolder);
639
640 hr = SHELL32_BindToSF(pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
641 if (FAILED_UNEXPECTEDLY(hr))
642 return hr;
643
644 TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
645
646 return S_OK;
647
648 }
649
650 /**************************************************************************
651 * CFSFolder::BindToStorage
652 * PARAMETERS
653 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
654 * LPBC pbc, //[in ] reserved
655 * REFIID riid, //[in ] Initial storage interface
656 * LPVOID* ppvObject //[out] Interface* returned
657 */
658 HRESULT WINAPI CFSFolder::BindToStorage(
659 PCUIDLIST_RELATIVE pidl,
660 LPBC pbcReserved,
661 REFIID riid,
662 LPVOID *ppvOut)
663 {
664 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
665 shdebugstr_guid (&riid), ppvOut);
666
667 *ppvOut = NULL;
668 return E_NOTIMPL;
669 }
670
671 /**************************************************************************
672 * CFSFolder::CompareIDs
673 */
674
675 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
676 PCUIDLIST_RELATIVE pidl1,
677 PCUIDLIST_RELATIVE pidl2)
678 {
679 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
680 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
681 FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
682 FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
683 BOOL bIsFolder1 = _ILIsFolder(pidl1);
684 BOOL bIsFolder2 = _ILIsFolder(pidl2);
685 LPWSTR pExtension1, pExtension2;
686
687 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
688 return E_INVALIDARG;
689
690 /* When sorting between a File and a Folder, the Folder gets sorted first */
691 if (bIsFolder1 != bIsFolder2)
692 {
693 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
694 }
695
696 int result;
697 switch (LOWORD(lParam))
698 {
699 case 0: /* Name */
700 result = wcsicmp(pDataW1->wszName, pDataW2->wszName);
701 break;
702 case 1: /* Comments */
703 result = 0;
704 break;
705 case 2: /* Type */
706 pExtension1 = PathFindExtensionW(pDataW1->wszName);
707 pExtension2 = PathFindExtensionW(pDataW2->wszName);
708 result = wcsicmp(pExtension1, pExtension2);
709 break;
710 case 3: /* Size */
711 result = pData1->u.file.dwFileSize - pData2->u.file.dwFileSize;
712 break;
713 case 4: /* Modified */
714 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
715 if (result == 0)
716 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
717 break;
718 case 5: /* Attributes */
719 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
720 }
721
722 if (result == 0)
723 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
724
725 return MAKE_COMPARE_HRESULT(result);
726 }
727
728 /**************************************************************************
729 * CFSFolder::CreateViewObject
730 */
731 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
732 REFIID riid, LPVOID * ppvOut)
733 {
734 CComPtr<IShellView> pShellView;
735 HRESULT hr = E_INVALIDARG;
736
737 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
738 ppvOut);
739
740 if (ppvOut)
741 {
742 *ppvOut = NULL;
743
744 if (IsEqualIID (riid, IID_IDropTarget))
745 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
746 else if (IsEqualIID (riid, IID_IContextMenu))
747 {
748 HKEY hKeys[16];
749 UINT cKeys = 0;
750 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
751
752 DEFCONTEXTMENU dcm;
753 dcm.hwnd = hwndOwner;
754 dcm.pcmcb = this;
755 dcm.pidlFolder = pidlRoot;
756 dcm.psf = this;
757 dcm.cidl = 0;
758 dcm.apidl = NULL;
759 dcm.cKeys = cKeys;
760 dcm.aKeys = hKeys;
761 dcm.punkAssociationInfo = NULL;
762 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
763 }
764 else if (IsEqualIID (riid, IID_IShellView))
765 {
766 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
767 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
768 }
769 }
770 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
771 return hr;
772 }
773
774 /**************************************************************************
775 * CFSFolder::GetAttributesOf
776 *
777 * PARAMETERS
778 * UINT cidl, //[in ] num elements in pidl array
779 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
780 * ULONG* rgfInOut) //[out] result array
781 *
782 */
783 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
784 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
785 {
786 HRESULT hr = S_OK;
787
788 if (!rgfInOut)
789 return E_INVALIDARG;
790 if (cidl && !apidl)
791 return E_INVALIDARG;
792
793 if (*rgfInOut == 0)
794 *rgfInOut = ~0;
795
796 if(cidl == 0)
797 {
798 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot);
799
800 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
801 {
802 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
803 }
804 else if (_ILIsDrive(rpidl))
805 {
806 IShellFolder *psfParent = NULL;
807 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
808 if(SUCCEEDED(hr))
809 {
810 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
811 psfParent->Release();
812 }
813 }
814 else
815 {
816 ERR("Got and unknown pidl!\n");
817 }
818 }
819 else
820 {
821 while (cidl > 0 && *apidl)
822 {
823 pdump(*apidl);
824 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
825 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
826 else
827 ERR("Got an unknown type of pidl!!!\n");
828 apidl++;
829 cidl--;
830 }
831 }
832 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
833 *rgfInOut &= ~SFGAO_VALIDATE;
834
835 TRACE("-- result=0x%08x\n", *rgfInOut);
836
837 return hr;
838 }
839
840 /**************************************************************************
841 * CFSFolder::GetUIObjectOf
842 *
843 * PARAMETERS
844 * HWND hwndOwner, //[in ] Parent window for any output
845 * UINT cidl, //[in ] array size
846 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
847 * REFIID riid, //[in ] Requested Interface
848 * UINT* prgfInOut, //[ ] reserved
849 * LPVOID* ppvObject) //[out] Resulting Interface
850 *
851 * NOTES
852 * This function gets asked to return "view objects" for one or more (multiple
853 * select) items:
854 * The viewobject typically is an COM object with one of the following
855 * interfaces:
856 * IExtractIcon,IDataObject,IContextMenu
857 * In order to support icon positions in the default Listview your DataObject
858 * must implement the SetData method (in addition to GetData :) - the shell
859 * passes a barely documented "Icon positions" structure to SetData when the
860 * drag starts, and GetData's it if the drop is in another explorer window that
861 * needs the positions.
862 */
863 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
864 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
865 REFIID riid, UINT * prgfInOut,
866 LPVOID * ppvOut)
867 {
868 LPVOID pObj = NULL;
869 HRESULT hr = E_INVALIDARG;
870
871 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
872 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
873
874 if (ppvOut)
875 {
876 *ppvOut = NULL;
877
878 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
879 {
880 HKEY hKeys[16];
881 UINT cKeys = 0;
882 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
883
884 DEFCONTEXTMENU dcm;
885 dcm.hwnd = hwndOwner;
886 dcm.pcmcb = this;
887 dcm.pidlFolder = pidlRoot;
888 dcm.psf = this;
889 dcm.cidl = cidl;
890 dcm.apidl = apidl;
891 dcm.cKeys = cKeys;
892 dcm.aKeys = hKeys;
893 dcm.punkAssociationInfo = NULL;
894 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
895 }
896 else if (IsEqualIID (riid, IID_IDataObject))
897 {
898 if (cidl >= 1)
899 {
900 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
901 }
902 else
903 {
904 hr = E_INVALIDARG;
905 }
906 }
907 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
908 {
909 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
910 }
911 else if (IsEqualIID (riid, IID_IDropTarget))
912 {
913 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
914 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
915 {
916 IDropTarget * pDt = NULL;
917 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
918 pObj = pDt;
919 }
920 }
921 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
922 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
923 {
924 hr = IShellLink_ConstructFromFile(this, apidl[0], riid, &pObj);
925 }
926 else
927 hr = E_NOINTERFACE;
928
929 if (SUCCEEDED(hr) && !pObj)
930 hr = E_OUTOFMEMORY;
931
932 *ppvOut = pObj;
933 }
934 TRACE("(%p)->hr=0x%08x\n", this, hr);
935 return hr;
936 }
937
938 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
939 static const WCHAR HideFileExtW[] = L"HideFileExt";
940 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
941
942 /******************************************************************************
943 * SHELL_FS_HideExtension [Internal]
944 *
945 * Query the registry if the filename extension of a given path should be
946 * hidden.
947 *
948 * PARAMS
949 * szPath [I] Relative or absolute path of a file
950 *
951 * RETURNS
952 * TRUE, if the filename's extension should be hidden
953 * FALSE, otherwise.
954 */
955 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
956 {
957 HKEY hKey;
958 DWORD dwData;
959 DWORD dwDataSize = sizeof (DWORD);
960 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
961
962 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
963 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
964 doHide = dwData;
965 RegCloseKey (hKey);
966 }
967
968 if (!doHide) {
969 LPWSTR ext = PathFindExtensionW(szPath);
970
971 if (*ext != '\0') {
972 WCHAR classname[MAX_PATH];
973 LONG classlen = sizeof(classname);
974
975 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
976 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
977 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
978 doHide = TRUE;
979 RegCloseKey(hKey);
980 }
981 }
982 }
983 return doHide;
984 }
985
986 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
987 {
988 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
989 if (!(dwFlags & SHGDN_FORPARSING) &&
990 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
991 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
992 PathRemoveExtensionW(szPath);
993 }
994 }
995
996 /**************************************************************************
997 * CFSFolder::GetDisplayNameOf
998 * Retrieves the display name for the specified file object or subfolder
999 *
1000 * PARAMETERS
1001 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
1002 * DWORD dwFlags, //[in ] SHGNO formatting flags
1003 * LPSTRRET lpName) //[out] Returned display name
1004 *
1005 * FIXME
1006 * if the name is in the pidl the ret value should be a STRRET_OFFSET
1007 */
1008
1009 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
1010 DWORD dwFlags, LPSTRRET strRet)
1011 {
1012 if (!strRet)
1013 return E_INVALIDARG;
1014
1015 /* If it is a complex pidl, let the child handle it */
1016 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
1017 {
1018 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1019 }
1020 else if (pidl && !pidl->mkid.cb) /* empty pidl */
1021 {
1022 /* If it is an empty pidl return only the path of the folder */
1023 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1024 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1025 sPathTarget)
1026 {
1027 return SHSetStrRet(strRet, sPathTarget);
1028 }
1029 return E_INVALIDARG;
1030 }
1031
1032 int len = 0;
1033 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1034 if (!pszPath)
1035 return E_OUTOFMEMORY;
1036
1037 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1038 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1039 sPathTarget)
1040 {
1041 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
1042 PathAddBackslashW(pszPath);
1043 len = wcslen(pszPath);
1044 }
1045 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
1046 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
1047
1048 strRet->uType = STRRET_WSTR;
1049 strRet->pOleStr = pszPath;
1050
1051 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1052 return S_OK;
1053 }
1054
1055 /**************************************************************************
1056 * CFSFolder::SetNameOf
1057 * Changes the name of a file object or subfolder, possibly changing its item
1058 * identifier in the process.
1059 *
1060 * PARAMETERS
1061 * HWND hwndOwner, //[in ] Owner window for output
1062 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
1063 * LPCOLESTR lpszName, //[in ] the items new display name
1064 * DWORD dwFlags, //[in ] SHGNO formatting flags
1065 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
1066 */
1067 HRESULT WINAPI CFSFolder::SetNameOf(
1068 HWND hwndOwner,
1069 PCUITEMID_CHILD pidl,
1070 LPCOLESTR lpName,
1071 DWORD dwFlags,
1072 PITEMID_CHILD *pPidlOut)
1073 {
1074 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
1075 LPWSTR ptr;
1076 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
1077
1078 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
1079 debugstr_w (lpName), dwFlags, pPidlOut);
1080
1081 /* build source path */
1082 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
1083 ptr = PathAddBackslashW (szSrc);
1084 if (ptr)
1085 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
1086
1087 /* build destination path */
1088 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
1089 lstrcpynW(szDest, sPathTarget, MAX_PATH);
1090 ptr = PathAddBackslashW (szDest);
1091 if (ptr)
1092 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
1093 } else
1094 lstrcpynW(szDest, lpName, MAX_PATH);
1095
1096 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
1097 WCHAR *ext = PathFindExtensionW(szSrc);
1098 if(*ext != '\0') {
1099 INT len = wcslen(szDest);
1100 lstrcpynW(szDest + len, ext, MAX_PATH - len);
1101 }
1102 }
1103
1104 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
1105 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
1106 {
1107 /* src and destination is the same */
1108 HRESULT hr = S_OK;
1109 if (pPidlOut)
1110 hr = _ILCreateFromPathW(szDest, pPidlOut);
1111
1112 return hr;
1113 }
1114
1115
1116 if (MoveFileW (szSrc, szDest))
1117 {
1118 HRESULT hr = S_OK;
1119
1120 if (pPidlOut)
1121 hr = _ILCreateFromPathW(szDest, pPidlOut);
1122
1123 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
1124 SHCNF_PATHW, szSrc, szDest);
1125
1126 return hr;
1127 }
1128
1129 return E_FAIL;
1130 }
1131
1132 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
1133 {
1134 FIXME ("(%p)\n", this);
1135 return E_NOTIMPL;
1136 }
1137
1138 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1139 {
1140 FIXME ("(%p)\n", this);
1141 return E_NOTIMPL;
1142 }
1143
1144 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
1145 ULONG * pSort, ULONG * pDisplay)
1146 {
1147 TRACE ("(%p)\n", this);
1148
1149 if (pSort)
1150 *pSort = 0;
1151 if (pDisplay)
1152 *pDisplay = 0;
1153
1154 return S_OK;
1155 }
1156
1157 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
1158 DWORD * pcsFlags)
1159 {
1160 TRACE ("(%p)\n", this);
1161
1162 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
1163 return E_INVALIDARG;
1164
1165 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
1166
1167 return S_OK;
1168 }
1169
1170 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
1171 const SHCOLUMNID * pscid, VARIANT * pv)
1172 {
1173 FIXME ("(%p)\n", this);
1174
1175 return E_NOTIMPL;
1176 }
1177
1178 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
1179 UINT iColumn, SHELLDETAILS * psd)
1180 {
1181 HRESULT hr = E_FAIL;
1182
1183 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1184
1185 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1186 return E_INVALIDARG;
1187
1188 if (!pidl)
1189 {
1190 /* the header titles */
1191 psd->fmt = GenericSFHeader[iColumn].fmt;
1192 psd->cxChar = GenericSFHeader[iColumn].cxChar;
1193 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
1194 }
1195 else
1196 {
1197 hr = S_OK;
1198 psd->str.uType = STRRET_CSTR;
1199 /* the data from the pidl */
1200 switch (iColumn)
1201 {
1202 case 0: /* name */
1203 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1204 break;
1205 case 1: /* FIXME: comments */
1206 psd->str.cStr[0] = 0;
1207 break;
1208 case 2: /* type */
1209 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
1210 break;
1211 case 3: /* size */
1212 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
1213 break;
1214 case 4: /* date */
1215 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
1216 break;
1217 case 5: /* attributes */
1218 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
1219 break;
1220 }
1221 }
1222
1223 return hr;
1224 }
1225
1226 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
1227 SHCOLUMNID * pscid)
1228 {
1229 FIXME ("(%p)\n", this);
1230 return E_NOTIMPL;
1231 }
1232
1233 /************************************************************************
1234 * CFSFolder::GetClassID
1235 */
1236 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1237 {
1238 TRACE ("(%p)\n", this);
1239
1240 if (!lpClassId)
1241 return E_POINTER;
1242
1243 *lpClassId = *pclsid;
1244
1245 return S_OK;
1246 }
1247
1248 /************************************************************************
1249 * CFSFolder::Initialize
1250 *
1251 * NOTES
1252 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
1253 */
1254 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
1255 {
1256 WCHAR wszTemp[MAX_PATH];
1257
1258 TRACE ("(%p)->(%p)\n", this, pidl);
1259
1260 SHFree (pidlRoot); /* free the old pidl */
1261 pidlRoot = ILClone (pidl); /* set my pidl */
1262
1263 SHFree (sPathTarget);
1264 sPathTarget = NULL;
1265
1266 /* set my path */
1267 if (SHGetPathFromIDListW (pidl, wszTemp))
1268 {
1269 int len = wcslen(wszTemp);
1270 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1271 if (!sPathTarget)
1272 return E_OUTOFMEMORY;
1273 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1274 }
1275
1276 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
1277 return S_OK;
1278 }
1279
1280 /**************************************************************************
1281 * CFSFolder::GetCurFolder
1282 */
1283 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
1284 {
1285 TRACE ("(%p)->(%p)\n", this, pidl);
1286
1287 if (!pidl)
1288 return E_POINTER;
1289
1290 *pidl = ILClone(pidlRoot);
1291 return S_OK;
1292 }
1293
1294 /**************************************************************************
1295 * CFSFolder::InitializeEx
1296 *
1297 * FIXME: error handling
1298 */
1299 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1300 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1301 {
1302 WCHAR wszTemp[MAX_PATH];
1303
1304 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1305 if (ppfti)
1306 TRACE("--%p %s %s 0x%08x 0x%08x\n",
1307 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1308 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1309 ppfti->csidl);
1310
1311 pdump (pidlRootx);
1312 if (ppfti && ppfti->pidlTargetFolder)
1313 pdump(ppfti->pidlTargetFolder);
1314
1315 if (pidlRoot)
1316 __SHFreeAndNil(&pidlRoot); /* free the old */
1317 if (sPathTarget)
1318 __SHFreeAndNil(&sPathTarget);
1319
1320 /*
1321 * Root path and pidl
1322 */
1323 pidlRoot = ILClone(pidlRootx);
1324
1325 /*
1326 * the target folder is spezified in csidl OR pidlTargetFolder OR
1327 * szTargetParsingName
1328 */
1329 if (ppfti)
1330 {
1331 if (ppfti->csidl != -1)
1332 {
1333 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1334 ppfti->csidl & CSIDL_FLAG_CREATE)) {
1335 int len = wcslen(wszTemp);
1336 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1337 if (!sPathTarget)
1338 return E_OUTOFMEMORY;
1339 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1340 }
1341 }
1342 else if (ppfti->szTargetParsingName[0])
1343 {
1344 int len = wcslen(ppfti->szTargetParsingName);
1345 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1346 if (!sPathTarget)
1347 return E_OUTOFMEMORY;
1348 memcpy(sPathTarget, ppfti->szTargetParsingName,
1349 (len + 1) * sizeof(WCHAR));
1350 }
1351 else if (ppfti->pidlTargetFolder)
1352 {
1353 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1354 {
1355 int len = wcslen(wszTemp);
1356 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1357 if (!sPathTarget)
1358 return E_OUTOFMEMORY;
1359 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1360 }
1361 }
1362 }
1363
1364 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
1365 pdump(pidlRoot);
1366 return (sPathTarget) ? S_OK : E_FAIL;
1367 }
1368
1369 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1370 {
1371 FIXME("(%p)->(%p)\n", this, ppfti);
1372 ZeroMemory(ppfti, sizeof (*ppfti));
1373 return E_NOTIMPL;
1374 }
1375
1376 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1377 HKEY hKey;
1378 HRESULT hr;
1379
1380 TRACE("CFSFolder::_GetDropTarget entered\n");
1381
1382 if (_ILIsFolder (pidl))
1383 {
1384 CComPtr<IShellFolder> psfChild;
1385 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1386 if (FAILED_UNEXPECTEDLY(hr))
1387 return hr;
1388
1389 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1390 }
1391
1392 STRRET strFile;
1393 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1394 if (hr == S_OK)
1395 {
1396 WCHAR wszPath[MAX_PATH];
1397 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1398
1399 if (hr == S_OK)
1400 {
1401 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1402 if (pwszExt[0])
1403 {
1404 /* enumerate dynamic/static for a given file class */
1405 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1406 {
1407 /* load dynamic extensions from file extension key, for example .jpg */
1408 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1409 RegCloseKey(hKey);
1410 }
1411
1412 WCHAR wszTemp[40];
1413 DWORD dwSize = sizeof(wszTemp);
1414 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1415 {
1416 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1417 {
1418 /* load dynamic extensions from progid key, for example jpegfile */
1419 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1420 RegCloseKey(hKey);
1421 }
1422 }
1423 }
1424 }
1425 }
1426 else
1427 ERR("GetDisplayNameOf failed: %x\n", hr);
1428
1429 return hr;
1430 }
1431
1432 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1433 {
1434 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1435
1436 WCHAR wszName[MAX_PATH];
1437 DWORD dwSize = sizeof(wszName);
1438 HRESULT hr;
1439 LRESULT res;
1440
1441 res = RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize);
1442 if (res != ERROR_SUCCESS)
1443 return S_FALSE;
1444
1445 CLSID clsid;
1446 hr = CLSIDFromString(wszName, &clsid);
1447 if (FAILED_UNEXPECTEDLY(hr))
1448 return hr;
1449
1450 if (m_bGroupPolicyActive)
1451 {
1452 res = RegGetValueW(HKEY_LOCAL_MACHINE,
1453 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1454 wszName,
1455 RRF_RT_REG_SZ,
1456 NULL,
1457 NULL,
1458 NULL);
1459 if (res != ERROR_SUCCESS)
1460 {
1461 ERR("DropHandler extension %S not approved\n", wszName);
1462 return E_FAIL;
1463 }
1464 }
1465
1466 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1467 if (FAILED_UNEXPECTEDLY(hr))
1468 return hr;
1469
1470 return S_OK;
1471 }
1472
1473 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1474 {
1475 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1476 HRESULT hr;
1477
1478 CComPtr<IPersistFile> pp;
1479 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1480 if (hr != S_OK)
1481 {
1482 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1483 }
1484 pp->Load(pwcsname, 0);
1485
1486 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1487 if (hr != S_OK)
1488 {
1489 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1490 return hr;
1491 }
1492 return hr;
1493 }
1494
1495 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1496 {
1497 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1498 return S_OK;
1499
1500 /* no data object means no selection */
1501 if (!pdtobj)
1502 {
1503 if (uMsg == DFM_INVOKECOMMAND && wParam == 0)
1504 {
1505 PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(pidlRoot));
1506 LPITEMIDLIST pidlParent = ILClone(pidlRoot);
1507 ILRemoveLastID(pidlParent);
1508 HRESULT hr = SH_ShowPropertiesDialog(sPathTarget, pidlParent, &pidlChild);
1509 if (FAILED(hr))
1510 ERR("SH_ShowPropertiesDialog failed\n");
1511 ILFree(pidlChild);
1512 ILFree(pidlParent);
1513 }
1514 else if (uMsg == DFM_MERGECONTEXTMENU)
1515 {
1516 QCMINFO *pqcminfo = (QCMINFO *)lParam;
1517 HMENU hpopup = CreatePopupMenu();
1518 _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1519 Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu++, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1520 DestroyMenu(hpopup);
1521 }
1522
1523 return S_OK;
1524 }
1525
1526 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1527 return S_OK;
1528
1529 PIDLIST_ABSOLUTE pidlFolder;
1530 PUITEMID_CHILD *apidl;
1531 UINT cidl;
1532 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
1533 if (FAILED_UNEXPECTEDLY(hr))
1534 return hr;
1535
1536 if (cidl > 1)
1537 ERR("SHMultiFileProperties is not yet implemented\n");
1538
1539 STRRET strFile;
1540 hr = GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strFile);
1541 if (SUCCEEDED(hr))
1542 {
1543 hr = SH_ShowPropertiesDialog(strFile.pOleStr, pidlFolder, apidl);
1544 if (FAILED(hr))
1545 ERR("SH_ShowPropertiesDialog failed\n");
1546 }
1547 else
1548 {
1549 ERR("Failed to get display name\n");
1550 }
1551
1552 SHFree(pidlFolder);
1553 _ILFreeaPidl(apidl, cidl);
1554
1555 return hr;
1556 }