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