Move control panel applet enumeration to cpanelfolder.c.
[wine] / dlls / shell32 / enumidlist.c
1 /*
2  *      IEnumIDList
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "wine/debug.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "undocshell.h"
29 #include "shlwapi.h"
30 #include "winerror.h"
31 #include "objbase.h"
32
33 #include "pidl.h"
34 #include "shlguid.h"
35 #include "enumidlist.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39 typedef struct tagENUMLIST
40 {
41         struct tagENUMLIST      *pNext;
42         LPITEMIDLIST            pidl;
43
44 } ENUMLIST, *LPENUMLIST;
45
46 typedef struct
47 {
48         ICOM_VFIELD(IEnumIDList);
49         DWORD                           ref;
50         LPENUMLIST                      mpFirst;
51         LPENUMLIST                      mpLast;
52         LPENUMLIST                      mpCurrent;
53
54 } IEnumIDListImpl;
55
56 static struct ICOM_VTABLE(IEnumIDList) eidlvt;
57
58 /**************************************************************************
59  *  AddToEnumList()
60  */
61 BOOL AddToEnumList(
62         IEnumIDList * iface,
63         LPITEMIDLIST pidl)
64 {
65         ICOM_THIS(IEnumIDListImpl,iface);
66
67         LPENUMLIST  pNew;
68
69         TRACE("(%p)->(pidl=%p)\n",This,pidl);
70
71     if (!iface || !pidl)
72         return FALSE;
73
74         pNew = (LPENUMLIST)SHAlloc(sizeof(ENUMLIST));
75         if(pNew)
76         {
77           /*set the next pointer */
78           pNew->pNext = NULL;
79           pNew->pidl = pidl;
80
81           /*is This the first item in the list? */
82           if(!This->mpFirst)
83           {
84             This->mpFirst = pNew;
85             This->mpCurrent = pNew;
86           }
87
88           if(This->mpLast)
89           {
90             /*add the new item to the end of the list */
91             This->mpLast->pNext = pNew;
92           }
93
94           /*update the last item pointer */
95           This->mpLast = pNew;
96           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
97           return TRUE;
98         }
99         return FALSE;
100 }
101
102 /**************************************************************************
103  *  CreateFolderEnumList()
104  */
105 static BOOL CreateFolderEnumList(
106         IEnumIDList * iface,
107         LPCSTR lpszPath,
108         DWORD dwFlags)
109 {
110         ICOM_THIS(IEnumIDListImpl,iface);
111
112         LPITEMIDLIST    pidl=NULL;
113         WIN32_FIND_DATAA stffile;
114         HANDLE hFile;
115         CHAR  szPath[MAX_PATH];
116
117         TRACE("(%p)->(path=%s flags=0x%08lx) \n",This,debugstr_a(lpszPath),dwFlags);
118
119         if(!lpszPath || !lpszPath[0]) return FALSE;
120
121         strcpy(szPath, lpszPath);
122         PathAddBackslashA(szPath);
123         strcat(szPath,"*.*");
124
125         /*enumerate the folders*/
126         if(dwFlags & SHCONTF_FOLDERS)
127         {
128           TRACE("-- (%p)-> enumerate SHCONTF_FOLDERS of %s\n",This,debugstr_a(szPath));
129           hFile = FindFirstFileA(szPath,&stffile);
130           if ( hFile != INVALID_HANDLE_VALUE )
131           {
132             do
133             {
134               if ( !(dwFlags & SHCONTF_INCLUDEHIDDEN) && (stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ) continue;
135               if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, ".."))
136               {
137                 pidl = _ILCreateFromFindDataA (&stffile);
138                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
139                 {
140                   continue;
141                 }
142                 return FALSE;
143               }
144             } while( FindNextFileA(hFile,&stffile));
145             FindClose (hFile);
146           }
147         }
148
149         /*enumerate the non-folder items (values) */
150         if(dwFlags & SHCONTF_NONFOLDERS)
151         {
152           TRACE("-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
153           hFile = FindFirstFileA(szPath,&stffile);
154           if ( hFile != INVALID_HANDLE_VALUE )
155           {
156             do
157             {
158               if ( !(dwFlags & SHCONTF_INCLUDEHIDDEN) && (stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ) continue;
159               if (! (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
160               {
161                 pidl = _ILCreateFromFindDataA(&stffile);
162                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
163                 {
164                   continue;
165                 }
166                 return FALSE;
167               }
168             } while( FindNextFileA(hFile,&stffile));
169             FindClose (hFile);
170           }
171         }
172         return TRUE;
173 }
174
175 /**************************************************************************
176  *  CreateDesktopEnumList()
177  */
178 static BOOL CreateDesktopEnumList(
179         IEnumIDList * iface,
180         DWORD dwFlags)
181 {
182         ICOM_THIS(IEnumIDListImpl,iface);
183
184         LPITEMIDLIST    pidl=NULL;
185         HKEY hkey;
186         char    szPath[MAX_PATH];
187
188         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
189
190         /*enumerate the root folders */
191         if(dwFlags & SHCONTF_FOLDERS)
192         {
193           /*create the pidl for This item */
194           pidl = _ILCreateMyComputer();
195           if(pidl)
196           {
197             if(!AddToEnumList((IEnumIDList*)This, pidl))
198               return FALSE;
199           }
200
201           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\desktop\\NameSpace", 0, KEY_READ, &hkey))
202           {
203             char iid[50];
204             int i=0;
205
206             while (1)
207             {
208               DWORD size = sizeof (iid);
209
210               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
211                 break;
212
213               pidl = _ILCreateGuidFromStrA(iid);
214
215               if(pidl)
216                 AddToEnumList((IEnumIDList*)This, pidl);
217
218               i++;
219             }
220             RegCloseKey(hkey);
221           }
222         }
223
224         /*enumerate the elements in %windir%\desktop */
225         SHGetSpecialFolderPathA(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
226         CreateFolderEnumList( (IEnumIDList*)This, szPath, dwFlags);
227
228         return TRUE;
229 }
230
231 /**************************************************************************
232  *  CreateMyCompEnumList()
233  */
234 static BOOL CreateMyCompEnumList(
235         IEnumIDList * iface,
236         DWORD dwFlags)
237 {
238         ICOM_THIS(IEnumIDListImpl,iface);
239
240         LPITEMIDLIST    pidl=NULL;
241         DWORD           dwDrivemap;
242         CHAR            szDriveName[4];
243         HKEY            hkey;
244
245         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
246
247         /*enumerate the folders*/
248         if(dwFlags & SHCONTF_FOLDERS)
249         {
250           dwDrivemap = GetLogicalDrives();
251           strcpy (szDriveName,"A:\\");
252           while (szDriveName[0]<='Z')
253           {
254             if(dwDrivemap & 0x00000001L)
255             {
256               pidl = _ILCreateDrive(szDriveName);
257               if(pidl)
258               {
259                 if(!AddToEnumList((IEnumIDList*)This, pidl))
260                   return FALSE;
261               }
262             }
263             szDriveName[0]++;
264             dwDrivemap = dwDrivemap >> 1;
265           }
266
267           TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n",This);
268           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\mycomputer\\NameSpace", 0, KEY_READ, &hkey))
269           {
270             char iid[50];
271             int i=0;
272
273             while (1)
274             {
275               DWORD size = sizeof (iid);
276
277               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
278                 break;
279
280               pidl = _ILCreateGuidFromStrA(iid);
281
282               if(pidl)
283                 AddToEnumList((IEnumIDList*)This, pidl);
284
285               i++;
286             }
287             RegCloseKey(hkey);
288           }
289         }
290         return TRUE;
291 }
292
293 /**************************************************************************
294 *   DeleteList()
295 */
296 static BOOL DeleteList(
297         IEnumIDList * iface)
298 {
299         ICOM_THIS(IEnumIDListImpl,iface);
300
301         LPENUMLIST  pDelete;
302
303         TRACE("(%p)->()\n",This);
304
305         while(This->mpFirst)
306         { pDelete = This->mpFirst;
307           This->mpFirst = pDelete->pNext;
308           SHFree(pDelete->pidl);
309           SHFree(pDelete);
310         }
311         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
312         return TRUE;
313 }
314
315 /**************************************************************************
316  *  IEnumIDList_Folder_Constructor
317  *
318  */
319
320 IEnumIDList * IEnumIDList_Constructor(void)
321 {
322     IEnumIDListImpl *lpeidl = (IEnumIDListImpl*)HeapAlloc(GetProcessHeap(),
323      HEAP_ZERO_MEMORY, sizeof(IEnumIDListImpl));
324
325     if (lpeidl)
326     {
327         lpeidl->ref = 1;
328         lpeidl->lpVtbl = &eidlvt;
329     }
330     TRACE("-- (%p)->()\n",lpeidl);
331
332     return (IEnumIDList*)lpeidl;
333 }
334
335 IEnumIDList * IEnumIDList_BadConstructor(
336         LPCSTR lpszPath,
337         DWORD dwFlags,
338         DWORD dwKind)
339 {
340         IEnumIDListImpl*        lpeidl;
341         BOOL                    ret = FALSE;
342
343         TRACE("()->(%s flags=0x%08lx kind=0x%08lx)\n",debugstr_a(lpszPath),dwFlags, dwKind);
344
345         lpeidl = (IEnumIDListImpl *)IEnumIDList_Constructor();
346
347         if (lpeidl)
348         {
349           switch (dwKind)
350           {
351             case EIDL_DESK:
352               ret = CreateDesktopEnumList((IEnumIDList*)lpeidl, dwFlags);
353               break;
354
355             case EIDL_MYCOMP:
356               ret = CreateMyCompEnumList((IEnumIDList*)lpeidl, dwFlags);
357               break;
358
359             case EIDL_FILE:
360               ret = CreateFolderEnumList((IEnumIDList*)lpeidl, lpszPath, dwFlags);
361               break;
362           }
363
364             if(!ret) {
365                 HeapFree(GetProcessHeap(),0,lpeidl);
366                 lpeidl = NULL;
367             }
368         }
369
370         TRACE("-- (%p)->()\n",lpeidl);
371
372         return (IEnumIDList*)lpeidl;
373 }
374
375 /**************************************************************************
376  *  EnumIDList_QueryInterface
377  */
378 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
379         IEnumIDList * iface,
380         REFIID riid,
381         LPVOID *ppvObj)
382 {
383         ICOM_THIS(IEnumIDListImpl,iface);
384
385         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
386
387         *ppvObj = NULL;
388
389         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
390         { *ppvObj = This;
391         }
392         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
393         {    *ppvObj = (IEnumIDList*)This;
394         }
395
396         if(*ppvObj)
397         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
398           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
399           return S_OK;
400         }
401
402         TRACE("-- Interface: E_NOINTERFACE\n");
403         return E_NOINTERFACE;
404 }
405
406 /******************************************************************************
407  * IEnumIDList_fnAddRef
408  */
409 static ULONG WINAPI IEnumIDList_fnAddRef(
410         IEnumIDList * iface)
411 {
412         ICOM_THIS(IEnumIDListImpl,iface);
413         TRACE("(%p)->(%lu)\n",This,This->ref);
414         return ++(This->ref);
415 }
416 /******************************************************************************
417  * IEnumIDList_fnRelease
418  */
419 static ULONG WINAPI IEnumIDList_fnRelease(
420         IEnumIDList * iface)
421 {
422         ICOM_THIS(IEnumIDListImpl,iface);
423
424         TRACE("(%p)->(%lu)\n",This,This->ref);
425
426         if (!--(This->ref)) {
427           TRACE(" destroying IEnumIDList(%p)\n",This);
428           DeleteList((IEnumIDList*)This);
429           HeapFree(GetProcessHeap(),0,This);
430           return 0;
431         }
432         return This->ref;
433 }
434
435 /**************************************************************************
436  *  IEnumIDList_fnNext
437  */
438
439 static HRESULT WINAPI IEnumIDList_fnNext(
440         IEnumIDList * iface,
441         ULONG celt,
442         LPITEMIDLIST * rgelt,
443         ULONG *pceltFetched)
444 {
445         ICOM_THIS(IEnumIDListImpl,iface);
446
447         ULONG    i;
448         HRESULT  hr = S_OK;
449         LPITEMIDLIST  temp;
450
451         TRACE("(%p)->(%ld,%p, %p)\n",This,celt,rgelt,pceltFetched);
452
453 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
454  * subsystems actually use it (and so may a third party browser)
455  */
456         if(pceltFetched)
457           *pceltFetched = 0;
458
459         *rgelt=0;
460
461         if(celt > 1 && !pceltFetched)
462         { return E_INVALIDARG;
463         }
464
465         if(celt > 0 && !This->mpCurrent)
466         { return S_FALSE;
467         }
468
469         for(i = 0; i < celt; i++)
470         { if(!(This->mpCurrent))
471             break;
472
473           temp = ILClone(This->mpCurrent->pidl);
474           rgelt[i] = temp;
475           This->mpCurrent = This->mpCurrent->pNext;
476         }
477         if(pceltFetched)
478         {  *pceltFetched = i;
479         }
480
481         return hr;
482 }
483
484 /**************************************************************************
485 *  IEnumIDList_fnSkip
486 */
487 static HRESULT WINAPI IEnumIDList_fnSkip(
488         IEnumIDList * iface,ULONG celt)
489 {
490         ICOM_THIS(IEnumIDListImpl,iface);
491
492         DWORD    dwIndex;
493         HRESULT  hr = S_OK;
494
495         TRACE("(%p)->(%lu)\n",This,celt);
496
497         for(dwIndex = 0; dwIndex < celt; dwIndex++)
498         { if(!This->mpCurrent)
499           { hr = S_FALSE;
500             break;
501           }
502           This->mpCurrent = This->mpCurrent->pNext;
503         }
504         return hr;
505 }
506 /**************************************************************************
507 *  IEnumIDList_fnReset
508 */
509 static HRESULT WINAPI IEnumIDList_fnReset(
510         IEnumIDList * iface)
511 {
512         ICOM_THIS(IEnumIDListImpl,iface);
513
514         TRACE("(%p)\n",This);
515         This->mpCurrent = This->mpFirst;
516         return S_OK;
517 }
518 /**************************************************************************
519 *  IEnumIDList_fnClone
520 */
521 static HRESULT WINAPI IEnumIDList_fnClone(
522         IEnumIDList * iface,LPENUMIDLIST * ppenum)
523 {
524         ICOM_THIS(IEnumIDListImpl,iface);
525
526         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
527         return E_NOTIMPL;
528 }
529
530 /**************************************************************************
531  *  IEnumIDList_fnVTable
532  */
533 static ICOM_VTABLE (IEnumIDList) eidlvt =
534 {
535         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
536         IEnumIDList_fnQueryInterface,
537         IEnumIDList_fnAddRef,
538         IEnumIDList_fnRelease,
539         IEnumIDList_fnNext,
540         IEnumIDList_fnSkip,
541         IEnumIDList_fnReset,
542         IEnumIDList_fnClone,
543 };