[SHELL32]
[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 /*
28 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is
29 returned by Next. When the enumerator is created, it can do numerous additional operations
30 including formatting a drive, reconnecting a network share drive, and requesting a disk
31 be inserted in a removable drive.
32 */
33
34 /***********************************************************************
35 * IShellFolder implementation
36 */
37
38 class CFileSysEnum :
39 public CEnumIDListBase
40 {
41 private:
42 public:
43 CFileSysEnum();
44 ~CFileSysEnum();
45 HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags);
46
47 BEGIN_COM_MAP(CFileSysEnum)
48 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
49 END_COM_MAP()
50 };
51
52 CFileSysEnum::CFileSysEnum()
53 {
54 }
55
56 CFileSysEnum::~CFileSysEnum()
57 {
58 }
59
60 HRESULT WINAPI CFileSysEnum::Initialize(LPWSTR sPathTarget, DWORD dwFlags)
61 {
62 return CreateFolderEnumList(sPathTarget, dwFlags);
63 }
64
65 CFSFolder::CFSFolder()
66 {
67 pclsid = (CLSID *)&CLSID_ShellFSFolder;
68 sPathTarget = NULL;
69 pidlRoot = NULL;
70 m_bGroupPolicyActive = 0;
71 }
72
73 CFSFolder::~CFSFolder()
74 {
75 TRACE("-- destroying IShellFolder(%p)\n", this);
76
77 SHFree(pidlRoot);
78 SHFree(sPathTarget);
79 }
80
81
82 static const shvheader GenericSFHeader[] = {
83 {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
84 {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
85 {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
86 {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12},
87 {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}
88 };
89
90 #define GENERICSHELLVIEWCOLUMNS 5
91
92 /**************************************************************************
93 * SHELL32_CreatePidlFromBindCtx [internal]
94 *
95 * If the caller bound File System Bind Data, assume it is the
96 * find data for the path.
97 * This allows binding of paths that don't exist.
98 */
99 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
100 {
101 IFileSystemBindData *fsbd = NULL;
102 LPITEMIDLIST pidl = NULL;
103 IUnknown *param = NULL;
104 WIN32_FIND_DATAW wfd;
105 HRESULT r;
106
107 TRACE("%p %s\n", pbc, debugstr_w(path));
108
109 if (!pbc)
110 return NULL;
111
112 /* see if the caller bound File System Bind Data */
113 r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, &param);
114 if (FAILED(r))
115 return NULL;
116
117 r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd));
118 if (SUCCEEDED(r))
119 {
120 r = fsbd->GetFindData(&wfd);
121 if (SUCCEEDED(r))
122 {
123 lstrcpynW(&wfd.cFileName[0], path, MAX_PATH);
124 pidl = _ILCreateFromFindDataW(&wfd);
125 }
126 fsbd->Release();
127 }
128
129 return pidl;
130 }
131
132 /**************************************************************************
133 * CFSFolder::ParseDisplayName {SHELL32}
134 *
135 * Parse a display name.
136 *
137 * PARAMS
138 * hwndOwner [in] Parent window for any message's
139 * pbc [in] optional FileSystemBindData context
140 * lpszDisplayName [in] Unicode displayname.
141 * pchEaten [out] (unicode) characters processed
142 * ppidl [out] complex pidl to item
143 * pdwAttributes [out] items attributes
144 *
145 * NOTES
146 * Every folder tries to parse only its own (the leftmost) pidl and creates a
147 * subfolder to evaluate the remaining parts.
148 * Now we can parse into namespaces implemented by shell extensions
149 *
150 * Behaviour on win98: lpszDisplayName=NULL -> crash
151 * lpszDisplayName="" -> returns mycoputer-pidl
152 *
153 * FIXME
154 * pdwAttributes is not set
155 * pchEaten is not set like in windows
156 */
157 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
158 LPBC pbc,
159 LPOLESTR lpszDisplayName,
160 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
161 DWORD *pdwAttributes)
162 {
163 HRESULT hr = E_INVALIDARG;
164 LPCWSTR szNext = NULL;
165 WCHAR szElement[MAX_PATH];
166 WCHAR szPath[MAX_PATH];
167 LPITEMIDLIST pidlTemp = NULL;
168 DWORD len;
169
170 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
171 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
172 pchEaten, ppidl, pdwAttributes);
173
174 if (!ppidl)
175 return E_INVALIDARG;
176
177 if (!lpszDisplayName)
178 {
179 *ppidl = NULL;
180 return E_INVALIDARG;
181 }
182
183 *ppidl = NULL;
184
185 if (pchEaten)
186 *pchEaten = 0; /* strange but like the original */
187
188 if (*lpszDisplayName)
189 {
190 /* get the next element */
191 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
192
193 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement);
194 if (pidlTemp != NULL)
195 {
196 /* We are creating an id list without ensuring that the items exist.
197 If we have a remaining path, this must be a folder.
198 We have to do it now because it is set as a file by default */
199 if (szNext)
200 {
201 pidlTemp->mkid.abID[0] = PT_FOLDER;
202 }
203 hr = S_OK;
204 }
205 else
206 {
207 /* build the full pathname to the element */
208 lstrcpynW(szPath, sPathTarget, MAX_PATH - 1);
209 PathAddBackslashW(szPath);
210 len = wcslen(szPath);
211 lstrcpynW(szPath + len, szElement, MAX_PATH - len);
212
213 /* get the pidl */
214 hr = _ILCreateFromPathW(szPath, &pidlTemp);
215 }
216
217 if (SUCCEEDED(hr))
218 {
219 if (szNext && *szNext)
220 {
221 /* try to analyse the next element */
222 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
223 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
224 }
225 else
226 {
227 /* it's the last element */
228 if (pdwAttributes && *pdwAttributes)
229 hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes);
230 }
231 }
232 }
233
234 if (SUCCEEDED(hr))
235 *ppidl = pidlTemp;
236 else
237 *ppidl = NULL;
238
239 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
240
241 return hr;
242 }
243
244 /**************************************************************************
245 * CFSFolder::EnumObjects
246 * PARAMETERS
247 * HWND hwndOwner, //[in ] Parent Window
248 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
249 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
250 */
251 HRESULT WINAPI CFSFolder::EnumObjects(
252 HWND hwndOwner,
253 DWORD dwFlags,
254 LPENUMIDLIST *ppEnumIDList)
255 {
256 return ShellObjectCreatorInit<CFileSysEnum>(sPathTarget, dwFlags, IID_IEnumIDList, ppEnumIDList);
257 }
258
259 /**************************************************************************
260 * CFSFolder::BindToObject
261 * PARAMETERS
262 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
263 * LPBC pbc, //[in ] optional FileSystemBindData context
264 * REFIID riid, //[in ] Initial Interface
265 * LPVOID* ppvObject //[out] Interface*
266 */
267 HRESULT WINAPI CFSFolder::BindToObject(
268 PCUIDLIST_RELATIVE pidl,
269 LPBC pbc,
270 REFIID riid,
271 LPVOID * ppvOut)
272 {
273 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
274 shdebugstr_guid(&riid), ppvOut);
275
276 return SHELL32_BindToFS(pidlRoot, sPathTarget, pidl, riid, ppvOut);
277 }
278
279 /**************************************************************************
280 * CFSFolder::BindToStorage
281 * PARAMETERS
282 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
283 * LPBC pbc, //[in ] reserved
284 * REFIID riid, //[in ] Initial storage interface
285 * LPVOID* ppvObject //[out] Interface* returned
286 */
287 HRESULT WINAPI CFSFolder::BindToStorage(
288 PCUIDLIST_RELATIVE pidl,
289 LPBC pbcReserved,
290 REFIID riid,
291 LPVOID *ppvOut)
292 {
293 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
294 shdebugstr_guid (&riid), ppvOut);
295
296 *ppvOut = NULL;
297 return E_NOTIMPL;
298 }
299
300 /**************************************************************************
301 * CFSFolder::CompareIDs
302 */
303
304 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
305 PCUIDLIST_RELATIVE pidl1,
306 PCUIDLIST_RELATIVE pidl2)
307 {
308 LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
309 LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
310 FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
311 FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
312 BOOL bIsFolder1 = _ILIsFolder(pidl1);
313 BOOL bIsFolder2 = _ILIsFolder(pidl2);
314 LPWSTR pExtension1, pExtension2;
315
316 if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
317 return E_INVALIDARG;
318
319 /* When sorting between a File and a Folder, the Folder gets sorted first */
320 if (bIsFolder1 != bIsFolder2)
321 {
322 return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
323 }
324
325 int result;
326 switch (LOWORD(lParam))
327 {
328 case 0: /* Name */
329 result = wcsicmp(pDataW1->wszName, pDataW2->wszName);
330 break;
331 case 2: /* Type */
332 pExtension1 = PathFindExtensionW(pDataW1->wszName);
333 pExtension2 = PathFindExtensionW(pDataW2->wszName);
334 result = wcsicmp(pExtension1, pExtension2);
335 break;
336 case 1: /* Size */
337 result = pData1->u.file.dwFileSize - pData2->u.file.dwFileSize;
338 break;
339 case 3: /* Modified */
340 result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
341 if (result == 0)
342 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
343 break;
344 case 4: /* Attributes */
345 return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
346 }
347
348 if (result == 0)
349 return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
350
351 return MAKE_COMPARE_HRESULT(result);
352 }
353
354 /**************************************************************************
355 * CFSFolder::CreateViewObject
356 */
357 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
358 REFIID riid, LPVOID * ppvOut)
359 {
360 CComPtr<IShellView> pShellView;
361 HRESULT hr = E_INVALIDARG;
362
363 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
364 ppvOut);
365
366 if (ppvOut)
367 {
368 *ppvOut = NULL;
369
370 if (IsEqualIID (riid, IID_IDropTarget))
371 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
372 else if (IsEqualIID (riid, IID_IContextMenu))
373 {
374 FIXME ("IContextMenu not implemented\n");
375 hr = E_NOTIMPL;
376 }
377 else if (IsEqualIID (riid, IID_IShellView))
378 {
379 hr = CDefView_Constructor(this, riid, ppvOut);
380 }
381 }
382 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
383 return hr;
384 }
385
386 /**************************************************************************
387 * CFSFolder::GetAttributesOf
388 *
389 * PARAMETERS
390 * UINT cidl, //[in ] num elements in pidl array
391 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
392 * ULONG* rgfInOut) //[out] result array
393 *
394 */
395 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
396 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
397 {
398 HRESULT hr = S_OK;
399
400 if (!rgfInOut)
401 return E_INVALIDARG;
402 if (cidl && !apidl)
403 return E_INVALIDARG;
404
405 if (*rgfInOut == 0)
406 *rgfInOut = ~0;
407
408 if(cidl == 0)
409 {
410 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot);
411
412 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
413 {
414 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
415 }
416 else if (_ILIsDrive(rpidl))
417 {
418 IShellFolder *psfParent = NULL;
419 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
420 if(SUCCEEDED(hr))
421 {
422 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
423 psfParent->Release();
424 }
425 }
426 else
427 {
428 ERR("Got and unknown pidl!\n");
429 }
430 }
431 else
432 {
433 while (cidl > 0 && *apidl)
434 {
435 pdump(*apidl);
436 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
437 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
438 else
439 ERR("Got an unknown type of pidl!!!\n");
440 apidl++;
441 cidl--;
442 }
443 }
444 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
445 *rgfInOut &= ~SFGAO_VALIDATE;
446
447 TRACE("-- result=0x%08x\n", *rgfInOut);
448
449 return hr;
450 }
451
452 /**************************************************************************
453 * CFSFolder::GetUIObjectOf
454 *
455 * PARAMETERS
456 * HWND hwndOwner, //[in ] Parent window for any output
457 * UINT cidl, //[in ] array size
458 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
459 * REFIID riid, //[in ] Requested Interface
460 * UINT* prgfInOut, //[ ] reserved
461 * LPVOID* ppvObject) //[out] Resulting Interface
462 *
463 * NOTES
464 * This function gets asked to return "view objects" for one or more (multiple
465 * select) items:
466 * The viewobject typically is an COM object with one of the following
467 * interfaces:
468 * IExtractIcon,IDataObject,IContextMenu
469 * In order to support icon positions in the default Listview your DataObject
470 * must implement the SetData method (in addition to GetData :) - the shell
471 * passes a barely documented "Icon positions" structure to SetData when the
472 * drag starts, and GetData's it if the drop is in another explorer window that
473 * needs the positions.
474 */
475 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
476 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
477 REFIID riid, UINT * prgfInOut,
478 LPVOID * ppvOut)
479 {
480 LPVOID pObj = NULL;
481 HRESULT hr = E_INVALIDARG;
482
483 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
484 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
485
486 if (ppvOut)
487 {
488 *ppvOut = NULL;
489
490 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
491 {
492 HKEY hKeys[16];
493 UINT cKeys = 0;
494 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
495
496 DEFCONTEXTMENU dcm;
497 dcm.hwnd = hwndOwner;
498 dcm.pcmcb = this;
499 dcm.pidlFolder = pidlRoot;
500 dcm.psf = this;
501 dcm.cidl = cidl;
502 dcm.apidl = apidl;
503 dcm.cKeys = cKeys;
504 dcm.aKeys = hKeys;
505 dcm.punkAssociationInfo = NULL;
506 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
507 }
508 else if (IsEqualIID (riid, IID_IDataObject))
509 {
510 if (cidl >= 1)
511 {
512 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
513 }
514 else
515 {
516 hr = IDataObject_Constructor (hwndOwner, pidlRoot, (LPCITEMIDLIST*)&pidlRoot, 1, (IDataObject **)&pObj);
517 }
518 }
519 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
520 {
521 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
522 }
523 else if (IsEqualIID (riid, IID_IDropTarget))
524 {
525 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
526 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
527 {
528 IDropTarget * pDt = NULL;
529 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
530 pObj = pDt;
531 }
532 }
533 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
534 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
535 {
536 hr = IShellLink_ConstructFromFile(this, apidl[0], riid, &pObj);
537 }
538 else
539 hr = E_NOINTERFACE;
540
541 if (SUCCEEDED(hr) && !pObj)
542 hr = E_OUTOFMEMORY;
543
544 *ppvOut = pObj;
545 }
546 TRACE("(%p)->hr=0x%08x\n", this, hr);
547 return hr;
548 }
549
550 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
551 static const WCHAR HideFileExtW[] = L"HideFileExt";
552 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
553
554 /******************************************************************************
555 * SHELL_FS_HideExtension [Internal]
556 *
557 * Query the registry if the filename extension of a given path should be
558 * hidden.
559 *
560 * PARAMS
561 * szPath [I] Relative or absolute path of a file
562 *
563 * RETURNS
564 * TRUE, if the filename's extension should be hidden
565 * FALSE, otherwise.
566 */
567 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
568 {
569 HKEY hKey;
570 DWORD dwData;
571 DWORD dwDataSize = sizeof (DWORD);
572 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
573
574 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
575 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
576 doHide = dwData;
577 RegCloseKey (hKey);
578 }
579
580 if (!doHide) {
581 LPWSTR ext = PathFindExtensionW(szPath);
582
583 if (*ext != '\0') {
584 WCHAR classname[MAX_PATH];
585 LONG classlen = sizeof(classname);
586
587 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
588 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
589 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
590 doHide = TRUE;
591 RegCloseKey(hKey);
592 }
593 }
594 }
595 return doHide;
596 }
597
598 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
599 {
600 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
601 if (!(dwFlags & SHGDN_FORPARSING) &&
602 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
603 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
604 PathRemoveExtensionW(szPath);
605 }
606 }
607
608 /**************************************************************************
609 * CFSFolder::GetDisplayNameOf
610 * Retrieves the display name for the specified file object or subfolder
611 *
612 * PARAMETERS
613 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
614 * DWORD dwFlags, //[in ] SHGNO formatting flags
615 * LPSTRRET lpName) //[out] Returned display name
616 *
617 * FIXME
618 * if the name is in the pidl the ret value should be a STRRET_OFFSET
619 */
620
621 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
622 DWORD dwFlags, LPSTRRET strRet)
623 {
624 if (!pidl || !strRet)
625 return E_INVALIDARG;
626
627 /* If it is a complex pidl, let the child handle it */
628 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
629 {
630 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
631 }
632 else if (!pidl->mkid.cb) /* empty pidl */
633 {
634 /* If it is an empty pidl return only the path of the folder */
635 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
636 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
637 sPathTarget)
638 {
639 return SHSetStrRet(strRet, sPathTarget);
640 }
641 return E_INVALIDARG;
642 }
643
644 int len = 0;
645 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
646 if (!pszPath)
647 return E_OUTOFMEMORY;
648
649 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
650 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
651 sPathTarget)
652 {
653 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
654 PathAddBackslashW(pszPath);
655 len = wcslen(pszPath);
656 }
657 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
658 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
659
660 strRet->uType = STRRET_WSTR;
661 strRet->pOleStr = pszPath;
662
663 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
664 return S_OK;
665 }
666
667 /**************************************************************************
668 * CFSFolder::SetNameOf
669 * Changes the name of a file object or subfolder, possibly changing its item
670 * identifier in the process.
671 *
672 * PARAMETERS
673 * HWND hwndOwner, //[in ] Owner window for output
674 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
675 * LPCOLESTR lpszName, //[in ] the items new display name
676 * DWORD dwFlags, //[in ] SHGNO formatting flags
677 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
678 */
679 HRESULT WINAPI CFSFolder::SetNameOf(
680 HWND hwndOwner,
681 PCUITEMID_CHILD pidl,
682 LPCOLESTR lpName,
683 DWORD dwFlags,
684 PITEMID_CHILD *pPidlOut)
685 {
686 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
687 LPWSTR ptr;
688 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
689
690 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
691 debugstr_w (lpName), dwFlags, pPidlOut);
692
693 /* build source path */
694 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
695 ptr = PathAddBackslashW (szSrc);
696 if (ptr)
697 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
698
699 /* build destination path */
700 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
701 lstrcpynW(szDest, sPathTarget, MAX_PATH);
702 ptr = PathAddBackslashW (szDest);
703 if (ptr)
704 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
705 } else
706 lstrcpynW(szDest, lpName, MAX_PATH);
707
708 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
709 WCHAR *ext = PathFindExtensionW(szSrc);
710 if(*ext != '\0') {
711 INT len = wcslen(szDest);
712 lstrcpynW(szDest + len, ext, MAX_PATH - len);
713 }
714 }
715
716 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
717 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
718 {
719 /* src and destination is the same */
720 HRESULT hr = S_OK;
721 if (pPidlOut)
722 hr = _ILCreateFromPathW(szDest, pPidlOut);
723
724 return hr;
725 }
726
727
728 if (MoveFileW (szSrc, szDest))
729 {
730 HRESULT hr = S_OK;
731
732 if (pPidlOut)
733 hr = _ILCreateFromPathW(szDest, pPidlOut);
734
735 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
736 SHCNF_PATHW, szSrc, szDest);
737
738 return hr;
739 }
740
741 return E_FAIL;
742 }
743
744 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
745 {
746 FIXME ("(%p)\n", this);
747 return E_NOTIMPL;
748 }
749
750 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
751 {
752 FIXME ("(%p)\n", this);
753 return E_NOTIMPL;
754 }
755
756 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
757 ULONG * pSort, ULONG * pDisplay)
758 {
759 TRACE ("(%p)\n", this);
760
761 if (pSort)
762 *pSort = 0;
763 if (pDisplay)
764 *pDisplay = 0;
765
766 return S_OK;
767 }
768
769 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
770 DWORD * pcsFlags)
771 {
772 TRACE ("(%p)\n", this);
773
774 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
775 return E_INVALIDARG;
776
777 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
778
779 return S_OK;
780 }
781
782 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
783 const SHCOLUMNID * pscid, VARIANT * pv)
784 {
785 FIXME ("(%p)\n", this);
786
787 return E_NOTIMPL;
788 }
789
790 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
791 UINT iColumn, SHELLDETAILS * psd)
792 {
793 HRESULT hr = E_FAIL;
794
795 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
796
797 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
798 return E_INVALIDARG;
799
800 if (!pidl)
801 {
802 /* the header titles */
803 psd->fmt = GenericSFHeader[iColumn].fmt;
804 psd->cxChar = GenericSFHeader[iColumn].cxChar;
805 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
806 }
807 else
808 {
809 hr = S_OK;
810 psd->str.uType = STRRET_CSTR;
811 /* the data from the pidl */
812 switch (iColumn)
813 {
814 case 0: /* name */
815 hr = GetDisplayNameOf (pidl,
816 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
817 break;
818 case 1: /* size */
819 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
820 break;
821 case 2: /* type */
822 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
823 break;
824 case 3: /* date */
825 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
826 break;
827 case 4: /* attributes */
828 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
829 break;
830 }
831 }
832
833 return hr;
834 }
835
836 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
837 SHCOLUMNID * pscid)
838 {
839 FIXME ("(%p)\n", this);
840 return E_NOTIMPL;
841 }
842
843 /************************************************************************
844 * CFSFolder::GetClassID
845 */
846 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
847 {
848 TRACE ("(%p)\n", this);
849
850 if (!lpClassId)
851 return E_POINTER;
852
853 *lpClassId = *pclsid;
854
855 return S_OK;
856 }
857
858 /************************************************************************
859 * CFSFolder::Initialize
860 *
861 * NOTES
862 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
863 */
864 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
865 {
866 WCHAR wszTemp[MAX_PATH];
867
868 TRACE ("(%p)->(%p)\n", this, pidl);
869
870 SHFree (pidlRoot); /* free the old pidl */
871 pidlRoot = ILClone (pidl); /* set my pidl */
872
873 SHFree (sPathTarget);
874 sPathTarget = NULL;
875
876 /* set my path */
877 if (SHGetPathFromIDListW (pidl, wszTemp))
878 {
879 int len = wcslen(wszTemp);
880 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
881 if (!sPathTarget)
882 return E_OUTOFMEMORY;
883 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
884 }
885
886 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
887 return S_OK;
888 }
889
890 /**************************************************************************
891 * CFSFolder::GetCurFolder
892 */
893 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
894 {
895 TRACE ("(%p)->(%p)\n", this, pidl);
896
897 if (!pidl)
898 return E_POINTER;
899
900 *pidl = ILClone(pidlRoot);
901 return S_OK;
902 }
903
904 /**************************************************************************
905 * CFSFolder::InitializeEx
906 *
907 * FIXME: error handling
908 */
909 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
910 const PERSIST_FOLDER_TARGET_INFO * ppfti)
911 {
912 WCHAR wszTemp[MAX_PATH];
913
914 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
915 if (ppfti)
916 TRACE("--%p %s %s 0x%08x 0x%08x\n",
917 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
918 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
919 ppfti->csidl);
920
921 pdump (pidlRootx);
922 if (ppfti && ppfti->pidlTargetFolder)
923 pdump(ppfti->pidlTargetFolder);
924
925 if (pidlRoot)
926 __SHFreeAndNil(&pidlRoot); /* free the old */
927 if (sPathTarget)
928 __SHFreeAndNil(&sPathTarget);
929
930 /*
931 * Root path and pidl
932 */
933 pidlRoot = ILClone(pidlRootx);
934
935 /*
936 * the target folder is spezified in csidl OR pidlTargetFolder OR
937 * szTargetParsingName
938 */
939 if (ppfti)
940 {
941 if (ppfti->csidl != -1)
942 {
943 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
944 ppfti->csidl & CSIDL_FLAG_CREATE)) {
945 int len = wcslen(wszTemp);
946 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
947 if (!sPathTarget)
948 return E_OUTOFMEMORY;
949 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
950 }
951 }
952 else if (ppfti->szTargetParsingName[0])
953 {
954 int len = wcslen(ppfti->szTargetParsingName);
955 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
956 if (!sPathTarget)
957 return E_OUTOFMEMORY;
958 memcpy(sPathTarget, ppfti->szTargetParsingName,
959 (len + 1) * sizeof(WCHAR));
960 }
961 else if (ppfti->pidlTargetFolder)
962 {
963 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
964 {
965 int len = wcslen(wszTemp);
966 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
967 if (!sPathTarget)
968 return E_OUTOFMEMORY;
969 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
970 }
971 }
972 }
973
974 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
975 pdump(pidlRoot);
976 return (sPathTarget) ? S_OK : E_FAIL;
977 }
978
979 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
980 {
981 FIXME("(%p)->(%p)\n", this, ppfti);
982 ZeroMemory(ppfti, sizeof (*ppfti));
983 return E_NOTIMPL;
984 }
985
986 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
987 HKEY hKey;
988 HRESULT hr;
989
990 TRACE("CFSFolder::_GetDropTarget entered\n");
991
992 if (_ILIsFolder (pidl))
993 {
994 CComPtr<IShellFolder> psfChild;
995 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
996 if (FAILED_UNEXPECTEDLY(hr))
997 return hr;
998
999 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1000 }
1001
1002 STRRET strFile;
1003 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1004 if (hr == S_OK)
1005 {
1006 WCHAR wszPath[MAX_PATH];
1007 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1008
1009 if (hr == S_OK)
1010 {
1011 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1012 if (pwszExt[0])
1013 {
1014 /* enumerate dynamic/static for a given file class */
1015 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1016 {
1017 /* load dynamic extensions from file extension key, for example .jpg */
1018 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1019 RegCloseKey(hKey);
1020 }
1021
1022 WCHAR wszTemp[40];
1023 DWORD dwSize = sizeof(wszTemp);
1024 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1025 {
1026 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1027 {
1028 /* load dynamic extensions from progid key, for example jpegfile */
1029 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1030 RegCloseKey(hKey);
1031 }
1032 }
1033 }
1034 }
1035 }
1036 else
1037 ERR("GetDisplayNameOf failed: %x\n", hr);
1038
1039 return hr;
1040 }
1041
1042 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1043 {
1044 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1045
1046 WCHAR wszName[MAX_PATH], *pwszClsid;
1047 DWORD dwSize = sizeof(wszName);
1048 HRESULT hr;
1049
1050 if (RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize) == ERROR_SUCCESS)
1051 {
1052 CLSID clsid;
1053 hr = CLSIDFromString(wszName, &clsid);
1054 if (hr == S_OK)
1055 pwszClsid = wszName;
1056
1057 if (m_bGroupPolicyActive)
1058 {
1059 if (RegGetValueW(HKEY_LOCAL_MACHINE,
1060 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1061 pwszClsid,
1062 RRF_RT_REG_SZ,
1063 NULL,
1064 NULL,
1065 NULL) == ERROR_SUCCESS)
1066 {
1067 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1068 }
1069 }
1070 else
1071 {
1072 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1073 }
1074 }
1075 else
1076 return E_FAIL;
1077 return hr;
1078 }
1079
1080 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1081 {
1082 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1083 HRESULT hr;
1084
1085 CComPtr<IPersistFile> pp;
1086 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1087 if (hr != S_OK)
1088 {
1089 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1090 }
1091 pp->Load(pwcsname, 0);
1092
1093 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1094 if (hr != S_OK)
1095 {
1096 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1097 return hr;
1098 }
1099 return hr;
1100 }
1101
1102 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1103 {
1104 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1105 return S_OK;
1106
1107 PIDLIST_ABSOLUTE pidlFolder;
1108 PUITEMID_CHILD *apidl;
1109 UINT cidl;
1110 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
1111 if (FAILED_UNEXPECTEDLY(hr))
1112 return hr;
1113
1114 if (cidl > 1)
1115 ERR("SHMultiFileProperties is not yet implemented\n");
1116
1117 STRRET strFile;
1118 hr = GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strFile);
1119 if (SUCCEEDED(hr))
1120 {
1121 hr = SH_ShowPropertiesDialog(strFile.pOleStr, pidlFolder, apidl);
1122 if (FAILED(hr))
1123 ERR("SH_ShowPropertiesDialog failed\n");
1124 }
1125 else
1126 {
1127 ERR("Failed to get display name\n");
1128 }
1129
1130 SHFree(pidlFolder);
1131 _ILFreeaPidl(apidl, cidl);
1132
1133 return hr;
1134 }