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