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