Sync with trunk r63743.
[reactos.git] / dll / win32 / shell32 / folders / desktop.cpp
1 /*
2 * Virtual Desktop Folder
3 *
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2009 Andrew Hill
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 CDesktopFolder should create two file system folders internally, one representing the
29 user's desktop folder, and the other representing the common desktop folder. It should
30 also create a CRegFolder to represent the virtual items that exist only in the registry.
31 The CRegFolder is aggregated by the CDesktopFolder, and queries for the CLSID_IShellFolder,
32 CLSID_IShellFolder2, or CLSID_IShellIconOverlay interfaces prefer the CRegFolder
33 implementation.
34 The CDesktopFolderEnum class should create two enumerators, one for each of the file
35 system folders, and enumerate the contents of each folder. Since the CRegFolder
36 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
37 CDesktopFolderEnum is only responsible for returning the physical items.
38 CDesktopFolderEnum is incorrect where it filters My Computer from the enumeration
39 if the new start menu is used. The CDesktopViewCallback is responsible for filtering
40 it from the view by handling the IncludeObject query to return S_FALSE. The enumerator
41 always shows My Computer.
42 */
43
44 /* Undocumented functions from shdocvw */
45 extern "C" HRESULT WINAPI IEParseDisplayNameWithBCW(DWORD codepage, LPCWSTR lpszDisplayName, LPBC pbc, LPITEMIDLIST *ppidl);
46
47 /***********************************************************************
48 * Desktopfolder implementation
49 */
50
51 class CDesktopFolder;
52
53 class CDesktopFolderEnum :
54 public IEnumIDListImpl
55 {
56 private:
57 // CComPtr fDesktopEnumerator;
58 // CComPtr fCommonDesktopEnumerator;
59 public:
60 CDesktopFolderEnum();
61 ~CDesktopFolderEnum();
62 HRESULT WINAPI Initialize(CDesktopFolder *desktopFolder, HWND hwndOwner, DWORD dwFlags);
63
64 BEGIN_COM_MAP(CDesktopFolderEnum)
65 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
66 END_COM_MAP()
67 };
68
69 int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll);
70
71 static const shvheader DesktopSFHeader[] = {
72 {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
73 {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
74 {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
75 {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
76 {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 5}
77 };
78
79 #define DESKTOPSHELLVIEWCOLUMNS 5
80
81 CDesktopFolderEnum::CDesktopFolderEnum()
82 {
83 }
84
85 CDesktopFolderEnum::~CDesktopFolderEnum()
86 {
87 }
88
89 static const WCHAR ClassicStartMenuW[] = L"SOFTWARE\\Microsoft\\Windows\\"
90 L"CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu";
91
92 static INT
93 IsNamespaceExtensionHidden(const WCHAR *iid)
94 {
95 DWORD Result, dwResult;
96 dwResult = sizeof(DWORD);
97
98 if (RegGetValueW(HKEY_CURRENT_USER, /* FIXME use NewStartPanel when activated */
99 ClassicStartMenuW,
100 iid,
101 RRF_RT_DWORD,
102 NULL,
103 &Result,
104 &dwResult) != ERROR_SUCCESS)
105 {
106 return -1;
107 }
108
109 return Result;
110 }
111
112 static VOID
113 SetNamespaceExtensionVisibleStatus(const WCHAR * iid, DWORD dwStatus)
114 {
115 HKEY hKey;
116
117 if (RegOpenKeyExW(HKEY_CURRENT_USER, ClassicStartMenuW, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
118 {
119 RegSetValueExW(hKey, iid, 0, REG_DWORD, (LPBYTE)&dwStatus, sizeof(DWORD));
120 RegCloseKey(hKey);
121 }
122 }
123
124 /**************************************************************************
125 * CreateDesktopEnumList()
126 */
127
128 HRESULT WINAPI CDesktopFolderEnum::Initialize(CDesktopFolder *desktopFolder, HWND hwndOwner, DWORD dwFlags)
129 {
130 BOOL ret = TRUE;
131 WCHAR szPath[MAX_PATH];
132
133 static const WCHAR MyDocumentsClassString[] = L"{450D8FBA-AD25-11D0-98A8-0800361B1103}";
134 static const WCHAR Desktop_NameSpaceW[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\Namespace";
135
136 TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
137
138 /* enumerate the root folders */
139 if (dwFlags & SHCONTF_FOLDERS)
140 {
141 HKEY hkey;
142 UINT i;
143 DWORD dwResult;
144
145 /* create the pidl for This item */
146 if (IsNamespaceExtensionHidden(MyDocumentsClassString) < 1)
147 {
148 ret = AddToEnumList(_ILCreateMyDocuments());
149 }
150 ret = AddToEnumList(_ILCreateMyComputer());
151
152 for (i = 0; i < 2; i++)
153 {
154 if (i == 0)
155 dwResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Desktop_NameSpaceW, 0, KEY_READ, &hkey);
156 else
157 dwResult = RegOpenKeyExW(HKEY_CURRENT_USER, Desktop_NameSpaceW, 0, KEY_READ, &hkey);
158
159 if (dwResult == ERROR_SUCCESS)
160 {
161 WCHAR iid[50];
162 LPITEMIDLIST pidl;
163 int i = 0;
164
165 while (ret)
166 {
167 DWORD size;
168 LONG r;
169
170 size = sizeof (iid) / sizeof (iid[0]);
171 r = RegEnumKeyExW(hkey, i, iid, &size, 0, NULL, NULL, NULL);
172 if (ERROR_SUCCESS == r)
173 {
174 if (IsNamespaceExtensionHidden(iid) < 1)
175 {
176 pidl = _ILCreateGuidFromStrW(iid);
177 if (pidl != NULL)
178 {
179 if (!HasItemWithCLSID(pidl))
180 {
181 ret = AddToEnumList(pidl);
182 }
183 else
184 {
185 SHFree(pidl);
186 }
187 }
188 }
189 }
190 else if (ERROR_NO_MORE_ITEMS == r)
191 break;
192 else
193 ret = FALSE;
194 i++;
195 }
196 RegCloseKey(hkey);
197 }
198 }
199 for (i = 0; i < 2; i++)
200 {
201 if (i == 0)
202 dwResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, ClassicStartMenuW, 0, KEY_READ, &hkey);
203 else
204 dwResult = RegOpenKeyExW(HKEY_CURRENT_USER, ClassicStartMenuW, 0, KEY_READ, &hkey);
205
206 if (dwResult == ERROR_SUCCESS)
207 {
208 DWORD j = 0, dwVal, Val, dwType, dwIID;
209 LONG r;
210 WCHAR iid[50];
211
212 while(ret)
213 {
214 dwVal = sizeof(Val);
215 dwIID = sizeof(iid) / sizeof(WCHAR);
216
217 r = RegEnumValueW(hkey, j++, iid, &dwIID, NULL, &dwType, (LPBYTE)&Val, &dwVal);
218 if (r == ERROR_SUCCESS)
219 {
220 if (Val == 0 && dwType == REG_DWORD)
221 {
222 LPITEMIDLIST pidl = _ILCreateGuidFromStrW(iid);
223 if (pidl != NULL)
224 {
225 if (!HasItemWithCLSID(pidl))
226 {
227 AddToEnumList(pidl);
228 }
229 else
230 {
231 SHFree(pidl);
232 }
233 }
234 }
235 }
236 else if (ERROR_NO_MORE_ITEMS == r)
237 break;
238 else
239 ret = FALSE;
240 }
241 RegCloseKey(hkey);
242 }
243
244 }
245 }
246
247 /* enumerate the elements in %windir%\desktop */
248 ret = ret && SHGetSpecialFolderPathW(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
249 ret = ret && CreateFolderEnumList(szPath, dwFlags);
250
251 ret = ret && SHGetSpecialFolderPathW(0, szPath, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
252 ret = ret && CreateFolderEnumList(szPath, dwFlags);
253
254 return ret ? S_OK : E_FAIL;
255 }
256
257 void CDesktopFolder::SF_RegisterClipFmt()
258 {
259 TRACE ("(%p)\n", this);
260
261 if (!cfShellIDList)
262 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
263 }
264
265 CDesktopFolder::CDesktopFolder()
266 {
267 pidlRoot = NULL;
268 sPathTarget = NULL;
269 cfShellIDList = 0;
270 SF_RegisterClipFmt();
271 fAcceptFmt = FALSE;
272 }
273
274 CDesktopFolder::~CDesktopFolder()
275 {
276 }
277
278 HRESULT WINAPI CDesktopFolder::FinalConstruct()
279 {
280 WCHAR szMyPath[MAX_PATH];
281
282 if (!SHGetSpecialFolderPathW( 0, szMyPath, CSIDL_DESKTOPDIRECTORY, TRUE ))
283 return E_UNEXPECTED;
284
285 pidlRoot = _ILCreateDesktop(); /* my qualified pidl */
286 sPathTarget = (LPWSTR)SHAlloc((wcslen(szMyPath) + 1) * sizeof(WCHAR));
287 wcscpy(sPathTarget, szMyPath);
288 return S_OK;
289 }
290
291 /**************************************************************************
292 * CDesktopFolder::ParseDisplayName
293 *
294 * NOTES
295 * "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds
296 * to MyComputer
297 */
298 HRESULT WINAPI CDesktopFolder::ParseDisplayName(
299 HWND hwndOwner,
300 LPBC pbc,
301 LPOLESTR lpszDisplayName,
302 DWORD *pchEaten,
303 LPITEMIDLIST *ppidl,
304 DWORD *pdwAttributes)
305 {
306 WCHAR szElement[MAX_PATH];
307 LPCWSTR szNext = NULL;
308 LPITEMIDLIST pidlTemp = NULL;
309 PARSEDURLW urldata;
310 HRESULT hr = S_OK;
311 CLSID clsid;
312
313 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
314 this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
315 pchEaten, ppidl, pdwAttributes);
316
317 if (!ppidl)
318 return E_INVALIDARG;
319
320 if (!lpszDisplayName)
321 {
322 *ppidl = NULL;
323 return E_INVALIDARG;
324 }
325
326 *ppidl = NULL;
327
328 if (pchEaten)
329 *pchEaten = 0; /* strange but like the original */
330
331 urldata.cbSize = sizeof(urldata);
332
333 if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
334 {
335 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
336 TRACE ("-- element: %s\n", debugstr_w (szElement));
337 CLSIDFromString (szElement + 2, &clsid);
338 pidlTemp = _ILCreateGuid (PT_GUID, clsid);
339 }
340 else if (PathGetDriveNumberW (lpszDisplayName) >= 0)
341 {
342 /* it's a filesystem path with a drive. Let MyComputer/UnixDosFolder parse it */
343 pidlTemp = _ILCreateMyComputer ();
344 szNext = lpszDisplayName;
345 }
346 else if (PathIsUNCW(lpszDisplayName))
347 {
348 pidlTemp = _ILCreateNetwork();
349 szNext = lpszDisplayName;
350 }
351 else if( (pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName)) )
352 {
353 *ppidl = pidlTemp;
354 return S_OK;
355 }
356 else if (SUCCEEDED(ParseURLW(lpszDisplayName, &urldata)))
357 {
358 if (urldata.nScheme == URL_SCHEME_SHELL) /* handle shell: urls */
359 {
360 TRACE ("-- shell url: %s\n", debugstr_w(urldata.pszSuffix));
361 SHCLSIDFromStringW (urldata.pszSuffix + 2, &clsid);
362 pidlTemp = _ILCreateGuid (PT_GUID, clsid);
363 }
364 else
365 return IEParseDisplayNameWithBCW(CP_ACP, lpszDisplayName, pbc, ppidl);
366 }
367 else
368 {
369 /* it's a filesystem path on the desktop. Let a FSFolder parse it */
370
371 if (*lpszDisplayName)
372 {
373 WCHAR szPath[MAX_PATH];
374 LPWSTR pathPtr;
375
376 /* build a complete path to create a simple pidl */
377 lstrcpynW(szPath, sPathTarget, MAX_PATH);
378 pathPtr = PathAddBackslashW(szPath);
379 if (pathPtr)
380 {
381 lstrcpynW(pathPtr, lpszDisplayName, MAX_PATH - (pathPtr - szPath));
382 hr = _ILCreateFromPathW(szPath, &pidlTemp);
383 }
384 else
385 {
386 /* should never reach here, but for completeness */
387 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
388 }
389 }
390 else
391 pidlTemp = _ILCreateMyComputer();
392
393 szNext = NULL;
394 }
395
396 if (SUCCEEDED(hr) && pidlTemp)
397 {
398 if (szNext && *szNext)
399 {
400 hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
401 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
402 }
403 else
404 {
405 if (pdwAttributes && *pdwAttributes)
406 hr = SHELL32_GetItemAttributes((IShellFolder *)this,
407 pidlTemp, pdwAttributes);
408 }
409 }
410
411 if (SUCCEEDED(hr))
412 *ppidl = pidlTemp;
413 else
414 *ppidl = NULL;
415
416 TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
417
418 return hr;
419 }
420
421 /**************************************************************************
422 * CDesktopFolder::EnumObjects
423 */
424 HRESULT WINAPI CDesktopFolder::EnumObjects(
425 HWND hwndOwner,
426 DWORD dwFlags,
427 LPENUMIDLIST *ppEnumIDList)
428 {
429 CComObject<CDesktopFolderEnum> *theEnumerator;
430 CComPtr<IEnumIDList> result;
431 HRESULT hResult;
432
433 TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", this, hwndOwner, dwFlags, ppEnumIDList);
434
435 if (ppEnumIDList == NULL)
436 return E_POINTER;
437 *ppEnumIDList = NULL;
438
439 ATLTRY (theEnumerator = new CComObject<CDesktopFolderEnum>);
440
441 if (theEnumerator == NULL)
442 return E_OUTOFMEMORY;
443
444 hResult = theEnumerator->QueryInterface(IID_PPV_ARG(IEnumIDList, &result));
445 if (FAILED (hResult))
446 {
447 delete theEnumerator;
448 return hResult;
449 }
450
451 hResult = theEnumerator->Initialize (this, hwndOwner, dwFlags);
452 if (FAILED (hResult))
453 return hResult;
454 *ppEnumIDList = result.Detach ();
455
456 TRACE ("-- (%p)->(new ID List: %p)\n", this, *ppEnumIDList);
457
458 return S_OK;
459 }
460
461 /**************************************************************************
462 * CDesktopFolder::BindToObject
463 */
464 HRESULT WINAPI CDesktopFolder::BindToObject(
465 LPCITEMIDLIST pidl,
466 LPBC pbcReserved,
467 REFIID riid,
468 LPVOID *ppvOut)
469 {
470 TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n",
471 this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
472
473 return SHELL32_BindToChild( pidlRoot, sPathTarget, pidl, riid, ppvOut );
474 }
475
476 /**************************************************************************
477 * CDesktopFolder::BindToStorage
478 */
479 HRESULT WINAPI CDesktopFolder::BindToStorage(
480 LPCITEMIDLIST pidl,
481 LPBC pbcReserved,
482 REFIID riid,
483 LPVOID *ppvOut)
484 {
485 FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
486 this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
487
488 *ppvOut = NULL;
489 return E_NOTIMPL;
490 }
491
492 /**************************************************************************
493 * CDesktopFolder::CompareIDs
494 */
495 HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
496 {
497 int nReturn;
498
499 TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam, pidl1, pidl2);
500 nReturn = SHELL32_CompareIDs ((IShellFolder *)this, lParam, pidl1, pidl2);
501 TRACE ("-- %i\n", nReturn);
502 return nReturn;
503 }
504
505 /**************************************************************************
506 * CDesktopFolder::CreateViewObject
507 */
508 HRESULT WINAPI CDesktopFolder::CreateViewObject(
509 HWND hwndOwner,
510 REFIID riid,
511 LPVOID *ppvOut)
512 {
513 CComPtr<IShellView> pShellView;
514 HRESULT hr = E_INVALIDARG;
515
516 TRACE ("(%p)->(hwnd=%p,%s,%p)\n",
517 this, hwndOwner, shdebugstr_guid (&riid), ppvOut);
518
519 if (!ppvOut)
520 return hr;
521
522 *ppvOut = NULL;
523
524 if (IsEqualIID (riid, IID_IDropTarget))
525 {
526 hr = this->QueryInterface (IID_IDropTarget, ppvOut);
527 }
528 else if (IsEqualIID (riid, IID_IContextMenu))
529 {
530 WARN ("IContextMenu not implemented\n");
531 hr = E_NOTIMPL;
532 }
533 else if (IsEqualIID (riid, IID_IShellView))
534 {
535 hr = IShellView_Constructor((IShellFolder *)this, &pShellView);
536 if (pShellView)
537 hr = pShellView->QueryInterface(riid, ppvOut);
538 }
539 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
540 return hr;
541 }
542
543 /**************************************************************************
544 * CDesktopFolder::GetAttributesOf
545 */
546 HRESULT WINAPI CDesktopFolder::GetAttributesOf(
547 UINT cidl,
548 LPCITEMIDLIST *apidl,
549 DWORD *rgfInOut)
550 {
551 HRESULT hr = S_OK;
552 static const DWORD dwDesktopAttributes =
553 SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
554 SFGAO_STORAGEANCESTOR | SFGAO_HASPROPSHEET | SFGAO_STORAGE | SFGAO_CANLINK;
555 static const DWORD dwMyComputerAttributes =
556 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
557 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
558 static DWORD dwMyNetPlacesAttributes =
559 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
560 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
561
562 TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
563 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
564
565 if (cidl && !apidl)
566 return E_INVALIDARG;
567
568 if (*rgfInOut == 0)
569 *rgfInOut = ~0;
570
571 if(cidl == 0)
572 *rgfInOut &= dwDesktopAttributes;
573 else
574 {
575 /* TODO: always add SFGAO_CANLINK */
576 for (UINT i = 0; i < cidl; ++i)
577 {
578 pdump(*apidl);
579 if (_ILIsDesktop(*apidl))
580 *rgfInOut &= dwDesktopAttributes;
581 else if (_ILIsMyComputer(apidl[i]))
582 *rgfInOut &= dwMyComputerAttributes;
583 else if (_ILIsNetHood(apidl[i]))
584 *rgfInOut &= dwMyNetPlacesAttributes;
585 else
586 SHELL32_GetItemAttributes((IShellFolder *)this, apidl[i], rgfInOut);
587 }
588 }
589 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
590 *rgfInOut &= ~SFGAO_VALIDATE;
591
592 TRACE("-- result=0x%08x\n", *rgfInOut);
593
594 return hr;
595 }
596
597 /**************************************************************************
598 * CDesktopFolder::GetUIObjectOf
599 *
600 * PARAMETERS
601 * HWND hwndOwner, //[in ] Parent window for any output
602 * UINT cidl, //[in ] array size
603 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
604 * REFIID riid, //[in ] Requested Interface
605 * UINT* prgfInOut, //[ ] reserved
606 * LPVOID* ppvObject) //[out] Resulting Interface
607 *
608 */
609 HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
610 HWND hwndOwner,
611 UINT cidl,
612 LPCITEMIDLIST *apidl,
613 REFIID riid,
614 UINT *prgfInOut,
615 LPVOID *ppvOut)
616 {
617 LPITEMIDLIST pidl;
618 IUnknown *pObj = NULL;
619 HRESULT hr = E_INVALIDARG;
620
621 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
622 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
623
624 if (!ppvOut)
625 return hr;
626
627 *ppvOut = NULL;
628
629 if (IsEqualIID (riid, IID_IContextMenu))
630 {
631 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, (IShellFolder *)this, NULL, 0, NULL, (IContextMenu **)&pObj);
632 }
633 else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
634 {
635 hr = IDataObject_Constructor( hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
636 }
637 else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
638 {
639 pidl = ILCombine (pidlRoot, apidl[0]);
640 pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl);
641 SHFree (pidl);
642 hr = S_OK;
643 }
644 else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
645 {
646 pidl = ILCombine (pidlRoot, apidl[0]);
647 pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl);
648 SHFree (pidl);
649 hr = S_OK;
650 }
651 else if (IsEqualIID (riid, IID_IDropTarget))
652 {
653 /* only interested in attempting to bind to shell folders, not files, semicolon intentionate */
654 if (cidl == 1 && SUCCEEDED(hr = this->_GetDropTarget(apidl[0], (LPVOID*)&pObj)));
655 else
656 hr = this->QueryInterface(IID_IDropTarget, (LPVOID*)&pObj);
657 }
658 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
659 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
660 {
661 pidl = ILCombine (pidlRoot, apidl[0]);
662 hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
663 SHFree (pidl);
664 }
665 else
666 hr = E_NOINTERFACE;
667
668 if (SUCCEEDED(hr) && !pObj)
669 hr = E_OUTOFMEMORY;
670
671 *ppvOut = pObj;
672 TRACE ("(%p)->hr=0x%08x\n", this, hr);
673 return hr;
674 }
675
676 /**************************************************************************
677 * CDesktopFolder::GetDisplayNameOf
678 *
679 * NOTES
680 * special case: pidl = null gives desktop-name back
681 */
682 HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
683 {
684 HRESULT hr = S_OK;
685 LPWSTR pszPath;
686
687 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
688 pdump (pidl);
689
690 if (!strRet)
691 return E_INVALIDARG;
692
693 pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
694 if (!pszPath)
695 return E_OUTOFMEMORY;
696
697 if (_ILIsDesktop (pidl))
698 {
699 if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
700 (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING))
701 wcscpy(pszPath, sPathTarget);
702 else
703 HCR_GetClassNameW(CLSID_ShellDesktop, pszPath, MAX_PATH);
704 }
705 else if (_ILIsPidlSimple (pidl))
706 {
707 GUID const *clsid;
708
709 if ((clsid = _ILGetGUIDPointer (pidl)))
710 {
711 if (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING)
712 {
713 int bWantsForParsing;
714
715 /*
716 * We can only get a filesystem path from a shellfolder if the
717 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
718 *
719 * Exception: The MyComputer folder doesn't have this key,
720 * but any other filesystem backed folder it needs it.
721 */
722 if (IsEqualIID (*clsid, CLSID_MyComputer))
723 {
724 bWantsForParsing = TRUE;
725 }
726 else
727 {
728 /* get the "WantsFORPARSING" flag from the registry */
729 static const WCHAR clsidW[] =
730 { 'C', 'L', 'S', 'I', 'D', '\\', 0 };
731 static const WCHAR shellfolderW[] =
732 { '\\', 's', 'h', 'e', 'l', 'l', 'f', 'o', 'l', 'd', 'e', 'r', 0 };
733 static const WCHAR wantsForParsingW[] =
734 { 'W', 'a', 'n', 't', 's', 'F', 'o', 'r', 'P', 'a', 'r', 's', 'i', 'n',
735 'g', 0
736 };
737 WCHAR szRegPath[100];
738 LONG r;
739
740 wcscpy (szRegPath, clsidW);
741 SHELL32_GUIDToStringW (*clsid, &szRegPath[6]);
742 wcscat (szRegPath, shellfolderW);
743 r = SHGetValueW(HKEY_CLASSES_ROOT, szRegPath,
744 wantsForParsingW, NULL, NULL, NULL);
745 if (r == ERROR_SUCCESS)
746 bWantsForParsing = TRUE;
747 else
748 bWantsForParsing = FALSE;
749 }
750
751 if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
752 bWantsForParsing)
753 {
754 /*
755 * we need the filesystem path to the destination folder.
756 * Only the folder itself can know it
757 */
758 hr = SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags,
759 pszPath,
760 MAX_PATH);
761 }
762 else
763 {
764 /* parsing name like ::{...} */
765 pszPath[0] = ':';
766 pszPath[1] = ':';
767 SHELL32_GUIDToStringW (*clsid, &pszPath[2]);
768 }
769 }
770 else
771 {
772 /* user friendly name */
773 HCR_GetClassNameW (*clsid, pszPath, MAX_PATH);
774 }
775 }
776 else
777 {
778 int cLen = 0;
779
780 /* file system folder or file rooted at the desktop */
781 if ((GET_SHGDN_FOR(dwFlags) == SHGDN_FORPARSING) &&
782 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
783 {
784 lstrcpynW(pszPath, sPathTarget, MAX_PATH - 1);
785 PathAddBackslashW(pszPath);
786 cLen = wcslen(pszPath);
787 }
788
789 _ILSimpleGetTextW(pidl, pszPath + cLen, MAX_PATH - cLen);
790 if (!_ILIsFolder(pidl))
791 SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
792
793 if (GetFileAttributes(pszPath) == INVALID_FILE_ATTRIBUTES)
794 {
795 /* file system folder or file rooted at the AllUsers desktop */
796 if ((GET_SHGDN_FOR(dwFlags) == SHGDN_FORPARSING) &&
797 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
798 {
799 SHGetSpecialFolderPathW(0, pszPath, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
800 PathAddBackslashW(pszPath);
801 cLen = wcslen(pszPath);
802 }
803
804 _ILSimpleGetTextW(pidl, pszPath + cLen, MAX_PATH - cLen);
805 if (!_ILIsFolder(pidl))
806 SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
807 }
808 }
809 }
810 else
811 {
812 /* a complex pidl, let the subfolder do the work */
813 hr = SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags,
814 pszPath, MAX_PATH);
815 }
816
817 if (SUCCEEDED(hr))
818 {
819 /* Win9x always returns ANSI strings, NT always returns Unicode strings */
820 if (GetVersion() & 0x80000000)
821 {
822 strRet->uType = STRRET_CSTR;
823 if (!WideCharToMultiByte(CP_ACP, 0, pszPath, -1, strRet->cStr, MAX_PATH,
824 NULL, NULL))
825 strRet->cStr[0] = '\0';
826 CoTaskMemFree(pszPath);
827 }
828 else
829 {
830 strRet->uType = STRRET_WSTR;
831 strRet->pOleStr = pszPath;
832 }
833 }
834 else
835 CoTaskMemFree(pszPath);
836
837 TRACE ("-- (%p)->(%s,0x%08x)\n", this,
838 strRet->uType == STRRET_CSTR ? strRet->cStr :
839 debugstr_w(strRet->pOleStr), hr);
840 return hr;
841 }
842
843 /**************************************************************************
844 * CDesktopFolder::SetNameOf
845 * Changes the name of a file object or subfolder, possibly changing its item
846 * identifier in the process.
847 *
848 * PARAMETERS
849 * HWND hwndOwner, //[in ] Owner window for output
850 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
851 * LPCOLESTR lpszName, //[in ] the items new display name
852 * DWORD dwFlags, //[in ] SHGNO formatting flags
853 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
854 */
855 HRESULT WINAPI CDesktopFolder::SetNameOf(
856 HWND hwndOwner,
857 LPCITEMIDLIST pidl, /* simple pidl */
858 LPCOLESTR lpName,
859 DWORD dwFlags,
860 LPITEMIDLIST *pPidlOut)
861 {
862 CComPtr<IShellFolder2> psf;
863 HRESULT hr;
864 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
865 LPWSTR ptr;
866 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
867
868 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
869 debugstr_w (lpName), dwFlags, pPidlOut);
870
871 if (_ILGetGUIDPointer(pidl))
872 {
873 if (SUCCEEDED(BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &psf))))
874 {
875 hr = psf->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
876 return hr;
877 }
878 }
879
880 /* build source path */
881 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
882 ptr = PathAddBackslashW (szSrc);
883 if (ptr)
884 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
885
886 /* build destination path */
887 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
888 lstrcpynW(szDest, sPathTarget, MAX_PATH);
889 ptr = PathAddBackslashW (szDest);
890 if (ptr)
891 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
892 } else
893 lstrcpynW(szDest, lpName, MAX_PATH);
894
895 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
896 WCHAR *ext = PathFindExtensionW(szSrc);
897 if(*ext != '\0') {
898 INT len = wcslen(szDest);
899 lstrcpynW(szDest + len, ext, MAX_PATH - len);
900 }
901 }
902
903 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
904 {
905 /* src and destination is the same */
906 hr = S_OK;
907 if (pPidlOut)
908 hr = _ILCreateFromPathW(szDest, pPidlOut);
909
910 return hr;
911 }
912
913 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
914 if (MoveFileW (szSrc, szDest))
915 {
916 hr = S_OK;
917
918 if (pPidlOut)
919 hr = _ILCreateFromPathW(szDest, pPidlOut);
920
921 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
922 SHCNF_PATHW, szSrc, szDest);
923
924 return hr;
925 }
926 return E_FAIL;
927 }
928
929 HRESULT WINAPI CDesktopFolder::GetDefaultSearchGUID(GUID *pguid)
930 {
931 FIXME ("(%p)\n", this);
932 return E_NOTIMPL;
933 }
934
935 HRESULT WINAPI CDesktopFolder::EnumSearches(IEnumExtraSearch **ppenum)
936 {
937 FIXME ("(%p)\n", this);
938 return E_NOTIMPL;
939 }
940
941 HRESULT WINAPI CDesktopFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
942 {
943 TRACE ("(%p)\n", this);
944
945 if (pSort)
946 *pSort = 0;
947 if (pDisplay)
948 *pDisplay = 0;
949
950 return S_OK;
951 }
952
953 HRESULT WINAPI CDesktopFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
954 {
955 TRACE ("(%p)\n", this);
956
957 if (!pcsFlags || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
958 return E_INVALIDARG;
959
960 *pcsFlags = DesktopSFHeader[iColumn].pcsFlags;
961
962 return S_OK;
963 }
964
965 HRESULT WINAPI CDesktopFolder::GetDetailsEx(
966 LPCITEMIDLIST pidl,
967 const SHCOLUMNID *pscid,
968 VARIANT *pv)
969 {
970 FIXME ("(%p)\n", this);
971
972 return E_NOTIMPL;
973 }
974
975 HRESULT WINAPI CDesktopFolder::GetDetailsOf(
976 LPCITEMIDLIST pidl,
977 UINT iColumn,
978 SHELLDETAILS *psd)
979 {
980 HRESULT hr = S_OK;
981
982 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
983
984 if (!psd || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
985 return E_INVALIDARG;
986
987 if (!pidl)
988 {
989 psd->fmt = DesktopSFHeader[iColumn].fmt;
990 psd->cxChar = DesktopSFHeader[iColumn].cxChar;
991 psd->str.uType = STRRET_CSTR;
992 LoadStringA (shell32_hInstance, DesktopSFHeader[iColumn].colnameid,
993 psd->str.cStr, MAX_PATH);
994 return S_OK;
995 }
996
997 /* the data from the pidl */
998 psd->str.uType = STRRET_CSTR;
999 switch (iColumn)
1000 {
1001 case 0: /* name */
1002 hr = GetDisplayNameOf(pidl,
1003 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1004 break;
1005 case 1: /* size */
1006 _ILGetFileSize (pidl, psd->str.cStr, MAX_PATH);
1007 break;
1008 case 2: /* type */
1009 _ILGetFileType (pidl, psd->str.cStr, MAX_PATH);
1010 break;
1011 case 3: /* date */
1012 _ILGetFileDate (pidl, psd->str.cStr, MAX_PATH);
1013 break;
1014 case 4: /* attributes */
1015 _ILGetFileAttributes (pidl, psd->str.cStr, MAX_PATH);
1016 break;
1017 }
1018
1019 return hr;
1020 }
1021
1022 HRESULT WINAPI CDesktopFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
1023 {
1024 FIXME ("(%p)\n", this);
1025 return E_NOTIMPL;
1026 }
1027
1028 HRESULT WINAPI CDesktopFolder::GetClassID(CLSID *lpClassId)
1029 {
1030 TRACE ("(%p)\n", this);
1031
1032 if (!lpClassId)
1033 return E_POINTER;
1034
1035 *lpClassId = CLSID_ShellDesktop;
1036
1037 return S_OK;
1038 }
1039
1040 HRESULT WINAPI CDesktopFolder::Initialize(LPCITEMIDLIST pidl)
1041 {
1042 TRACE ("(%p)->(%p)\n", this, pidl);
1043
1044 return E_NOTIMPL;
1045 }
1046
1047 HRESULT WINAPI CDesktopFolder::GetCurFolder(LPITEMIDLIST * pidl)
1048 {
1049 TRACE ("(%p)->(%p)\n", this, pidl);
1050
1051 if (!pidl) return E_POINTER;
1052 *pidl = ILClone (pidlRoot);
1053 return S_OK;
1054 }
1055
1056 HRESULT WINAPI CDesktopFolder::GetUniqueName(LPWSTR pwszName, UINT uLen)
1057 {
1058 CComPtr<IEnumIDList> penum;
1059 HRESULT hr;
1060 WCHAR wszText[MAX_PATH];
1061 WCHAR wszNewFolder[25];
1062 const WCHAR wszFormat[] = {'%', 's', ' ', '%', 'd', 0 };
1063
1064 LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, sizeof(wszNewFolder) / sizeof(WCHAR));
1065
1066 TRACE ("(%p)(%p %u)\n", this, pwszName, uLen);
1067
1068 if (uLen < sizeof(wszNewFolder) / sizeof(WCHAR) + 3)
1069 return E_POINTER;
1070
1071 lstrcpynW (pwszName, wszNewFolder, uLen);
1072
1073 hr = EnumObjects(0,
1074 SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum);
1075 if (penum) {
1076 LPITEMIDLIST pidl;
1077 DWORD dwFetched;
1078 int i = 1;
1079
1080 next:
1081 penum->Reset ();
1082 while (S_OK == penum->Next(1, &pidl, &dwFetched) &&
1083 dwFetched) {
1084 _ILSimpleGetTextW (pidl, wszText, MAX_PATH);
1085 if (0 == lstrcmpiW (wszText, pwszName)) {
1086 _snwprintf (pwszName, uLen, wszFormat, wszNewFolder, i++);
1087 if (i > 99) {
1088 hr = E_FAIL;
1089 break;
1090 }
1091 goto next;
1092 }
1093 }
1094
1095 }
1096 return hr;
1097 }
1098
1099 HRESULT WINAPI CDesktopFolder::AddFolder(HWND hwnd, LPCWSTR pwszName, LPITEMIDLIST *ppidlOut)
1100 {
1101 WCHAR wszNewDir[MAX_PATH];
1102 DWORD bRes;
1103 HRESULT hres = E_FAIL;
1104
1105 TRACE ("(%p)(%s %p)\n", this, debugstr_w(pwszName), ppidlOut);
1106
1107 wszNewDir[0] = 0;
1108 if (sPathTarget)
1109 lstrcpynW(wszNewDir, sPathTarget, MAX_PATH);
1110 PathAppendW(wszNewDir, pwszName);
1111 bRes = CreateDirectoryW (wszNewDir, NULL);
1112 if (bRes)
1113 {
1114 SHChangeNotify (SHCNE_MKDIR, SHCNF_PATHW, wszNewDir, NULL);
1115 hres = S_OK;
1116 if (ppidlOut)
1117 hres = _ILCreateFromPathW(wszNewDir, ppidlOut);
1118 }
1119
1120 return hres;
1121 }
1122
1123 HRESULT WINAPI CDesktopFolder::DeleteItems(UINT cidl, LPCITEMIDLIST *apidl)
1124 {
1125 UINT i;
1126 SHFILEOPSTRUCTW op;
1127 WCHAR wszPath[MAX_PATH];
1128 WCHAR wszCaption[50];
1129 WCHAR *wszPathsList;
1130 HRESULT ret;
1131 WCHAR *wszCurrentPath;
1132 UINT bRestoreWithDeskCpl = FALSE;
1133 int res;
1134
1135 TRACE ("(%p)(%u %p)\n", this, cidl, apidl);
1136 if (cidl == 0) return S_OK;
1137
1138 for(i = 0; i < cidl; i++)
1139 {
1140 if (_ILIsMyComputer(apidl[i]))
1141 bRestoreWithDeskCpl++;
1142 else if (_ILIsNetHood(apidl[i]))
1143 bRestoreWithDeskCpl++;
1144 else if (_ILIsMyDocuments(apidl[i]))
1145 bRestoreWithDeskCpl++;
1146 }
1147
1148 if (bRestoreWithDeskCpl)
1149 {
1150 /* FIXME use FormatMessage
1151 * use a similar message resource as in windows
1152 */
1153 LoadStringW(shell32_hInstance, IDS_DELETEMULTIPLE_TEXT, wszPath, sizeof(wszPath) / sizeof(WCHAR));
1154 wszPath[(sizeof(wszPath)/sizeof(WCHAR))-1] = 0;
1155
1156 LoadStringW(shell32_hInstance, IDS_DELETEITEM_CAPTION, wszCaption, sizeof(wszCaption) / sizeof(WCHAR));
1157 wszCaption[(sizeof(wszCaption)/sizeof(WCHAR))-1] = 0;
1158
1159 res = SHELL_ConfirmMsgBox(GetActiveWindow(), wszPath, wszCaption, NULL, cidl > 1);
1160 if (res == IDC_YESTOALL || res == IDYES)
1161 {
1162 for(i = 0; i < cidl; i++)
1163 {
1164 if (_ILIsMyComputer(apidl[i]))
1165 SetNamespaceExtensionVisibleStatus(L"{20D04FE0-3AEA-1069-A2D8-08002B30309D}", 0x1);
1166 else if (_ILIsNetHood(apidl[i]))
1167 SetNamespaceExtensionVisibleStatus(L"{208D2C60-3AEA-1069-A2D7-08002B30309D}", 0x1);
1168 else if (_ILIsMyDocuments(apidl[i]))
1169 SetNamespaceExtensionVisibleStatus(L"{450D8FBA-AD25-11D0-98A8-0800361B1103}", 0x1);
1170 }
1171 }
1172 }
1173 if (sPathTarget)
1174 lstrcpynW(wszPath, sPathTarget, MAX_PATH);
1175 else
1176 wszPath[0] = '\0';
1177
1178 PathAddBackslashW(wszPath);
1179 wszPathsList = BuildPathsList(wszPath, cidl, apidl);
1180
1181 ZeroMemory(&op, sizeof(op));
1182 op.hwnd = GetActiveWindow();
1183 op.wFunc = FO_DELETE;
1184 op.pFrom = wszPathsList;
1185 op.fFlags = FOF_ALLOWUNDO;
1186 if (SHFileOperationW(&op))
1187 {
1188 WARN("SHFileOperation failed\n");
1189 ret = E_FAIL;
1190 }
1191 else
1192 ret = S_OK;
1193
1194 /* we currently need to manually send the notifies */
1195 wszCurrentPath = wszPathsList;
1196 for (i = 0; i < cidl; i++)
1197 {
1198 LONG wEventId;
1199
1200 if (_ILIsFolder(apidl[i]))
1201 wEventId = SHCNE_RMDIR;
1202 else if (_ILIsValue(apidl[i]))
1203 wEventId = SHCNE_DELETE;
1204 else
1205 continue;
1206
1207 /* check if file exists */
1208 if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES)
1209 {
1210 LPITEMIDLIST pidl = ILCombine(pidlRoot, apidl[i]);
1211 SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL);
1212 SHFree(pidl);
1213 }
1214
1215 wszCurrentPath += wcslen(wszCurrentPath) + 1;
1216 }
1217 HeapFree(GetProcessHeap(), 0, wszPathsList);
1218 return ret;
1219 }
1220
1221 HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl, bool bCopy)
1222 {
1223 CComPtr<IPersistFolder2> ppf2;
1224 WCHAR szSrcPath[MAX_PATH];
1225 WCHAR szTargetPath[MAX_PATH];
1226 SHFILEOPSTRUCTW op;
1227 LPITEMIDLIST pidl;
1228 LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
1229 int res, length;
1230 STRRET strRet;
1231
1232 TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
1233
1234 pSFFrom->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1235 if (ppf2)
1236 {
1237 if (FAILED(ppf2->GetCurFolder(&pidl)))
1238 return E_FAIL;
1239
1240 if (FAILED(pSFFrom->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet)))
1241 {
1242 SHFree (pidl);
1243 return E_FAIL;
1244 }
1245
1246 if (FAILED(StrRetToBufW(&strRet, pidl, szSrcPath, MAX_PATH)))
1247 {
1248 SHFree (pidl);
1249 return E_FAIL;
1250 }
1251 SHFree (pidl);
1252
1253 pszSrc = PathAddBackslashW (szSrcPath);
1254
1255 wcscpy(szTargetPath, sPathTarget);
1256 pszTarget = PathAddBackslashW (szTargetPath);
1257
1258 pszSrcList = BuildPathsList(szSrcPath, cidl, apidl);
1259 pszTargetList = BuildPathsList(szTargetPath, cidl, apidl);
1260
1261 if (!pszSrcList || !pszTargetList)
1262 {
1263 if (pszSrcList)
1264 HeapFree(GetProcessHeap(), 0, pszSrcList);
1265
1266 if (pszTargetList)
1267 HeapFree(GetProcessHeap(), 0, pszTargetList);
1268
1269 SHFree (pidl);
1270 return E_OUTOFMEMORY;
1271 }
1272 ZeroMemory(&op, sizeof(op));
1273 if (!pszSrcList[0])
1274 {
1275 /* remove trailing backslash */
1276 pszSrc--;
1277 pszSrc[0] = L'\0';
1278 op.pFrom = szSrcPath;
1279 }
1280 else
1281 {
1282 op.pFrom = pszSrcList;
1283 }
1284
1285 if (!pszTargetList[0])
1286 {
1287 /* remove trailing backslash */
1288 if (pszTarget - szTargetPath > 3)
1289 {
1290 pszTarget--;
1291 pszTarget[0] = L'\0';
1292 }
1293 else
1294 {
1295 pszTarget[1] = L'\0';
1296 }
1297
1298 op.pTo = szTargetPath;
1299 op.fFlags = 0;
1300 }
1301 else
1302 {
1303 op.pTo = pszTargetList;
1304 op.fFlags = FOF_MULTIDESTFILES;
1305 }
1306 op.hwnd = GetActiveWindow();
1307 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
1308 op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1309
1310 res = SHFileOperationW(&op);
1311
1312 if (res == DE_SAMEFILE)
1313 {
1314 length = wcslen(szTargetPath);
1315
1316
1317 pszFileName = wcsrchr(pszSrcList, '\\');
1318 pszFileName++;
1319
1320 if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
1321 {
1322 wcscat(szTargetPath, L" ");
1323 }
1324
1325 wcscat(szTargetPath, pszFileName);
1326 op.pTo = szTargetPath;
1327
1328 res = SHFileOperationW(&op);
1329 }
1330
1331
1332 HeapFree(GetProcessHeap(), 0, pszSrcList);
1333 HeapFree(GetProcessHeap(), 0, pszTargetList);
1334
1335 if (res)
1336 return E_FAIL;
1337 else
1338 return S_OK;
1339 }
1340 return E_FAIL;
1341 }
1342
1343 /****************************************************************************
1344 * IDropTarget implementation
1345 *
1346 * This should allow two somewhat separate things, copying files to the users directory,
1347 * as well as allowing icons to be moved anywhere and updating the registry to save.
1348 *
1349 * The first thing I think is best done using fs.cpp to prevent WET code. So we'll simulate
1350 * a drop to the user's home directory. The second will look at the pointer location and
1351 * set sensible places for the icons to live.
1352 *
1353 */
1354 BOOL CDesktopFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1355 {
1356 /* TODO Windows does different drop effects if dragging across drives.
1357 i.e., it will copy instead of move if the directories are on different disks. */
1358
1359 DWORD dwEffect = DROPEFFECT_MOVE;
1360
1361 *pdwEffect = DROPEFFECT_NONE;
1362
1363 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1364 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1365
1366 if (*pdwEffect == DROPEFFECT_NONE)
1367 *pdwEffect = dwEffect;
1368
1369 /* ... matches the desired effect ? */
1370 if (dwEffect & *pdwEffect) {
1371 return TRUE;
1372 }
1373 }
1374 return FALSE;
1375 }
1376
1377 HRESULT WINAPI CDesktopFolder::DragEnter(IDataObject *pDataObject,
1378 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1379 {
1380 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1381 FORMATETC fmt;
1382 FORMATETC fmt2;
1383 fAcceptFmt = FALSE;
1384
1385 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1386 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1387
1388 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1389 fAcceptFmt = TRUE;
1390 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1391 fAcceptFmt = TRUE;
1392
1393 QueryDrop(dwKeyState, pdwEffect);
1394 return S_OK;
1395 }
1396
1397 HRESULT WINAPI CDesktopFolder::DragOver(DWORD dwKeyState, POINTL pt,
1398 DWORD *pdwEffect)
1399 {
1400 TRACE("(%p)\n", this);
1401
1402 if (!pdwEffect)
1403 return E_INVALIDARG;
1404
1405 QueryDrop(dwKeyState, pdwEffect);
1406
1407 return S_OK;
1408 }
1409
1410 HRESULT WINAPI CDesktopFolder::DragLeave()
1411 {
1412 TRACE("(%p)\n", this);
1413 fAcceptFmt = FALSE;
1414 return S_OK;
1415 }
1416
1417 HRESULT WINAPI CDesktopFolder::Drop(IDataObject *pDataObject,
1418 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1419 {
1420 TRACE("(%p) object dropped desktop\n", this);
1421
1422 STGMEDIUM medium;
1423 bool passthroughtofs = FALSE;
1424 FORMATETC formatetc;
1425 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
1426
1427 HRESULT hr = pDataObject->GetData(&formatetc, &medium);
1428 if (SUCCEEDED(hr))
1429 {
1430 /* lock the handle */
1431 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1432 if (!lpcida)
1433 {
1434 ReleaseStgMedium(&medium);
1435 return E_FAIL;
1436 }
1437
1438 /* convert the clipboard data into pidl (pointer to id list) */
1439 LPITEMIDLIST pidl;
1440 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1441 if (!apidl)
1442 {
1443 ReleaseStgMedium(&medium);
1444 return E_FAIL;
1445 }
1446 passthroughtofs = !_ILIsDesktop(pidl) || (dwKeyState & MK_CONTROL);
1447 SHFree(pidl);
1448 _ILFreeaPidl(apidl, lpcida->cidl);
1449 ReleaseStgMedium(&medium);
1450 }
1451 else
1452 {
1453 InitFormatEtc (formatetc, CF_HDROP, TYMED_HGLOBAL);
1454 if (SUCCEEDED(pDataObject->QueryGetData(&formatetc)))
1455 {
1456 passthroughtofs = TRUE;
1457 }
1458 }
1459 /* We only want to really move files around if they don't already
1460 come from the desktop, or we're linking or copying */
1461 if (passthroughtofs)
1462 {
1463 LPITEMIDLIST pidl = NULL;
1464
1465 WCHAR szPath[MAX_PATH];
1466 //LPWSTR pathPtr;
1467
1468 /* build a complete path to create a simple pidl */
1469 lstrcpynW(szPath, sPathTarget, MAX_PATH);
1470 /*pathPtr = */PathAddBackslashW(szPath);
1471 //hr = _ILCreateFromPathW(szPath, &pidl);
1472 hr = this->ParseDisplayName(NULL, NULL, szPath, NULL, &pidl, NULL);
1473
1474 if (SUCCEEDED(hr))
1475 {
1476 IDropTarget *pDT;
1477 hr = this->BindToObject(pidl, NULL, IID_IDropTarget, (LPVOID*)&pDT);
1478 CoTaskMemFree(pidl);
1479 if (SUCCEEDED(hr))
1480 SHSimulateDrop(pDT, pDataObject, dwKeyState, NULL, pdwEffect);
1481 else
1482 ERR("Error Binding");
1483 }
1484 else
1485 ERR("Error creating from %s\n", debugstr_w(szPath));
1486 }
1487
1488 /* Todo, rewrite the registry such that the icons are well placed.
1489 Blocked by no bags implementation. */
1490 return hr;
1491 }
1492
1493 HRESULT WINAPI CDesktopFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1494 HRESULT hr;
1495
1496 TRACE("CFSFolder::_GetDropTarget entered\n");
1497
1498 if (_ILGetGUIDPointer (pidl) || _ILIsFolder (pidl))
1499 return this->BindToObject(pidl, NULL, IID_IDropTarget, ppvOut);
1500
1501 LPITEMIDLIST pidlNext = NULL;
1502
1503 STRRET strFile;
1504 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1505 if (SUCCEEDED(hr))
1506 {
1507 WCHAR wszPath[MAX_PATH];
1508 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1509
1510 if (SUCCEEDED(hr))
1511 {
1512 PathRemoveFileSpecW (wszPath);
1513 hr = this->ParseDisplayName(NULL, NULL, wszPath, NULL, &pidlNext, NULL);
1514
1515 if (SUCCEEDED(hr))
1516 {
1517 IShellFolder *psf;
1518 hr = this->BindToObject(pidlNext, NULL, IID_IShellFolder, (LPVOID*)&psf);
1519 CoTaskMemFree(pidlNext);
1520 if (SUCCEEDED(hr))
1521 {
1522 hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IDropTarget, NULL, ppvOut);
1523 psf->Release();
1524 if (FAILED(hr))
1525 ERR("FS GetUIObjectOf failed: %x\n", hr);
1526 }
1527 else
1528 ERR("BindToObject failed: %x\n", hr);
1529 }
1530 else
1531 ERR("ParseDisplayName failed: %x\n", hr);
1532 }
1533 else
1534 ERR("StrRetToBufW failed: %x\n", hr);
1535 }
1536 else
1537 ERR("GetDisplayNameOf failed: %x\n", hr);
1538
1539 return hr;
1540 }