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