shell32: Implement SHCreateItemFromIDList.
[wine] / dlls / shell32 / shellitem.c
1 /*
2  * IShellItem implementation
3  *
4  * Copyright 2008 Vincent Povirk for CodeWeavers
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdio.h>
25 #include <stdarg.h>
26
27 #define COBJMACROS
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wine/debug.h"
34
35 #include "pidl.h"
36 #include "shell32_main.h"
37 #include "debughlp.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(shell);
40
41 typedef struct _ShellItem {
42     const IShellItemVtbl    *lpIShellItemVtbl;
43     LONG                    ref;
44     LPITEMIDLIST            pidl;
45     const IPersistIDListVtbl *lpIPersistIDListVtbl;
46 } ShellItem;
47
48
49 static inline ShellItem *impl_from_IPersistIDList( IPersistIDList *iface )
50 {
51     return (ShellItem*)((char*)iface - FIELD_OFFSET(ShellItem, lpIPersistIDListVtbl));
52 }
53
54
55 static HRESULT WINAPI ShellItem_QueryInterface(IShellItem *iface, REFIID riid,
56     void **ppv)
57 {
58     ShellItem *This = (ShellItem*)iface;
59
60     TRACE("(%p,%p,%p)\n", iface, riid, ppv);
61
62     if (!ppv) return E_INVALIDARG;
63
64     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellItem, riid))
65     {
66         *ppv = This;
67     }
68     else if (IsEqualIID(&IID_IPersist, riid) || IsEqualIID(&IID_IPersistIDList, riid))
69     {
70         *ppv = &(This->lpIPersistIDListVtbl);
71     }
72     else {
73         FIXME("not implemented for %s\n", shdebugstr_guid(riid));
74         *ppv = NULL;
75         return E_NOINTERFACE;
76     }
77
78     IUnknown_AddRef((IUnknown*)*ppv);
79     return S_OK;
80 }
81
82 static ULONG WINAPI ShellItem_AddRef(IShellItem *iface)
83 {
84     ShellItem *This = (ShellItem*)iface;
85     ULONG ref = InterlockedIncrement(&This->ref);
86
87     TRACE("(%p), new refcount=%i\n", iface, ref);
88
89     return ref;
90 }
91
92 static ULONG WINAPI ShellItem_Release(IShellItem *iface)
93 {
94     ShellItem *This = (ShellItem*)iface;
95     ULONG ref = InterlockedDecrement(&This->ref);
96
97     TRACE("(%p), new refcount=%i\n", iface, ref);
98
99     if (ref == 0)
100     {
101         ILFree(This->pidl);
102         HeapFree(GetProcessHeap(), 0, This);
103     }
104
105     return ref;
106 }
107
108 static HRESULT ShellItem_get_parent_pidl(ShellItem *This, LPITEMIDLIST *parent_pidl)
109 {
110     *parent_pidl = ILClone(This->pidl);
111     if (*parent_pidl)
112     {
113         if (ILRemoveLastID(*parent_pidl))
114             return S_OK;
115         else
116         {
117             ILFree(*parent_pidl);
118             *parent_pidl = NULL;
119             return E_INVALIDARG;
120         }
121     }
122     else
123     {
124         *parent_pidl = NULL;
125         return E_OUTOFMEMORY;
126     }
127 }
128
129 static HRESULT ShellItem_get_parent_shellfolder(ShellItem *This, IShellFolder **ppsf)
130 {
131     LPITEMIDLIST parent_pidl;
132     IShellFolder *desktop;
133     HRESULT ret;
134
135     ret = ShellItem_get_parent_pidl(This, &parent_pidl);
136     if (SUCCEEDED(ret))
137     {
138         ret = SHGetDesktopFolder(&desktop);
139         if (SUCCEEDED(ret))
140         {
141             ret = IShellFolder_BindToObject(desktop, parent_pidl, NULL, &IID_IShellFolder, (void**)ppsf);
142             IShellFolder_Release(desktop);
143         }
144         ILFree(parent_pidl);
145     }
146
147     return ret;
148 }
149
150 static HRESULT WINAPI ShellItem_BindToHandler(IShellItem *iface, IBindCtx *pbc,
151     REFGUID rbhid, REFIID riid, void **ppvOut)
152 {
153     FIXME("(%p,%p,%s,%p,%p)\n", iface, pbc, shdebugstr_guid(rbhid), riid, ppvOut);
154
155     *ppvOut = NULL;
156
157     return E_NOTIMPL;
158 }
159
160 static HRESULT WINAPI ShellItem_GetParent(IShellItem *iface, IShellItem **ppsi)
161 {
162     ShellItem *This = (ShellItem*)iface;
163     LPITEMIDLIST parent_pidl;
164     HRESULT ret;
165
166     TRACE("(%p,%p)\n", iface, ppsi);
167
168     ret = ShellItem_get_parent_pidl(This, &parent_pidl);
169     if (SUCCEEDED(ret))
170     {
171         ret = SHCreateShellItem(NULL, NULL, parent_pidl, ppsi);
172         ILFree(parent_pidl);
173     }
174
175     return ret;
176 }
177
178 static HRESULT WINAPI ShellItem_GetDisplayName(IShellItem *iface, SIGDN sigdnName,
179     LPWSTR *ppszName)
180 {
181     ShellItem *This = (ShellItem*)iface;
182     TRACE("(%p,%x,%p)\n", iface, sigdnName, ppszName);
183
184     return SHGetNameFromIDList(This->pidl, sigdnName, ppszName);
185 }
186
187 static HRESULT WINAPI ShellItem_GetAttributes(IShellItem *iface, SFGAOF sfgaoMask,
188     SFGAOF *psfgaoAttribs)
189 {
190     ShellItem *This = (ShellItem*)iface;
191     IShellFolder *parent_folder;
192     LPITEMIDLIST child_pidl;
193     HRESULT ret;
194
195     TRACE("(%p,%x,%p)\n", iface, sfgaoMask, psfgaoAttribs);
196
197     ret = ShellItem_get_parent_shellfolder(This, &parent_folder);
198     if (SUCCEEDED(ret))
199     {
200         child_pidl = ILFindLastID(This->pidl);
201         *psfgaoAttribs = sfgaoMask;
202         ret = IShellFolder_GetAttributesOf(parent_folder, 1, (LPCITEMIDLIST*)&child_pidl, psfgaoAttribs);
203         IShellFolder_Release(parent_folder);
204     }
205
206     return ret;
207 }
208
209 static HRESULT WINAPI ShellItem_Compare(IShellItem *iface, IShellItem *oth,
210     SICHINTF hint, int *piOrder)
211 {
212     FIXME("(%p,%p,%x,%p)\n", iface, oth, hint, piOrder);
213
214     return E_NOTIMPL;
215 }
216
217 static const IShellItemVtbl ShellItem_Vtbl = {
218     ShellItem_QueryInterface,
219     ShellItem_AddRef,
220     ShellItem_Release,
221     ShellItem_BindToHandler,
222     ShellItem_GetParent,
223     ShellItem_GetDisplayName,
224     ShellItem_GetAttributes,
225     ShellItem_Compare
226 };
227
228
229 static HRESULT ShellItem_GetClassID(ShellItem* This, CLSID *pClassID)
230 {
231     TRACE("(%p,%p)\n", This, pClassID);
232
233     *pClassID = CLSID_ShellItem;
234     return S_OK;
235 }
236
237
238 static HRESULT WINAPI ShellItem_IPersistIDList_QueryInterface(IPersistIDList *iface,
239     REFIID riid, void **ppv)
240 {
241     ShellItem *This = impl_from_IPersistIDList(iface);
242     return ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
243 }
244
245 static ULONG WINAPI ShellItem_IPersistIDList_AddRef(IPersistIDList *iface)
246 {
247     ShellItem *This = impl_from_IPersistIDList(iface);
248     return ShellItem_AddRef((IShellItem*)This);
249 }
250
251 static ULONG WINAPI ShellItem_IPersistIDList_Release(IPersistIDList *iface)
252 {
253     ShellItem *This = impl_from_IPersistIDList(iface);
254     return ShellItem_Release((IShellItem*)This);
255 }
256
257 static HRESULT WINAPI ShellItem_IPersistIDList_GetClassID(IPersistIDList* iface,
258     CLSID *pClassID)
259 {
260     ShellItem *This = impl_from_IPersistIDList(iface);
261
262     return ShellItem_GetClassID(This, pClassID);
263 }
264
265 static HRESULT WINAPI ShellItem_IPersistIDList_SetIDList(IPersistIDList* iface,
266     LPCITEMIDLIST pidl)
267 {
268     ShellItem *This = impl_from_IPersistIDList(iface);
269     LPITEMIDLIST new_pidl;
270
271     TRACE("(%p,%p)\n", This, pidl);
272
273     new_pidl = ILClone(pidl);
274
275     if (new_pidl)
276     {
277         ILFree(This->pidl);
278         This->pidl = new_pidl;
279         return S_OK;
280     }
281     else
282         return E_OUTOFMEMORY;
283 }
284
285 static HRESULT WINAPI ShellItem_IPersistIDList_GetIDList(IPersistIDList* iface,
286     LPITEMIDLIST *ppidl)
287 {
288     ShellItem *This = impl_from_IPersistIDList(iface);
289
290     TRACE("(%p,%p)\n", This, ppidl);
291
292     *ppidl = ILClone(This->pidl);
293     if (*ppidl)
294         return S_OK;
295     else
296         return E_OUTOFMEMORY;
297 }
298
299 static const IPersistIDListVtbl ShellItem_IPersistIDList_Vtbl = {
300     ShellItem_IPersistIDList_QueryInterface,
301     ShellItem_IPersistIDList_AddRef,
302     ShellItem_IPersistIDList_Release,
303     ShellItem_IPersistIDList_GetClassID,
304     ShellItem_IPersistIDList_SetIDList,
305     ShellItem_IPersistIDList_GetIDList
306 };
307
308
309 HRESULT WINAPI IShellItem_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
310 {
311     ShellItem *This;
312     HRESULT ret;
313
314     TRACE("(%p,%s)\n",pUnkOuter, debugstr_guid(riid));
315
316     *ppv = NULL;
317
318     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
319
320     This = HeapAlloc(GetProcessHeap(), 0, sizeof(ShellItem));
321     This->lpIShellItemVtbl = &ShellItem_Vtbl;
322     This->ref = 1;
323     This->pidl = NULL;
324     This->lpIPersistIDListVtbl = &ShellItem_IPersistIDList_Vtbl;
325
326     ret = ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
327     ShellItem_Release((IShellItem*)This);
328
329     return ret;
330 }
331
332 HRESULT WINAPI SHCreateShellItem(LPCITEMIDLIST pidlParent,
333     IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
334 {
335     ShellItem *This;
336     LPITEMIDLIST new_pidl;
337     HRESULT ret;
338
339     TRACE("(%p,%p,%p,%p)\n", pidlParent, psfParent, pidl, ppsi);
340
341     if (!pidl)
342     {
343         return E_INVALIDARG;
344     }
345     else if (pidlParent || psfParent)
346     {
347         LPITEMIDLIST temp_parent=NULL;
348         if (!pidlParent)
349         {
350             IPersistFolder2* ppf2Parent;
351
352             if (FAILED(IPersistFolder2_QueryInterface(psfParent, &IID_IPersistFolder2, (void**)&ppf2Parent)))
353             {
354                 FIXME("couldn't get IPersistFolder2 interface of parent\n");
355                 return E_NOINTERFACE;
356             }
357
358             if (FAILED(IPersistFolder2_GetCurFolder(ppf2Parent, &temp_parent)))
359             {
360                 FIXME("couldn't get parent PIDL\n");
361                 IPersistFolder2_Release(ppf2Parent);
362                 return E_NOINTERFACE;
363             }
364
365             pidlParent = temp_parent;
366             IPersistFolder2_Release(ppf2Parent);
367         }
368
369         new_pidl = ILCombine(pidlParent, pidl);
370         ILFree(temp_parent);
371
372         if (!new_pidl)
373             return E_OUTOFMEMORY;
374     }
375     else
376     {
377         new_pidl = ILClone(pidl);
378         if (!new_pidl)
379             return E_OUTOFMEMORY;
380     }
381
382     ret = IShellItem_Constructor(NULL, &IID_IShellItem, (void**)&This);
383     if (This)
384     {
385         *ppsi = (IShellItem*)This;
386         This->pidl = new_pidl;
387     }
388     else
389     {
390         *ppsi = NULL;
391         ILFree(new_pidl);
392     }
393     return ret;
394 }
395
396 HRESULT WINAPI SHCreateItemFromParsingName(PCWSTR pszPath,
397     IBindCtx *pbc, REFIID riid, void **ppv)
398 {
399     LPITEMIDLIST pidl;
400     HRESULT ret;
401
402     *ppv = NULL;
403
404     ret = SHParseDisplayName(pszPath, pbc, &pidl, 0, NULL);
405     if(SUCCEEDED(ret))
406     {
407         ShellItem *This;
408         ret = IShellItem_Constructor(NULL, riid, (void**)&This);
409
410         if(SUCCEEDED(ret))
411         {
412             This->pidl = pidl;
413             *ppv = (void*)This;
414         }
415         else
416         {
417             ILFree(pidl);
418         }
419     }
420     return ret;
421 }
422
423 HRESULT WINAPI SHCreateItemFromIDList(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv)
424 {
425     ShellItem *psiimpl;
426     HRESULT ret;
427
428     if(!pidl)
429         return E_INVALIDARG;
430
431     ret = IShellItem_Constructor(NULL, riid, ppv);
432     if(SUCCEEDED(ret))
433     {
434         psiimpl = (ShellItem*)*ppv;
435         psiimpl->pidl = ILClone(pidl);
436     }
437
438     return ret;
439 }