feb6f5a0426179b35dc9110f6049e995df3cb457
[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 HKEY hKeys[16];
375 UINT cKeys = 0;
376 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
377
378 DEFCONTEXTMENU dcm;
379 dcm.hwnd = hwndOwner;
380 dcm.pcmcb = this;
381 dcm.pidlFolder = pidlRoot;
382 dcm.psf = this;
383 dcm.cidl = 0;
384 dcm.apidl = NULL;
385 dcm.cKeys = cKeys;
386 dcm.aKeys = hKeys;
387 dcm.punkAssociationInfo = NULL;
388 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
389 }
390 else if (IsEqualIID (riid, IID_IShellView))
391 {
392 hr = CDefView_Constructor(this, riid, ppvOut);
393 }
394 }
395 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
396 return hr;
397 }
398
399 /**************************************************************************
400 * CFSFolder::GetAttributesOf
401 *
402 * PARAMETERS
403 * UINT cidl, //[in ] num elements in pidl array
404 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
405 * ULONG* rgfInOut) //[out] result array
406 *
407 */
408 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
409 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
410 {
411 HRESULT hr = S_OK;
412
413 if (!rgfInOut)
414 return E_INVALIDARG;
415 if (cidl && !apidl)
416 return E_INVALIDARG;
417
418 if (*rgfInOut == 0)
419 *rgfInOut = ~0;
420
421 if(cidl == 0)
422 {
423 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot);
424
425 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
426 {
427 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
428 }
429 else if (_ILIsDrive(rpidl))
430 {
431 IShellFolder *psfParent = NULL;
432 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
433 if(SUCCEEDED(hr))
434 {
435 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
436 psfParent->Release();
437 }
438 }
439 else
440 {
441 ERR("Got and unknown pidl!\n");
442 }
443 }
444 else
445 {
446 while (cidl > 0 && *apidl)
447 {
448 pdump(*apidl);
449 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
450 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
451 else
452 ERR("Got an unknown type of pidl!!!\n");
453 apidl++;
454 cidl--;
455 }
456 }
457 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
458 *rgfInOut &= ~SFGAO_VALIDATE;
459
460 TRACE("-- result=0x%08x\n", *rgfInOut);
461
462 return hr;
463 }
464
465 /**************************************************************************
466 * CFSFolder::GetUIObjectOf
467 *
468 * PARAMETERS
469 * HWND hwndOwner, //[in ] Parent window for any output
470 * UINT cidl, //[in ] array size
471 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
472 * REFIID riid, //[in ] Requested Interface
473 * UINT* prgfInOut, //[ ] reserved
474 * LPVOID* ppvObject) //[out] Resulting Interface
475 *
476 * NOTES
477 * This function gets asked to return "view objects" for one or more (multiple
478 * select) items:
479 * The viewobject typically is an COM object with one of the following
480 * interfaces:
481 * IExtractIcon,IDataObject,IContextMenu
482 * In order to support icon positions in the default Listview your DataObject
483 * must implement the SetData method (in addition to GetData :) - the shell
484 * passes a barely documented "Icon positions" structure to SetData when the
485 * drag starts, and GetData's it if the drop is in another explorer window that
486 * needs the positions.
487 */
488 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
489 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
490 REFIID riid, UINT * prgfInOut,
491 LPVOID * ppvOut)
492 {
493 LPVOID pObj = NULL;
494 HRESULT hr = E_INVALIDARG;
495
496 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
497 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
498
499 if (ppvOut)
500 {
501 *ppvOut = NULL;
502
503 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
504 {
505 HKEY hKeys[16];
506 UINT cKeys = 0;
507 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
508
509 DEFCONTEXTMENU dcm;
510 dcm.hwnd = hwndOwner;
511 dcm.pcmcb = this;
512 dcm.pidlFolder = pidlRoot;
513 dcm.psf = this;
514 dcm.cidl = cidl;
515 dcm.apidl = apidl;
516 dcm.cKeys = cKeys;
517 dcm.aKeys = hKeys;
518 dcm.punkAssociationInfo = NULL;
519 hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
520 }
521 else if (IsEqualIID (riid, IID_IDataObject))
522 {
523 if (cidl >= 1)
524 {
525 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
526 }
527 else
528 {
529 hr = E_INVALIDARG;
530 }
531 }
532 else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
533 {
534 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
535 }
536 else if (IsEqualIID (riid, IID_IDropTarget))
537 {
538 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
539 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
540 {
541 IDropTarget * pDt = NULL;
542 hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut);
543 pObj = pDt;
544 }
545 }
546 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
547 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
548 {
549 hr = IShellLink_ConstructFromFile(this, apidl[0], riid, &pObj);
550 }
551 else
552 hr = E_NOINTERFACE;
553
554 if (SUCCEEDED(hr) && !pObj)
555 hr = E_OUTOFMEMORY;
556
557 *ppvOut = pObj;
558 }
559 TRACE("(%p)->hr=0x%08x\n", this, hr);
560 return hr;
561 }
562
563 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
564 static const WCHAR HideFileExtW[] = L"HideFileExt";
565 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
566
567 /******************************************************************************
568 * SHELL_FS_HideExtension [Internal]
569 *
570 * Query the registry if the filename extension of a given path should be
571 * hidden.
572 *
573 * PARAMS
574 * szPath [I] Relative or absolute path of a file
575 *
576 * RETURNS
577 * TRUE, if the filename's extension should be hidden
578 * FALSE, otherwise.
579 */
580 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
581 {
582 HKEY hKey;
583 DWORD dwData;
584 DWORD dwDataSize = sizeof (DWORD);
585 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
586
587 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
588 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
589 doHide = dwData;
590 RegCloseKey (hKey);
591 }
592
593 if (!doHide) {
594 LPWSTR ext = PathFindExtensionW(szPath);
595
596 if (*ext != '\0') {
597 WCHAR classname[MAX_PATH];
598 LONG classlen = sizeof(classname);
599
600 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
601 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
602 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
603 doHide = TRUE;
604 RegCloseKey(hKey);
605 }
606 }
607 }
608 return doHide;
609 }
610
611 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
612 {
613 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
614 if (!(dwFlags & SHGDN_FORPARSING) &&
615 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
616 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
617 PathRemoveExtensionW(szPath);
618 }
619 }
620
621 /**************************************************************************
622 * CFSFolder::GetDisplayNameOf
623 * Retrieves the display name for the specified file object or subfolder
624 *
625 * PARAMETERS
626 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
627 * DWORD dwFlags, //[in ] SHGNO formatting flags
628 * LPSTRRET lpName) //[out] Returned display name
629 *
630 * FIXME
631 * if the name is in the pidl the ret value should be a STRRET_OFFSET
632 */
633
634 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
635 DWORD dwFlags, LPSTRRET strRet)
636 {
637 if (!pidl || !strRet)
638 return E_INVALIDARG;
639
640 /* If it is a complex pidl, let the child handle it */
641 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
642 {
643 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
644 }
645 else if (!pidl->mkid.cb) /* empty pidl */
646 {
647 /* If it is an empty pidl return only the path of the folder */
648 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
649 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
650 sPathTarget)
651 {
652 return SHSetStrRet(strRet, sPathTarget);
653 }
654 return E_INVALIDARG;
655 }
656
657 int len = 0;
658 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
659 if (!pszPath)
660 return E_OUTOFMEMORY;
661
662 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
663 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
664 sPathTarget)
665 {
666 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
667 PathAddBackslashW(pszPath);
668 len = wcslen(pszPath);
669 }
670 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
671 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
672
673 strRet->uType = STRRET_WSTR;
674 strRet->pOleStr = pszPath;
675
676 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
677 return S_OK;
678 }
679
680 /**************************************************************************
681 * CFSFolder::SetNameOf
682 * Changes the name of a file object or subfolder, possibly changing its item
683 * identifier in the process.
684 *
685 * PARAMETERS
686 * HWND hwndOwner, //[in ] Owner window for output
687 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
688 * LPCOLESTR lpszName, //[in ] the items new display name
689 * DWORD dwFlags, //[in ] SHGNO formatting flags
690 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
691 */
692 HRESULT WINAPI CFSFolder::SetNameOf(
693 HWND hwndOwner,
694 PCUITEMID_CHILD pidl,
695 LPCOLESTR lpName,
696 DWORD dwFlags,
697 PITEMID_CHILD *pPidlOut)
698 {
699 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
700 LPWSTR ptr;
701 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
702
703 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
704 debugstr_w (lpName), dwFlags, pPidlOut);
705
706 /* build source path */
707 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
708 ptr = PathAddBackslashW (szSrc);
709 if (ptr)
710 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
711
712 /* build destination path */
713 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
714 lstrcpynW(szDest, sPathTarget, MAX_PATH);
715 ptr = PathAddBackslashW (szDest);
716 if (ptr)
717 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
718 } else
719 lstrcpynW(szDest, lpName, MAX_PATH);
720
721 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
722 WCHAR *ext = PathFindExtensionW(szSrc);
723 if(*ext != '\0') {
724 INT len = wcslen(szDest);
725 lstrcpynW(szDest + len, ext, MAX_PATH - len);
726 }
727 }
728
729 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
730 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
731 {
732 /* src and destination is the same */
733 HRESULT hr = S_OK;
734 if (pPidlOut)
735 hr = _ILCreateFromPathW(szDest, pPidlOut);
736
737 return hr;
738 }
739
740
741 if (MoveFileW (szSrc, szDest))
742 {
743 HRESULT hr = S_OK;
744
745 if (pPidlOut)
746 hr = _ILCreateFromPathW(szDest, pPidlOut);
747
748 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
749 SHCNF_PATHW, szSrc, szDest);
750
751 return hr;
752 }
753
754 return E_FAIL;
755 }
756
757 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
758 {
759 FIXME ("(%p)\n", this);
760 return E_NOTIMPL;
761 }
762
763 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
764 {
765 FIXME ("(%p)\n", this);
766 return E_NOTIMPL;
767 }
768
769 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
770 ULONG * pSort, ULONG * pDisplay)
771 {
772 TRACE ("(%p)\n", this);
773
774 if (pSort)
775 *pSort = 0;
776 if (pDisplay)
777 *pDisplay = 0;
778
779 return S_OK;
780 }
781
782 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
783 DWORD * pcsFlags)
784 {
785 TRACE ("(%p)\n", this);
786
787 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
788 return E_INVALIDARG;
789
790 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
791
792 return S_OK;
793 }
794
795 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
796 const SHCOLUMNID * pscid, VARIANT * pv)
797 {
798 FIXME ("(%p)\n", this);
799
800 return E_NOTIMPL;
801 }
802
803 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
804 UINT iColumn, SHELLDETAILS * psd)
805 {
806 HRESULT hr = E_FAIL;
807
808 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
809
810 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
811 return E_INVALIDARG;
812
813 if (!pidl)
814 {
815 /* the header titles */
816 psd->fmt = GenericSFHeader[iColumn].fmt;
817 psd->cxChar = GenericSFHeader[iColumn].cxChar;
818 return SHSetStrRet(&psd->str, GenericSFHeader[iColumn].colnameid);
819 }
820 else
821 {
822 hr = S_OK;
823 psd->str.uType = STRRET_CSTR;
824 /* the data from the pidl */
825 switch (iColumn)
826 {
827 case 0: /* name */
828 hr = GetDisplayNameOf (pidl,
829 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
830 break;
831 case 1: /* size */
832 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
833 break;
834 case 2: /* type */
835 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
836 break;
837 case 3: /* date */
838 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
839 break;
840 case 4: /* attributes */
841 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
842 break;
843 }
844 }
845
846 return hr;
847 }
848
849 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
850 SHCOLUMNID * pscid)
851 {
852 FIXME ("(%p)\n", this);
853 return E_NOTIMPL;
854 }
855
856 /************************************************************************
857 * CFSFolder::GetClassID
858 */
859 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
860 {
861 TRACE ("(%p)\n", this);
862
863 if (!lpClassId)
864 return E_POINTER;
865
866 *lpClassId = *pclsid;
867
868 return S_OK;
869 }
870
871 /************************************************************************
872 * CFSFolder::Initialize
873 *
874 * NOTES
875 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
876 */
877 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
878 {
879 WCHAR wszTemp[MAX_PATH];
880
881 TRACE ("(%p)->(%p)\n", this, pidl);
882
883 SHFree (pidlRoot); /* free the old pidl */
884 pidlRoot = ILClone (pidl); /* set my pidl */
885
886 SHFree (sPathTarget);
887 sPathTarget = NULL;
888
889 /* set my path */
890 if (SHGetPathFromIDListW (pidl, wszTemp))
891 {
892 int len = wcslen(wszTemp);
893 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
894 if (!sPathTarget)
895 return E_OUTOFMEMORY;
896 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
897 }
898
899 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
900 return S_OK;
901 }
902
903 /**************************************************************************
904 * CFSFolder::GetCurFolder
905 */
906 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
907 {
908 TRACE ("(%p)->(%p)\n", this, pidl);
909
910 if (!pidl)
911 return E_POINTER;
912
913 *pidl = ILClone(pidlRoot);
914 return S_OK;
915 }
916
917 /**************************************************************************
918 * CFSFolder::InitializeEx
919 *
920 * FIXME: error handling
921 */
922 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
923 const PERSIST_FOLDER_TARGET_INFO * ppfti)
924 {
925 WCHAR wszTemp[MAX_PATH];
926
927 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
928 if (ppfti)
929 TRACE("--%p %s %s 0x%08x 0x%08x\n",
930 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
931 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
932 ppfti->csidl);
933
934 pdump (pidlRootx);
935 if (ppfti && ppfti->pidlTargetFolder)
936 pdump(ppfti->pidlTargetFolder);
937
938 if (pidlRoot)
939 __SHFreeAndNil(&pidlRoot); /* free the old */
940 if (sPathTarget)
941 __SHFreeAndNil(&sPathTarget);
942
943 /*
944 * Root path and pidl
945 */
946 pidlRoot = ILClone(pidlRootx);
947
948 /*
949 * the target folder is spezified in csidl OR pidlTargetFolder OR
950 * szTargetParsingName
951 */
952 if (ppfti)
953 {
954 if (ppfti->csidl != -1)
955 {
956 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
957 ppfti->csidl & CSIDL_FLAG_CREATE)) {
958 int len = wcslen(wszTemp);
959 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
960 if (!sPathTarget)
961 return E_OUTOFMEMORY;
962 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
963 }
964 }
965 else if (ppfti->szTargetParsingName[0])
966 {
967 int len = wcslen(ppfti->szTargetParsingName);
968 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
969 if (!sPathTarget)
970 return E_OUTOFMEMORY;
971 memcpy(sPathTarget, ppfti->szTargetParsingName,
972 (len + 1) * sizeof(WCHAR));
973 }
974 else if (ppfti->pidlTargetFolder)
975 {
976 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
977 {
978 int len = wcslen(wszTemp);
979 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
980 if (!sPathTarget)
981 return E_OUTOFMEMORY;
982 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
983 }
984 }
985 }
986
987 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
988 pdump(pidlRoot);
989 return (sPathTarget) ? S_OK : E_FAIL;
990 }
991
992 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
993 {
994 FIXME("(%p)->(%p)\n", this, ppfti);
995 ZeroMemory(ppfti, sizeof (*ppfti));
996 return E_NOTIMPL;
997 }
998
999 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1000 HKEY hKey;
1001 HRESULT hr;
1002
1003 TRACE("CFSFolder::_GetDropTarget entered\n");
1004
1005 if (_ILIsFolder (pidl))
1006 {
1007 CComPtr<IShellFolder> psfChild;
1008 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1009 if (FAILED_UNEXPECTEDLY(hr))
1010 return hr;
1011
1012 return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1013 }
1014
1015 STRRET strFile;
1016 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1017 if (hr == S_OK)
1018 {
1019 WCHAR wszPath[MAX_PATH];
1020 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1021
1022 if (hr == S_OK)
1023 {
1024 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1025 if (pwszExt[0])
1026 {
1027 /* enumerate dynamic/static for a given file class */
1028 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1029 {
1030 /* load dynamic extensions from file extension key, for example .jpg */
1031 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1032 RegCloseKey(hKey);
1033 }
1034
1035 WCHAR wszTemp[40];
1036 DWORD dwSize = sizeof(wszTemp);
1037 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1038 {
1039 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1040 {
1041 /* load dynamic extensions from progid key, for example jpegfile */
1042 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1043 RegCloseKey(hKey);
1044 }
1045 }
1046 }
1047 }
1048 }
1049 else
1050 ERR("GetDisplayNameOf failed: %x\n", hr);
1051
1052 return hr;
1053 }
1054
1055 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1056 {
1057 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1058
1059 WCHAR wszName[MAX_PATH];
1060 DWORD dwSize = sizeof(wszName);
1061 HRESULT hr;
1062 LRESULT res;
1063
1064 res = RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize);
1065 if (res != ERROR_SUCCESS)
1066 return S_FALSE;
1067
1068 CLSID clsid;
1069 hr = CLSIDFromString(wszName, &clsid);
1070 if (FAILED_UNEXPECTEDLY(hr))
1071 return hr;
1072
1073 if (m_bGroupPolicyActive)
1074 {
1075 res = RegGetValueW(HKEY_LOCAL_MACHINE,
1076 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1077 wszName,
1078 RRF_RT_REG_SZ,
1079 NULL,
1080 NULL,
1081 NULL);
1082 if (res != ERROR_SUCCESS)
1083 {
1084 ERR("DropHandler extension %S not approved\n", wszName);
1085 return E_FAIL;
1086 }
1087 }
1088
1089 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1090 if (FAILED_UNEXPECTEDLY(hr))
1091 return hr;
1092
1093 return S_OK;
1094 }
1095
1096 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1097 {
1098 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1099 HRESULT hr;
1100
1101 CComPtr<IPersistFile> pp;
1102 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1103 if (hr != S_OK)
1104 {
1105 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1106 }
1107 pp->Load(pwcsname, 0);
1108
1109 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1110 if (hr != S_OK)
1111 {
1112 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1113 return hr;
1114 }
1115 return hr;
1116 }
1117
1118 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1119 {
1120 if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1121 return S_OK;
1122
1123 /* no data object means no selection */
1124 if (!pdtobj)
1125 {
1126 if (uMsg == DFM_INVOKECOMMAND && wParam == DFM_CMD_PROPERTIES)
1127 {
1128 PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(pidlRoot));
1129 LPITEMIDLIST pidlParent = ILClone(pidlRoot);
1130 ILRemoveLastID(pidlParent);
1131 HRESULT hr = SH_ShowPropertiesDialog(sPathTarget, pidlParent, &pidlChild);
1132 if (FAILED(hr))
1133 ERR("SH_ShowPropertiesDialog failed\n");
1134 ILFree(pidlChild);
1135 ILFree(pidlParent);
1136 }
1137 else if (uMsg == DFM_MERGECONTEXTMENU)
1138 {
1139 QCMINFO *pqcminfo = (QCMINFO *)lParam;
1140 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
1141 _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1142 }
1143
1144 return S_OK;
1145 }
1146
1147 if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1148 return S_OK;
1149
1150 PIDLIST_ABSOLUTE pidlFolder;
1151 PUITEMID_CHILD *apidl;
1152 UINT cidl;
1153 HRESULT hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
1154 if (FAILED_UNEXPECTEDLY(hr))
1155 return hr;
1156
1157 if (cidl > 1)
1158 ERR("SHMultiFileProperties is not yet implemented\n");
1159
1160 STRRET strFile;
1161 hr = GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strFile);
1162 if (SUCCEEDED(hr))
1163 {
1164 hr = SH_ShowPropertiesDialog(strFile.pOleStr, pidlFolder, apidl);
1165 if (FAILED(hr))
1166 ERR("SH_ShowPropertiesDialog failed\n");
1167 }
1168 else
1169 {
1170 ERR("Failed to get display name\n");
1171 }
1172
1173 SHFree(pidlFolder);
1174 _ILFreeaPidl(apidl, cidl);
1175
1176 return hr;
1177 }