Sync with trunk r43123
[reactos.git] / reactos / dll / win32 / shell32 / shfldr_fs.c
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 * IShellFolder implementation
29 */
30
31 typedef struct {
32 const IUnknownVtbl *lpVtbl;
33 LONG ref;
34 const IShellFolder2Vtbl *lpvtblShellFolder;
35 const IPersistFolder3Vtbl *lpvtblPersistFolder3;
36 const IDropTargetVtbl *lpvtblDropTarget;
37 const ISFHelperVtbl *lpvtblSFHelper;
38
39 IUnknown *pUnkOuter; /* used for aggregation */
40
41 CLSID *pclsid;
42
43 /* both paths are parsible from the desktop */
44 LPWSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */
45
46 LPITEMIDLIST pidlRoot; /* absolute pidl */
47
48 UINT cfShellIDList; /* clipboardformat for IDropTarget */
49 BOOL fAcceptFmt; /* flag for pending Drop */
50 } IGenericSFImpl, *LPIGenericSFImpl;
51
52 static const IUnknownVtbl unkvt;
53 static const IShellFolder2Vtbl sfvt;
54 static const IPersistFolder3Vtbl vt_FSFldr_PersistFolder3; /* IPersistFolder3 for a FS_Folder */
55 static const IDropTargetVtbl dtvt;
56 static const ISFHelperVtbl shvt;
57
58 static LPIGenericSFImpl __inline impl_from_IShellFolder2( IShellFolder2 *iface )
59 {
60 return (LPIGenericSFImpl)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblShellFolder));
61 }
62
63 static LPIGenericSFImpl __inline impl_from_IPersistFolder3( IPersistFolder3 *iface )
64 {
65 return (LPIGenericSFImpl)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblPersistFolder3));
66 }
67
68 static LPIGenericSFImpl __inline impl_from_IDropTarget( IDropTarget *iface )
69 {
70 return (LPIGenericSFImpl)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblDropTarget));
71 }
72
73 static LPIGenericSFImpl __inline impl_from_ISFHelper( ISFHelper *iface )
74 {
75 return (LPIGenericSFImpl)((char*)iface - FIELD_OFFSET(IGenericSFImpl, lpvtblSFHelper));
76 }
77
78
79 /*
80 converts This to an interface pointer
81 */
82 #define _IUnknown_(This) (IUnknown*)&(This->lpVtbl)
83 #define _IShellFolder_(This) (IShellFolder*)&(This->lpvtblShellFolder)
84 #define _IShellFolder2_(This) (IShellFolder2*)&(This->lpvtblShellFolder)
85 #define _IPersist_(This) (IPersist*)&(This->lpvtblPersistFolder3)
86 #define _IPersistFolder_(This) (IPersistFolder*)&(This->lpvtblPersistFolder3)
87 #define _IPersistFolder2_(This) (IPersistFolder2*)&(This->lpvtblPersistFolder3)
88 #define _IPersistFolder3_(This) (IPersistFolder3*)&(This->lpvtblPersistFolder3)
89 #define _IDropTarget_(This) (IDropTarget*)&(This->lpvtblDropTarget)
90 #define _ISFHelper_(This) (ISFHelper*)&(This->lpvtblSFHelper)
91
92 /**************************************************************************
93 * registers clipboardformat once
94 */
95 static void SF_RegisterClipFmt (IGenericSFImpl * This)
96 {
97 TRACE ("(%p)\n", This);
98
99 if (!This->cfShellIDList) {
100 This->cfShellIDList = RegisterClipboardFormatA (CFSTR_SHELLIDLIST);
101 }
102 }
103
104 /**************************************************************************
105 * we need a separate IUnknown to handle aggregation
106 * (inner IUnknown)
107 */
108 static HRESULT WINAPI IUnknown_fnQueryInterface (IUnknown * iface, REFIID riid, LPVOID * ppvObj)
109 {
110 IGenericSFImpl *This = (IGenericSFImpl *)iface;
111
112 TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj);
113
114 *ppvObj = NULL;
115
116 if (IsEqualIID (riid, &IID_IUnknown))
117 *ppvObj = _IUnknown_ (This);
118 else if (IsEqualIID (riid, &IID_IShellFolder))
119 *ppvObj = _IShellFolder_ (This);
120 else if (IsEqualIID (riid, &IID_IShellFolder2))
121 *ppvObj = _IShellFolder_ (This);
122 else if (IsEqualIID (riid, &IID_IPersist))
123 *ppvObj = _IPersist_ (This);
124 else if (IsEqualIID (riid, &IID_IPersistFolder))
125 *ppvObj = _IPersistFolder_ (This);
126 else if (IsEqualIID (riid, &IID_IPersistFolder2))
127 *ppvObj = _IPersistFolder2_ (This);
128 else if (IsEqualIID (riid, &IID_IPersistFolder3))
129 *ppvObj = _IPersistFolder3_ (This);
130 else if (IsEqualIID (riid, &IID_ISFHelper))
131 *ppvObj = _ISFHelper_ (This);
132 else if (IsEqualIID (riid, &IID_IDropTarget)) {
133 *ppvObj = _IDropTarget_ (This);
134 SF_RegisterClipFmt (This);
135 }
136
137 if (*ppvObj) {
138 IUnknown_AddRef ((IUnknown *) (*ppvObj));
139 TRACE ("-- Interface = %p\n", *ppvObj);
140 return S_OK;
141 }
142 TRACE ("-- Interface: E_NOINTERFACE\n");
143 return E_NOINTERFACE;
144 }
145
146 static ULONG WINAPI IUnknown_fnAddRef (IUnknown * iface)
147 {
148 IGenericSFImpl *This = (IGenericSFImpl *)iface;
149 ULONG refCount = InterlockedIncrement(&This->ref);
150
151 TRACE ("(%p)->(count=%u)\n", This, refCount - 1);
152
153 return refCount;
154 }
155
156 static ULONG WINAPI IUnknown_fnRelease (IUnknown * iface)
157 {
158 IGenericSFImpl *This = (IGenericSFImpl *)iface;
159 ULONG refCount = InterlockedDecrement(&This->ref);
160
161 TRACE ("(%p)->(count=%u)\n", This, refCount + 1);
162
163 if (!refCount) {
164 TRACE ("-- destroying IShellFolder(%p)\n", This);
165
166 SHFree (This->pidlRoot);
167 SHFree (This->sPathTarget);
168 LocalFree ((HLOCAL) This);
169 }
170 return refCount;
171 }
172
173 static const IUnknownVtbl unkvt =
174 {
175 IUnknown_fnQueryInterface,
176 IUnknown_fnAddRef,
177 IUnknown_fnRelease,
178 };
179
180 static const shvheader GenericSFHeader[] = {
181 {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
182 {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
183 {IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
184 {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12},
185 {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 5}
186 };
187
188 #define GENERICSHELLVIEWCOLUMNS 5
189
190 /**************************************************************************
191 * IFSFolder_Constructor
192 *
193 * NOTES
194 * creating undocumented ShellFS_Folder as part of an aggregation
195 * {F3364BA0-65B9-11CE-A9BA-00AA004AE837}
196 *
197 */
198 HRESULT WINAPI
199 IFSFolder_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv)
200 {
201 IGenericSFImpl *sf;
202
203 TRACE ("unkOut=%p %s\n", pUnkOuter, shdebugstr_guid (riid));
204
205 if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
206 return CLASS_E_NOAGGREGATION;
207 sf = (IGenericSFImpl *) LocalAlloc (LMEM_ZEROINIT, sizeof (IGenericSFImpl));
208 if (!sf)
209 return E_OUTOFMEMORY;
210
211 sf->ref = 0;
212 sf->lpVtbl = &unkvt;
213 sf->lpvtblShellFolder = &sfvt;
214 sf->lpvtblPersistFolder3 = &vt_FSFldr_PersistFolder3;
215 sf->lpvtblDropTarget = &dtvt;
216 sf->lpvtblSFHelper = &shvt;
217 sf->pclsid = (CLSID *) & CLSID_ShellFSFolder;
218 sf->pUnkOuter = pUnkOuter ? pUnkOuter : _IUnknown_ (sf);
219
220 if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) {
221 IUnknown_Release (_IUnknown_ (sf));
222 return E_NOINTERFACE;
223 }
224
225 TRACE ("--%p\n", *ppv);
226 return S_OK;
227 }
228
229 /**************************************************************************
230 * IShellFolder_fnQueryInterface
231 *
232 * PARAMETERS
233 * REFIID riid [in ] Requested InterfaceID
234 * LPVOID* ppvObject [out] Interface* to hold the result
235 */
236 static HRESULT WINAPI
237 IShellFolder_fnQueryInterface (IShellFolder2 * iface, REFIID riid,
238 LPVOID * ppvObj)
239 {
240 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
241
242 TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj);
243
244 return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj);
245 }
246
247 /**************************************************************************
248 * IShellFolder_AddRef
249 */
250
251 static ULONG WINAPI IShellFolder_fnAddRef (IShellFolder2 * iface)
252 {
253 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
254
255 TRACE ("(%p)->(count=%u)\n", This, This->ref);
256
257 return IUnknown_AddRef (This->pUnkOuter);
258 }
259
260 /**************************************************************************
261 * IShellFolder_fnRelease
262 */
263 static ULONG WINAPI IShellFolder_fnRelease (IShellFolder2 * iface)
264 {
265 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
266
267 TRACE ("(%p)->(count=%u)\n", This, This->ref);
268
269 return IUnknown_Release (This->pUnkOuter);
270 }
271
272 /**************************************************************************
273 * SHELL32_CreatePidlFromBindCtx [internal]
274 *
275 * If the caller bound File System Bind Data, assume it is the
276 * find data for the path.
277 * This allows binding of paths that don't exist.
278 */
279 LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
280 {
281 static WCHAR szfsbc[] = {
282 'F','i','l','e',' ','S','y','s','t','e','m',' ',
283 'B','i','n','d',' ','D','a','t','a',0 };
284 IFileSystemBindData *fsbd = NULL;
285 LPITEMIDLIST pidl = NULL;
286 IUnknown *param = NULL;
287 WIN32_FIND_DATAW wfd;
288 HRESULT r;
289
290 TRACE("%p %s\n", pbc, debugstr_w(path));
291
292 if (!pbc)
293 return NULL;
294
295 /* see if the caller bound File System Bind Data */
296 r = IBindCtx_GetObjectParam( pbc, (LPOLESTR) szfsbc, &param );
297 if (FAILED(r))
298 return NULL;
299
300 r = IUnknown_QueryInterface( param, &IID_IFileSystemBindData,
301 (LPVOID*) &fsbd );
302 if (SUCCEEDED(r))
303 {
304 r = IFileSystemBindData_GetFindData( fsbd, &wfd );
305 if (SUCCEEDED(r))
306 {
307 lstrcpynW( &wfd.cFileName[0], path, MAX_PATH );
308 pidl = _ILCreateFromFindDataW( &wfd );
309 }
310 IFileSystemBindData_Release( fsbd );
311 }
312
313 return pidl;
314 }
315
316 /**************************************************************************
317 * IShellFolder_ParseDisplayName {SHELL32}
318 *
319 * Parse a display name.
320 *
321 * PARAMS
322 * hwndOwner [in] Parent window for any message's
323 * pbc [in] optional FileSystemBindData context
324 * lpszDisplayName [in] Unicode displayname.
325 * pchEaten [out] (unicode) characters processed
326 * ppidl [out] complex pidl to item
327 * pdwAttributes [out] items attributes
328 *
329 * NOTES
330 * Every folder tries to parse only its own (the leftmost) pidl and creates a
331 * subfolder to evaluate the remaining parts.
332 * Now we can parse into namespaces implemented by shell extensions
333 *
334 * Behaviour on win98: lpszDisplayName=NULL -> crash
335 * lpszDisplayName="" -> returns mycoputer-pidl
336 *
337 * FIXME
338 * pdwAttributes is not set
339 * pchEaten is not set like in windows
340 */
341 static HRESULT WINAPI
342 IShellFolder_fnParseDisplayName (IShellFolder2 * iface,
343 HWND hwndOwner,
344 LPBC pbc,
345 LPOLESTR lpszDisplayName,
346 DWORD * pchEaten, LPITEMIDLIST * ppidl,
347 DWORD * pdwAttributes)
348 {
349 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
350
351 HRESULT hr = E_INVALIDARG;
352 LPCWSTR szNext = NULL;
353 WCHAR szElement[MAX_PATH];
354 WCHAR szPath[MAX_PATH];
355 LPITEMIDLIST pidlTemp = NULL;
356 DWORD len;
357
358 TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
359 This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
360 pchEaten, ppidl, pdwAttributes);
361
362 if (!lpszDisplayName || !ppidl)
363 return E_INVALIDARG;
364
365 if (pchEaten)
366 *pchEaten = 0; /* strange but like the original */
367
368 pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName);
369 if (!pidlTemp && *lpszDisplayName)
370 {
371 /* get the next element */
372 szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
373
374 /* build the full pathname to the element */
375 lstrcpynW(szPath, This->sPathTarget, MAX_PATH - 1);
376 PathAddBackslashW(szPath);
377 len = wcslen(szPath);
378 lstrcpynW(szPath + len, szElement, MAX_PATH - len);
379
380 /* get the pidl */
381 hr = _ILCreateFromPathW(szPath, &pidlTemp);
382
383 if (SUCCEEDED(hr)) {
384 if (szNext && *szNext) {
385 /* try to analyse the next element */
386 hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc,
387 &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
388 } else {
389 /* it's the last element */
390 if (pdwAttributes && *pdwAttributes) {
391 hr = SHELL32_GetItemAttributes (_IShellFolder_ (This),
392 pidlTemp, pdwAttributes);
393 }
394 }
395 }
396 }
397
398 if (SUCCEEDED(hr))
399 *ppidl = pidlTemp;
400 else
401 *ppidl = NULL;
402
403 TRACE ("(%p)->(-- pidl=%p ret=0x%08x)\n", This, ppidl ? *ppidl : 0, hr);
404
405 return hr;
406 }
407
408 /**************************************************************************
409 * IShellFolder_fnEnumObjects
410 * PARAMETERS
411 * HWND hwndOwner, //[in ] Parent Window
412 * DWORD grfFlags, //[in ] SHCONTF enumeration mask
413 * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface
414 */
415 static HRESULT WINAPI
416 IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner,
417 DWORD dwFlags, LPENUMIDLIST * ppEnumIDList)
418 {
419 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
420
421 TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", This, hwndOwner,
422 dwFlags, ppEnumIDList);
423
424 *ppEnumIDList = IEnumIDList_Constructor();
425 if (*ppEnumIDList)
426 CreateFolderEnumList(*ppEnumIDList, This->sPathTarget, dwFlags);
427
428 TRACE ("-- (%p)->(new ID List: %p)\n", This, *ppEnumIDList);
429
430 return *ppEnumIDList ? S_OK : E_OUTOFMEMORY;
431 }
432
433 /**************************************************************************
434 * IShellFolder_fnBindToObject
435 * PARAMETERS
436 * LPCITEMIDLIST pidl, //[in ] relative pidl to open
437 * LPBC pbc, //[in ] optional FileSystemBindData context
438 * REFIID riid, //[in ] Initial Interface
439 * LPVOID* ppvObject //[out] Interface*
440 */
441 static HRESULT WINAPI
442 IShellFolder_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl,
443 LPBC pbc, REFIID riid, LPVOID * ppvOut)
444 {
445 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
446
447 TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbc,
448 shdebugstr_guid (riid), ppvOut);
449
450 return SHELL32_BindToChild (This->pidlRoot, This->sPathTarget, pidl, riid,
451 ppvOut);
452 }
453
454 /**************************************************************************
455 * IShellFolder_fnBindToStorage
456 * PARAMETERS
457 * LPCITEMIDLIST pidl, //[in ] complex pidl to store
458 * LPBC pbc, //[in ] reserved
459 * REFIID riid, //[in ] Initial storage interface
460 * LPVOID* ppvObject //[out] Interface* returned
461 */
462 static HRESULT WINAPI
463 IShellFolder_fnBindToStorage (IShellFolder2 * iface, LPCITEMIDLIST pidl,
464 LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
465 {
466 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
467
468 FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved,
469 shdebugstr_guid (riid), ppvOut);
470
471 *ppvOut = NULL;
472 return E_NOTIMPL;
473 }
474
475 /**************************************************************************
476 * IShellFolder_fnCompareIDs
477 */
478
479 static HRESULT WINAPI
480 IShellFolder_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam,
481 LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
482 {
483 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
484
485 int nReturn;
486
487 TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", This, lParam, pidl1, pidl2);
488 nReturn = SHELL32_CompareIDs (_IShellFolder_ (This), lParam, pidl1, pidl2);
489 TRACE ("-- %i\n", nReturn);
490 return nReturn;
491 }
492
493 /**************************************************************************
494 * IShellFolder_fnCreateViewObject
495 */
496 static HRESULT WINAPI
497 IShellFolder_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner,
498 REFIID riid, LPVOID * ppvOut)
499 {
500 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
501
502 LPSHELLVIEW pShellView;
503 HRESULT hr = E_INVALIDARG;
504
505 TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid),
506 ppvOut);
507
508 if (ppvOut) {
509 *ppvOut = NULL;
510
511 if (IsEqualIID (riid, &IID_IDropTarget)) {
512 hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, ppvOut);
513 } else if (IsEqualIID (riid, &IID_IContextMenu)) {
514 FIXME ("IContextMenu not implemented\n");
515 hr = E_NOTIMPL;
516 } else if (IsEqualIID (riid, &IID_IShellView)) {
517 pShellView = IShellView_Constructor ((IShellFolder *) iface);
518 if (pShellView) {
519 hr = IShellView_QueryInterface (pShellView, riid, ppvOut);
520 IShellView_Release (pShellView);
521 }
522 }
523 }
524 TRACE ("-- (%p)->(interface=%p)\n", This, ppvOut);
525 return hr;
526 }
527
528 /**************************************************************************
529 * IShellFolder_fnGetAttributesOf
530 *
531 * PARAMETERS
532 * UINT cidl, //[in ] num elements in pidl array
533 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
534 * ULONG* rgfInOut) //[out] result array
535 *
536 */
537 static HRESULT WINAPI
538 IShellFolder_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl,
539 LPCITEMIDLIST * apidl, DWORD * rgfInOut)
540 {
541 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
542
543 HRESULT hr = S_OK;
544
545 TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n", This, cidl, apidl,
546 rgfInOut, rgfInOut ? *rgfInOut : 0);
547
548 if (!rgfInOut)
549 return E_INVALIDARG;
550 if (cidl && !apidl)
551 return E_INVALIDARG;
552
553 if (*rgfInOut == 0)
554 *rgfInOut = ~0;
555
556 if(cidl == 0){
557 IShellFolder *psfParent = NULL;
558 LPCITEMIDLIST rpidl = NULL;
559
560 hr = SHBindToParent(This->pidlRoot, &IID_IShellFolder, (LPVOID*)&psfParent, (LPCITEMIDLIST*)&rpidl);
561 if(SUCCEEDED(hr)) {
562 SHELL32_GetItemAttributes (psfParent, rpidl, rgfInOut);
563 IShellFolder_Release(psfParent);
564 }
565 }
566 else {
567 while (cidl > 0 && *apidl) {
568 pdump (*apidl);
569 SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut);
570 apidl++;
571 cidl--;
572 }
573 }
574 /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
575 *rgfInOut &= ~SFGAO_VALIDATE;
576
577 TRACE ("-- result=0x%08x\n", *rgfInOut);
578
579 return hr;
580 }
581
582 /**************************************************************************
583 * IShellFolder_fnGetUIObjectOf
584 *
585 * PARAMETERS
586 * HWND hwndOwner, //[in ] Parent window for any output
587 * UINT cidl, //[in ] array size
588 * LPCITEMIDLIST* apidl, //[in ] simple pidl array
589 * REFIID riid, //[in ] Requested Interface
590 * UINT* prgfInOut, //[ ] reserved
591 * LPVOID* ppvObject) //[out] Resulting Interface
592 *
593 * NOTES
594 * This function gets asked to return "view objects" for one or more (multiple
595 * select) items:
596 * The viewobject typically is an COM object with one of the following
597 * interfaces:
598 * IExtractIcon,IDataObject,IContextMenu
599 * In order to support icon positions in the default Listview your DataObject
600 * must implement the SetData method (in addition to GetData :) - the shell
601 * passes a barely documented "Icon positions" structure to SetData when the
602 * drag starts, and GetData's it if the drop is in another explorer window that
603 * needs the positions.
604 */
605 static HRESULT WINAPI
606 IShellFolder_fnGetUIObjectOf (IShellFolder2 * iface,
607 HWND hwndOwner,
608 UINT cidl, LPCITEMIDLIST * apidl, REFIID riid,
609 UINT * prgfInOut, LPVOID * ppvOut)
610 {
611 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
612
613 LPITEMIDLIST pidl;
614 IUnknown *pObj = NULL;
615 HRESULT hr = E_INVALIDARG;
616
617 TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
618 This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut);
619
620 if (ppvOut) {
621 *ppvOut = NULL;
622
623 if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) {
624 hr = CDefFolderMenu_Create2(This->pidlRoot, hwndOwner, cidl, apidl, (IShellFolder*)iface, NULL, 0, NULL, (IContextMenu**)&pObj);
625 } else if (IsEqualIID (riid, &IID_IDataObject)){
626 if (cidl >= 1) {
627 pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner,
628 This->pidlRoot, apidl, cidl);
629 hr = S_OK;
630 }
631 else
632 {
633 pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, This->pidlRoot, (LPCITEMIDLIST*)&This->pidlRoot, 1);
634 hr = S_OK;
635 }
636 } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) {
637 pidl = ILCombine (This->pidlRoot, apidl[0]);
638 pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl);
639 SHFree (pidl);
640 hr = S_OK;
641 } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) {
642 pidl = ILCombine (This->pidlRoot, apidl[0]);
643 pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl);
644 SHFree (pidl);
645 hr = S_OK;
646 } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) {
647 hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget,
648 (LPVOID *) & pObj);
649 } else if ((IsEqualIID(riid,&IID_IShellLinkW) ||
650 IsEqualIID(riid,&IID_IShellLinkA)) && (cidl == 1)) {
651 pidl = ILCombine (This->pidlRoot, apidl[0]);
652 hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
653 SHFree (pidl);
654 } else {
655 hr = E_NOINTERFACE;
656 }
657
658 if (SUCCEEDED(hr) && !pObj)
659 hr = E_OUTOFMEMORY;
660
661 *ppvOut = pObj;
662 }
663 TRACE ("(%p)->hr=0x%08x\n", This, hr);
664 return hr;
665 }
666
667 static const WCHAR AdvancedW[] = { 'S','O','F','T','W','A','R','E',
668 '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
669 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l',
670 'o','r','e','r','\\','A','d','v','a','n','c','e','d',0 };
671 static const WCHAR HideFileExtW[] = { 'H','i','d','e','F','i','l','e','E','x',
672 't',0 };
673 static const WCHAR NeverShowExtW[] = { 'N','e','v','e','r','S','h','o','w','E',
674 'x','t',0 };
675
676 /******************************************************************************
677 * SHELL_FS_HideExtension [Internal]
678 *
679 * Query the registry if the filename extension of a given path should be
680 * hidden.
681 *
682 * PARAMS
683 * szPath [I] Relative or absolute path of a file
684 *
685 * RETURNS
686 * TRUE, if the filename's extension should be hidden
687 * FALSE, otherwise.
688 */
689 BOOL SHELL_FS_HideExtension(LPWSTR szPath)
690 {
691 HKEY hKey;
692 DWORD dwData;
693 DWORD dwDataSize = sizeof (DWORD);
694 BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
695
696 if (!RegCreateKeyExW(HKEY_CURRENT_USER, AdvancedW, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
697 if (!RegQueryValueExW(hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, &dwDataSize))
698 doHide = dwData;
699 RegCloseKey (hKey);
700 }
701
702 if (!doHide) {
703 LPWSTR ext = PathFindExtensionW(szPath);
704
705 if (*ext != '\0') {
706 WCHAR classname[MAX_PATH];
707 LONG classlen = sizeof(classname);
708
709 if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
710 if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
711 if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, NULL, NULL))
712 doHide = TRUE;
713 RegCloseKey(hKey);
714 }
715 }
716 }
717 return doHide;
718 }
719
720 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
721 {
722 /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
723 if (!(dwFlags & SHGDN_FORPARSING) &&
724 ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
725 if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
726 PathRemoveExtensionW(szPath);
727 }
728 }
729
730 /**************************************************************************
731 * IShellFolder_fnGetDisplayNameOf
732 * Retrieves the display name for the specified file object or subfolder
733 *
734 * PARAMETERS
735 * LPCITEMIDLIST pidl, //[in ] complex pidl to item
736 * DWORD dwFlags, //[in ] SHGNO formatting flags
737 * LPSTRRET lpName) //[out] Returned display name
738 *
739 * FIXME
740 * if the name is in the pidl the ret value should be a STRRET_OFFSET
741 */
742
743 static HRESULT WINAPI
744 IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl,
745 DWORD dwFlags, LPSTRRET strRet)
746 {
747 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
748 LPWSTR pszPath;
749
750 HRESULT hr = S_OK;
751 int len = 0;
752
753 TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", This, pidl, dwFlags, strRet);
754 pdump (pidl);
755
756 if (!pidl || !strRet)
757 return E_INVALIDARG;
758
759 pszPath = CoTaskMemAlloc((MAX_PATH +1) * sizeof(WCHAR));
760 if (!pszPath)
761 return E_OUTOFMEMORY;
762
763 if (_ILIsDesktop(pidl)) { /* empty pidl */
764 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
765 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
766 {
767 if (This->sPathTarget)
768 lstrcpynW(pszPath, This->sPathTarget, MAX_PATH);
769 } else {
770 /* pidl has to contain exactly one non null SHITEMID */
771 hr = E_INVALIDARG;
772 }
773 } else if (_ILIsPidlSimple(pidl)) {
774 if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
775 (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
776 This->sPathTarget)
777 {
778 lstrcpynW(pszPath, This->sPathTarget, MAX_PATH);
779 PathAddBackslashW(pszPath);
780 len = wcslen(pszPath);
781 }
782 _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
783 if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
784 } else {
785 hr = SHELL32_GetDisplayNameOfChild(iface, pidl, dwFlags, pszPath, MAX_PATH);
786 }
787
788 if (SUCCEEDED(hr)) {
789 /* Win9x always returns ANSI strings, NT always returns Unicode strings */
790 if (GetVersion() & 0x80000000) {
791 strRet->uType = STRRET_CSTR;
792 if (!WideCharToMultiByte(CP_ACP, 0, pszPath, -1, strRet->u.cStr, MAX_PATH,
793 NULL, NULL))
794 strRet->u.cStr[0] = '\0';
795 CoTaskMemFree(pszPath);
796 } else {
797 strRet->uType = STRRET_WSTR;
798 strRet->u.pOleStr = pszPath;
799 }
800 } else
801 CoTaskMemFree(pszPath);
802
803 TRACE ("-- (%p)->(%s)\n", This, strRet->uType == STRRET_CSTR ? strRet->u.cStr : debugstr_w(strRet->u.pOleStr));
804 return hr;
805 }
806
807 /**************************************************************************
808 * IShellFolder_fnSetNameOf
809 * Changes the name of a file object or subfolder, possibly changing its item
810 * identifier in the process.
811 *
812 * PARAMETERS
813 * HWND hwndOwner, //[in ] Owner window for output
814 * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change
815 * LPCOLESTR lpszName, //[in ] the items new display name
816 * DWORD dwFlags, //[in ] SHGNO formatting flags
817 * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned
818 */
819 static HRESULT WINAPI IShellFolder_fnSetNameOf (IShellFolder2 * iface,
820 HWND hwndOwner,
821 LPCITEMIDLIST pidl,
822 LPCOLESTR lpName,
823 DWORD dwFlags,
824 LPITEMIDLIST * pPidlOut)
825 {
826 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
827 WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
828 LPWSTR ptr;
829 BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
830
831 TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", This, hwndOwner, pidl,
832 debugstr_w (lpName), dwFlags, pPidlOut);
833
834 /* build source path */
835 lstrcpynW(szSrc, This->sPathTarget, MAX_PATH);
836 ptr = PathAddBackslashW (szSrc);
837 if (ptr)
838 _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
839
840 /* build destination path */
841 if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER) {
842 lstrcpynW(szDest, This->sPathTarget, MAX_PATH);
843 ptr = PathAddBackslashW (szDest);
844 if (ptr)
845 lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
846 } else
847 lstrcpynW(szDest, lpName, MAX_PATH);
848
849 if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
850 WCHAR *ext = PathFindExtensionW(szSrc);
851 if(*ext != '\0') {
852 INT len = wcslen(szDest);
853 lstrcpynW(szDest + len, ext, MAX_PATH - len);
854 }
855 }
856
857 TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
858 if (!memcmp(szSrc, szDest, (wcslen(szDest)+1) * sizeof(WCHAR)))
859 {
860 /* src and destination is the same */
861 HRESULT hr = S_OK;
862 if (pPidlOut)
863 hr = _ILCreateFromPathW(szDest, pPidlOut);
864
865 return hr;
866 }
867
868
869 if (MoveFileW (szSrc, szDest)) {
870 HRESULT hr = S_OK;
871
872 if (pPidlOut)
873 hr = _ILCreateFromPathW(szDest, pPidlOut);
874
875 SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
876 SHCNF_PATHW, szSrc, szDest);
877
878 return hr;
879 }
880
881 return E_FAIL;
882 }
883
884 static HRESULT WINAPI IShellFolder_fnGetDefaultSearchGUID (IShellFolder2 *iface,
885 GUID * pguid)
886 {
887 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
888 FIXME ("(%p)\n", This);
889 return E_NOTIMPL;
890 }
891 static HRESULT WINAPI IShellFolder_fnEnumSearches (IShellFolder2 * iface,
892 IEnumExtraSearch ** ppenum)
893 {
894 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
895 FIXME ("(%p)\n", This);
896 return E_NOTIMPL;
897 }
898
899 static HRESULT WINAPI
900 IShellFolder_fnGetDefaultColumn (IShellFolder2 * iface, DWORD dwRes,
901 ULONG * pSort, ULONG * pDisplay)
902 {
903 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
904
905 TRACE ("(%p)\n", This);
906
907 if (pSort)
908 *pSort = 0;
909 if (pDisplay)
910 *pDisplay = 0;
911
912 return S_OK;
913 }
914
915 static HRESULT WINAPI
916 IShellFolder_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn,
917 DWORD * pcsFlags)
918 {
919 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
920
921 TRACE ("(%p)\n", This);
922
923 if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
924 return E_INVALIDARG;
925
926 *pcsFlags = GenericSFHeader[iColumn].pcsFlags;
927
928 return S_OK;
929 }
930
931 static HRESULT WINAPI
932 IShellFolder_fnGetDetailsEx (IShellFolder2 * iface, LPCITEMIDLIST pidl,
933 const SHCOLUMNID * pscid, VARIANT * pv)
934 {
935 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
936 FIXME ("(%p)\n", This);
937
938 return E_NOTIMPL;
939 }
940
941 static HRESULT WINAPI
942 IShellFolder_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl,
943 UINT iColumn, SHELLDETAILS * psd)
944 {
945 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
946 HRESULT hr = E_FAIL;
947
948 TRACE ("(%p)->(%p %i %p)\n", This, pidl, iColumn, psd);
949
950 if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
951 return E_INVALIDARG;
952
953 if (!pidl) {
954 /* the header titles */
955 psd->fmt = GenericSFHeader[iColumn].fmt;
956 psd->cxChar = GenericSFHeader[iColumn].cxChar;
957 psd->str.uType = STRRET_CSTR;
958 LoadStringA (shell32_hInstance, GenericSFHeader[iColumn].colnameid,
959 psd->str.u.cStr, MAX_PATH);
960 return S_OK;
961 } else {
962 hr = S_OK;
963 psd->str.uType = STRRET_CSTR;
964 /* the data from the pidl */
965 switch (iColumn) {
966 case 0: /* name */
967 hr = IShellFolder_GetDisplayNameOf (iface, pidl,
968 SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
969 break;
970 case 1: /* size */
971 _ILGetFileSize (pidl, psd->str.u.cStr, MAX_PATH);
972 break;
973 case 2: /* type */
974 _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH);
975 break;
976 case 3: /* date */
977 _ILGetFileDate (pidl, psd->str.u.cStr, MAX_PATH);
978 break;
979 case 4: /* attributes */
980 _ILGetFileAttributes (pidl, psd->str.u.cStr, MAX_PATH);
981 break;
982 }
983 }
984
985 return hr;
986 }
987
988 static HRESULT WINAPI
989 IShellFolder_fnMapColumnToSCID (IShellFolder2 * iface, UINT column,
990 SHCOLUMNID * pscid)
991 {
992 IGenericSFImpl *This = impl_from_IShellFolder2(iface);
993 FIXME ("(%p)\n", This);
994 return E_NOTIMPL;
995 }
996
997 static const IShellFolder2Vtbl sfvt =
998 {
999 IShellFolder_fnQueryInterface,
1000 IShellFolder_fnAddRef,
1001 IShellFolder_fnRelease,
1002 IShellFolder_fnParseDisplayName,
1003 IShellFolder_fnEnumObjects,
1004 IShellFolder_fnBindToObject,
1005 IShellFolder_fnBindToStorage,
1006 IShellFolder_fnCompareIDs,
1007 IShellFolder_fnCreateViewObject,
1008 IShellFolder_fnGetAttributesOf,
1009 IShellFolder_fnGetUIObjectOf,
1010 IShellFolder_fnGetDisplayNameOf,
1011 IShellFolder_fnSetNameOf,
1012 /* ShellFolder2 */
1013 IShellFolder_fnGetDefaultSearchGUID,
1014 IShellFolder_fnEnumSearches,
1015 IShellFolder_fnGetDefaultColumn,
1016 IShellFolder_fnGetDefaultColumnState,
1017 IShellFolder_fnGetDetailsEx,
1018 IShellFolder_fnGetDetailsOf,
1019 IShellFolder_fnMapColumnToSCID
1020 };
1021
1022 /****************************************************************************
1023 * ISFHelper for IShellFolder implementation
1024 */
1025
1026 static HRESULT WINAPI
1027 ISFHelper_fnQueryInterface (ISFHelper * iface, REFIID riid, LPVOID * ppvObj)
1028 {
1029 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1030
1031 TRACE ("(%p)->(count=%u)\n", This, This->ref);
1032
1033 return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj);
1034 }
1035
1036 static ULONG WINAPI ISFHelper_fnAddRef (ISFHelper * iface)
1037 {
1038 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1039
1040 TRACE ("(%p)->(count=%u)\n", This, This->ref);
1041
1042 return IUnknown_AddRef (This->pUnkOuter);
1043 }
1044
1045 static ULONG WINAPI ISFHelper_fnRelease (ISFHelper * iface)
1046 {
1047 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1048
1049 TRACE ("(%p)\n", This);
1050
1051 return IUnknown_Release (This->pUnkOuter);
1052 }
1053
1054 /****************************************************************************
1055 * ISFHelper_fnAddFolder
1056 *
1057 * creates a unique folder name
1058 */
1059
1060 static HRESULT WINAPI
1061 ISFHelper_fnGetUniqueName (ISFHelper * iface, LPWSTR pwszName, UINT uLen)
1062 {
1063 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1064 IEnumIDList *penum;
1065 HRESULT hr;
1066 WCHAR wszText[MAX_PATH];
1067 WCHAR wszNewFolder[25];
1068 const WCHAR wszFormat[] = {'%','s',' ','%','d',0 };
1069
1070 LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, sizeof(wszNewFolder)/sizeof(WCHAR));
1071
1072 TRACE ("(%p)(%p %u)\n", This, pwszName, uLen);
1073
1074 if (uLen < sizeof(wszNewFolder)/sizeof(WCHAR) + 3)
1075 return E_POINTER;
1076
1077 lstrcpynW (pwszName, wszNewFolder, uLen);
1078
1079 hr = IShellFolder_fnEnumObjects (_IShellFolder2_ (This), 0,
1080 SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum);
1081 if (penum) {
1082 LPITEMIDLIST pidl;
1083 DWORD dwFetched;
1084 int i = 1;
1085
1086 next:
1087 IEnumIDList_Reset (penum);
1088 while (S_OK == IEnumIDList_Next (penum, 1, &pidl, &dwFetched) &&
1089 dwFetched) {
1090 _ILSimpleGetTextW (pidl, wszText, MAX_PATH);
1091 if (0 == lstrcmpiW (wszText, pwszName)) {
1092 _snwprintf (pwszName, uLen, wszFormat, wszNewFolder, i++);
1093 if (i > 99) {
1094 hr = E_FAIL;
1095 break;
1096 }
1097 goto next;
1098 }
1099 }
1100
1101 IEnumIDList_Release (penum);
1102 }
1103 return hr;
1104 }
1105
1106 /****************************************************************************
1107 * ISFHelper_fnAddFolder
1108 *
1109 * adds a new folder.
1110 */
1111
1112 static HRESULT WINAPI
1113 ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCWSTR pwszName,
1114 LPITEMIDLIST * ppidlOut)
1115 {
1116 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1117 WCHAR wszNewDir[MAX_PATH];
1118 DWORD bRes;
1119 HRESULT hres = E_FAIL;
1120
1121 TRACE ("(%p)(%s %p)\n", This, debugstr_w(pwszName), ppidlOut);
1122
1123 wszNewDir[0] = 0;
1124 if (This->sPathTarget)
1125 lstrcpynW(wszNewDir, This->sPathTarget, MAX_PATH);
1126 PathAppendW(wszNewDir, pwszName);
1127
1128 bRes = CreateDirectoryW (wszNewDir, NULL);
1129 if (bRes) {
1130 SHChangeNotify (SHCNE_MKDIR, SHCNF_PATHW, wszNewDir, NULL);
1131
1132 hres = S_OK;
1133
1134 if (ppidlOut)
1135 hres = _ILCreateFromPathW(wszNewDir, ppidlOut);
1136 } else {
1137 WCHAR wszText[128 + MAX_PATH];
1138 WCHAR wszTempText[128];
1139 WCHAR wszCaption[256];
1140
1141 /* Cannot Create folder because of permissions */
1142 LoadStringW (shell32_hInstance, IDS_CREATEFOLDER_DENIED, wszTempText,
1143 sizeof (wszTempText));
1144 LoadStringW (shell32_hInstance, IDS_CREATEFOLDER_CAPTION, wszCaption,
1145 sizeof (wszCaption));
1146 swprintf (wszText, wszTempText, wszNewDir);
1147 MessageBoxW (hwnd, wszText, wszCaption, MB_OK | MB_ICONEXCLAMATION);
1148 }
1149
1150 return hres;
1151 }
1152
1153 /****************************************************************************
1154 * build_paths_list
1155 *
1156 * Builds a list of paths like the one used in SHFileOperation from a table of
1157 * PIDLs relative to the given base folder
1158 */
1159 WCHAR *build_paths_list(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls)
1160 {
1161 WCHAR *wszPathsList;
1162 WCHAR *wszListPos;
1163 int iPathLen;
1164 int i;
1165
1166 iPathLen = wcslen(wszBasePath);
1167 wszPathsList = HeapAlloc(GetProcessHeap(), 0, MAX_PATH*sizeof(WCHAR)*cidl+1);
1168 wszListPos = wszPathsList;
1169
1170 for (i = 0; i < cidl; i++) {
1171 if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i]))
1172 continue;
1173
1174 lstrcpynW(wszListPos, wszBasePath, MAX_PATH);
1175 /* FIXME: abort if path too long */
1176 _ILSimpleGetTextW(pidls[i], wszListPos+iPathLen, MAX_PATH-iPathLen);
1177 wszListPos += wcslen(wszListPos)+1;
1178 }
1179 *wszListPos=0;
1180 return wszPathsList;
1181 }
1182
1183 /****************************************************************************
1184 * ISFHelper_fnDeleteItems
1185 *
1186 * deletes items in folder
1187 */
1188 static HRESULT WINAPI
1189 ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPCITEMIDLIST * apidl)
1190 {
1191 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1192 UINT i;
1193 SHFILEOPSTRUCTW op;
1194 WCHAR wszPath[MAX_PATH];
1195 WCHAR *wszPathsList;
1196 HRESULT ret;
1197 WCHAR *wszCurrentPath;
1198
1199 TRACE ("(%p)(%u %p)\n", This, cidl, apidl);
1200 if (cidl==0) return S_OK;
1201
1202 if (This->sPathTarget)
1203 lstrcpynW(wszPath, This->sPathTarget, MAX_PATH);
1204 else
1205 wszPath[0] = '\0';
1206 PathAddBackslashW(wszPath);
1207 wszPathsList = build_paths_list(wszPath, cidl, apidl);
1208
1209 ZeroMemory(&op, sizeof(op));
1210 op.hwnd = GetActiveWindow();
1211 op.wFunc = FO_DELETE;
1212 op.pFrom = wszPathsList;
1213 op.fFlags = FOF_ALLOWUNDO;
1214 if (SHFileOperationW(&op))
1215 {
1216 WARN("SHFileOperation failed\n");
1217 ret = E_FAIL;
1218 }
1219 else
1220 ret = S_OK;
1221
1222 /* we currently need to manually send the notifies */
1223 wszCurrentPath = wszPathsList;
1224 for (i = 0; i < cidl; i++)
1225 {
1226 LONG wEventId;
1227
1228 if (_ILIsFolder(apidl[i]))
1229 wEventId = SHCNE_RMDIR;
1230 else if (_ILIsValue(apidl[i]))
1231 wEventId = SHCNE_DELETE;
1232 else
1233 continue;
1234
1235 /* check if file exists */
1236 if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES)
1237 {
1238 LPITEMIDLIST pidl = ILCombine(This->pidlRoot, apidl[i]);
1239 SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL);
1240 SHFree(pidl);
1241 }
1242
1243 wszCurrentPath += wcslen(wszCurrentPath)+1;
1244 }
1245 HeapFree(GetProcessHeap(), 0, wszPathsList);
1246 return ret;
1247 }
1248
1249 /****************************************************************************
1250 * ISFHelper_fnCopyItems
1251 *
1252 * copies items to this folder
1253 */
1254 static HRESULT WINAPI
1255 ISFHelper_fnCopyItems (ISFHelper * iface, IShellFolder * pSFFrom, UINT cidl,
1256 LPCITEMIDLIST * apidl)
1257 {
1258 IPersistFolder2 *ppf2 = NULL;
1259 WCHAR szSrcPath[MAX_PATH];
1260 WCHAR szTargetPath[MAX_PATH];
1261 SHFILEOPSTRUCTW op;
1262 LPITEMIDLIST pidl;
1263 LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName;
1264 int res, length;
1265 HRESULT hr;
1266 STRRET strRet;
1267
1268 IGenericSFImpl *This = impl_from_ISFHelper(iface);
1269
1270 TRACE ("(%p)->(%p,%u,%p)\n", This, pSFFrom, cidl, apidl);
1271
1272 hr = IShellFolder_QueryInterface (pSFFrom, &IID_IPersistFolder2, (LPVOID *) & ppf2);
1273 if (SUCCEEDED(hr))
1274 {
1275 if (FAILED(IPersistFolder2_GetCurFolder (ppf2, &pidl)))
1276 {
1277 IPersistFolder2_Release(ppf2);
1278 return E_FAIL;
1279 }
1280 IPersistFolder2_Release(ppf2);
1281
1282 if (FAILED(IShellFolder_GetDisplayNameOf(pSFFrom, pidl, SHGDN_FORPARSING, &strRet)))
1283 {
1284 SHFree (pidl);
1285 return E_FAIL;
1286 }
1287
1288 if (FAILED(StrRetToBufW(&strRet, pidl, szSrcPath, MAX_PATH)))
1289 {
1290 SHFree (pidl);
1291 return E_FAIL;
1292 }
1293 SHFree (pidl);
1294
1295 pszSrc = PathAddBackslashW (szSrcPath);
1296
1297 wcscpy(szTargetPath, This->sPathTarget);
1298 pszTarget = PathAddBackslashW (szTargetPath);
1299
1300 pszSrcList = build_paths_list(szSrcPath, cidl, apidl);
1301 pszTargetList = build_paths_list(szTargetPath, cidl, apidl);
1302
1303 if (!pszSrcList || !pszTargetList)
1304 {
1305 if (pszSrcList)
1306 HeapFree(GetProcessHeap(), 0, pszSrcList);
1307
1308 if (pszTargetList)
1309 HeapFree(GetProcessHeap(), 0, pszTargetList);
1310
1311 SHFree (pidl);
1312 IPersistFolder2_Release (ppf2);
1313 return E_OUTOFMEMORY;
1314 }
1315 ZeroMemory(&op, sizeof(op));
1316 if (!pszSrcList[0])
1317 {
1318 /* remove trailing backslash */
1319 pszSrc--;
1320 pszSrc[0] = L'\0';
1321 op.pFrom = szSrcPath;
1322 }
1323 else
1324 {
1325 op.pFrom = pszSrcList;
1326 }
1327
1328 if (!pszTargetList[0])
1329 {
1330 /* remove trailing backslash */
1331 if (pszTarget - szTargetPath > 3)
1332 {
1333 pszTarget--;
1334 pszTarget[0] = L'\0';
1335 }
1336 else
1337 {
1338 pszTarget[1] = L'\0';
1339 }
1340
1341 op.pTo = szTargetPath;
1342 }
1343 else
1344 {
1345 op.pTo = pszTargetList;
1346 }
1347 op.hwnd = GetActiveWindow();
1348 op.wFunc = FO_COPY;
1349 op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
1350
1351 res = SHFileOperationW(&op);
1352
1353 if (res == DE_SAMEFILE)
1354 {
1355 length = wcslen(szTargetPath);
1356
1357 pszFileName = wcsrchr(pszSrcList, '\\');
1358 pszFileName++;
1359
1360 if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length))
1361 {
1362 wcscat(szTargetPath, L" ");
1363 }
1364
1365 wcscat(szTargetPath, pszFileName);
1366 op.pTo = szTargetPath;
1367
1368 res = SHFileOperationW(&op);
1369 }
1370
1371 HeapFree(GetProcessHeap(), 0, pszSrcList);
1372 HeapFree(GetProcessHeap(), 0, pszTargetList);
1373
1374 if (res)
1375 return E_FAIL;
1376 else
1377 return S_OK;
1378 }
1379 return E_FAIL;
1380 }
1381
1382 static const ISFHelperVtbl shvt =
1383 {
1384 ISFHelper_fnQueryInterface,
1385 ISFHelper_fnAddRef,
1386 ISFHelper_fnRelease,
1387 ISFHelper_fnGetUniqueName,
1388 ISFHelper_fnAddFolder,
1389 ISFHelper_fnDeleteItems,
1390 ISFHelper_fnCopyItems
1391 };
1392
1393 /************************************************************************
1394 * IFSFldr_PersistFolder3_QueryInterface
1395 *
1396 */
1397 static HRESULT WINAPI
1398 IFSFldr_PersistFolder3_QueryInterface (IPersistFolder3 * iface, REFIID iid,
1399 LPVOID * ppvObj)
1400 {
1401 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1402
1403 TRACE ("(%p)\n", This);
1404
1405 return IUnknown_QueryInterface (This->pUnkOuter, iid, ppvObj);
1406 }
1407
1408 /************************************************************************
1409 * IFSFldr_PersistFolder3_AddRef
1410 *
1411 */
1412 static ULONG WINAPI
1413 IFSFldr_PersistFolder3_AddRef (IPersistFolder3 * iface)
1414 {
1415 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1416
1417 TRACE ("(%p)->(count=%u)\n", This, This->ref);
1418
1419 return IUnknown_AddRef (This->pUnkOuter);
1420 }
1421
1422 /************************************************************************
1423 * IFSFldr_PersistFolder3_Release
1424 *
1425 */
1426 static ULONG WINAPI
1427 IFSFldr_PersistFolder3_Release (IPersistFolder3 * iface)
1428 {
1429 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1430
1431 TRACE ("(%p)->(count=%u)\n", This, This->ref);
1432
1433 return IUnknown_Release (This->pUnkOuter);
1434 }
1435
1436 /************************************************************************
1437 * IFSFldr_PersistFolder3_GetClassID
1438 */
1439 static HRESULT WINAPI
1440 IFSFldr_PersistFolder3_GetClassID (IPersistFolder3 * iface, CLSID * lpClassId)
1441 {
1442 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1443
1444 TRACE ("(%p)\n", This);
1445
1446 if (!lpClassId)
1447 return E_POINTER;
1448 *lpClassId = *This->pclsid;
1449
1450 return S_OK;
1451 }
1452
1453 /************************************************************************
1454 * IFSFldr_PersistFolder3_Initialize
1455 *
1456 * NOTES
1457 * sPathTarget is not set. Don't know how to handle in a non rooted environment.
1458 */
1459 static HRESULT WINAPI
1460 IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface, LPCITEMIDLIST pidl)
1461 {
1462 WCHAR wszTemp[MAX_PATH];
1463
1464 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1465
1466 TRACE ("(%p)->(%p)\n", This, pidl);
1467
1468 SHFree (This->pidlRoot); /* free the old pidl */
1469 This->pidlRoot = ILClone (pidl); /* set my pidl */
1470
1471 SHFree (This->sPathTarget);
1472 This->sPathTarget = NULL;
1473
1474 /* set my path */
1475 if (SHGetPathFromIDListW (pidl, wszTemp)) {
1476 int len = wcslen(wszTemp);
1477 This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
1478 if (!This->sPathTarget)
1479 return E_OUTOFMEMORY;
1480 memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1481 }
1482
1483 TRACE ("--(%p)->(%s)\n", This, debugstr_w(This->sPathTarget));
1484 return S_OK;
1485 }
1486
1487 /**************************************************************************
1488 * IFSFldr_PersistFolder3_GetCurFolder
1489 */
1490 static HRESULT WINAPI
1491 IFSFldr_PersistFolder3_fnGetCurFolder (IPersistFolder3 * iface,
1492 LPITEMIDLIST * pidl)
1493 {
1494 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1495
1496 TRACE ("(%p)->(%p)\n", This, pidl);
1497
1498 if (!pidl) return E_POINTER;
1499 *pidl = ILClone (This->pidlRoot);
1500 return S_OK;
1501 }
1502
1503 /**************************************************************************
1504 * IFSFldr_PersistFolder3_InitializeEx
1505 *
1506 * FIXME: error handling
1507 */
1508 static HRESULT WINAPI
1509 IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface,
1510 IBindCtx * pbc, LPCITEMIDLIST pidlRoot,
1511 const PERSIST_FOLDER_TARGET_INFO * ppfti)
1512 {
1513 WCHAR wszTemp[MAX_PATH];
1514
1515 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1516
1517 TRACE ("(%p)->(%p,%p,%p)\n", This, pbc, pidlRoot, ppfti);
1518 if (ppfti)
1519 TRACE ("--%p %s %s 0x%08x 0x%08x\n",
1520 ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1521 debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1522 ppfti->csidl);
1523
1524 pdump (pidlRoot);
1525 if (ppfti && ppfti->pidlTargetFolder)
1526 pdump (ppfti->pidlTargetFolder);
1527
1528 if (This->pidlRoot)
1529 __SHFreeAndNil (&This->pidlRoot); /* free the old */
1530 if (This->sPathTarget)
1531 __SHFreeAndNil (&This->sPathTarget);
1532
1533 /*
1534 * Root path and pidl
1535 */
1536 This->pidlRoot = ILClone (pidlRoot);
1537
1538 /*
1539 * the target folder is spezified in csidl OR pidlTargetFolder OR
1540 * szTargetParsingName
1541 */
1542 if (ppfti) {
1543 if (ppfti->csidl != -1) {
1544 if (SHGetSpecialFolderPathW (0, wszTemp, ppfti->csidl,
1545 ppfti->csidl & CSIDL_FLAG_CREATE)) {
1546 int len = wcslen(wszTemp);
1547 This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
1548 if (!This->sPathTarget)
1549 return E_OUTOFMEMORY;
1550 memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1551 }
1552 } else if (ppfti->szTargetParsingName[0]) {
1553 int len = wcslen(ppfti->szTargetParsingName);
1554 This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
1555 if (!This->sPathTarget)
1556 return E_OUTOFMEMORY;
1557 memcpy(This->sPathTarget, ppfti->szTargetParsingName,
1558 (len + 1) * sizeof(WCHAR));
1559 } else if (ppfti->pidlTargetFolder) {
1560 if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) {
1561 int len = wcslen(wszTemp);
1562 This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
1563 if (!This->sPathTarget)
1564 return E_OUTOFMEMORY;
1565 memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1566 }
1567 }
1568 }
1569
1570 TRACE ("--(%p)->(target=%s)\n", This, debugstr_w(This->sPathTarget));
1571 pdump (This->pidlRoot);
1572 return (This->sPathTarget) ? S_OK : E_FAIL;
1573 }
1574
1575 static HRESULT WINAPI
1576 IFSFldr_PersistFolder3_GetFolderTargetInfo (IPersistFolder3 * iface,
1577 PERSIST_FOLDER_TARGET_INFO * ppfti)
1578 {
1579 IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1580 FIXME ("(%p)->(%p)\n", This, ppfti);
1581 ZeroMemory (ppfti, sizeof (ppfti));
1582 return E_NOTIMPL;
1583 }
1584
1585 static const IPersistFolder3Vtbl vt_FSFldr_PersistFolder3 =
1586 {
1587 IFSFldr_PersistFolder3_QueryInterface,
1588 IFSFldr_PersistFolder3_AddRef,
1589 IFSFldr_PersistFolder3_Release,
1590 IFSFldr_PersistFolder3_GetClassID,
1591 IFSFldr_PersistFolder3_Initialize,
1592 IFSFldr_PersistFolder3_fnGetCurFolder,
1593 IFSFldr_PersistFolder3_InitializeEx,
1594 IFSFldr_PersistFolder3_GetFolderTargetInfo
1595 };
1596
1597 /****************************************************************************
1598 * ISFDropTarget implementation
1599 */
1600 static BOOL
1601 ISFDropTarget_QueryDrop (IDropTarget * iface, DWORD dwKeyState,
1602 LPDWORD pdwEffect)
1603 {
1604 DWORD dwEffect = *pdwEffect;
1605
1606 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1607
1608 *pdwEffect = DROPEFFECT_NONE;
1609
1610 if (This->fAcceptFmt) { /* Does our interpretation of the keystate ... */
1611 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1612
1613 /* ... matches the desired effect ? */
1614 if (dwEffect & *pdwEffect) {
1615 return TRUE;
1616 }
1617 }
1618 return FALSE;
1619 }
1620
1621 static HRESULT WINAPI
1622 ISFDropTarget_QueryInterface (IDropTarget * iface, REFIID riid, LPVOID * ppvObj)
1623 {
1624 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1625
1626 TRACE ("(%p)\n", This);
1627
1628 return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj);
1629 }
1630
1631 static ULONG WINAPI ISFDropTarget_AddRef (IDropTarget * iface)
1632 {
1633 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1634
1635 TRACE ("(%p)\n", This);
1636
1637 return IUnknown_AddRef (This->pUnkOuter);
1638 }
1639
1640 static ULONG WINAPI ISFDropTarget_Release (IDropTarget * iface)
1641 {
1642 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1643
1644 TRACE ("(%p)\n", This);
1645
1646 return IUnknown_Release (This->pUnkOuter);
1647 }
1648
1649 static HRESULT WINAPI
1650 ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject,
1651 DWORD dwKeyState, POINTL pt, DWORD * pdwEffect)
1652 {
1653 FORMATETC fmt;
1654
1655 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1656
1657 TRACE ("(%p)->(DataObject=%p)\n", This, pDataObject);
1658
1659 InitFormatEtc (fmt, This->cfShellIDList, TYMED_HGLOBAL);
1660
1661 This->fAcceptFmt = (S_OK == IDataObject_QueryGetData (pDataObject, &fmt)) ?
1662 TRUE : FALSE;
1663
1664 ISFDropTarget_QueryDrop (iface, dwKeyState, pdwEffect);
1665
1666 return S_OK;
1667 }
1668
1669 static HRESULT WINAPI
1670 ISFDropTarget_DragOver (IDropTarget * iface, DWORD dwKeyState, POINTL pt,
1671 DWORD * pdwEffect)
1672 {
1673 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1674
1675 TRACE ("(%p)\n", This);
1676
1677 if (!pdwEffect)
1678 return E_INVALIDARG;
1679
1680 ISFDropTarget_QueryDrop (iface, dwKeyState, pdwEffect);
1681
1682 return S_OK;
1683 }
1684
1685 static HRESULT WINAPI ISFDropTarget_DragLeave (IDropTarget * iface)
1686 {
1687 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1688
1689 TRACE ("(%p)\n", This);
1690
1691 This->fAcceptFmt = FALSE;
1692
1693 return S_OK;
1694 }
1695
1696 static HRESULT WINAPI
1697 ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject,
1698 DWORD dwKeyState, POINTL pt, DWORD * pdwEffect)
1699 {
1700 IGenericSFImpl *This = impl_from_IDropTarget(iface);
1701
1702 FIXME ("(%p) object dropped\n", This);
1703
1704 return E_NOTIMPL;
1705 }
1706
1707 static const IDropTargetVtbl dtvt = {
1708 ISFDropTarget_QueryInterface,
1709 ISFDropTarget_AddRef,
1710 ISFDropTarget_Release,
1711 ISFDropTarget_DragEnter,
1712 ISFDropTarget_DragOver,
1713 ISFDropTarget_DragLeave,
1714 ISFDropTarget_Drop
1715 };