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