[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 IContextMenu * pCm = NULL;
493 HKEY hKeys[16];
494 UINT cKeys = 0;
495 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
496 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), NULL, cKeys, hKeys, &pCm);
497 pObj = pCm;
498 }
499 else if (IsEqualIID (riid, IID_IDataObject))
500 {
501 if (cidl >= 1)
502 {
503 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
504 }
505 else
506 {
507 hr = IDataObject_Constructor (hwndOwner, pidlRoot, (LPCITEMIDLIST*)&pidlRoot, 1, (IDataObject **)&pObj);
508 }
509 }
510 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
511 {
512 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
513 }
514 else if (IsEqualIID (riid, IID_IDropTarget))
515 {
516 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
517 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
518 {
519 IDropTarget * pDt = NULL;
520 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
521 pObj = pDt;
522 }
523 }
524 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
525 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
526 {
527 hr = IShellLink_ConstructFromFile(this, apidl[0], riid, &pObj);
528 }
529 else
530 hr = E_NOINTERFACE;
531
532 if (SUCCEEDED(hr) && !pObj)
533 hr = E_OUTOFMEMORY;
534
535 *ppvOut = pObj;
536 }
537 TRACE("(%p)->hr=0x%08x\n", this, hr);
538 return hr;
539 }
540
541 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
542 static const WCHAR HideFileExtW[] = L"HideFileExt";
543 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
544
545 /******************************************************************************
546 * SHELL_FS_HideExtension [Internal]
547 *
548 * Query the registry if the filename extension of a given path should be
549 * hidden.
550 *
551 * PARAMS
552 * szPath [I] Relative or absolute path of a file
553 *
554 * RETURNS
555 * TRUE, if the filename's extension should be hidden
556 * FALSE, otherwise.
557 */
558 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
559 {
560 HKEY hKey;
561 DWORD dwData;
562 DWORD dwDataSize = sizeof (DWORD);
563 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
564
565 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
566 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
567 doHide = dwData;
568 RegCloseKey (hKey);
569 }
570
571 if (!doHide) {
572 LPWSTR ext = PathFindExtensionW(szPath);
573
574 if (*ext != '\0') {
575 WCHAR classname[MAX_PATH];
576 LONG classlen = sizeof(classname);
577
578 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
579 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
580 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
581 doHide = TRUE;
582 RegCloseKey(hKey);
583 }
584 }
585 }
586 return doHide;
587 }
588
589 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
590 {
591 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
592 if (!(dwFlags & SHGDN_FORPARSING) &&
593 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
594 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
595 PathRemoveExtensionW(szPath);
596 }
597 }
598
599 /**************************************************************************
600 * CFSFolder::GetDisplayNameOf
601 * Retrieves the display name for the specified file object or subfolder
602 *
603 * PARAMETERS
604 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
605 * DWORD dwFlags, //[in ] SHGNO formatting flags
606 * LPSTRRET lpName) //[out] Returned display name
607 *
608 * FIXME
609 * if the name is in the pidl the ret value should be a STRRET_OFFSET
610 */
611
612 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
613 DWORD dwFlags, LPSTRRET strRet)
614 {
615 if (!pidl || !strRet)
616 return E_INVALIDARG;
617
618 /* If it is a complex pidl, let the child handle it */
619 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
620 {
621 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
622 }
623 else if (!pidl->mkid.cb) /* empty pidl */
624 {
625 /* If it is an empty pidl return only the path of the folder */
626 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
627 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
628 sPathTarget)
629 {
630 return SHSetStrRet(strRet, sPathTarget);
631 }
632 return E_INVALIDARG;
633 }
634
635 int len = 0;
636 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
637 if (!pszPath)
638 return E_OUTOFMEMORY;
639
640 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
641 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
642 sPathTarget)
643 {
644 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
645 PathAddBackslashW(pszPath);
646 len = wcslen(pszPath);
647 }
648 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
649 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
650
651 strRet->uType = STRRET_WSTR;
652 strRet->pOleStr = pszPath;
653
654 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
655 return S_OK;
656 }
657
658 /**************************************************************************
659 * CFSFolder::SetNameOf
660 * Changes the name of a file object or subfolder, possibly changing its item
661 * identifier in the process.
662 *
663 * PARAMETERS
664 * HWND hwndOwner, //[in ] Owner window for output
665 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
666 * LPCOLESTR lpszName, //[in ] the items new display name
667 * DWORD dwFlags, //[in ] SHGNO formatting flags
668 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
669 */
670 HRESULT WINAPI CFSFolder::SetNameOf(
671 HWND hwndOwner,
672 PCUITEMID_CHILD pidl,
673 LPCOLESTR lpName,
674 DWORD dwFlags,
675 PITEMID_CHILD *pPidlOut)
676 {
677 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
678 LPWSTR ptr;
679 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
680
681 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
682 debugstr_w (lpName), dwFlags, pPidlOut);
683
684 /* build source path */
685 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
686 ptr = PathAddBackslashW (szSrc);
687 if (ptr)
688 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
689
690 /* build destination path */
691 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
692 lstrcpynW(szDest, sPathTarget, MAX_PATH);
693 ptr = PathAddBackslashW (szDest);
694 if (ptr)
695 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
696 } else
697 lstrcpynW(szDest, lpName, MAX_PATH);
698
699 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
700 WCHAR *ext = PathFindExtensionW(szSrc);
701 if(*ext != '\0') {
702 INT len = wcslen(szDest);
703 lstrcpynW(szDest + len, ext, MAX_PATH - len);
704 }
705 }
706
707 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
708 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
709 {
710 /* src and destination is the same */
711 HRESULT hr = S_OK;
712 if (pPidlOut)
713 hr = _ILCreateFromPathW(szDest, pPidlOut);
714
715 return hr;
716 }
717
718
719 if (MoveFileW (szSrc, szDest))
720 {
721 HRESULT hr = S_OK;
722
723 if (pPidlOut)
724 hr = _ILCreateFromPathW(szDest, pPidlOut);
725
726 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
727 SHCNF_PATHW, szSrc, szDest);
728
729 return hr;
730 }
731
732 return E_FAIL;
733 }
734
735 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
736 {
737 FIXME ("(%p)\n", this);
738 return E_NOTIMPL;
739 }
740
741 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
742 {
743 FIXME ("(%p)\n", this);
744 return E_NOTIMPL;
745 }
746
747 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
748 ULONG * pSort, ULONG * pDisplay)
749 {
750 TRACE ("(%p)\n", this);
751
752 if (pSort)
753 *pSort = 0;
754 if (pDisplay)
755 *pDisplay = 0;
756
757 return S_OK;
758 }
759
760 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
761 DWORD * pcsFlags)
762 {
763 TRACE ("(%p)\n", this);
764
765 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
766 return E_INVALIDARG;
767
768 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
769
770 return S_OK;
771 }
772
773 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
774 const SHCOLUMNID * pscid, VARIANT * pv)
775 {
776 FIXME ("(%p)\n", this);
777
778 return E_NOTIMPL;
779 }
780
781 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
782 UINT iColumn, SHELLDETAILS * psd)
783 {
784 HRESULT hr = E_FAIL;
785
786 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
787
788 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
789 return E_INVALIDARG;
790
791 if (!pidl)
792 {
793 /* the header titles */
794 psd->fmt = GenericSFHeader[iColumn].fmt;
795 psd->cxChar = GenericSFHeader[iColumn].cxChar;
796 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
797 }
798 else
799 {
800 hr = S_OK;
801 psd->str.uType = STRRET_CSTR;
802 /* the data from the pidl */
803 switch (iColumn)
804 {
805 case 0: /* name */
806 hr = GetDisplayNameOf (pidl,
807 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
808 break;
809 case 1: /* size */
810 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
811 break;
812 case 2: /* type */
813 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
814 break;
815 case 3: /* date */
816 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
817 break;
818 case 4: /* attributes */
819 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
820 break;
821 }
822 }
823
824 return hr;
825 }
826
827 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
828 SHCOLUMNID * pscid)
829 {
830 FIXME ("(%p)\n", this);
831 return E_NOTIMPL;
832 }
833
834 /************************************************************************
835 * CFSFolder::GetClassID
836 */
837 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
838 {
839 TRACE ("(%p)\n", this);
840
841 if (!lpClassId)
842 return E_POINTER;
843
844 *lpClassId = *pclsid;
845
846 return S_OK;
847 }
848
849 /************************************************************************
850 * CFSFolder::Initialize
851 *
852 * NOTES
853 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
854 */
855 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
856 {
857 WCHAR wszTemp[MAX_PATH];
858
859 TRACE ("(%p)->(%p)\n", this, pidl);
860
861 SHFree (pidlRoot); /* free the old pidl */
862 pidlRoot = ILClone (pidl); /* set my pidl */
863
864 SHFree (sPathTarget);
865 sPathTarget = NULL;
866
867 /* set my path */
868 if (SHGetPathFromIDListW (pidl, wszTemp))
869 {
870 int len = wcslen(wszTemp);
871 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
872 if (!sPathTarget)
873 return E_OUTOFMEMORY;
874 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
875 }
876
877 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
878 return S_OK;
879 }
880
881 /**************************************************************************
882 * CFSFolder::GetCurFolder
883 */
884 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
885 {
886 TRACE ("(%p)->(%p)\n", this, pidl);
887
888 if (!pidl)
889 return E_POINTER;
890
891 *pidl = ILClone(pidlRoot);
892 return S_OK;
893 }
894
895 /**************************************************************************
896 * CFSFolder::InitializeEx
897 *
898 * FIXME: error handling
899 */
900 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
901 const PERSIST_FOLDER_TARGET_INFO * ppfti)
902 {
903 WCHAR wszTemp[MAX_PATH];
904
905 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
906 if (ppfti)
907 TRACE("--%p %s %s 0x%08x 0x%08x\n",
908 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
909 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
910 ppfti->csidl);
911
912 pdump (pidlRootx);
913 if (ppfti && ppfti->pidlTargetFolder)
914 pdump(ppfti->pidlTargetFolder);
915
916 if (pidlRoot)
917 __SHFreeAndNil(&pidlRoot); /* free the old */
918 if (sPathTarget)
919 __SHFreeAndNil(&sPathTarget);
920
921 /*
922 * Root path and pidl
923 */
924 pidlRoot = ILClone(pidlRootx);
925
926 /*
927 * the target folder is spezified in csidl OR pidlTargetFolder OR
928 * szTargetParsingName
929 */
930 if (ppfti)
931 {
932 if (ppfti->csidl != -1)
933 {
934 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
935 ppfti->csidl & CSIDL_FLAG_CREATE)) {
936 int len = wcslen(wszTemp);
937 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
938 if (!sPathTarget)
939 return E_OUTOFMEMORY;
940 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
941 }
942 }
943 else if (ppfti->szTargetParsingName[0])
944 {
945 int len = wcslen(ppfti->szTargetParsingName);
946 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
947 if (!sPathTarget)
948 return E_OUTOFMEMORY;
949 memcpy(sPathTarget, ppfti->szTargetParsingName,
950 (len + 1) * sizeof(WCHAR));
951 }
952 else if (ppfti->pidlTargetFolder)
953 {
954 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
955 {
956 int len = wcslen(wszTemp);
957 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
958 if (!sPathTarget)
959 return E_OUTOFMEMORY;
960 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
961 }
962 }
963 }
964
965 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
966 pdump(pidlRoot);
967 return (sPathTarget) ? S_OK : E_FAIL;
968 }
969
970 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
971 {
972 FIXME("(%p)->(%p)\n", this, ppfti);
973 ZeroMemory(ppfti, sizeof (*ppfti));
974 return E_NOTIMPL;
975 }
976
977 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
978 HKEY hKey;
979 HRESULT hr;
980
981 TRACE("CFSFolder::_GetDropTarget entered\n");
982
983 if (_ILIsFolder (pidl))
984 {
985 CComPtr<IShellFolder> psfChild;
986 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
987 if (FAILED_UNEXPECTEDLY(hr))
988 return hr;
989
990 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
991 }
992
993 STRRET strFile;
994 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
995 if (hr == S_OK)
996 {
997 WCHAR wszPath[MAX_PATH];
998 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
999
1000 if (hr == S_OK)
1001 {
1002 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1003 if (pwszExt[0])
1004 {
1005 /* enumerate dynamic/static for a given file class */
1006 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1007 {
1008 /* load dynamic extensions from file extension key, for example .jpg */
1009 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1010 RegCloseKey(hKey);
1011 }
1012
1013 WCHAR wszTemp[40];
1014 DWORD dwSize = sizeof(wszTemp);
1015 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1016 {
1017 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1018 {
1019 /* load dynamic extensions from progid key, for example jpegfile */
1020 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1021 RegCloseKey(hKey);
1022 }
1023 }
1024 }
1025 }
1026 }
1027 else
1028 ERR("GetDisplayNameOf failed: %x\n", hr);
1029
1030 return hr;
1031 }
1032
1033 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1034 {
1035 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1036
1037 WCHAR wszName[MAX_PATH], *pwszClsid;
1038 DWORD dwSize = sizeof(wszName);
1039 HRESULT hr;
1040
1041 if (RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize) == ERROR_SUCCESS)
1042 {
1043 CLSID clsid;
1044 hr = CLSIDFromString(wszName, &clsid);
1045 if (hr == S_OK)
1046 pwszClsid = wszName;
1047
1048 if (m_bGroupPolicyActive)
1049 {
1050 if (RegGetValueW(HKEY_LOCAL_MACHINE,
1051 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1052 pwszClsid,
1053 RRF_RT_REG_SZ,
1054 NULL,
1055 NULL,
1056 NULL) == ERROR_SUCCESS)
1057 {
1058 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1059 }
1060 }
1061 else
1062 {
1063 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1064 }
1065 }
1066 else
1067 return E_FAIL;
1068 return hr;
1069 }
1070
1071 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1072 {
1073 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1074 HRESULT hr;
1075
1076 CComPtr<IPersistFile> pp;
1077 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1078 if (hr != S_OK)
1079 {
1080 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1081 }
1082 pp->Load(pwcsname, 0);
1083
1084 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1085 if (hr != S_OK)
1086 {
1087 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1088 return hr;
1089 }
1090 return hr;
1091 }