[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 /**************************************************************************
66 * registers clipboardformat once
67 */
68 void CFSFolder::SF_RegisterClipFmt()
69 {
70 TRACE ("(%p)\n", this);
71
72 if (!cfShellIDList)
73 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
74 }
75
76 CFSFolder::CFSFolder()
77 {
78 pclsid = (CLSID *)&CLSID_ShellFSFolder;
79 sPathTarget = NULL;
80 pidlRoot = NULL;
81 cfShellIDList = 0;
82 SF_RegisterClipFmt();
83 fAcceptFmt = FALSE;
84 m_bGroupPolicyActive = 0;
85 }
86
87 CFSFolder::~CFSFolder()
88 {
89 TRACE("-- destroying IShellFolder(%p)\n", this);
90
91 SHFree(pidlRoot);
92 SHFree(sPathTarget);
93 }
94
95
96 static const shvheader GenericSFHeader[] = {
97 {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
98 {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
99 {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
100 {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
101 {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 5}
102 };
103
104 #define GENERICSHELLVIEWCOLUMNS 5
105
106 /**************************************************************************
107 * SHELL32_CreatePidlFromBindCtx [internal]
108 *
109 * If the caller bound File System Bind Data, assume it is the
110 * find data for the path.
111 * This allows binding of paths that don't exist.
112 */
113 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
114 {
115 IFileSystemBindData *fsbd = NULL;
116 LPITEMIDLIST pidl = NULL;
117 IUnknown *param = NULL;
118 WIN32_FIND_DATAW wfd;
119 HRESULT r;
120
121 TRACE("%p %s\n", pbc, debugstr_w(path));
122
123 if (!pbc)
124 return NULL;
125
126 /* see if the caller bound File System Bind Data */
127 r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, &param);
128 if (FAILED(r))
129 return NULL;
130
131 r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd));
132 if (SUCCEEDED(r))
133 {
134 r = fsbd->GetFindData(&wfd);
135 if (SUCCEEDED(r))
136 {
137 lstrcpynW(&wfd.cFileName[0], path, MAX_PATH);
138 pidl = _ILCreateFromFindDataW(&wfd);
139 }
140 fsbd->Release();
141 }
142
143 return pidl;
144 }
145
146 /**************************************************************************
147 * CFSFolder::ParseDisplayName {SHELL32}
148 *
149 * Parse a display name.
150 *
151 * PARAMS
152 * hwndOwner [in] Parent window for any message's
153 * pbc [in] optional FileSystemBindData context
154 * lpszDisplayName [in] Unicode displayname.
155 * pchEaten [out] (unicode) characters processed
156 * ppidl [out] complex pidl to item
157 * pdwAttributes [out] items attributes
158 *
159 * NOTES
160 * Every folder tries to parse only its own (the leftmost) pidl and creates a
161 * subfolder to evaluate the remaining parts.
162 * Now we can parse into namespaces implemented by shell extensions
163 *
164 * Behaviour on win98: lpszDisplayName=NULL -> crash
165 * lpszDisplayName="" -> returns mycoputer-pidl
166 *
167 * FIXME
168 * pdwAttributes is not set
169 * pchEaten is not set like in windows
170 */
171 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
172 LPBC pbc,
173 LPOLESTR lpszDisplayName,
174 DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
175 DWORD *pdwAttributes)
176 {
177 HRESULT hr = E_INVALIDARG;
178 LPCWSTR szNext = NULL;
179 WCHAR szElement[MAX_PATH];
180 WCHAR szPath[MAX_PATH];
181 LPITEMIDLIST pidlTemp = NULL;
182 DWORD len;
183
184 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
185 this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
186 pchEaten, ppidl, pdwAttributes);
187
188 if (!ppidl)
189 return E_INVALIDARG;
190
191 if (!lpszDisplayName)
192 {
193 *ppidl = NULL;
194 return E_INVALIDARG;
195 }
196
197 *ppidl = NULL;
198
199 if (pchEaten)
200 *pchEaten = 0; /* strange but like the original */
201
202 if (*lpszDisplayName)
203 {
204 /* get the next element */
205 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
206
207 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement);
208 if (pidlTemp != NULL)
209 {
210 hr = S_OK;
211 }
212 else
213 {
214 /* build the full pathname to the element */
215 lstrcpynW(szPath, sPathTarget, MAX_PATH - 1);
216 PathAddBackslashW(szPath);
217 len = wcslen(szPath);
218 lstrcpynW(szPath + len, szElement, MAX_PATH - len);
219
220 /* get the pidl */
221 hr = _ILCreateFromPathW(szPath, &pidlTemp);
222 }
223
224 if (SUCCEEDED(hr))
225 {
226 if (szNext && *szNext)
227 {
228 /* try to analyse the next element */
229 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
230 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
231 }
232 else
233 {
234 /* it's the last element */
235 if (pdwAttributes && *pdwAttributes)
236 hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes);
237 }
238 }
239 }
240
241 if (SUCCEEDED(hr))
242 *ppidl = pidlTemp;
243 else
244 *ppidl = NULL;
245
246 TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
247
248 return hr;
249 }
250
251 /**************************************************************************
252 * CFSFolder::EnumObjects
253 * PARAMETERS
254 * HWND hwndOwner, //[in ] Parent Window
255 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
256 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
257 */
258 HRESULT WINAPI CFSFolder::EnumObjects(
259 HWND hwndOwner,
260 DWORD dwFlags,
261 LPENUMIDLIST *ppEnumIDList)
262 {
263 return ShellObjectCreatorInit<CFileSysEnum>(sPathTarget, dwFlags, IID_IEnumIDList, ppEnumIDList);
264 }
265
266 /**************************************************************************
267 * CFSFolder::BindToObject
268 * PARAMETERS
269 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
270 * LPBC pbc, //[in ] optional FileSystemBindData context
271 * REFIID riid, //[in ] Initial Interface
272 * LPVOID* ppvObject //[out] Interface*
273 */
274 HRESULT WINAPI CFSFolder::BindToObject(
275 PCUIDLIST_RELATIVE pidl,
276 LPBC pbc,
277 REFIID riid,
278 LPVOID * ppvOut)
279 {
280 TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
281 shdebugstr_guid(&riid), ppvOut);
282
283 return SHELL32_BindToChild(pidlRoot, sPathTarget, pidl, riid, ppvOut);
284 }
285
286 /**************************************************************************
287 * CFSFolder::BindToStorage
288 * PARAMETERS
289 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
290 * LPBC pbc, //[in ] reserved
291 * REFIID riid, //[in ] Initial storage interface
292 * LPVOID* ppvObject //[out] Interface* returned
293 */
294 HRESULT WINAPI CFSFolder::BindToStorage(
295 PCUIDLIST_RELATIVE pidl,
296 LPBC pbcReserved,
297 REFIID riid,
298 LPVOID *ppvOut)
299 {
300 FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
301 shdebugstr_guid (&riid), ppvOut);
302
303 *ppvOut = NULL;
304 return E_NOTIMPL;
305 }
306
307 /**************************************************************************
308 * CFSFolder::CompareIDs
309 */
310
311 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
312 PCUIDLIST_RELATIVE pidl1,
313 PCUIDLIST_RELATIVE pidl2)
314 {
315 int nReturn;
316
317 TRACE("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam, pidl1, pidl2);
318 nReturn = SHELL32_CompareIDs(this, lParam, pidl1, pidl2);
319 TRACE("-- %i\n", nReturn);
320 return nReturn;
321 }
322
323 /**************************************************************************
324 * CFSFolder::CreateViewObject
325 */
326 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
327 REFIID riid, LPVOID * ppvOut)
328 {
329 CComPtr<IShellView> pShellView;
330 HRESULT hr = E_INVALIDARG;
331
332 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
333 ppvOut);
334
335 if (ppvOut)
336 {
337 *ppvOut = NULL;
338
339 if (IsEqualIID (riid, IID_IDropTarget))
340 hr = this->QueryInterface (IID_IDropTarget, ppvOut);
341 else if (IsEqualIID (riid, IID_IContextMenu))
342 {
343 FIXME ("IContextMenu not implemented\n");
344 hr = E_NOTIMPL;
345 }
346 else if (IsEqualIID (riid, IID_IShellView))
347 {
348 hr = CDefView_Constructor(this, riid, ppvOut);
349 }
350 }
351 TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
352 return hr;
353 }
354
355 /**************************************************************************
356 * CFSFolder::GetAttributesOf
357 *
358 * PARAMETERS
359 * UINT cidl, //[in ] num elements in pidl array
360 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
361 * ULONG* rgfInOut) //[out] result array
362 *
363 */
364 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
365 PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
366 {
367 HRESULT hr = S_OK;
368
369 if (!rgfInOut)
370 return E_INVALIDARG;
371 if (cidl && !apidl)
372 return E_INVALIDARG;
373
374 if (*rgfInOut == 0)
375 *rgfInOut = ~0;
376
377 if(cidl == 0)
378 {
379 LPCITEMIDLIST rpidl = ILFindLastID(pidlRoot);
380
381 if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
382 {
383 SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
384 }
385 else if (_ILIsDrive(rpidl))
386 {
387 IShellFolder *psfParent = NULL;
388 hr = SHBindToParent(pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
389 if(SUCCEEDED(hr))
390 {
391 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
392 psfParent->Release();
393 }
394 }
395 else
396 {
397 ERR("Got and unknown pidl!\n");
398 }
399 }
400 else
401 {
402 while (cidl > 0 && *apidl)
403 {
404 pdump(*apidl);
405 if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
406 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
407 else
408 ERR("Got an unknown type of pidl!!!\n");
409 apidl++;
410 cidl--;
411 }
412 }
413 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
414 *rgfInOut &= ~SFGAO_VALIDATE;
415
416 TRACE("-- result=0x%08x\n", *rgfInOut);
417
418 return hr;
419 }
420
421 /**************************************************************************
422 * CFSFolder::GetUIObjectOf
423 *
424 * PARAMETERS
425 * HWND hwndOwner, //[in ] Parent window for any output
426 * UINT cidl, //[in ] array size
427 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
428 * REFIID riid, //[in ] Requested Interface
429 * UINT* prgfInOut, //[ ] reserved
430 * LPVOID* ppvObject) //[out] Resulting Interface
431 *
432 * NOTES
433 * This function gets asked to return "view objects" for one or more (multiple
434 * select) items:
435 * The viewobject typically is an COM object with one of the following
436 * interfaces:
437 * IExtractIcon,IDataObject,IContextMenu
438 * In order to support icon positions in the default Listview your DataObject
439 * must implement the SetData method (in addition to GetData :) - the shell
440 * passes a barely documented "Icon positions" structure to SetData when the
441 * drag starts, and GetData's it if the drop is in another explorer window that
442 * needs the positions.
443 */
444 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
445 UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
446 REFIID riid, UINT * prgfInOut,
447 LPVOID * ppvOut)
448 {
449 LPITEMIDLIST pidl;
450 IUnknown *pObj = NULL;
451 HRESULT hr = E_INVALIDARG;
452
453 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
454 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
455
456 if (ppvOut)
457 {
458 *ppvOut = NULL;
459
460 if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
461 {
462 IContextMenu * pCm = NULL;
463 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, static_cast<IShellFolder*>(this), NULL, 0, NULL, &pCm);
464 pObj = pCm;
465 }
466 else if (IsEqualIID (riid, IID_IDataObject))
467 {
468 if (cidl >= 1)
469 {
470 hr = IDataObject_Constructor (hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
471 }
472 else
473 {
474 hr = IDataObject_Constructor (hwndOwner, pidlRoot, (LPCITEMIDLIST*)&pidlRoot, 1, (IDataObject **)&pObj);
475 }
476 }
477 else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
478 {
479 pidl = ILCombine (pidlRoot, apidl[0]);
480 pObj = IExtractIconA_Constructor (pidl);
481 SHFree (pidl);
482 hr = S_OK;
483 }
484 else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
485 {
486 pidl = ILCombine (pidlRoot, apidl[0]);
487 pObj = IExtractIconW_Constructor (pidl);
488 SHFree (pidl);
489 hr = S_OK;
490 }
491 else if (IsEqualIID (riid, IID_IDropTarget))
492 {
493 /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
494 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
495 {
496 IDropTarget * pDt = NULL;
497 hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
498 pObj = pDt;
499 }
500 }
501 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
502 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
503 {
504 pidl = ILCombine (pidlRoot, apidl[0]);
505 hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
506 SHFree (pidl);
507 }
508 else
509 hr = E_NOINTERFACE;
510
511 if (SUCCEEDED(hr) && !pObj)
512 hr = E_OUTOFMEMORY;
513
514 *ppvOut = pObj;
515 }
516 TRACE("(%p)->hr=0x%08x\n", this, hr);
517 return hr;
518 }
519
520 static const WCHAR AdvancedW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
521 static const WCHAR HideFileExtW[] = L"HideFileExt";
522 static const WCHAR NeverShowExtW[] = L"NeverShowExt";
523
524 /******************************************************************************
525 * SHELL_FS_HideExtension [Internal]
526 *
527 * Query the registry if the filename extension of a given path should be
528 * hidden.
529 *
530 * PARAMS
531 * szPath [I] Relative or absolute path of a file
532 *
533 * RETURNS
534 * TRUE, if the filename's extension should be hidden
535 * FALSE, otherwise.
536 */
537 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
538 {
539 HKEY hKey;
540 DWORD dwData;
541 DWORD dwDataSize = sizeof (DWORD);
542 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
543
544 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
545 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
546 doHide = dwData;
547 RegCloseKey (hKey);
548 }
549
550 if (!doHide) {
551 LPWSTR ext = PathFindExtensionW(szPath);
552
553 if (*ext != '\0') {
554 WCHAR classname[MAX_PATH];
555 LONG classlen = sizeof(classname);
556
557 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
558 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
559 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
560 doHide = TRUE;
561 RegCloseKey(hKey);
562 }
563 }
564 }
565 return doHide;
566 }
567
568 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
569 {
570 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
571 if (!(dwFlags & SHGDN_FORPARSING) &&
572 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
573 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
574 PathRemoveExtensionW(szPath);
575 }
576 }
577
578 /**************************************************************************
579 * CFSFolder::GetDisplayNameOf
580 * Retrieves the display name for the specified file object or subfolder
581 *
582 * PARAMETERS
583 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
584 * DWORD dwFlags, //[in ] SHGNO formatting flags
585 * LPSTRRET lpName) //[out] Returned display name
586 *
587 * FIXME
588 * if the name is in the pidl the ret value should be a STRRET_OFFSET
589 */
590
591 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
592 DWORD dwFlags, LPSTRRET strRet)
593 {
594 if (!pidl || !strRet)
595 return E_INVALIDARG;
596
597 /* If it is a complex pidl, let the child handle it */
598 if (!_ILIsPidlSimple (pidl)) /* complex pidl */
599 {
600 return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
601 }
602 else if (!pidl->mkid.cb) /* empty pidl */
603 {
604 /* If it is an empty pidl return only the path of the folder */
605 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
606 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
607 sPathTarget)
608 {
609 return SHSetStrRet(strRet, sPathTarget);
610 }
611 return E_INVALIDARG;
612 }
613
614 int len = 0;
615 LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
616 if (!pszPath)
617 return E_OUTOFMEMORY;
618
619 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
620 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
621 sPathTarget)
622 {
623 lstrcpynW(pszPath, sPathTarget, MAX_PATH);
624 PathAddBackslashW(pszPath);
625 len = wcslen(pszPath);
626 }
627 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
628 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
629
630 strRet->uType = STRRET_WSTR;
631 strRet->pOleStr = pszPath;
632
633 TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
634 return S_OK;
635 }
636
637 /**************************************************************************
638 * CFSFolder::SetNameOf
639 * Changes the name of a file object or subfolder, possibly changing its item
640 * identifier in the process.
641 *
642 * PARAMETERS
643 * HWND hwndOwner, //[in ] Owner window for output
644 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
645 * LPCOLESTR lpszName, //[in ] the items new display name
646 * DWORD dwFlags, //[in ] SHGNO formatting flags
647 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
648 */
649 HRESULT WINAPI CFSFolder::SetNameOf(
650 HWND hwndOwner,
651 PCUITEMID_CHILD pidl,
652 LPCOLESTR lpName,
653 DWORD dwFlags,
654 PITEMID_CHILD *pPidlOut)
655 {
656 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
657 LPWSTR ptr;
658 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
659
660 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
661 debugstr_w (lpName), dwFlags, pPidlOut);
662
663 /* build source path */
664 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
665 ptr = PathAddBackslashW (szSrc);
666 if (ptr)
667 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
668
669 /* build destination path */
670 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
671 lstrcpynW(szDest, sPathTarget, MAX_PATH);
672 ptr = PathAddBackslashW (szDest);
673 if (ptr)
674 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
675 } else
676 lstrcpynW(szDest, lpName, MAX_PATH);
677
678 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
679 WCHAR *ext = PathFindExtensionW(szSrc);
680 if(*ext != '\0') {
681 INT len = wcslen(szDest);
682 lstrcpynW(szDest + len, ext, MAX_PATH - len);
683 }
684 }
685
686 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
687 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
688 {
689 /* src and destination is the same */
690 HRESULT hr = S_OK;
691 if (pPidlOut)
692 hr = _ILCreateFromPathW(szDest, pPidlOut);
693
694 return hr;
695 }
696
697
698 if (MoveFileW (szSrc, szDest))
699 {
700 HRESULT hr = S_OK;
701
702 if (pPidlOut)
703 hr = _ILCreateFromPathW(szDest, pPidlOut);
704
705 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
706 SHCNF_PATHW, szSrc, szDest);
707
708 return hr;
709 }
710
711 return E_FAIL;
712 }
713
714 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
715 {
716 FIXME ("(%p)\n", this);
717 return E_NOTIMPL;
718 }
719
720 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
721 {
722 FIXME ("(%p)\n", this);
723 return E_NOTIMPL;
724 }
725
726 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
727 ULONG * pSort, ULONG * pDisplay)
728 {
729 TRACE ("(%p)\n", this);
730
731 if (pSort)
732 *pSort = 0;
733 if (pDisplay)
734 *pDisplay = 0;
735
736 return S_OK;
737 }
738
739 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
740 DWORD * pcsFlags)
741 {
742 TRACE ("(%p)\n", this);
743
744 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
745 return E_INVALIDARG;
746
747 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
748
749 return S_OK;
750 }
751
752 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
753 const SHCOLUMNID * pscid, VARIANT * pv)
754 {
755 FIXME ("(%p)\n", this);
756
757 return E_NOTIMPL;
758 }
759
760 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
761 UINT iColumn, SHELLDETAILS * psd)
762 {
763 HRESULT hr = E_FAIL;
764
765 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
766
767 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
768 return E_INVALIDARG;
769
770 if (!pidl)
771 {
772 /* the header titles */
773 psd->fmt = GenericSFHeader[iColumn].fmt;
774 psd->cxChar = GenericSFHeader[iColumn].cxChar;
775 psd->str.uType = STRRET_CSTR;
776 LoadStringA(shell32_hInstance, GenericSFHeader[iColumn].colnameid,
777 psd->str.cStr, MAX_PATH);
778 return S_OK;
779 }
780 else
781 {
782 hr = S_OK;
783 psd->str.uType = STRRET_CSTR;
784 /* the data from the pidl */
785 switch (iColumn)
786 {
787 case 0: /* name */
788 hr = GetDisplayNameOf (pidl,
789 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
790 break;
791 case 1: /* size */
792 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
793 break;
794 case 2: /* type */
795 _ILGetFileType(pidl, psd->str.cStr, MAX_PATH);
796 break;
797 case 3: /* date */
798 _ILGetFileDate(pidl, psd->str.cStr, MAX_PATH);
799 break;
800 case 4: /* attributes */
801 _ILGetFileAttributes(pidl, psd->str.cStr, MAX_PATH);
802 break;
803 }
804 }
805
806 return hr;
807 }
808
809 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
810 SHCOLUMNID * pscid)
811 {
812 FIXME ("(%p)\n", this);
813 return E_NOTIMPL;
814 }
815
816 /****************************************************************************
817 * ISFHelper for IShellFolder implementation
818 */
819
820 /****************************************************************************
821 * CFSFolder::GetUniqueName
822 *
823 * creates a unique folder name
824 */
825
826 HRESULT WINAPI CFSFolder::GetUniqueName(LPWSTR pwszName, UINT uLen)
827 {
828 CComPtr<IEnumIDList> penum;
829 HRESULT hr;
830 WCHAR wszText[MAX_PATH];
831 WCHAR wszNewFolder[25];
832 const WCHAR wszFormat[] = L"%s %d";
833
834 LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder));
835
836 TRACE ("(%p)(%p %u)\n", this, pwszName, uLen);
837
838 if (uLen < _countof(wszNewFolder) + 3)
839 return E_POINTER;
840
841 lstrcpynW (pwszName, wszNewFolder, uLen);
842
843 hr = EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum);
844 if (penum)
845 {
846 LPITEMIDLIST pidl;
847 DWORD dwFetched;
848 int i = 1;
849
850 next:
851 penum->Reset ();
852 while (S_OK == penum->Next(1, &pidl, &dwFetched) && dwFetched)
853 {
854 _ILSimpleGetTextW(pidl, wszText, MAX_PATH);
855 if (0 == lstrcmpiW(wszText, pwszName))
856 {
857 _snwprintf(pwszName, uLen, wszFormat, wszNewFolder, i++);
858 if (i > 99)
859 {
860 hr = E_FAIL;
861 break;
862 }
863 goto next;
864 }
865 }
866 }
867 return hr;
868 }
869
870 /****************************************************************************
871 * CFSFolder::AddFolder
872 *
873 * adds a new folder.
874 */
875
876 HRESULT WINAPI CFSFolder::AddFolder(HWND hwnd, LPCWSTR pwszName,
877 LPITEMIDLIST * ppidlOut)
878 {
879 WCHAR wszNewDir[MAX_PATH];
880 DWORD bRes;
881 HRESULT hres = E_FAIL;
882
883 TRACE ("(%p)(%s %p)\n", this, debugstr_w(pwszName), ppidlOut);
884
885 wszNewDir[0] = 0;
886 if (sPathTarget)
887 lstrcpynW(wszNewDir, sPathTarget, MAX_PATH);
888 PathAppendW(wszNewDir, pwszName);
889
890 bRes = CreateDirectoryW(wszNewDir, NULL);
891 if (bRes)
892 {
893 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, wszNewDir, NULL);
894
895 hres = S_OK;
896
897 if (ppidlOut)
898 hres = _ILCreateFromPathW(wszNewDir, ppidlOut);
899 }
900 else
901 {
902 WCHAR wszText[128 + MAX_PATH];
903 WCHAR wszTempText[128];
904 WCHAR wszCaption[256];
905
906 /* Cannot Create folder because of permissions */
907 LoadStringW(shell32_hInstance, IDS_CREATEFOLDER_DENIED, wszTempText,
908 _countof(wszTempText));
909 LoadStringW(shell32_hInstance, IDS_CREATEFOLDER_CAPTION, wszCaption,
910 _countof(wszCaption));
911 swprintf(wszText, wszTempText, wszNewDir);
912 MessageBoxW(hwnd, wszText, wszCaption, MB_OK | MB_ICONEXCLAMATION);
913 }
914
915 return hres;
916 }
917
918 /****************************************************************************
919 * BuildPathsList
920 *
921 * Builds a list of paths like the one used in SHFileOperation from a table of
922 * PIDLs relative to the given base folder
923 */
924 WCHAR *
925 BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls)
926 {
927 WCHAR *pwszPathsList;
928 WCHAR *pwszListPos;
929 int iPathLen, i;
930
931 iPathLen = wcslen(wszBasePath);
932 pwszPathsList = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1);
933 pwszListPos = pwszPathsList;
934
935 for (i = 0; i < cidl; i++)
936 {
937 if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i]))
938 continue;
939
940 wcscpy(pwszListPos, wszBasePath);
941 pwszListPos += iPathLen;
942 /* FIXME: abort if path too long */
943 _ILSimpleGetTextW(pidls[i], pwszListPos, MAX_PATH - iPathLen);
944 pwszListPos += wcslen(pwszListPos) + 1;
945 }
946 *pwszListPos = 0;
947 return pwszPathsList;
948 }
949
950 /****************************************************************************
951 * CFSFolder::DeleteItems
952 *
953 * deletes items in folder
954 */
955 HRESULT WINAPI CFSFolder::DeleteItems(UINT cidl, LPCITEMIDLIST *apidl)
956 {
957 UINT i;
958 SHFILEOPSTRUCTW op;
959 WCHAR wszPath[MAX_PATH];
960 WCHAR *wszPathsList;
961 HRESULT ret;
962 WCHAR *wszCurrentPath;
963
964 TRACE ("(%p)(%u %p)\n", this, cidl, apidl);
965 if (cidl == 0) return S_OK;
966
967 if (sPathTarget)
968 lstrcpynW(wszPath, sPathTarget, MAX_PATH);
969 else
970 wszPath[0] = '\0';
971 PathAddBackslashW(wszPath);
972 wszPathsList = BuildPathsList(wszPath, cidl, apidl);
973
974 ZeroMemory(&op, sizeof(op));
975 op.hwnd = GetActiveWindow();
976 op.wFunc = FO_DELETE;
977 op.pFrom = wszPathsList;
978 op.fFlags = FOF_ALLOWUNDO;
979 if (SHFileOperationW(&op))
980 {
981 WARN("SHFileOperation failed\n");
982 ret = E_FAIL;
983 }
984 else
985 ret = S_OK;
986
987 /* we currently need to manually send the notifies */
988 wszCurrentPath = wszPathsList;
989 for (i = 0; i < cidl; i++)
990 {
991 LONG wEventId;
992
993 if (_ILIsFolder(apidl[i]))
994 wEventId = SHCNE_RMDIR;
995 else if (_ILIsValue(apidl[i]))
996 wEventId = SHCNE_DELETE;
997 else
998 continue;
999
1000 /* check if file exists */
1001 if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES)
1002 {
1003 LPITEMIDLIST pidl = ILCombine(pidlRoot, apidl[i]);
1004 SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL);
1005 SHFree(pidl);
1006 }
1007
1008 wszCurrentPath += wcslen(wszCurrentPath) + 1;
1009 }
1010 HeapFree(GetProcessHeap(), 0, wszPathsList);
1011 return ret;
1012 }
1013
1014 /****************************************************************************
1015 * CFSFolder::CopyItems
1016 *
1017 * copies items to this folder
1018 */
1019 HRESULT WINAPI CFSFolder::CopyItems(IShellFolder * pSFFrom, UINT cidl,
1020 LPCITEMIDLIST * apidl, BOOL bCopy)
1021 {
1022 CComPtr<IPersistFolder2> ppf2 = NULL;
1023 WCHAR szSrcPath[MAX_PATH];
1024 WCHAR szTargetPath[MAX_PATH];
1025 SHFILEOPSTRUCTW op;
1026 LPITEMIDLIST pidl;
1027 LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
1028 int res, length;
1029 HRESULT hr;
1030 STRRET strRet;
1031
1032 TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
1033
1034 hr = pSFFrom->QueryInterface (IID_PPV_ARG(IPersistFolder2, &ppf2));
1035 if (SUCCEEDED(hr))
1036 {
1037 hr = ppf2->GetCurFolder(&pidl);
1038 if (FAILED(hr))
1039 {
1040 return hr;
1041 }
1042
1043 hr = pSFFrom->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet);
1044 if (FAILED(hr))
1045 {
1046 SHFree(pidl);
1047 return hr;
1048 }
1049
1050 hr = StrRetToBufW(&strRet, pidl, szSrcPath, MAX_PATH);
1051 if (FAILED(hr))
1052 {
1053 SHFree(pidl);
1054 return hr;
1055 }
1056 SHFree(pidl);
1057
1058 pszSrc = PathAddBackslashW(szSrcPath);
1059
1060 wcscpy(szTargetPath, sPathTarget);
1061 pszTarget = PathAddBackslashW(szTargetPath);
1062
1063 pszSrcList = BuildPathsList(szSrcPath, cidl, apidl);
1064 pszTargetList = BuildPathsList(szTargetPath, cidl, apidl);
1065
1066 if (!pszSrcList || !pszTargetList)
1067 {
1068 if (pszSrcList)
1069 HeapFree(GetProcessHeap(), 0, pszSrcList);
1070
1071 if (pszTargetList)
1072 HeapFree(GetProcessHeap(), 0, pszTargetList);
1073
1074 SHFree(pidl);
1075 return E_OUTOFMEMORY;
1076 }
1077
1078 ZeroMemory(&op, sizeof(op));
1079 if (!pszSrcList[0])
1080 {
1081 /* remove trailing backslash */
1082 pszSrc--;
1083 pszSrc[0] = L'\0';
1084 op.pFrom = szSrcPath;
1085 }
1086 else
1087 {
1088 op.pFrom = pszSrcList;
1089 }
1090
1091 if (!pszTargetList[0])
1092 {
1093 /* remove trailing backslash */
1094 if (pszTarget - szTargetPath > 3)
1095 {
1096 pszTarget--;
1097 pszTarget[0] = L'\0';
1098 }
1099 else
1100 {
1101 pszTarget[1] = L'\0';
1102 }
1103
1104 op.pTo = szTargetPath;
1105 op.fFlags = 0;
1106 }
1107 else
1108 {
1109 op.pTo = pszTargetList;
1110 op.fFlags = FOF_MULTIDESTFILES;
1111 }
1112 op.hwnd = GetActiveWindow();
1113 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
1114 op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1115
1116 res = SHFileOperationW(&op);
1117
1118 if (res == DE_SAMEFILE)
1119 {
1120 length = wcslen(szTargetPath);
1121
1122 pszFileName = wcsrchr(pszSrcList, '\\');
1123 pszFileName++;
1124
1125 if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
1126 {
1127 wcscat(szTargetPath, L" ");
1128 }
1129
1130 wcscat(szTargetPath, pszFileName);
1131 op.pTo = szTargetPath;
1132
1133 res = SHFileOperationW(&op);
1134 }
1135
1136 HeapFree(GetProcessHeap(), 0, pszSrcList);
1137 HeapFree(GetProcessHeap(), 0, pszTargetList);
1138
1139 if (res)
1140 return E_FAIL;
1141 else
1142 return S_OK;
1143 }
1144 return E_FAIL;
1145 }
1146
1147 /************************************************************************
1148 * CFSFolder::GetClassID
1149 */
1150 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1151 {
1152 TRACE ("(%p)\n", this);
1153
1154 if (!lpClassId)
1155 return E_POINTER;
1156
1157 *lpClassId = *pclsid;
1158
1159 return S_OK;
1160 }
1161
1162 /************************************************************************
1163 * CFSFolder::Initialize
1164 *
1165 * NOTES
1166 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
1167 */
1168 HRESULT WINAPI CFSFolder::Initialize(LPCITEMIDLIST pidl)
1169 {
1170 WCHAR wszTemp[MAX_PATH];
1171
1172 TRACE ("(%p)->(%p)\n", this, pidl);
1173
1174 SHFree (pidlRoot); /* free the old pidl */
1175 pidlRoot = ILClone (pidl); /* set my pidl */
1176
1177 SHFree (sPathTarget);
1178 sPathTarget = NULL;
1179
1180 /* set my path */
1181 if (SHGetPathFromIDListW (pidl, wszTemp))
1182 {
1183 int len = wcslen(wszTemp);
1184 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1185 if (!sPathTarget)
1186 return E_OUTOFMEMORY;
1187 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1188 }
1189
1190 TRACE ("--(%p)->(%s)\n", this, debugstr_w(sPathTarget));
1191 return S_OK;
1192 }
1193
1194 /**************************************************************************
1195 * CFSFolder::GetCurFolder
1196 */
1197 HRESULT WINAPI CFSFolder::GetCurFolder(LPITEMIDLIST * pidl)
1198 {
1199 TRACE ("(%p)->(%p)\n", this, pidl);
1200
1201 if (!pidl)
1202 return E_POINTER;
1203
1204 *pidl = ILClone(pidlRoot);
1205 return S_OK;
1206 }
1207
1208 /**************************************************************************
1209 * CFSFolder::InitializeEx
1210 *
1211 * FIXME: error handling
1212 */
1213 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1214 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1215 {
1216 WCHAR wszTemp[MAX_PATH];
1217
1218 TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1219 if (ppfti)
1220 TRACE("--%p %s %s 0x%08x 0x%08x\n",
1221 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1222 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1223 ppfti->csidl);
1224
1225 pdump (pidlRootx);
1226 if (ppfti && ppfti->pidlTargetFolder)
1227 pdump(ppfti->pidlTargetFolder);
1228
1229 if (pidlRoot)
1230 __SHFreeAndNil(&pidlRoot); /* free the old */
1231 if (sPathTarget)
1232 __SHFreeAndNil(&sPathTarget);
1233
1234 /*
1235 * Root path and pidl
1236 */
1237 pidlRoot = ILClone(pidlRootx);
1238
1239 /*
1240 * the target folder is spezified in csidl OR pidlTargetFolder OR
1241 * szTargetParsingName
1242 */
1243 if (ppfti)
1244 {
1245 if (ppfti->csidl != -1)
1246 {
1247 if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1248 ppfti->csidl & CSIDL_FLAG_CREATE)) {
1249 int len = wcslen(wszTemp);
1250 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1251 if (!sPathTarget)
1252 return E_OUTOFMEMORY;
1253 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1254 }
1255 }
1256 else if (ppfti->szTargetParsingName[0])
1257 {
1258 int len = wcslen(ppfti->szTargetParsingName);
1259 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1260 if (!sPathTarget)
1261 return E_OUTOFMEMORY;
1262 memcpy(sPathTarget, ppfti->szTargetParsingName,
1263 (len + 1) * sizeof(WCHAR));
1264 }
1265 else if (ppfti->pidlTargetFolder)
1266 {
1267 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1268 {
1269 int len = wcslen(wszTemp);
1270 sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1271 if (!sPathTarget)
1272 return E_OUTOFMEMORY;
1273 memcpy(sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1274 }
1275 }
1276 }
1277
1278 TRACE("--(%p)->(target=%s)\n", this, debugstr_w(sPathTarget));
1279 pdump(pidlRoot);
1280 return (sPathTarget) ? S_OK : E_FAIL;
1281 }
1282
1283 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1284 {
1285 FIXME("(%p)->(%p)\n", this, ppfti);
1286 ZeroMemory(ppfti, sizeof (*ppfti));
1287 return E_NOTIMPL;
1288 }
1289
1290 BOOL
1291 CFSFolder::GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut)
1292 {
1293 WCHAR wszLink[40];
1294
1295 if (!bShortcut)
1296 {
1297 if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink)))
1298 wszLink[0] = L'\0';
1299 }
1300
1301 if (!bShortcut)
1302 swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt);
1303 else
1304 swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt);
1305
1306 for (UINT i = 2; PathFileExistsW(pwszTarget); ++i)
1307 {
1308 if (!bShortcut)
1309 swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt);
1310 else
1311 swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt);
1312 }
1313
1314 return TRUE;
1315 }
1316
1317 /****************************************************************************
1318 * IDropTarget implementation
1319 */
1320 BOOL CFSFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1321 {
1322 /* TODO Windows does different drop effects if dragging across drives.
1323 i.e., it will copy instead of move if the directories are on different disks. */
1324
1325 DWORD dwEffect = DROPEFFECT_MOVE;
1326
1327 *pdwEffect = DROPEFFECT_NONE;
1328
1329 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1330 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1331
1332 if (*pdwEffect == DROPEFFECT_NONE)
1333 *pdwEffect = dwEffect;
1334
1335 /* ... matches the desired effect ? */
1336 if (dwEffect & *pdwEffect) {
1337 return TRUE;
1338 }
1339 }
1340 return FALSE;
1341 }
1342
1343 HRESULT WINAPI CFSFolder::DragEnter(IDataObject *pDataObject,
1344 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1345 {
1346 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1347 FORMATETC fmt;
1348 FORMATETC fmt2;
1349 fAcceptFmt = FALSE;
1350
1351 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1352 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1353
1354 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1355 fAcceptFmt = TRUE;
1356 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1357 fAcceptFmt = TRUE;
1358
1359 QueryDrop(dwKeyState, pdwEffect);
1360 return S_OK;
1361 }
1362
1363 HRESULT WINAPI CFSFolder::DragOver(DWORD dwKeyState, POINTL pt,
1364 DWORD *pdwEffect)
1365 {
1366 TRACE("(%p)\n", this);
1367
1368 if (!pdwEffect)
1369 return E_INVALIDARG;
1370
1371 QueryDrop(dwKeyState, pdwEffect);
1372
1373 return S_OK;
1374 }
1375
1376 HRESULT WINAPI CFSFolder::DragLeave()
1377 {
1378 TRACE("(%p)\n", this);
1379
1380 fAcceptFmt = FALSE;
1381
1382 return S_OK;
1383 }
1384
1385 HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
1386 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1387 {
1388 TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
1389
1390 BOOL fIsOpAsync = FALSE;
1391 CComPtr<IAsyncOperation> pAsyncOperation;
1392
1393 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
1394 {
1395 if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync)
1396 {
1397 _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData)));
1398 data->This = this;
1399 // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder).
1400 pDataObject->AddRef();
1401 pAsyncOperation->StartOperation(NULL);
1402 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream);
1403 this->AddRef();
1404 data->dwKeyState = dwKeyState;
1405 data->pt = pt;
1406 // Need to dereference as pdweffect gets freed.
1407 data->pdwEffect = *pdwEffect;
1408 SHCreateThread(CFSFolder::_DoDropThreadProc, data, NULL, NULL);
1409 return S_OK;
1410 }
1411 }
1412 return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect);
1413 }
1414
1415 HRESULT WINAPI CFSFolder::_DoDrop(IDataObject *pDataObject,
1416 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1417 {
1418 TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect);
1419 FORMATETC fmt;
1420 FORMATETC fmt2;
1421 STGMEDIUM medium;
1422
1423 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1424 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1425
1426 HRESULT hr;
1427 bool bCopy = TRUE;
1428 bool bLinking = FALSE;
1429
1430 /* Figure out what drop operation we're doing */
1431 if (pdwEffect)
1432 {
1433 TRACE("Current drop effect flag %i\n", *pdwEffect);
1434 if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
1435 bCopy = FALSE;
1436 if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK)
1437 bLinking = TRUE;
1438 }
1439
1440 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1441 {
1442 hr = pDataObject->GetData(&fmt, &medium);
1443 TRACE("CFSTR_SHELLIDLIST.\n");
1444
1445 /* lock the handle */
1446 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1447 if (!lpcida)
1448 {
1449 ReleaseStgMedium(&medium);
1450 return E_FAIL;
1451 }
1452
1453 /* convert the data into pidl */
1454 LPITEMIDLIST pidl;
1455 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1456 if (!apidl)
1457 {
1458 ReleaseStgMedium(&medium);
1459 return E_FAIL;
1460 }
1461
1462 CComPtr<IShellFolder> psfDesktop;
1463 CComPtr<IShellFolder> psfFrom = NULL;
1464 CComPtr<IShellFolder> psfTarget = NULL;
1465
1466 hr = this->QueryInterface(IID_PPV_ARG(IShellFolder, &psfTarget));
1467 if (FAILED(hr))
1468 {
1469 ERR("psfTarget setting failed\n");
1470 SHFree(pidl);
1471 _ILFreeaPidl(apidl, lpcida->cidl);
1472 ReleaseStgMedium(&medium);
1473 return E_FAIL;
1474 }
1475
1476 /* Grab the desktop shell folder */
1477 hr = SHGetDesktopFolder(&psfDesktop);
1478 if (FAILED(hr))
1479 {
1480 ERR("SHGetDesktopFolder failed\n");
1481 SHFree(pidl);
1482 _ILFreeaPidl(apidl, lpcida->cidl);
1483 ReleaseStgMedium(&medium);
1484 return E_FAIL;
1485 }
1486
1487 /* Find source folder, this is where the clipboard data was copied from */
1488 if (_ILIsDesktop(pidl))
1489 {
1490 /* use desktop shell folder */
1491 psfFrom = psfDesktop;
1492 }
1493 else
1494 {
1495 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
1496 if (FAILED(hr))
1497 {
1498 ERR("no IShellFolder\n");
1499 SHFree(pidl);
1500 _ILFreeaPidl(apidl, lpcida->cidl);
1501 ReleaseStgMedium(&medium);
1502 return E_FAIL;
1503 }
1504 }
1505
1506 if (bLinking)
1507 {
1508 CComPtr<IPersistFolder2> ppf2 = NULL;
1509 STRRET strFile;
1510 WCHAR wszTargetPath[MAX_PATH];
1511 LPITEMIDLIST targetpidl;
1512 WCHAR wszPath[MAX_PATH];
1513 WCHAR wszTarget[MAX_PATH];
1514
1515 hr = this->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1516 if (SUCCEEDED(hr))
1517 {
1518 hr = ppf2->GetCurFolder(&targetpidl);
1519 if (SUCCEEDED(hr))
1520 {
1521 hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
1522 ILFree(targetpidl);
1523 if (SUCCEEDED(hr))
1524 {
1525 hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
1526 }
1527 }
1528 }
1529
1530 if (FAILED(hr))
1531 {
1532 ERR("Error obtaining target path");
1533 }
1534
1535 TRACE("target path = %s", debugstr_w(wszTargetPath));
1536
1537 /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
1538 for (UINT i = 0; i < lpcida->cidl; i++)
1539 {
1540 //Find out which file we're copying
1541 STRRET strFile;
1542 hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
1543 if (FAILED(hr))
1544 {
1545 ERR("Error source obtaining path");
1546 break;
1547 }
1548
1549 hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
1550 if (FAILED(hr))
1551 {
1552 ERR("Error putting source path into buffer");
1553 break;
1554 }
1555 TRACE("source path = %s", debugstr_w(wszPath));
1556
1557 // Creating a buffer to hold the combined path
1558 WCHAR buffer_1[MAX_PATH] = L"";
1559 WCHAR *lpStr1;
1560 lpStr1 = buffer_1;
1561
1562 LPWSTR pwszFileName = PathFindFileNameW(wszPath);
1563 LPWSTR pwszExt = PathFindExtensionW(wszPath);
1564 LPWSTR placementPath = PathCombineW(lpStr1, wszTargetPath, pwszFileName);
1565 CComPtr<IPersistFile> ppf;
1566
1567 //Check to see if it's already a link.
1568 if (!wcsicmp(pwszExt, L".lnk"))
1569 {
1570 //It's a link so, we create a new one which copies the old.
1571 if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE))
1572 {
1573 ERR("Error getting unique file name");
1574 hr = E_FAIL;
1575 break;
1576 }
1577 hr = IShellLink_ConstructFromFile(NULL, IID_IPersistFile, ILCombine(pidl, apidl[i]), (LPVOID*)&ppf);
1578 if (FAILED(hr)) {
1579 ERR("Error constructing link from file");
1580 break;
1581 }
1582
1583 hr = ppf->Save(wszTarget, FALSE);
1584 if (FAILED(hr))
1585 break;
1586 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
1587 }
1588 else
1589 {
1590 //It's not a link, so build a new link using the creator class and fill it in.
1591 //Create a file name for the link
1592 if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE))
1593 {
1594 ERR("Error creating unique file name");
1595 hr = E_FAIL;
1596 break;
1597 }
1598
1599 CComPtr<IShellLinkW> pLink;
1600 hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
1601 if (FAILED(hr)) {
1602 ERR("Error instantiating IShellLinkW");
1603 break;
1604 }
1605
1606 WCHAR szDirPath[MAX_PATH], *pwszFile;
1607 GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
1608 if (pwszFile) pwszFile[0] = 0;
1609
1610 hr = pLink->SetPath(wszPath);
1611 if(FAILED(hr))
1612 break;
1613
1614 hr = pLink->SetWorkingDirectory(szDirPath);
1615 if(FAILED(hr))
1616 break;
1617
1618 hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
1619 if(FAILED(hr))
1620 break;
1621
1622 hr = ppf->Save(wszTarget, TRUE);
1623 if (FAILED(hr))
1624 break;
1625 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
1626 }
1627 }
1628 }
1629 else
1630 {
1631 hr = this->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
1632 }
1633
1634 SHFree(pidl);
1635 _ILFreeaPidl(apidl, lpcida->cidl);
1636 ReleaseStgMedium(&medium);
1637 }
1638 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1639 {
1640 FORMATETC fmt2;
1641 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1642 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/)
1643 {
1644 CComPtr<IPersistFolder2> ppf2 = NULL;
1645 STRRET strFile;
1646 WCHAR wszTargetPath[MAX_PATH + 1];
1647 LPWSTR pszSrcList;
1648 LPITEMIDLIST targetpidl;
1649 CComPtr<IShellFolder> psfDesktop = NULL;
1650 hr = SHGetDesktopFolder(&psfDesktop);
1651 if (FAILED(hr))
1652 {
1653 ERR("SHGetDesktopFolder failed\n");
1654 return E_FAIL;
1655 }
1656
1657 hr = this->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1658 if (SUCCEEDED(hr))
1659 {
1660 hr = ppf2->GetCurFolder(&targetpidl);
1661 if (SUCCEEDED(hr))
1662 {
1663 hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
1664 ILFree(targetpidl);
1665 if (SUCCEEDED(hr))
1666 {
1667 hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
1668 //Double NULL terminate.
1669 wszTargetPath[wcslen(wszTargetPath) + 1] = '\0';
1670 }
1671 }
1672 }
1673 if (FAILED(hr))
1674 {
1675 ERR("Error obtaining target path");
1676 return E_FAIL;
1677 }
1678
1679 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
1680 if (!lpdf)
1681 {
1682 ERR("Error locking global\n");
1683 return E_FAIL;
1684 }
1685 pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);
1686 TRACE("Source file (just the first) = %s\n", debugstr_w(pszSrcList));
1687 TRACE("Target path = %s\n", debugstr_w(wszTargetPath));
1688
1689 SHFILEOPSTRUCTW op;
1690 ZeroMemory(&op, sizeof(op));
1691 op.pFrom = pszSrcList;
1692 op.pTo = wszTargetPath;
1693 op.hwnd = GetActiveWindow();
1694 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
1695 op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1696 hr = SHFileOperationW(&op);
1697 return hr;
1698 }
1699 ERR("Error calling GetData\n");
1700 hr = E_FAIL;
1701 }
1702 else
1703 {
1704 ERR("No viable drop format.\n");
1705 hr = E_FAIL;
1706 }
1707 return hr;
1708 }
1709
1710 DWORD WINAPI CFSFolder::_DoDropThreadProc(LPVOID lpParameter) {
1711 CoInitialize(NULL);
1712 _DoDropData *data = static_cast<_DoDropData*>(lpParameter);
1713 CComPtr<IDataObject> pDataObject;
1714 HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject));
1715
1716 if (SUCCEEDED(hr))
1717 {
1718 CComPtr<IAsyncOperation> pAsyncOperation;
1719 hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect);
1720 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
1721 {
1722 pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect);
1723 }
1724 }
1725 //Release the CFSFolder and data object holds in the copying thread.
1726 data->This->Release();
1727 //Release the parameter from the heap.
1728 HeapFree(GetProcessHeap(), 0, data);
1729 CoUninitialize();
1730 return 0;
1731 }
1732
1733 HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1734 HKEY hKey;
1735 HRESULT hr;
1736
1737 TRACE("CFSFolder::_GetDropTarget entered\n");
1738
1739 if (_ILGetGUIDPointer (pidl) || _ILIsFolder (pidl))
1740 return this->BindToObject(pidl, NULL, IID_IDropTarget, ppvOut);
1741
1742 STRRET strFile;
1743 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1744 if (hr == S_OK)
1745 {
1746 WCHAR wszPath[MAX_PATH];
1747 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1748
1749 if (hr == S_OK)
1750 {
1751 LPCWSTR pwszExt = PathFindExtensionW(wszPath);
1752 if (pwszExt[0])
1753 {
1754 /* enumerate dynamic/static for a given file class */
1755 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1756 {
1757 /* load dynamic extensions from file extension key, for example .jpg */
1758 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1759 RegCloseKey(hKey);
1760 }
1761
1762 WCHAR wszTemp[40];
1763 DWORD dwSize = sizeof(wszTemp);
1764 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
1765 {
1766 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
1767 {
1768 /* load dynamic extensions from progid key, for example jpegfile */
1769 _LoadDynamicDropTargetHandlerForKey(hKey, wszPath, ppvOut);
1770 RegCloseKey(hKey);
1771 }
1772 }
1773 }
1774 }
1775 }
1776 else
1777 ERR("GetDisplayNameOf failed: %x\n", hr);
1778
1779 return hr;
1780 }
1781
1782 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandlerForKey(HKEY hRootKey, LPCWSTR pwcsname, LPVOID *ppvOut)
1783 {
1784 TRACE("CFSFolder::_LoadDynamicDropTargetHandlerForKey entered\n");
1785
1786 WCHAR wszName[MAX_PATH], *pwszClsid;
1787 DWORD dwSize = sizeof(wszName);
1788 HRESULT hr;
1789
1790 if (RegGetValueW(hRootKey, L"shellex\\DropHandler", NULL, RRF_RT_REG_SZ, NULL, wszName, &dwSize) == ERROR_SUCCESS)
1791 {
1792 CLSID clsid;
1793 hr = CLSIDFromString(wszName, &clsid);
1794 if (hr == S_OK)
1795 pwszClsid = wszName;
1796
1797 if (m_bGroupPolicyActive)
1798 {
1799 if (RegGetValueW(HKEY_LOCAL_MACHINE,
1800 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1801 pwszClsid,
1802 RRF_RT_REG_SZ,
1803 NULL,
1804 NULL,
1805 NULL) == ERROR_SUCCESS)
1806 {
1807 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1808 }
1809 }
1810 else
1811 {
1812 hr = _LoadDynamicDropTargetHandler(&clsid, pwcsname, ppvOut);
1813 }
1814 }
1815 else
1816 return E_FAIL;
1817 return hr;
1818 }
1819
1820 HRESULT WINAPI CFSFolder::_LoadDynamicDropTargetHandler(const CLSID *pclsid, LPCWSTR pwcsname, LPVOID *ppvOut)
1821 {
1822 TRACE("CFSFolder::_LoadDynamicDropTargetHandler entered\n");
1823 HRESULT hr;
1824
1825 CComPtr<IPersistFile> pp;
1826 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1827 if (hr != S_OK)
1828 {
1829 ERR("SHCoCreateInstance failed %x\n", GetLastError());
1830 }
1831 pp->Load(pwcsname, 0);
1832
1833 hr = pp->QueryInterface(IID_PPV_ARG(IDropTarget, (IDropTarget**) ppvOut));
1834 if (hr != S_OK)
1835 {
1836 ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1837 return hr;
1838 }
1839 return hr;
1840 }