Made IShellFolder::fnParseDisplayName return a relative pidl.
[wine] / dlls / shell32 / shlfolder.c
1 /*
2  *      Shell Folder stuff
3  *
4  *      Copyright 1997  Marcus Meissner
5  *      Copyright 1998  Juergen Schmied
6  *      
7  *      IShellFolder with IDropTarget, IPersistFolder
8  *
9  */
10
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "debugtools.h"
15 #include "winerror.h"
16
17 #include "oleidl.h"
18 #include "shlguid.h"
19
20 #include "pidl.h"
21 #include "wine/obj_base.h"
22 #include "wine/obj_dragdrop.h"
23 #include "wine/obj_shellfolder.h"
24 #include "shell32_main.h"
25
26 DEFAULT_DEBUG_CHANNEL(shell)
27
28 /***************************************************************************
29  * IDropTarget interface definition for the ShellFolder
30  */
31
32 typedef struct
33 {       ICOM_VTABLE(IDropTarget)* lpvtbl;
34         ULONG ref;
35 } ISFDropTarget;
36
37 static struct ICOM_VTABLE(IDropTarget) dtvt;
38
39
40 /****************************************************************************
41  * ISFDropTarget implementation
42  */
43
44 static IDropTarget * WINAPI ISFDropTarget_Constructor(void)
45 {
46         ISFDropTarget* sf;
47
48         sf = HeapAlloc(GetProcessHeap(), 0, sizeof(ISFDropTarget));
49
50         if (sf)
51         { sf->lpvtbl = &dtvt;
52           sf->ref    = 1;
53         }
54
55         return (IDropTarget *)sf;
56 }
57
58 static HRESULT WINAPI ISFDropTarget_QueryInterface(
59         IDropTarget *iface,
60         REFIID riid,
61         LPVOID *ppvObj)
62 {
63         ICOM_THIS(ISFDropTarget,iface);
64
65         char    xriid[50];
66         WINE_StringFromCLSID((LPCLSID)riid,xriid);
67
68         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,xriid,ppvObj);
69
70         if ( !This || !ppvObj)
71           return E_INVALIDARG;
72
73         *ppvObj = NULL;
74
75         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
76         { *ppvObj = This; 
77         }
78         else if(IsEqualIID(riid, &IID_IDropTarget))  /*IShellFolder*/
79         {    *ppvObj = (ISFDropTarget*)This;
80         }   
81
82         if(*ppvObj)
83         { IDropTarget_AddRef((IDropTarget*)*ppvObj);
84           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
85           return S_OK;
86         }
87
88         TRACE("-- Interface: E_NOINTERFACE\n");
89
90         return E_NOINTERFACE;
91 }
92
93 static ULONG WINAPI ISFDropTarget_AddRef( IDropTarget *iface)
94 {
95         ICOM_THIS(ISFDropTarget,iface);
96
97         TRACE("(%p)->(count=%lu)\n",This,This->ref);
98
99         shell32_ObjCount++;
100
101         return ++(This->ref);
102 }
103
104 static ULONG WINAPI ISFDropTarget_Release( IDropTarget *iface)
105 {
106         ICOM_THIS(ISFDropTarget,iface);
107
108         shell32_ObjCount--;
109
110         if (!--(This->ref)) 
111         { TRACE("-- destroying ISFDropTarget (%p)\n",This);
112           HeapFree(GetProcessHeap(),0,This);
113           return 0;
114         }
115         return This->ref;
116 }
117
118 static HRESULT WINAPI ISFDropTarget_DragEnter(
119         IDropTarget     *iface,
120         IDataObject     *pDataObject,
121         DWORD           grfKeyState,
122         POINTL          pt,
123         DWORD           *pdwEffect)
124 {       
125
126         ICOM_THIS(ISFDropTarget,iface);
127
128         FIXME("Stub: This=%p, DataObject=%p\n",This,pDataObject);
129
130         return E_NOTIMPL;
131 }
132
133 static HRESULT WINAPI ISFDropTarget_DragOver(
134         IDropTarget     *iface,
135         DWORD           grfKeyState,
136         POINTL          pt,
137         DWORD           *pdwEffect)
138 {
139         ICOM_THIS(ISFDropTarget,iface);
140
141         FIXME("Stub: This=%p\n",This);
142
143         return E_NOTIMPL;
144 }
145
146 static HRESULT WINAPI ISFDropTarget_DragLeave(
147         IDropTarget     *iface)
148 {
149         ICOM_THIS(ISFDropTarget,iface);
150
151         FIXME("Stub: This=%p\n",This);
152
153         return E_NOTIMPL;
154 }
155
156 static HRESULT WINAPI ISFDropTarget_Drop(
157         IDropTarget     *iface,
158         IDataObject*    pDataObject,
159         DWORD           grfKeyState,
160         POINTL          pt,
161         DWORD           *pdwEffect)
162 {
163         ICOM_THIS(ISFDropTarget,iface);
164
165         FIXME("Stub: This=%p\n",This);
166
167         return E_NOTIMPL;
168 }
169
170 static struct ICOM_VTABLE(IDropTarget) dtvt = 
171 {
172         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
173         ISFDropTarget_QueryInterface,
174         ISFDropTarget_AddRef,
175         ISFDropTarget_Release,
176         ISFDropTarget_DragEnter,
177         ISFDropTarget_DragOver,
178         ISFDropTarget_DragLeave,
179         ISFDropTarget_Drop
180 };
181
182 /***************************************************************************
183  *  GetNextElement (internal function)
184  *
185  * gets a part of a string till the first backslash
186  *
187  * PARAMETERS
188  *  pszNext [IN] string to get the element from
189  *  pszOut  [IN] pointer to buffer whitch receives string
190  *  dwOut   [IN] length of pszOut
191  *
192  *  RETURNS
193  *    LPSTR pointer to first, not yet parsed char
194  */
195 LPSTR GetNextElement(LPSTR pszNext,LPSTR pszOut,DWORD dwOut)
196 {       LPSTR   pszTail = pszNext;
197         DWORD dwCopy;
198         TRACE("(%s %p 0x%08lx)\n",debugstr_a(pszNext),pszOut,dwOut);
199
200         if(!pszNext || !*pszNext)
201           return NULL;
202
203         while(*pszTail && (*pszTail != '\\'))
204         { pszTail++;
205         }
206         dwCopy=((LPBYTE)pszTail-(LPBYTE)pszNext)/sizeof(CHAR)+1;
207         lstrcpynA(pszOut, pszNext, (dwOut<dwCopy)? dwOut : dwCopy);
208
209         if(*pszTail)
210         {  pszTail++;
211         }
212
213         TRACE("--(%s %s 0x%08lx)\n",debugstr_a(pszNext),debugstr_a(pszOut),dwOut);
214         return pszTail;
215 }
216
217 /***********************************************************************
218 *   IShellFolder implementation
219 */
220
221 static struct ICOM_VTABLE(IShellFolder) sfvt;
222
223 static struct ICOM_VTABLE(IPersistFolder) psfvt;
224 #define _IPersistFolder_Offset ((int)(&(((IGenericSFImpl*)0)->lpvtblPersistFolder))) 
225 #define _ICOM_THIS_From_IPersistFolder(class, name) class* This = (class*)(((char*)name)-_IPersistFolder_Offset); 
226
227 /**************************************************************************
228 *         IShellFolder_Constructor
229 */
230
231 IShellFolder * IShellFolder_Constructor(
232         IGenericSFImpl * pParent,
233         LPITEMIDLIST pidl) 
234 {
235         IGenericSFImpl *        sf;
236         DWORD                   dwSize=0;
237
238         sf=(IGenericSFImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IGenericSFImpl));
239         sf->ref=1;
240
241         sf->lpvtbl=&sfvt;
242         sf->lpvtblPersistFolder=&psfvt;
243         
244         sf->sMyPath=NULL;       /* path of the folder */
245         sf->pMyPidl=NULL;       /* my qualified pidl */
246
247         TRACE("(%p)->(parent=%p, pidl=%p)\n",sf,pParent, pidl);
248         pdump(pidl);
249                 
250         /* keep a copy of the pidl in the instance*/
251         sf->mpidl = ILClone(pidl);              /* my short pidl */
252         
253         if(sf->mpidl)                           /* do we have a pidl? */
254         { dwSize = 0;
255           if(pParent->sMyPath)                  /* get the size of the parents path */
256           { dwSize += strlen(pParent->sMyPath) ;
257             TRACE("-- (%p)->(parent's path=%s)\n",sf, debugstr_a(pParent->sMyPath));
258           }   
259           dwSize += _ILGetFolderText(sf->mpidl,NULL,0); /* add the size of the foldername*/
260           sf->sMyPath = SHAlloc(dwSize+2);              /* '\0' and backslash */
261           if(sf->sMyPath)
262           { int len;
263             *(sf->sMyPath)=0x00;
264             if(pParent->sMyPath)                        /* if the parent has a path, get it*/
265             {  strcpy(sf->sMyPath, pParent->sMyPath);
266                PathAddBackslashA (sf->sMyPath);
267             }
268             sf->pMyPidl = ILCombine(pParent->pMyPidl, pidl);
269             len = strlen(sf->sMyPath);
270             _ILGetFolderText(sf->mpidl, sf->sMyPath+len, dwSize-len);
271             TRACE("-- (%p)->(my pidl=%p, my path=%s)\n",sf, sf->pMyPidl,debugstr_a(sf->sMyPath));
272             pdump (sf->pMyPidl);
273           }
274         }
275         shell32_ObjCount++;
276         return (IShellFolder *)sf;
277 }
278 /**************************************************************************
279  *  IShellFolder_fnQueryInterface
280  *
281  * PARAMETERS
282  *  REFIID riid         [in ] Requested InterfaceID
283  *  LPVOID* ppvObject   [out] Interface* to hold the result
284  */
285 static HRESULT WINAPI IShellFolder_fnQueryInterface(
286         IShellFolder * iface,
287         REFIID riid,
288         LPVOID *ppvObj)
289 {
290         ICOM_THIS(IGenericSFImpl, iface);
291
292         char    xriid[50];      
293         WINE_StringFromCLSID((LPCLSID)riid,xriid);
294         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,xriid,ppvObj);
295
296         *ppvObj = NULL;
297
298         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
299         { *ppvObj = This; 
300         }
301         else if(IsEqualIID(riid, &IID_IShellFolder))  /*IShellFolder*/
302         {    *ppvObj = (IShellFolder*)This;
303         }   
304         else if(IsEqualIID(riid, &IID_IPersistFolder))  /*IPersistFolder*/
305         {    *ppvObj = (IPersistFolder*)&(This->lpvtblPersistFolder);
306         }   
307
308         if(*ppvObj)
309         {
310           IUnknown_AddRef((IUnknown*)(*ppvObj));
311           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
312           return S_OK;
313         }
314         TRACE("-- Interface: E_NOINTERFACE\n");
315         return E_NOINTERFACE;
316 }
317
318 /**************************************************************************
319 *  IShellFolder::AddRef
320 */
321
322 static ULONG WINAPI IShellFolder_fnAddRef(IShellFolder * iface)
323 {
324         ICOM_THIS(IGenericSFImpl, iface);
325
326         TRACE("(%p)->(count=%lu)\n",This,This->ref);
327
328         shell32_ObjCount++;
329         return ++(This->ref);
330 }
331
332 /**************************************************************************
333  *  IShellFolder_fnRelease
334  */
335 static ULONG WINAPI IShellFolder_fnRelease(IShellFolder * iface) 
336 {
337         ICOM_THIS(IGenericSFImpl, iface);
338
339         TRACE("(%p)->(count=%lu)\n",This,This->ref);
340
341         shell32_ObjCount--;
342         if (!--(This->ref)) 
343         { TRACE("-- destroying IShellFolder(%p)\n",This);
344
345           if (pdesktopfolder == iface)
346           { pdesktopfolder=NULL;
347             TRACE("-- destroyed IShellFolder(%p) was Desktopfolder\n",This);
348           }
349           if(This->pMyPidl)
350           { SHFree(This->pMyPidl);
351           }
352           if(This->mpidl)
353           { SHFree(This->mpidl);
354           }
355           if(This->sMyPath)
356           { SHFree(This->sMyPath);
357           }
358
359           HeapFree(GetProcessHeap(),0,This);
360
361           return 0;
362         }
363         return This->ref;
364 }
365 /**************************************************************************
366 *               IShellFolder_fnParseDisplayName
367 * PARAMETERS
368 *  HWND          hwndOwner,      //[in ] Parent window for any message's
369 *  LPBC          pbc,            //[in ] reserved
370 *  LPOLESTR      lpszDisplayName,//[in ] "Unicode" displayname.
371 *  ULONG*        pchEaten,       //[out] (unicode) characters processed
372 *  LPITEMIDLIST* ppidl,          //[out] complex pidl to item
373 *  ULONG*        pdwAttributes   //[out] items attributes
374 *
375 * FIXME: 
376 *    pdwAttributes: not used
377 */
378 static HRESULT WINAPI IShellFolder_fnParseDisplayName(
379         IShellFolder * iface,
380         HWND hwndOwner,
381         LPBC pbcReserved,
382         LPOLESTR lpszDisplayName,
383         DWORD *pchEaten,
384         LPITEMIDLIST *ppidl,
385         DWORD *pdwAttributes)
386 {
387         ICOM_THIS(IGenericSFImpl, iface);
388
389         HRESULT         hr=E_OUTOFMEMORY;
390         LPITEMIDLIST    pidlFull=NULL, pidlTemp = NULL, pidlOld = NULL, pidlNew = NULL;
391         LPSTR           pszNext=NULL;
392         CHAR            szTemp[MAX_PATH],szElement[MAX_PATH];
393         BOOL            bIsFile;
394
395         TRACE("(%p)->(HWND=0x%08x,%p,%p=%s,%p,pidl=%p,%p)\n",
396         This,hwndOwner,pbcReserved,lpszDisplayName,
397         debugstr_w(lpszDisplayName),pchEaten,ppidl,pdwAttributes);
398
399         { hr = E_FAIL;
400           WideCharToLocal(szTemp, lpszDisplayName, lstrlenW(lpszDisplayName) + 1);
401           
402           if(szTemp[0])
403           { if (strcmp(szTemp,"Desktop")==0)
404             { pidlFull = _ILCreateDesktop();
405             }
406             else if (strcmp(szTemp,"My Computer")==0)
407             { pidlFull = _ILCreateMyComputer();
408             }
409             else
410             { if (!PathIsRootA(szTemp))
411               { if (This->sMyPath && strlen (This->sMyPath))
412                 { if (strcmp(This->sMyPath,"My Computer"))
413                   { strcpy (szElement,This->sMyPath);
414                     PathAddBackslashA (szElement);
415                     strcat (szElement, szTemp);
416                     strcpy (szTemp, szElement);
417                   }
418                 }
419               }
420               /* check if the lpszDisplayName is Folder or File*/
421               bIsFile = ! (GetFileAttributesA(szTemp) & FILE_ATTRIBUTE_DIRECTORY);
422               pszNext = GetNextElement(szTemp, szElement, MAX_PATH);
423
424               pidlFull = _ILCreateMyComputer();
425               pidlTemp = _ILCreateDrive(szElement);                     
426               pidlOld = pidlFull;
427               pidlFull = ILCombine(pidlFull,pidlTemp);
428               SHFree(pidlOld);
429
430               if(pidlFull)
431               { while((pszNext=GetNextElement(pszNext, szElement, MAX_PATH)))
432                 { if(!*pszNext && bIsFile)
433                   { pidlTemp = _ILCreateValue(NULL, szElement);         /* FIXME: shortname */
434                   }
435                   else                          
436                   { pidlTemp = _ILCreateFolder(NULL, szElement);        /* FIXME: shortname */
437                   }
438                   pidlOld = pidlFull;
439                   pidlFull = ILCombine(pidlFull,pidlTemp);
440                   SHFree(pidlOld);
441                 }
442                 hr = S_OK;
443               }
444
445               /* The following code is to make the absolute
446                pidl (pidlFull)  relative to the current folder */
447
448               if((pidlNew = ILFindChild(This->pMyPidl,pidlFull)))
449               {
450                   pidlOld = pidlFull;
451                   pidlFull = ILClone(pidlNew);
452                   SHFree(pidlOld);
453               }
454             }
455           }
456         }
457         *ppidl = pidlFull;
458         return hr;
459 }
460
461 /**************************************************************************
462 *               IShellFolder_fnEnumObjects
463 * PARAMETERS
464 *  HWND          hwndOwner,    //[in ] Parent Window
465 *  DWORD         grfFlags,     //[in ] SHCONTF enumeration mask
466 *  LPENUMIDLIST* ppenumIDList  //[out] IEnumIDList interface
467 */
468 static HRESULT WINAPI IShellFolder_fnEnumObjects(
469         IShellFolder * iface,
470         HWND hwndOwner,
471         DWORD dwFlags,
472         LPENUMIDLIST* ppEnumIDList)
473 {
474         ICOM_THIS(IGenericSFImpl, iface);
475
476         TRACE("(%p)->(HWND=0x%08x flags=0x%08lx pplist=%p)\n",This,hwndOwner,dwFlags,ppEnumIDList);
477
478         *ppEnumIDList = NULL;
479         *ppEnumIDList = IEnumIDList_Constructor (This->sMyPath, dwFlags);
480         TRACE("-- (%p)->(new ID List: %p)\n",This,*ppEnumIDList);
481         if(!*ppEnumIDList)
482         { return E_OUTOFMEMORY;
483         }
484         return S_OK;            
485 }
486
487 /**************************************************************************
488 *               IShellFolder_fnBindToObject
489 * PARAMETERS
490 *  LPCITEMIDLIST pidl,       //[in ] complex pidl to open
491 *  LPBC          pbc,        //[in ] reserved
492 *  REFIID        riid,       //[in ] Initial Interface
493 *  LPVOID*       ppvObject   //[out] Interface*
494 */
495 static HRESULT WINAPI IShellFolder_fnBindToObject( IShellFolder * iface, LPCITEMIDLIST pidl,
496                         LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
497 {
498         ICOM_THIS(IGenericSFImpl, iface);
499
500         char            xriid[50];
501         HRESULT         hr;
502         LPSHELLFOLDER   pShellFolder;
503         
504         WINE_StringFromCLSID(riid,xriid);
505
506         TRACE("(%p)->(pidl=%p,%p,\n\tIID:%s,%p)\n",This,pidl,pbcReserved,xriid,ppvOut);
507
508         *ppvOut = NULL;
509
510         pShellFolder = IShellFolder_Constructor(This, pidl);
511
512         if(!pShellFolder)
513           return E_OUTOFMEMORY;
514
515         hr = pShellFolder->lpvtbl->fnQueryInterface(pShellFolder, riid, ppvOut);
516         pShellFolder->lpvtbl->fnRelease(pShellFolder);
517         TRACE("-- (%p)->(interface=%p)\n",This, ppvOut);
518         return hr;
519 }
520
521 /**************************************************************************
522 *  IShellFolder_fnBindToStorage
523 * PARAMETERS
524 *  LPCITEMIDLIST pidl,       //[in ] complex pidl to store
525 *  LPBC          pbc,        //[in ] reserved
526 *  REFIID        riid,       //[in ] Initial storage interface 
527 *  LPVOID*       ppvObject   //[out] Interface* returned
528 */
529 static HRESULT WINAPI IShellFolder_fnBindToStorage(
530         IShellFolder * iface,
531         LPCITEMIDLIST pidl,
532         LPBC pbcReserved,
533         REFIID riid,
534         LPVOID *ppvOut)
535 {
536         ICOM_THIS(IGenericSFImpl, iface);
537
538         char xriid[50];
539         WINE_StringFromCLSID(riid,xriid);
540
541         FIXME("(%p)->(pidl=%p,%p,\n\tIID:%s,%p) stub\n",This,pidl,pbcReserved,xriid,ppvOut);
542
543         *ppvOut = NULL;
544         return E_NOTIMPL;
545 }
546
547 /**************************************************************************
548 *  IShellFolder_fnCompareIDs
549 *
550 * PARMETERS
551 *  LPARAM        lParam, //[in ] Column?
552 *  LPCITEMIDLIST pidl1,  //[in ] simple pidl
553 *  LPCITEMIDLIST pidl2)  //[in ] simple pidl
554 *
555 * NOTES
556 *   Special case - If one of the items is a Path and the other is a File,
557 *   always make the Path come before the File.
558 *
559 * FIXME
560 *  we have to handle simple pidl's only (?)
561 */
562 static HRESULT WINAPI  IShellFolder_fnCompareIDs(
563         IShellFolder * iface,
564         LPARAM lParam,
565         LPCITEMIDLIST pidl1,
566         LPCITEMIDLIST pidl2)
567 {
568         ICOM_THIS(IGenericSFImpl, iface);
569
570         CHAR szString1[MAX_PATH] = "";
571         CHAR szString2[MAX_PATH] = "";
572         int   nReturn;
573         LPCITEMIDLIST  pidlTemp1 = pidl1, pidlTemp2 = pidl2;
574
575         TRACE("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n",This,lParam,pidl1,pidl2);
576         pdump (pidl1);
577         pdump (pidl2);
578
579         if (!pidl1 && !pidl2)
580           return 0;
581         if (!pidl1)     /* Desktop < anything */
582           return -1;
583         if (!pidl2)
584           return 1;
585
586         /* get the last item in each list */
587         pidlTemp1 = ILFindLastID(pidlTemp1);
588         pidlTemp2 = ILFindLastID(pidlTemp2);
589
590         /* at This point, both pidlTemp1 and pidlTemp2 point to the last item in the list */
591         if(_ILIsValue(pidlTemp1) != _ILIsValue(pidlTemp2))
592         { if(_ILIsValue(pidlTemp1))
593             return 1;
594           return -1;
595         }
596
597         _ILGetDrive( pidl1,szString1,sizeof(szString1));
598         _ILGetDrive( pidl2,szString2,sizeof(szString2));
599         nReturn = strcasecmp(szString1, szString2);
600
601         if(nReturn)
602           return nReturn;
603
604         _ILGetFolderText( pidl1,szString1,sizeof(szString1));
605         _ILGetFolderText( pidl2,szString2,sizeof(szString2));
606         nReturn = strcasecmp(szString1, szString2);
607
608         if(nReturn)
609           return nReturn;
610
611         _ILGetValueText(pidl1,szString1,sizeof(szString1));
612         _ILGetValueText(pidl2,szString2,sizeof(szString2));
613         return strcasecmp(szString1, szString2);
614 }
615
616 /**************************************************************************
617 *         IShellFolder_fnCreateViewObject
618 * Creates an View Object representing the ShellFolder
619 *  IShellView / IShellBrowser / IContextMenu
620 *
621 * PARAMETERS
622 *  HWND    hwndOwner,  // Handle of owner window
623 *  REFIID  riid,       // Requested initial interface
624 *  LPVOID* ppvObject)  // Resultant interface*
625 *
626 * NOTES
627 *  the same as SHCreateShellFolderViewEx ???
628 */
629 static HRESULT WINAPI IShellFolder_fnCreateViewObject( IShellFolder * iface,
630                  HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
631 {
632         ICOM_THIS(IGenericSFImpl, iface);
633
634         LPSHELLVIEW pShellView;
635         char    xriid[50];
636         HRESULT       hr;
637
638         WINE_StringFromCLSID(riid,xriid);
639         TRACE("(%p)->(hwnd=0x%x,\n\tIID:\t%s,%p)\n",This,hwndOwner,xriid,ppvOut);
640         
641         *ppvOut = NULL;
642
643         pShellView = IShellView_Constructor((IShellFolder *) This, This->mpidl);
644
645         if(!pShellView)
646           return E_OUTOFMEMORY;
647           
648         hr = pShellView->lpvtbl->fnQueryInterface(pShellView, riid, ppvOut);
649         pShellView->lpvtbl->fnRelease(pShellView);
650         TRACE("-- (%p)->(interface=%p)\n",This, ppvOut);
651         return hr; 
652 }
653
654 /**************************************************************************
655 *  IShellFolder_fnGetAttributesOf
656 *
657 * PARAMETERS
658 *  UINT            cidl,     //[in ] num elements in pidl array
659 +  LPCITEMIDLIST*  apidl,    //[in ] simple pidl array 
660 *  ULONG*          rgfInOut) //[out] result array  
661 *
662 * FIXME: quick hack
663 *  Note: rgfInOut is documented as being an array of ULONGS.
664 *  This does not seem to be the case. Testing This function using the shell to 
665 *  call it with cidl > 1 (by deleting multiple items) reveals that the shell
666 *  passes ONE element in the array and writing to further elements will
667 *  cause the shell to fail later.
668 */
669 static HRESULT WINAPI IShellFolder_fnGetAttributesOf(IShellFolder * iface,UINT cidl,LPCITEMIDLIST *apidl,DWORD *rgfInOut)
670 {
671         ICOM_THIS(IGenericSFImpl, iface);
672
673         LPCITEMIDLIST * pidltemp;
674         DWORD i;
675
676         TRACE("(%p)->(%d,%p,%p)\n",This,cidl,apidl,rgfInOut);
677
678         if ( (!cidl) || (!apidl) || (!rgfInOut))
679           return E_INVALIDARG;
680
681         pidltemp=apidl;
682         *rgfInOut = 0x00;
683         i=cidl;
684
685         TRACE("-- mask=0x%08lx\n",*rgfInOut);
686
687         do
688         { if (*pidltemp)
689           { pdump (*pidltemp);
690             if (_ILIsDesktop( *pidltemp))
691             { *rgfInOut |= ( SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANLINK );
692             }
693             else if (_ILIsMyComputer( *pidltemp))
694             { *rgfInOut |= ( SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
695                              SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK );
696             }
697             else if (_ILIsDrive( *pidltemp))
698             { *rgfInOut |= ( SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM  | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR  | 
699                              SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANLINK );
700             }
701             else if (_ILIsFolder( *pidltemp))
702             { *rgfInOut |= ( SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_CAPABILITYMASK );
703             }
704             else if (_ILIsValue( *pidltemp))
705             { *rgfInOut |= (SFGAO_FILESYSTEM | SFGAO_CAPABILITYMASK );
706             }
707           }
708           pidltemp++;
709           cidl--;
710         } while (cidl > 0 && *pidltemp);
711
712         return S_OK;
713 }
714 /**************************************************************************
715 *  IShellFolder_fnGetUIObjectOf
716 *
717 * PARAMETERS
718 *  HWND           hwndOwner, //[in ] Parent window for any output
719 *  UINT           cidl,      //[in ] array size
720 *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
721 *  REFIID         riid,      //[in ] Requested Interface
722 *  UINT*          prgfInOut, //[   ] reserved 
723 *  LPVOID*        ppvObject) //[out] Resulting Interface
724 *
725 * NOTES
726 *  This function gets asked to return "view objects" for one or more (multiple select)
727 *  items:
728 *  The viewobject typically is an COM object with one of the following interfaces:
729 *  IExtractIcon,IDataObject,IContextMenu
730 *  In order to support icon positions in the default Listview your DataObject
731 *  must implement the SetData method (in addition to GetData :) - the shell passes
732 *  a barely documented "Icon positions" structure to SetData when the drag starts,
733 *  and GetData's it if the drop is in another explorer window that needs the positions.
734 */
735 static HRESULT WINAPI IShellFolder_fnGetUIObjectOf( 
736         IShellFolder *  iface,
737         HWND            hwndOwner,
738         UINT            cidl,
739         LPCITEMIDLIST * apidl, 
740         REFIID          riid, 
741         UINT *          prgfInOut,
742         LPVOID *        ppvOut)
743 {       
744         ICOM_THIS(IGenericSFImpl, iface);
745
746         char            xclsid[50];
747         LPITEMIDLIST    pidl;
748         LPUNKNOWN       pObj = NULL; 
749
750         WINE_StringFromCLSID(riid,xclsid);
751
752         TRACE("(%p)->(%u,%u,apidl=%p,\n\tIID:%s,%p,%p)\n",
753           This,hwndOwner,cidl,apidl,xclsid,prgfInOut,ppvOut);
754
755         *ppvOut = NULL;
756
757         if(IsEqualIID(riid, &IID_IContextMenu))
758         { 
759           if(cidl < 1)
760             return E_INVALIDARG;
761
762           pObj  = (LPUNKNOWN)IContextMenu_Constructor((IShellFolder *)This, apidl, cidl);
763         }
764         else if (IsEqualIID(riid, &IID_IDataObject))
765         { 
766           if (cidl < 1)
767             return(E_INVALIDARG);
768
769           pObj = (LPUNKNOWN)IDataObject_Constructor (hwndOwner, (IShellFolder *)This, apidl, cidl);
770         }
771         else if(IsEqualIID(riid, &IID_IExtractIconA))
772         { 
773           if (cidl != 1)
774             return(E_INVALIDARG);
775
776           pidl = ILCombine(This->pMyPidl,apidl[0]);
777           pObj = (LPUNKNOWN)IExtractIconA_Constructor( pidl );
778           SHFree(pidl);
779         } 
780         else if (IsEqualIID(riid, &IID_IDropTarget))
781         { 
782           if (cidl < 1)
783             return(E_INVALIDARG);
784
785           pObj = (LPUNKNOWN)ISFDropTarget_Constructor();
786         }
787         else
788         { 
789           ERR("(%p)->E_NOINTERFACE\n",This);
790           return E_NOINTERFACE;
791         }
792
793         if(!pObj)
794           return E_OUTOFMEMORY;
795
796         *ppvOut = pObj;
797         return S_OK;
798 }
799 /**************************************************************************
800 *  IShellFolder_fnGetDisplayNameOf
801 *  Retrieves the display name for the specified file object or subfolder
802 *
803 * PARAMETERS
804 *  LPCITEMIDLIST pidl,    //[in ] complex pidl to item
805 *  DWORD         dwFlags, //[in ] SHGNO formatting flags
806 *  LPSTRRET      lpName)  //[out] Returned display name
807 *
808 * FIXME
809 *  if the name is in the pidl the ret value should be a STRRET_OFFSET
810 */
811 #define GET_SHGDN_FOR(dwFlags)         ((DWORD)dwFlags & (DWORD)0x0000FF00)
812 #define GET_SHGDN_RELATION(dwFlags)    ((DWORD)dwFlags & (DWORD)0x000000FF)
813
814 static HRESULT WINAPI IShellFolder_fnGetDisplayNameOf(
815         IShellFolder * iface,
816         LPCITEMIDLIST pidl,
817         DWORD dwFlags,
818         LPSTRRET lpName)
819 {
820         ICOM_THIS(IGenericSFImpl, iface);
821
822         CHAR    szText[MAX_PATH];
823         CHAR    szTemp[MAX_PATH];
824         CHAR    szSpecial[MAX_PATH];
825         CHAR    szDrive[MAX_PATH];
826         DWORD   dwVolumeSerialNumber,dwMaximumComponetLength,dwFileSystemFlags;
827         LPITEMIDLIST    pidlTemp=NULL;
828         BOOL    bSimplePidl=FALSE;
829                 
830         TRACE("(%p)->(pidl=%p,0x%08lx,%p)\n",This,pidl,dwFlags,lpName);
831         pdump(pidl);
832         
833         szSpecial[0]=0x00; 
834         szDrive[0]=0x00;
835         szText[0]=0x00;
836         szTemp[0]=0x00;
837         
838         /* test if simple(relative) or complex(absolute) pidl */
839         pidlTemp = ILGetNext(pidl);
840         if (pidlTemp && !pidlTemp->mkid.cb )
841         { bSimplePidl = TRUE;
842           TRACE("-- simple pidl\n");
843         }
844
845         if (_ILIsDesktop( pidl))
846         { strcpy (szText,"Desktop");
847         }       
848         else
849         { if (_ILIsMyComputer(pidl))
850           { _ILGetItemText(pidl, szSpecial, MAX_PATH);
851             pidl = ILGetNext(pidl);
852           }
853
854           if (_ILIsDrive(pidl))
855           { _ILGetDrive( pidl, szTemp, MAX_PATH);
856
857             if ( dwFlags==SHGDN_NORMAL || dwFlags==SHGDN_INFOLDER)      /* like "A1-dos (C:)" */
858             { GetVolumeInformationA(szTemp,szDrive,MAX_PATH,&dwVolumeSerialNumber,&dwMaximumComponetLength,&dwFileSystemFlags,NULL,0);
859               szTemp[2]=0x00;                                           /* overwrite '\' */
860               strcat (szDrive," (");
861               strcat (szDrive,szTemp);
862               strcat (szDrive,")"); 
863             }
864             else                                                        /* like "C:\" */
865             {  PathAddBackslashA (szTemp);
866                strcpy(szDrive,szTemp);
867             }
868           }
869
870                 
871           switch(dwFlags)
872           { case SHGDN_NORMAL:                          /* 0x0000 */
873               _ILGetPidlPath( pidl, szText, MAX_PATH);
874               break;
875
876             case SHGDN_INFOLDER | SHGDN_FORPARSING:     /* 0x8001 */
877             case SHGDN_INFOLDER:                        /* 0x0001 */
878               pidlTemp = ILFindLastID(pidl);
879               if (pidlTemp)
880               { _ILGetItemText( pidlTemp, szText, MAX_PATH);
881               }
882               break;                            
883
884             case SHGDN_FORPARSING:                      /* 0x8000 */
885               if (bSimplePidl)
886               { /* if the IShellFolder has parents, get the path from the
887                 parent and add the ItemName*/
888                 szText[0]=0x00;
889                 if (This->sMyPath && strlen (This->sMyPath))
890                 { if (strcmp(This->sMyPath,"My Computer"))
891                   { strcpy (szText,This->sMyPath);
892                     PathAddBackslashA (szText);
893                   }
894                 }
895                 pidlTemp = ILFindLastID(pidl);
896                 if (pidlTemp)
897                 { _ILGetItemText( pidlTemp, szTemp, MAX_PATH );
898                 } 
899                 strcat(szText,szTemp);
900               }
901               else      /* if the pidl is absolute, get everything from the pidl*/                                      
902               { _ILGetPidlPath( pidl, szText, MAX_PATH);
903               }
904               break;
905             default:
906               TRACE("--- wrong flags=%lx\n", dwFlags);
907               return E_INVALIDARG;
908           }
909           if ((szText[0]==0x00 && szDrive[0]!=0x00)|| (bSimplePidl && szDrive[0]!=0x00))
910           { strcpy(szText,szDrive);
911           }
912           if (szText[0]==0x00 && szSpecial[0]!=0x00)
913           { strcpy(szText,szSpecial);
914           }
915         }
916
917         TRACE("-- (%p)->(%s)\n",This,szText);
918
919         if(!(lpName))
920         {  return E_OUTOFMEMORY;
921         }
922         lpName->uType = STRRET_CSTRA;   
923         strcpy(lpName->u.cStr,szText);
924         return S_OK;
925 }
926
927 /**************************************************************************
928 *  IShellFolder_fnSetNameOf
929 *  Changes the name of a file object or subfolder, possibly changing its item
930 *  identifier in the process.
931 *
932 * PARAMETERS
933 *  HWND          hwndOwner,  //[in ] Owner window for output
934 *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
935 *  LPCOLESTR     lpszName,   //[in ] the items new display name
936 *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
937 *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
938 */
939 static HRESULT WINAPI IShellFolder_fnSetNameOf(
940         IShellFolder * iface,
941         HWND hwndOwner, 
942         LPCITEMIDLIST pidl, /*simple pidl*/
943         LPCOLESTR lpName, 
944         DWORD dw, 
945         LPITEMIDLIST *pPidlOut)
946 {
947         ICOM_THIS(IGenericSFImpl, iface);
948
949         FIXME("(%p)->(%u,pidl=%p,%s,%lu,%p),stub!\n",
950         This,hwndOwner,pidl,debugstr_w(lpName),dw,pPidlOut);
951
952         return E_NOTIMPL;
953 }
954
955 /**************************************************************************
956 *  IShellFolder_fnGetFolderPath
957 *  FIXME: drive not included
958 */
959 static HRESULT WINAPI IShellFolder_fnGetFolderPath(IShellFolder * iface, LPSTR lpszOut, DWORD dwOutSize)
960 {
961         ICOM_THIS(IGenericSFImpl, iface);
962         DWORD   dwSize;
963         
964         TRACE("(%p)->(%p %lu)\n",This, lpszOut, dwOutSize);
965         if (!lpszOut)
966         { return FALSE;
967         }
968
969         *lpszOut=0;
970
971         if (! This->sMyPath)
972           return FALSE;
973           
974         dwSize = strlen (This->sMyPath) +1;
975         if ( dwSize > dwOutSize)
976           return FALSE;
977         strcpy(lpszOut, This->sMyPath);
978
979         TRACE("-- (%p)->(return=%s)\n",This, lpszOut);
980         return TRUE;
981 }
982
983 static ICOM_VTABLE(IShellFolder) sfvt = 
984 {       
985         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
986         IShellFolder_fnQueryInterface,
987         IShellFolder_fnAddRef,
988         IShellFolder_fnRelease,
989         IShellFolder_fnParseDisplayName,
990         IShellFolder_fnEnumObjects,
991         IShellFolder_fnBindToObject,
992         IShellFolder_fnBindToStorage,
993         IShellFolder_fnCompareIDs,
994         IShellFolder_fnCreateViewObject,
995         IShellFolder_fnGetAttributesOf,
996         IShellFolder_fnGetUIObjectOf,
997         IShellFolder_fnGetDisplayNameOf,
998         IShellFolder_fnSetNameOf,
999         IShellFolder_fnGetFolderPath
1000 };
1001
1002 /************************************************************************
1003  * ISFPersistFolder_QueryInterface (IUnknown)
1004  *
1005  * See Windows documentation for more details on IUnknown methods.
1006  */
1007 static HRESULT WINAPI ISFPersistFolder_QueryInterface(                                        
1008         IPersistFolder *        iface,
1009         REFIID                  iid,
1010         LPVOID*                 ppvObj)
1011 {
1012         _ICOM_THIS_From_IPersistFolder(IGenericSFImpl, iface);
1013
1014         return IShellFolder_QueryInterface((IShellFolder*)This, iid, ppvObj);
1015 }
1016
1017 /************************************************************************
1018  * ISFPersistFolder_AddRef (IUnknown)
1019  *
1020  * See Windows documentation for more details on IUnknown methods.
1021  */
1022 static ULONG WINAPI ISFPersistFolder_AddRef(
1023         IPersistFolder *        iface)
1024 {
1025         _ICOM_THIS_From_IPersistFolder(IShellFolder, iface);
1026
1027         return IShellFolder_AddRef((IShellFolder*)This);
1028 }
1029
1030 /************************************************************************
1031  * ISFPersistFolder_Release (IUnknown)
1032  *
1033  * See Windows documentation for more details on IUnknown methods.
1034  */
1035 static ULONG WINAPI ISFPersistFolder_Release(
1036         IPersistFolder *        iface)
1037 {
1038         _ICOM_THIS_From_IPersistFolder(IGenericSFImpl, iface);
1039
1040         return IShellFolder_Release((IShellFolder*)This);
1041 }
1042
1043 /************************************************************************
1044  * ISFPersistFolder_GetClassID (IPersist)
1045  *
1046  * See Windows documentation for more details on IPersist methods.
1047  */
1048 static HRESULT WINAPI ISFPersistFolder_GetClassID(
1049         const IPersistFolder *  iface,
1050         LPCLSID               lpClassId)
1051 {
1052         /* This ID is not documented anywhere but some tests in Windows tell 
1053          * me that This is the ID for the "standard" implementation of the 
1054          * IFolder interface. 
1055          */
1056
1057         CLSID StdFolderID = { 0xF3364BA0, 0x65B9, 0x11CE, {0xA9, 0xBA, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37} };
1058
1059         if (lpClassId==NULL)
1060           return E_POINTER;
1061
1062         memcpy(lpClassId, &StdFolderID, sizeof(StdFolderID));
1063
1064         return S_OK;
1065 }
1066
1067 /************************************************************************
1068  * ISFPersistFolder_Initialize (IPersistFolder)
1069  *
1070  * See Windows documentation for more details on IPersistFolder methods.
1071  */
1072 static HRESULT WINAPI ISFPersistFolder_Initialize(
1073         IPersistFolder *        iface,
1074         LPCITEMIDLIST           pidl)
1075 {
1076         _ICOM_THIS_From_IPersistFolder(IGenericSFImpl, iface);
1077
1078         if(This->pMyPidl)
1079         { SHFree(This->pMyPidl);
1080           This->pMyPidl = NULL;
1081         }
1082         This->pMyPidl = ILClone(pidl);
1083         return S_OK;
1084 }
1085
1086 static ICOM_VTABLE(IPersistFolder) psfvt = 
1087 {
1088         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1089         ISFPersistFolder_QueryInterface,
1090         ISFPersistFolder_AddRef,
1091         ISFPersistFolder_Release,
1092         ISFPersistFolder_GetClassID,
1093         ISFPersistFolder_Initialize
1094 };
1095