[SHELL32]
[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 PIDLIST_RELATIVE *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(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
425 {
426 return ShellObjectCreatorInit<CDesktopFolderEnum>(this, hwndOwner, dwFlags, IID_IEnumIDList, ppEnumIDList);
427 }
428
429 /**************************************************************************
430 * CDesktopFolder::BindToObject
431 */
432 HRESULT WINAPI CDesktopFolder::BindToObject(
433 PCUIDLIST_RELATIVE pidl,
434 LPBC pbcReserved,
435 REFIID riid,
436 LPVOID *ppvOut)
437 {
438 TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n",
439 this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
440
441 return SHELL32_BindToChild( pidlRoot, sPathTarget, pidl, riid, ppvOut );
442 }
443
444 /**************************************************************************
445 * CDesktopFolder::BindToStorage
446 */
447 HRESULT WINAPI CDesktopFolder::BindToStorage(
448 PCUIDLIST_RELATIVE pidl,
449 LPBC pbcReserved,
450 REFIID riid,
451 LPVOID *ppvOut)
452 {
453 FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
454 this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
455
456 *ppvOut = NULL;
457 return E_NOTIMPL;
458 }
459
460 /**************************************************************************
461 * CDesktopFolder::CompareIDs
462 */
463 HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
464 {
465 int nReturn;
466
467 TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", this, lParam, pidl1, pidl2);
468 nReturn = SHELL32_CompareIDs ((IShellFolder *)this, lParam, pidl1, pidl2);
469 TRACE ("-- %i\n", nReturn);
470 return nReturn;
471 }
472
473 /**************************************************************************
474 * CDesktopFolder::CreateViewObject
475 */
476 HRESULT WINAPI CDesktopFolder::CreateViewObject(
477 HWND hwndOwner,
478 REFIID riid,
479 LPVOID *ppvOut)
480 {
481 CComPtr<IShellView> pShellView;
482 HRESULT hr = E_INVALIDARG;
483
484 TRACE ("(%p)->(hwnd=%p,%s,%p)\n",
485 this, hwndOwner, shdebugstr_guid (&riid), ppvOut);
486
487 if (!ppvOut)
488 return hr;
489
490 *ppvOut = NULL;
491
492 if (IsEqualIID (riid, IID_IDropTarget))
493 {
494 hr = this->QueryInterface (IID_IDropTarget, ppvOut);
495 }
496 else if (IsEqualIID (riid, IID_IContextMenu))
497 {
498 WARN ("IContextMenu not implemented\n");
499 hr = E_NOTIMPL;
500 }
501 else if (IsEqualIID (riid, IID_IShellView))
502 {
503 hr = IShellView_Constructor((IShellFolder *)this, &pShellView);
504 if (pShellView)
505 hr = pShellView->QueryInterface(riid, ppvOut);
506 }
507 TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
508 return hr;
509 }
510
511 /**************************************************************************
512 * CDesktopFolder::GetAttributesOf
513 */
514 HRESULT WINAPI CDesktopFolder::GetAttributesOf(
515 UINT cidl,
516 PCUITEMID_CHILD_ARRAY apidl,
517 DWORD *rgfInOut)
518 {
519 HRESULT hr = S_OK;
520 static const DWORD dwDesktopAttributes =
521 SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
522 SFGAO_STORAGEANCESTOR | SFGAO_HASPROPSHEET | SFGAO_STORAGE | SFGAO_CANLINK;
523 static const DWORD dwMyComputerAttributes =
524 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
525 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
526 static DWORD dwMyNetPlacesAttributes =
527 SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
528 SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
529
530 TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
531 this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
532
533 if (cidl && !apidl)
534 return E_INVALIDARG;
535
536 if (*rgfInOut == 0)
537 *rgfInOut = ~0;
538
539 if(cidl == 0)
540 *rgfInOut &= dwDesktopAttributes;
541 else
542 {
543 /* TODO: always add SFGAO_CANLINK */
544 for (UINT i = 0; i < cidl; ++i)
545 {
546 pdump(*apidl);
547 if (_ILIsDesktop(*apidl))
548 *rgfInOut &= dwDesktopAttributes;
549 else if (_ILIsMyComputer(apidl[i]))
550 *rgfInOut &= dwMyComputerAttributes;
551 else if (_ILIsNetHood(apidl[i]))
552 *rgfInOut &= dwMyNetPlacesAttributes;
553 else
554 SHELL32_GetItemAttributes((IShellFolder *)this, apidl[i], rgfInOut);
555 }
556 }
557 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
558 *rgfInOut &= ~SFGAO_VALIDATE;
559
560 TRACE("-- result=0x%08x\n", *rgfInOut);
561
562 return hr;
563 }
564
565 /**************************************************************************
566 * CDesktopFolder::GetUIObjectOf
567 *
568 * PARAMETERS
569 * HWND hwndOwner, //[in ] Parent window for any output
570 * UINT cidl, //[in ] array size
571 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
572 * REFIID riid, //[in ] Requested Interface
573 * UINT* prgfInOut, //[ ] reserved
574 * LPVOID* ppvObject) //[out] Resulting Interface
575 *
576 */
577 HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
578 HWND hwndOwner,
579 UINT cidl,
580 PCUITEMID_CHILD_ARRAY apidl,
581 REFIID riid,
582 UINT *prgfInOut,
583 LPVOID *ppvOut)
584 {
585 LPITEMIDLIST pidl;
586 IUnknown *pObj = NULL;
587 HRESULT hr = E_INVALIDARG;
588
589 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
590 this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
591
592 if (!ppvOut)
593 return hr;
594
595 *ppvOut = NULL;
596
597 if (IsEqualIID (riid, IID_IContextMenu))
598 {
599 hr = CDefFolderMenu_Create2(pidlRoot, hwndOwner, cidl, apidl, (IShellFolder *)this, NULL, 0, NULL, (IContextMenu **)&pObj);
600 }
601 else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
602 {
603 hr = IDataObject_Constructor( hwndOwner, pidlRoot, apidl, cidl, (IDataObject **)&pObj);
604 }
605 else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
606 {
607 pidl = ILCombine (pidlRoot, apidl[0]);
608 pObj = IExtractIconA_Constructor (pidl);
609 SHFree (pidl);
610 hr = S_OK;
611 }
612 else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
613 {
614 pidl = ILCombine (pidlRoot, apidl[0]);
615 pObj = IExtractIconW_Constructor (pidl);
616 SHFree (pidl);
617 hr = S_OK;
618 }
619 else if (IsEqualIID (riid, IID_IDropTarget))
620 {
621 /* only interested in attempting to bind to shell folders, not files, semicolon intentionate */
622 if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
623 {
624 IDropTarget * pDt = NULL;
625 hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
626 pObj = pDt;
627 }
628 }
629 else if ((IsEqualIID(riid, IID_IShellLinkW) ||
630 IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
631 {
632 pidl = ILCombine (pidlRoot, apidl[0]);
633 hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
634 SHFree (pidl);
635 }
636 else
637 hr = E_NOINTERFACE;
638
639 if (SUCCEEDED(hr) && !pObj)
640 hr = E_OUTOFMEMORY;
641
642 *ppvOut = pObj;
643 TRACE ("(%p)->hr=0x%08x\n", this, hr);
644 return hr;
645 }
646
647 /**************************************************************************
648 * CDesktopFolder::GetDisplayNameOf
649 *
650 * NOTES
651 * special case: pidl = null gives desktop-name back
652 */
653 HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
654 {
655 HRESULT hr = S_OK;
656 LPWSTR pszPath;
657
658 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
659 pdump (pidl);
660
661 if (!strRet)
662 return E_INVALIDARG;
663
664 pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
665 if (!pszPath)
666 return E_OUTOFMEMORY;
667
668 if (_ILIsDesktop (pidl))
669 {
670 if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
671 (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING))
672 wcscpy(pszPath, sPathTarget);
673 else
674 HCR_GetClassNameW(CLSID_ShellDesktop, pszPath, MAX_PATH);
675 }
676 else if (_ILIsPidlSimple (pidl))
677 {
678 GUID const *clsid;
679
680 if ((clsid = _ILGetGUIDPointer (pidl)))
681 {
682 if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)
683 {
684 int bWantsForParsing;
685
686 /*
687 * We can only get a filesystem path from a shellfolder if the
688 * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists.
689 *
690 * Exception: The MyComputer folder doesn't have this key,
691 * but any other filesystem backed folder it needs it.
692 */
693 if (IsEqualIID (*clsid, CLSID_MyComputer))
694 {
695 bWantsForParsing = TRUE;
696 }
697 else
698 {
699 /* get the "WantsFORPARSING" flag from the registry */
700 static const WCHAR clsidW[] =
701 { 'C', 'L', 'S', 'I', 'D', '\\', 0 };
702 static const WCHAR shellfolderW[] =
703 { '\\', 's', 'h', 'e', 'l', 'l', 'f', 'o', 'l', 'd', 'e', 'r', 0 };
704 static const WCHAR wantsForParsingW[] =
705 { 'W', 'a', 'n', 't', 's', 'F', 'o', 'r', 'P', 'a', 'r', 's', 'i', 'n',
706 'g', 0
707 };
708 WCHAR szRegPath[100];
709 LONG r;
710
711 wcscpy (szRegPath, clsidW);
712 SHELL32_GUIDToStringW (*clsid, &szRegPath[6]);
713 wcscat (szRegPath, shellfolderW);
714 r = SHGetValueW(HKEY_CLASSES_ROOT, szRegPath,
715 wantsForParsingW, NULL, NULL, NULL);
716 if (r == ERROR_SUCCESS)
717 bWantsForParsing = TRUE;
718 else
719 bWantsForParsing = FALSE;
720 }
721
722 if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) &&
723 bWantsForParsing)
724 {
725 /*
726 * we need the filesystem path to the destination folder.
727 * Only the folder itself can know it
728 */
729 hr = SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags,
730 pszPath,
731 MAX_PATH);
732 }
733 else
734 {
735 /* parsing name like ::{...} */
736 pszPath[0] = ':';
737 pszPath[1] = ':';
738 SHELL32_GUIDToStringW (*clsid, &pszPath[2]);
739 }
740 }
741 else
742 {
743 /* user friendly name */
744 HCR_GetClassNameW (*clsid, pszPath, MAX_PATH);
745 }
746 }
747 else
748 {
749 int cLen = 0;
750
751 /* file system folder or file rooted at the desktop */
752 if ((GET_SHGDN_FOR(dwFlags) == SHGDN_FORPARSING) &&
753 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
754 {
755 lstrcpynW(pszPath, sPathTarget, MAX_PATH - 1);
756 PathAddBackslashW(pszPath);
757 cLen = wcslen(pszPath);
758 }
759
760 _ILSimpleGetTextW(pidl, pszPath + cLen, MAX_PATH - cLen);
761 if (!_ILIsFolder(pidl))
762 SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
763
764 if (GetFileAttributes(pszPath) == INVALID_FILE_ATTRIBUTES)
765 {
766 /* file system folder or file rooted at the AllUsers desktop */
767 if ((GET_SHGDN_FOR(dwFlags) == SHGDN_FORPARSING) &&
768 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
769 {
770 SHGetSpecialFolderPathW(0, pszPath, CSIDL_COMMON_DESKTOPDIRECTORY, FALSE);
771 PathAddBackslashW(pszPath);
772 cLen = wcslen(pszPath);
773 }
774
775 _ILSimpleGetTextW(pidl, pszPath + cLen, MAX_PATH - cLen);
776 if (!_ILIsFolder(pidl))
777 SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
778 }
779 }
780 }
781 else
782 {
783 /* a complex pidl, let the subfolder do the work */
784 hr = SHELL32_GetDisplayNameOfChild (this, pidl, dwFlags,
785 pszPath, MAX_PATH);
786 }
787
788 if (SUCCEEDED(hr))
789 {
790 /* Win9x always returns ANSI strings, NT always returns Unicode strings */
791 if (GetVersion() & 0x80000000)
792 {
793 strRet->uType = STRRET_CSTR;
794 if (!WideCharToMultiByte(CP_ACP, 0, pszPath, -1, strRet->cStr, MAX_PATH,
795 NULL, NULL))
796 strRet->cStr[0] = '\0';
797 CoTaskMemFree(pszPath);
798 }
799 else
800 {
801 strRet->uType = STRRET_WSTR;
802 strRet->pOleStr = pszPath;
803 }
804 }
805 else
806 CoTaskMemFree(pszPath);
807
808 TRACE ("-- (%p)->(%s,0x%08x)\n", this,
809 strRet->uType == STRRET_CSTR ? strRet->cStr :
810 debugstr_w(strRet->pOleStr), hr);
811 return hr;
812 }
813
814 /**************************************************************************
815 * CDesktopFolder::SetNameOf
816 * Changes the name of a file object or subfolder, possibly changing its item
817 * identifier in the process.
818 *
819 * PARAMETERS
820 * HWND hwndOwner, //[in ] Owner window for output
821 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
822 * LPCOLESTR lpszName, //[in ] the items new display name
823 * DWORD dwFlags, //[in ] SHGNO formatting flags
824 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
825 */
826 HRESULT WINAPI CDesktopFolder::SetNameOf(
827 HWND hwndOwner,
828 PCUITEMID_CHILD pidl, /* simple pidl */
829 LPCOLESTR lpName,
830 DWORD dwFlags,
831 PITEMID_CHILD *pPidlOut)
832 {
833 CComPtr<IShellFolder2> psf;
834 HRESULT hr;
835 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
836 LPWSTR ptr;
837 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
838
839 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
840 debugstr_w (lpName), dwFlags, pPidlOut);
841
842 if (_ILGetGUIDPointer(pidl))
843 {
844 if (SUCCEEDED(BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &psf))))
845 {
846 hr = psf->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
847 return hr;
848 }
849 }
850
851 /* build source path */
852 lstrcpynW(szSrc, sPathTarget, MAX_PATH);
853 ptr = PathAddBackslashW (szSrc);
854 if (ptr)
855 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
856
857 /* build destination path */
858 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
859 lstrcpynW(szDest, sPathTarget, MAX_PATH);
860 ptr = PathAddBackslashW (szDest);
861 if (ptr)
862 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
863 } else
864 lstrcpynW(szDest, lpName, MAX_PATH);
865
866 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
867 WCHAR *ext = PathFindExtensionW(szSrc);
868 if(*ext != '\0') {
869 INT len = wcslen(szDest);
870 lstrcpynW(szDest + len, ext, MAX_PATH - len);
871 }
872 }
873
874 if (!memcmp(szSrc, szDest, (wcslen(szDest) + 1) * sizeof(WCHAR)))
875 {
876 /* src and destination is the same */
877 hr = S_OK;
878 if (pPidlOut)
879 hr = _ILCreateFromPathW(szDest, pPidlOut);
880
881 return hr;
882 }
883
884 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
885 if (MoveFileW (szSrc, szDest))
886 {
887 hr = S_OK;
888
889 if (pPidlOut)
890 hr = _ILCreateFromPathW(szDest, pPidlOut);
891
892 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
893 SHCNF_PATHW, szSrc, szDest);
894
895 return hr;
896 }
897 return E_FAIL;
898 }
899
900 HRESULT WINAPI CDesktopFolder::GetDefaultSearchGUID(GUID *pguid)
901 {
902 FIXME ("(%p)\n", this);
903 return E_NOTIMPL;
904 }
905
906 HRESULT WINAPI CDesktopFolder::EnumSearches(IEnumExtraSearch **ppenum)
907 {
908 FIXME ("(%p)\n", this);
909 return E_NOTIMPL;
910 }
911
912 HRESULT WINAPI CDesktopFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
913 {
914 TRACE ("(%p)\n", this);
915
916 if (pSort)
917 *pSort = 0;
918 if (pDisplay)
919 *pDisplay = 0;
920
921 return S_OK;
922 }
923
924 HRESULT WINAPI CDesktopFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
925 {
926 TRACE ("(%p)\n", this);
927
928 if (!pcsFlags || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
929 return E_INVALIDARG;
930
931 *pcsFlags = DesktopSFHeader[iColumn].pcsFlags;
932
933 return S_OK;
934 }
935
936 HRESULT WINAPI CDesktopFolder::GetDetailsEx(
937 PCUITEMID_CHILD pidl,
938 const SHCOLUMNID *pscid,
939 VARIANT *pv)
940 {
941 FIXME ("(%p)\n", this);
942
943 return E_NOTIMPL;
944 }
945
946 HRESULT WINAPI CDesktopFolder::GetDetailsOf(
947 PCUITEMID_CHILD pidl,
948 UINT iColumn,
949 SHELLDETAILS *psd)
950 {
951 HRESULT hr = S_OK;
952
953 TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
954
955 if (!psd || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
956 return E_INVALIDARG;
957
958 if (!pidl)
959 {
960 psd->fmt = DesktopSFHeader[iColumn].fmt;
961 psd->cxChar = DesktopSFHeader[iColumn].cxChar;
962 psd->str.uType = STRRET_CSTR;
963 LoadStringA (shell32_hInstance, DesktopSFHeader[iColumn].colnameid,
964 psd->str.cStr, MAX_PATH);
965 return S_OK;
966 }
967
968 /* the data from the pidl */
969 psd->str.uType = STRRET_CSTR;
970 switch (iColumn)
971 {
972 case 0: /* name */
973 hr = GetDisplayNameOf(pidl,
974 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
975 break;
976 case 1: /* size */
977 _ILGetFileSize (pidl, psd->str.cStr, MAX_PATH);
978 break;
979 case 2: /* type */
980 _ILGetFileType (pidl, psd->str.cStr, MAX_PATH);
981 break;
982 case 3: /* date */
983 _ILGetFileDate (pidl, psd->str.cStr, MAX_PATH);
984 break;
985 case 4: /* attributes */
986 _ILGetFileAttributes (pidl, psd->str.cStr, MAX_PATH);
987 break;
988 }
989
990 return hr;
991 }
992
993 HRESULT WINAPI CDesktopFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
994 {
995 FIXME ("(%p)\n", this);
996 return E_NOTIMPL;
997 }
998
999 HRESULT WINAPI CDesktopFolder::GetClassID(CLSID *lpClassId)
1000 {
1001 TRACE ("(%p)\n", this);
1002
1003 if (!lpClassId)
1004 return E_POINTER;
1005
1006 *lpClassId = CLSID_ShellDesktop;
1007
1008 return S_OK;
1009 }
1010
1011 HRESULT WINAPI CDesktopFolder::Initialize(LPCITEMIDLIST pidl)
1012 {
1013 TRACE ("(%p)->(%p)\n", this, pidl);
1014
1015 return E_NOTIMPL;
1016 }
1017
1018 HRESULT WINAPI CDesktopFolder::GetCurFolder(LPITEMIDLIST * pidl)
1019 {
1020 TRACE ("(%p)->(%p)\n", this, pidl);
1021
1022 if (!pidl) return E_POINTER;
1023 *pidl = ILClone (pidlRoot);
1024 return S_OK;
1025 }
1026
1027 HRESULT WINAPI CDesktopFolder::GetUniqueName(LPWSTR pwszName, UINT uLen)
1028 {
1029 CComPtr<IEnumIDList> penum;
1030 HRESULT hr;
1031 WCHAR wszText[MAX_PATH];
1032 WCHAR wszNewFolder[25];
1033 const WCHAR wszFormat[] = {'%', 's', ' ', '%', 'd', 0 };
1034
1035 LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, sizeof(wszNewFolder) / sizeof(WCHAR));
1036
1037 TRACE ("(%p)(%p %u)\n", this, pwszName, uLen);
1038
1039 if (uLen < sizeof(wszNewFolder) / sizeof(WCHAR) + 3)
1040 return E_POINTER;
1041
1042 lstrcpynW (pwszName, wszNewFolder, uLen);
1043
1044 hr = EnumObjects(0,
1045 SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum);
1046 if (penum) {
1047 LPITEMIDLIST pidl;
1048 DWORD dwFetched;
1049 int i = 1;
1050
1051 next:
1052 penum->Reset ();
1053 while (S_OK == penum->Next(1, &pidl, &dwFetched) &&
1054 dwFetched) {
1055 _ILSimpleGetTextW (pidl, wszText, MAX_PATH);
1056 if (0 == lstrcmpiW (wszText, pwszName)) {
1057 _snwprintf (pwszName, uLen, wszFormat, wszNewFolder, i++);
1058 if (i > 99) {
1059 hr = E_FAIL;
1060 break;
1061 }
1062 goto next;
1063 }
1064 }
1065
1066 }
1067 return hr;
1068 }
1069
1070 HRESULT WINAPI CDesktopFolder::AddFolder(HWND hwnd, LPCWSTR pwszName, LPITEMIDLIST *ppidlOut)
1071 {
1072 WCHAR wszNewDir[MAX_PATH];
1073 DWORD bRes;
1074 HRESULT hres = E_FAIL;
1075
1076 TRACE ("(%p)(%s %p)\n", this, debugstr_w(pwszName), ppidlOut);
1077
1078 wszNewDir[0] = 0;
1079 if (sPathTarget)
1080 lstrcpynW(wszNewDir, sPathTarget, MAX_PATH);
1081 PathAppendW(wszNewDir, pwszName);
1082 bRes = CreateDirectoryW (wszNewDir, NULL);
1083 if (bRes)
1084 {
1085 SHChangeNotify (SHCNE_MKDIR, SHCNF_PATHW, wszNewDir, NULL);
1086 hres = S_OK;
1087 if (ppidlOut)
1088 hres = _ILCreateFromPathW(wszNewDir, ppidlOut);
1089 }
1090
1091 return hres;
1092 }
1093
1094 HRESULT WINAPI CDesktopFolder::DeleteItems(UINT cidl, LPCITEMIDLIST *apidl)
1095 {
1096 UINT i;
1097 SHFILEOPSTRUCTW op;
1098 WCHAR wszPath[MAX_PATH];
1099 WCHAR wszCaption[50];
1100 WCHAR *wszPathsList;
1101 HRESULT ret;
1102 WCHAR *wszCurrentPath;
1103 UINT bRestoreWithDeskCpl = FALSE;
1104 int res;
1105
1106 TRACE ("(%p)(%u %p)\n", this, cidl, apidl);
1107 if (cidl == 0) return S_OK;
1108
1109 for(i = 0; i < cidl; i++)
1110 {
1111 if (_ILIsMyComputer(apidl[i]))
1112 bRestoreWithDeskCpl++;
1113 else if (_ILIsNetHood(apidl[i]))
1114 bRestoreWithDeskCpl++;
1115 else if (_ILIsMyDocuments(apidl[i]))
1116 bRestoreWithDeskCpl++;
1117 }
1118
1119 if (bRestoreWithDeskCpl)
1120 {
1121 /* FIXME use FormatMessage
1122 * use a similar message resource as in windows
1123 */
1124 LoadStringW(shell32_hInstance, IDS_DELETEMULTIPLE_TEXT, wszPath, sizeof(wszPath) / sizeof(WCHAR));
1125 wszPath[(sizeof(wszPath)/sizeof(WCHAR))-1] = 0;
1126
1127 LoadStringW(shell32_hInstance, IDS_DELETEITEM_CAPTION, wszCaption, sizeof(wszCaption) / sizeof(WCHAR));
1128 wszCaption[(sizeof(wszCaption)/sizeof(WCHAR))-1] = 0;
1129
1130 res = SHELL_ConfirmMsgBox(GetActiveWindow(), wszPath, wszCaption, NULL, cidl > 1);
1131 if (res == IDC_YESTOALL || res == IDYES)
1132 {
1133 for(i = 0; i < cidl; i++)
1134 {
1135 if (_ILIsMyComputer(apidl[i]))
1136 SetNamespaceExtensionVisibleStatus(L"{20D04FE0-3AEA-1069-A2D8-08002B30309D}", 0x1);
1137 else if (_ILIsNetHood(apidl[i]))
1138 SetNamespaceExtensionVisibleStatus(L"{208D2C60-3AEA-1069-A2D7-08002B30309D}", 0x1);
1139 else if (_ILIsMyDocuments(apidl[i]))
1140 SetNamespaceExtensionVisibleStatus(L"{450D8FBA-AD25-11D0-98A8-0800361B1103}", 0x1);
1141 }
1142 }
1143 }
1144 if (sPathTarget)
1145 lstrcpynW(wszPath, sPathTarget, MAX_PATH);
1146 else
1147 wszPath[0] = '\0';
1148
1149 PathAddBackslashW(wszPath);
1150 wszPathsList = BuildPathsList(wszPath, cidl, apidl);
1151
1152 ZeroMemory(&op, sizeof(op));
1153 op.hwnd = GetActiveWindow();
1154 op.wFunc = FO_DELETE;
1155 op.pFrom = wszPathsList;
1156 op.fFlags = FOF_ALLOWUNDO;
1157 if (SHFileOperationW(&op))
1158 {
1159 WARN("SHFileOperation failed\n");
1160 ret = E_FAIL;
1161 }
1162 else
1163 ret = S_OK;
1164
1165 /* we currently need to manually send the notifies */
1166 wszCurrentPath = wszPathsList;
1167 for (i = 0; i < cidl; i++)
1168 {
1169 LONG wEventId;
1170
1171 if (_ILIsFolder(apidl[i]))
1172 wEventId = SHCNE_RMDIR;
1173 else if (_ILIsValue(apidl[i]))
1174 wEventId = SHCNE_DELETE;
1175 else
1176 continue;
1177
1178 /* check if file exists */
1179 if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES)
1180 {
1181 LPITEMIDLIST pidl = ILCombine(pidlRoot, apidl[i]);
1182 SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL);
1183 SHFree(pidl);
1184 }
1185
1186 wszCurrentPath += wcslen(wszCurrentPath) + 1;
1187 }
1188 HeapFree(GetProcessHeap(), 0, wszPathsList);
1189 return ret;
1190 }
1191
1192 HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl, bool bCopy)
1193 {
1194 CComPtr<IPersistFolder2> ppf2;
1195 WCHAR szSrcPath[MAX_PATH];
1196 WCHAR szTargetPath[MAX_PATH];
1197 SHFILEOPSTRUCTW op;
1198 LPITEMIDLIST pidl;
1199 LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
1200 int res, length;
1201 STRRET strRet;
1202
1203 TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
1204
1205 pSFFrom->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
1206 if (ppf2)
1207 {
1208 if (FAILED(ppf2->GetCurFolder(&pidl)))
1209 return E_FAIL;
1210
1211 if (FAILED(pSFFrom->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet)))
1212 {
1213 SHFree (pidl);
1214 return E_FAIL;
1215 }
1216
1217 if (FAILED(StrRetToBufW(&strRet, pidl, szSrcPath, MAX_PATH)))
1218 {
1219 SHFree (pidl);
1220 return E_FAIL;
1221 }
1222 SHFree (pidl);
1223
1224 pszSrc = PathAddBackslashW (szSrcPath);
1225
1226 wcscpy(szTargetPath, sPathTarget);
1227 pszTarget = PathAddBackslashW (szTargetPath);
1228
1229 pszSrcList = BuildPathsList(szSrcPath, cidl, apidl);
1230 pszTargetList = BuildPathsList(szTargetPath, cidl, apidl);
1231
1232 if (!pszSrcList || !pszTargetList)
1233 {
1234 if (pszSrcList)
1235 HeapFree(GetProcessHeap(), 0, pszSrcList);
1236
1237 if (pszTargetList)
1238 HeapFree(GetProcessHeap(), 0, pszTargetList);
1239
1240 SHFree (pidl);
1241 return E_OUTOFMEMORY;
1242 }
1243 ZeroMemory(&op, sizeof(op));
1244 if (!pszSrcList[0])
1245 {
1246 /* remove trailing backslash */
1247 pszSrc--;
1248 pszSrc[0] = L'\0';
1249 op.pFrom = szSrcPath;
1250 }
1251 else
1252 {
1253 op.pFrom = pszSrcList;
1254 }
1255
1256 if (!pszTargetList[0])
1257 {
1258 /* remove trailing backslash */
1259 if (pszTarget - szTargetPath > 3)
1260 {
1261 pszTarget--;
1262 pszTarget[0] = L'\0';
1263 }
1264 else
1265 {
1266 pszTarget[1] = L'\0';
1267 }
1268
1269 op.pTo = szTargetPath;
1270 op.fFlags = 0;
1271 }
1272 else
1273 {
1274 op.pTo = pszTargetList;
1275 op.fFlags = FOF_MULTIDESTFILES;
1276 }
1277 op.hwnd = GetActiveWindow();
1278 op.wFunc = bCopy ? FO_COPY : FO_MOVE;
1279 op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1280
1281 res = SHFileOperationW(&op);
1282
1283 if (res == DE_SAMEFILE)
1284 {
1285 length = wcslen(szTargetPath);
1286
1287
1288 pszFileName = wcsrchr(pszSrcList, '\\');
1289 pszFileName++;
1290
1291 if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
1292 {
1293 wcscat(szTargetPath, L" ");
1294 }
1295
1296 wcscat(szTargetPath, pszFileName);
1297 op.pTo = szTargetPath;
1298
1299 res = SHFileOperationW(&op);
1300 }
1301
1302
1303 HeapFree(GetProcessHeap(), 0, pszSrcList);
1304 HeapFree(GetProcessHeap(), 0, pszTargetList);
1305
1306 if (res)
1307 return E_FAIL;
1308 else
1309 return S_OK;
1310 }
1311 return E_FAIL;
1312 }
1313
1314 /****************************************************************************
1315 * IDropTarget implementation
1316 *
1317 * This should allow two somewhat separate things, copying files to the users directory,
1318 * as well as allowing icons to be moved anywhere and updating the registry to save.
1319 *
1320 * The first thing I think is best done using fs.cpp to prevent WET code. So we'll simulate
1321 * a drop to the user's home directory. The second will look at the pointer location and
1322 * set sensible places for the icons to live.
1323 *
1324 */
1325 BOOL CDesktopFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1326 {
1327 /* TODO Windows does different drop effects if dragging across drives.
1328 i.e., it will copy instead of move if the directories are on different disks. */
1329
1330 DWORD dwEffect = DROPEFFECT_MOVE;
1331
1332 *pdwEffect = DROPEFFECT_NONE;
1333
1334 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1335 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1336
1337 if (*pdwEffect == DROPEFFECT_NONE)
1338 *pdwEffect = dwEffect;
1339
1340 /* ... matches the desired effect ? */
1341 if (dwEffect & *pdwEffect) {
1342 return TRUE;
1343 }
1344 }
1345 return FALSE;
1346 }
1347
1348 HRESULT WINAPI CDesktopFolder::DragEnter(IDataObject *pDataObject,
1349 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1350 {
1351 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1352 FORMATETC fmt;
1353 FORMATETC fmt2;
1354 fAcceptFmt = FALSE;
1355
1356 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1357 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
1358
1359 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
1360 fAcceptFmt = TRUE;
1361 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
1362 fAcceptFmt = TRUE;
1363
1364 QueryDrop(dwKeyState, pdwEffect);
1365 return S_OK;
1366 }
1367
1368 HRESULT WINAPI CDesktopFolder::DragOver(DWORD dwKeyState, POINTL pt,
1369 DWORD *pdwEffect)
1370 {
1371 TRACE("(%p)\n", this);
1372
1373 if (!pdwEffect)
1374 return E_INVALIDARG;
1375
1376 QueryDrop(dwKeyState, pdwEffect);
1377
1378 return S_OK;
1379 }
1380
1381 HRESULT WINAPI CDesktopFolder::DragLeave()
1382 {
1383 TRACE("(%p)\n", this);
1384 fAcceptFmt = FALSE;
1385 return S_OK;
1386 }
1387
1388 HRESULT WINAPI CDesktopFolder::Drop(IDataObject *pDataObject,
1389 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1390 {
1391 TRACE("(%p) object dropped desktop\n", this);
1392
1393 STGMEDIUM medium;
1394 bool passthroughtofs = FALSE;
1395 FORMATETC formatetc;
1396 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
1397
1398 HRESULT hr = pDataObject->GetData(&formatetc, &medium);
1399 if (SUCCEEDED(hr))
1400 {
1401 /* lock the handle */
1402 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1403 if (!lpcida)
1404 {
1405 ReleaseStgMedium(&medium);
1406 return E_FAIL;
1407 }
1408
1409 /* convert the clipboard data into pidl (pointer to id list) */
1410 LPITEMIDLIST pidl;
1411 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1412 if (!apidl)
1413 {
1414 ReleaseStgMedium(&medium);
1415 return E_FAIL;
1416 }
1417 passthroughtofs = !_ILIsDesktop(pidl) || (dwKeyState & MK_CONTROL);
1418 SHFree(pidl);
1419 _ILFreeaPidl(apidl, lpcida->cidl);
1420 ReleaseStgMedium(&medium);
1421 }
1422 else
1423 {
1424 InitFormatEtc (formatetc, CF_HDROP, TYMED_HGLOBAL);
1425 if (SUCCEEDED(pDataObject->QueryGetData(&formatetc)))
1426 {
1427 passthroughtofs = TRUE;
1428 }
1429 }
1430 /* We only want to really move files around if they don't already
1431 come from the desktop, or we're linking or copying */
1432 if (passthroughtofs)
1433 {
1434 LPITEMIDLIST pidl = NULL;
1435
1436 WCHAR szPath[MAX_PATH];
1437 //LPWSTR pathPtr;
1438
1439 /* build a complete path to create a simple pidl */
1440 lstrcpynW(szPath, sPathTarget, MAX_PATH);
1441 /*pathPtr = */PathAddBackslashW(szPath);
1442 //hr = _ILCreateFromPathW(szPath, &pidl);
1443 hr = this->ParseDisplayName(NULL, NULL, szPath, NULL, &pidl, NULL);
1444
1445 if (SUCCEEDED(hr))
1446 {
1447 CComPtr<IDropTarget> pDT;
1448 hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IDropTarget, &pDT));
1449 CoTaskMemFree(pidl);
1450 if (SUCCEEDED(hr))
1451 SHSimulateDrop(pDT, pDataObject, dwKeyState, NULL, pdwEffect);
1452 else
1453 ERR("Error Binding");
1454 }
1455 else
1456 ERR("Error creating from %s\n", debugstr_w(szPath));
1457 }
1458
1459 /* Todo, rewrite the registry such that the icons are well placed.
1460 Blocked by no bags implementation. */
1461 return hr;
1462 }
1463
1464 HRESULT WINAPI CDesktopFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
1465 HRESULT hr;
1466
1467 TRACE("CFSFolder::_GetDropTarget entered\n");
1468
1469 if (_ILGetGUIDPointer (pidl) || _ILIsFolder (pidl))
1470 return this->BindToObject(pidl, NULL, IID_IDropTarget, ppvOut);
1471
1472 LPITEMIDLIST pidlNext = NULL;
1473
1474 STRRET strFile;
1475 hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
1476 if (SUCCEEDED(hr))
1477 {
1478 WCHAR wszPath[MAX_PATH];
1479 hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
1480
1481 if (SUCCEEDED(hr))
1482 {
1483 PathRemoveFileSpecW (wszPath);
1484 hr = this->ParseDisplayName(NULL, NULL, wszPath, NULL, &pidlNext, NULL);
1485
1486 if (SUCCEEDED(hr))
1487 {
1488 CComPtr<IShellFolder> psf;
1489 hr = this->BindToObject(pidlNext, NULL, IID_PPV_ARG(IShellFolder, &psf));
1490 CoTaskMemFree(pidlNext);
1491 if (SUCCEEDED(hr))
1492 {
1493 hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IDropTarget, NULL, ppvOut);
1494 if (FAILED(hr))
1495 ERR("FS GetUIObjectOf failed: %x\n", hr);
1496 }
1497 else
1498 ERR("BindToObject failed: %x\n", hr);
1499 }
1500 else
1501 ERR("ParseDisplayName failed: %x\n", hr);
1502 }
1503 else
1504 ERR("StrRetToBufW failed: %x\n", hr);
1505 }
1506 else
1507 ERR("GetDisplayNameOf failed: %x\n", hr);
1508
1509 return hr;
1510 }
1511
1512 /*************************************************************************
1513 * SHGetDesktopFolder [SHELL32.@]
1514 */
1515 HRESULT WINAPI SHGetDesktopFolder(IShellFolder **psf)
1516 {
1517 HRESULT hres = S_OK;
1518 TRACE("\n");
1519
1520 if(!psf) return E_INVALIDARG;
1521 *psf = NULL;
1522 hres = CDesktopFolder::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellFolder, psf));
1523
1524 TRACE("-- %p->(%p)\n",psf, *psf);
1525 return hres;
1526 }