shell32: Cast-qual warnings fix.
[wine] / dlls / shell32 / recyclebin.c
1 /*
2  * Trash virtual folder support. The trashing engine is implemented in trash.c
3  *
4  * Copyright (C) 2006 Mikolaj Zalewski
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
23 #define COBJMACROS
24
25 #include <stdarg.h>
26
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winuser.h"
32 #include "ntquery.h"
33 #include "shlwapi.h"
34 #include "shlobj.h"
35 #include "shresdef.h"
36 #include "wine/debug.h"
37
38 #include "shell32_main.h"
39 #include "enumidlist.h"
40 #include "xdg.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(recyclebin);
43
44 typedef struct
45 {
46     int column_name_id;
47     const GUID *fmtId;
48     DWORD pid;
49     int pcsFlags;
50     int fmt;
51     int cxChars;
52 } columninfo;
53
54 static const columninfo RecycleBinColumns[] =
55 {
56     {IDS_SHV_COLUMN1,        &FMTID_Storage,   PID_STG_NAME,       SHCOLSTATE_TYPE_STR|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  30},
57     {IDS_SHV_COLUMN_DELFROM, &FMTID_Displaced, PID_DISPLACED_FROM, SHCOLSTATE_TYPE_STR|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  30},
58     {IDS_SHV_COLUMN_DELDATE, &FMTID_Displaced, PID_DISPLACED_DATE, SHCOLSTATE_TYPE_DATE|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT,  20},
59     {IDS_SHV_COLUMN2,        &FMTID_Storage,   PID_STG_SIZE,       SHCOLSTATE_TYPE_INT|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 20},
60     {IDS_SHV_COLUMN3,        &FMTID_Storage,   PID_STG_STORAGETYPE,SHCOLSTATE_TYPE_INT|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  20},
61     {IDS_SHV_COLUMN4,        &FMTID_Storage,   PID_STG_WRITETIME,  SHCOLSTATE_TYPE_DATE|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT,  20},
62 /*    {"creation time",  &FMTID_Storage,   PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE,                        LVCFMT_LEFT,  20}, */
63 /*    {"attribs",        &FMTID_Storage,   PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR,                         LVCFMT_LEFT,  20},       */
64 };
65
66 #define COLUMN_NAME    0
67 #define COLUMN_DELFROM 1
68 #define COLUMN_DATEDEL 2
69 #define COLUMN_SIZE    3
70 #define COLUMN_TYPE    4
71 #define COLUMN_MTIME   5
72
73 #define COLUMNS_COUNT  6
74
75 HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME ft)
76 {
77     FILETIME lft;
78     SYSTEMTIME time;
79     int ret;
80
81     FileTimeToLocalFileTime(&ft, &lft);
82     FileTimeToSystemTime(&lft, &time);
83
84     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
85     if (ret>0 && ret<size)
86     {
87         /* Append space + time without seconds */
88         buffer[ret-1] = ' ';
89         GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
90     }
91
92     return (ret!=0 ? E_FAIL : S_OK);
93 }
94
95 /*
96  * Recycle Bin folder
97  */
98
99 typedef struct tagRecycleBin
100 {
101     IShellFolder2Vtbl *lpVtbl;
102     IPersistFolder2Vtbl *lpPersistFolderVtbl;
103     LONG refCount;
104
105     LPITEMIDLIST pidl;
106 } RecycleBin;
107
108 static IShellFolder2Vtbl recycleBinVtbl;
109 static IPersistFolder2Vtbl recycleBinPersistVtbl;
110
111 static RecycleBin *impl_from_IPersistFolder(IPersistFolder2 *iface)
112 {
113     return (RecycleBin *)((char*)iface - FIELD_OFFSET(RecycleBin, lpPersistFolderVtbl));
114 }
115
116 void RecycleBin_Destructor(RecycleBin *This);
117
118 HRESULT WINAPI RecycleBin_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppOutput)
119 {
120     RecycleBin *obj;
121     HRESULT ret;
122     if (pUnkOuter)
123         return CLASS_E_NOAGGREGATION;
124
125     obj = SHAlloc(sizeof(RecycleBin));
126     if (obj == NULL)
127         return E_OUTOFMEMORY;
128     ZeroMemory(obj, sizeof(RecycleBin));
129     obj->lpVtbl = &recycleBinVtbl;
130     obj->lpPersistFolderVtbl = &recycleBinPersistVtbl;
131     if (FAILED(ret = IUnknown_QueryInterface((IUnknown *)obj, riid, ppOutput)))
132     {
133         RecycleBin_Destructor(obj);
134         return ret;
135     }
136 /*    InterlockedIncrement(&objCount);*/
137     return S_OK;
138 }
139
140 void RecycleBin_Destructor(RecycleBin *This)
141 {
142 /*    InterlockedDecrement(&objCount);*/
143     SHFree(This->pidl);
144     SHFree(This);
145 }
146
147 static HRESULT WINAPI RecycleBin_QueryInterface(IShellFolder2 *iface, REFIID riid, void **ppvObject)
148 {
149     RecycleBin *This = (RecycleBin *)iface;
150     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
151
152     *ppvObject = NULL;
153     if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IShellFolder)
154             || IsEqualGUID(riid, &IID_IShellFolder2))
155         *ppvObject = This;
156
157     if (IsEqualGUID(riid, &IID_IPersist) || IsEqualGUID(riid, &IID_IPersistFolder)
158             || IsEqualGUID(riid, &IID_IPersistFolder2))
159         *ppvObject = &This->lpPersistFolderVtbl;
160
161     if (*ppvObject != NULL)
162     {
163         IUnknown_AddRef((IUnknown *)*ppvObject);
164         return S_OK;
165     }
166     WARN("no interface %s\n", debugstr_guid(riid));
167     return E_NOINTERFACE;
168 }
169
170 static ULONG WINAPI RecycleBin_AddRef(IShellFolder2 *iface)
171 {
172     RecycleBin *This = (RecycleBin *)iface;
173     TRACE("(%p)\n", This);
174     return InterlockedIncrement(&This->refCount);
175 }
176
177 static ULONG WINAPI RecycleBin_Release(IShellFolder2 *iface)
178 {
179     RecycleBin *This = (RecycleBin *)iface;
180     LONG result;
181
182     TRACE("(%p)\n", This);
183     result = InterlockedDecrement(&This->refCount);
184     if (result == 0)
185     {
186         TRACE("Destroy object\n");
187         RecycleBin_Destructor(This);
188     }
189     return result;
190 }
191
192 static HRESULT WINAPI RecycleBin_ParseDisplayName(IShellFolder2 *This, HWND hwnd, LPBC pbc,
193             LPOLESTR pszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl,
194             ULONG *pdwAttributes)
195 {
196     FIXME("stub\n");
197     return E_NOTIMPL;
198 }
199
200 static HRESULT WINAPI RecycleBin_EnumObjects(IShellFolder2 *iface, HWND hwnd, SHCONTF grfFlags, IEnumIDList **ppenumIDList)
201 {
202     RecycleBin *This = (RecycleBin *)iface;
203     IEnumIDList *list;
204     LPITEMIDLIST *pidls;
205     HRESULT ret;
206     int pidls_count;
207     int i=0;
208     
209     TRACE("(%p, %p, %x, %p)\n", This, hwnd, (unsigned int)grfFlags, ppenumIDList);
210     
211     if (grfFlags & SHCONTF_NONFOLDERS)
212     {
213         *ppenumIDList = NULL;
214         if (FAILED(ret = TRASH_EnumItems(&pidls, &pidls_count)))
215             return ret;
216
217         list = IEnumIDList_Constructor();
218         if (list == NULL)
219             goto failed;
220         for (i=0; i<pidls_count; i++)
221             if (!AddToEnumList(list, pidls[i]))
222                 goto failed;
223         *ppenumIDList = list;
224     }
225     else
226     {
227         *ppenumIDList = IEnumIDList_Constructor();
228         if (*ppenumIDList == NULL)
229             return E_OUTOFMEMORY;
230     }
231     
232     return S_OK;
233
234 failed:
235     if (list)
236         IEnumIDList_Release(list);
237     for (; i<pidls_count; i++)
238         ILFree(pidls[i]);
239     SHFree(pidls);
240     return E_OUTOFMEMORY;
241 }
242
243 static HRESULT WINAPI RecycleBin_BindToObject(IShellFolder2 *This, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
244 {
245     FIXME("(%p, %p, %p, %s, %p) - stub\n", This, pidl, pbc, debugstr_guid(riid), ppv);
246     return E_NOTIMPL;
247 }
248
249 static HRESULT WINAPI RecycleBin_BindToStorage(IShellFolder2 *This, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
250 {
251     FIXME("(%p, %p, %p, %s, %p) - stub\n", This, pidl, pbc, debugstr_guid(riid), ppv);
252     return E_NOTIMPL;
253 }
254
255 static HRESULT WINAPI RecycleBin_CompareIDs(IShellFolder2 *iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
256 {
257     RecycleBin *This = (RecycleBin *)iface;
258
259     /* TODO */
260     TRACE("(%p, %p, %p, %p)\n", This, (void *)lParam, pidl1, pidl2);
261     if (pidl1->mkid.cb != pidl2->mkid.cb)
262         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, pidl1->mkid.cb - pidl2->mkid.cb);
263     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb));
264 }
265
266 static HRESULT WINAPI RecycleBin_CreateViewObject(IShellFolder2 *iface, HWND hwndOwner, REFIID riid, void **ppv)
267 {
268     RecycleBin *This = (RecycleBin *)iface;
269     HRESULT ret;
270     TRACE("(%p, %p, %s, %p)\n", This, hwndOwner, debugstr_guid(riid), ppv);
271
272     *ppv = NULL;
273     if (IsEqualGUID(riid, &IID_IShellView))
274     {
275         IShellView *tmp;
276         CSFV sfv;
277
278         ZeroMemory(&sfv, sizeof(sfv));
279         sfv.cbSize = sizeof(sfv);
280         sfv.pshf = (IShellFolder *)This;
281
282         TRACE("Calling SHCreateShellFolderViewEx\n");
283         ret = SHCreateShellFolderViewEx(&sfv, &tmp);
284         TRACE("Result: %08x, output: %p\n", (unsigned int)ret, tmp);
285         *ppv = tmp;
286         return ret;
287     }
288
289     return E_NOINTERFACE;
290 }
291
292 static HRESULT WINAPI RecycleBin_GetAttributesOf(IShellFolder2 *This, UINT cidl, LPCITEMIDLIST *apidl,
293                                    SFGAOF *rgfInOut)
294 {
295     TRACE("(%p, %d, {%p, ...}, {%x})\n", This, cidl, apidl[0], (unsigned int)*rgfInOut);
296     *rgfInOut &= SFGAO_CANMOVE|SFGAO_CANDELETE|SFGAO_HASPROPSHEET|SFGAO_FILESYSTEM;
297     return S_OK;
298 }
299
300 static HRESULT WINAPI RecycleBin_GetUIObjectOf(IShellFolder2 *This, HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl,
301                       REFIID riid, UINT *rgfReserved, void **ppv)
302 {
303     FIXME("(%p, %p, %d, {%p, ...}, %s, %p, %p): stub!\n", This, hwndOwner, cidl, apidl[0], debugstr_guid(riid), rgfReserved, ppv);
304     *ppv = NULL;
305     return E_NOTIMPL;
306 }
307
308 static HRESULT WINAPI RecycleBin_GetDisplayNameOf(IShellFolder2 *This, LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET *pName)
309 {
310     WIN32_FIND_DATAW data;
311
312     TRACE("(%p, %p, %x, %p)\n", This, pidl, (unsigned int)uFlags, pName);
313     TRASH_UnpackItemID(&pidl->mkid, NULL, &data);
314     pName->uType = STRRET_WSTR;
315     pName->pOleStr = StrDupW(PathFindFileNameW(data.cFileName));
316     if (pName->pOleStr == NULL)
317         return E_OUTOFMEMORY;
318
319     return S_OK;
320 }
321
322 static HRESULT WINAPI RecycleBin_SetNameOf(IShellFolder2 *This, HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
323             SHGDNF uFlags, LPITEMIDLIST *ppidlOut)
324 {
325     TRACE("\n");
326     return E_FAIL; /* not supported */
327 }
328
329 static HRESULT WINAPI RecycleBin_GetClassID(IPersistFolder2 *This, CLSID *pClassID)
330 {
331     TRACE("(%p, %p)\n", This, pClassID);
332     if (This == NULL || pClassID == NULL)
333         return E_INVALIDARG;
334     memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
335     return S_OK;
336 }
337
338 static HRESULT WINAPI RecycleBin_Initialize(IPersistFolder2 *iface, LPCITEMIDLIST pidl)
339 {
340     RecycleBin *This = impl_from_IPersistFolder(iface);
341     TRACE("(%p, %p)\n", This, pidl);
342     
343     This->pidl = ILClone(pidl);
344     if (This->pidl == NULL)
345         return E_OUTOFMEMORY;
346     return S_OK;
347 }
348
349 static HRESULT WINAPI RecycleBin_GetCurFolder(IPersistFolder2 *iface, LPITEMIDLIST *ppidl)
350 {
351     RecycleBin *This = impl_from_IPersistFolder(iface);
352     TRACE("\n");
353     *ppidl = ILClone(This->pidl);
354     return S_OK;
355 }
356
357 static HRESULT WINAPI RecycleBin_GetDefaultSearchGUID(IShellFolder2 *iface, GUID *pguid)
358 {
359     FIXME("stub\n");
360     return E_NOTIMPL;
361 }
362
363 static HRESULT WINAPI RecycleBin_EnumSearches(IShellFolder2 *iface, IEnumExtraSearch **ppEnum)
364 {
365     FIXME("stub\n");
366     *ppEnum = NULL;
367     return E_NOTIMPL;
368 }
369
370 static HRESULT WINAPI RecycleBin_GetDefaultColumn(IShellFolder2 *iface, DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
371 {
372     RecycleBin *This = (RecycleBin *)iface;
373     TRACE("(%p, %x, %p, %p)\n", This, (unsigned int)dwReserved, pSort, pDisplay);
374     *pSort = 0;
375     *pDisplay = 0;
376     return S_OK;
377 }
378
379 static HRESULT WINAPI RecycleBin_GetDefaultColumnState(IShellFolder2 *iface, UINT iColumn, SHCOLSTATEF *pcsFlags)
380 {
381     RecycleBin *This = (RecycleBin *)iface;
382     TRACE("(%p, %d, %p)\n", This, iColumn, pcsFlags);
383     if (iColumn < 0 || iColumn >= COLUMNS_COUNT)
384         return E_INVALIDARG;
385     *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
386     return S_OK;
387 }
388
389 static HRESULT WINAPI RecycleBin_GetDetailsEx(IShellFolder2 *iface, LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
390 {
391     FIXME("stub\n");
392     return E_NOTIMPL;
393 }
394
395 static HRESULT WINAPI RecycleBin_GetDetailsOf(IShellFolder2 *iface, LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetails)
396 {
397     RecycleBin *This = (RecycleBin *)iface;
398     WIN32_FIND_DATAW data;
399     WCHAR buffer[MAX_PATH];
400
401     TRACE("(%p, %p, %d, %p)\n", This, pidl, iColumn, pDetails);
402     if (iColumn < 0 || iColumn >= COLUMNS_COUNT)
403         return E_FAIL;
404     pDetails->fmt = RecycleBinColumns[iColumn].fmt;
405     pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
406     if (pidl == NULL)
407     {
408         pDetails->str.uType = STRRET_WSTR;
409         LoadStringW(shell32_hInstance, RecycleBinColumns[iColumn].column_name_id, buffer, MAX_PATH);
410         return SHStrDupW(buffer, &pDetails->str.pOleStr);
411     }
412
413     if (iColumn == COLUMN_NAME)
414         return RecycleBin_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL, &pDetails->str);
415
416     TRASH_UnpackItemID(&pidl->mkid, NULL, &data);
417     switch (iColumn)
418     {
419         case COLUMN_DATEDEL:
420             FormatDateTime(buffer, MAX_PATH, data.ftLastAccessTime);
421             break;
422         case COLUMN_DELFROM:
423             lstrcpyW(buffer, data.cFileName);
424             PathRemoveFileSpecW(buffer);
425             break;
426         case COLUMN_SIZE:
427             StrFormatKBSizeW(((LONGLONG)data.nFileSizeHigh<<32)|data.nFileSizeLow, buffer, MAX_PATH);
428             break;
429         case COLUMN_MTIME:
430             FormatDateTime(buffer, MAX_PATH, data.ftLastWriteTime);
431             break;
432         case COLUMN_TYPE:
433             /* TODO */
434             buffer[0] = 0;
435             break;
436         default:
437             return E_FAIL;
438     }
439     
440     pDetails->str.uType = STRRET_WSTR;
441     pDetails->str.pOleStr = StrDupW(buffer);
442     return (pDetails->str.pOleStr != NULL ? S_OK : E_OUTOFMEMORY);
443 }
444
445 static HRESULT WINAPI RecycleBin_MapColumnToSCID(IShellFolder2 *iface, UINT iColumn, SHCOLUMNID *pscid)
446 {
447     RecycleBin *This = (RecycleBin *)iface;
448     TRACE("(%p, %d, %p)\n", This, iColumn, pscid);
449     if (iColumn<0 || iColumn>=COLUMNS_COUNT)
450         return E_INVALIDARG;
451     pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
452     pscid->pid = RecycleBinColumns[iColumn].pid;
453     return S_OK;
454 }
455
456 static IShellFolder2Vtbl recycleBinVtbl = 
457 {
458     /* IUnknown */
459     RecycleBin_QueryInterface,
460     RecycleBin_AddRef,
461     RecycleBin_Release,
462
463     /* IShellFolder */
464     RecycleBin_ParseDisplayName,
465     RecycleBin_EnumObjects,
466     RecycleBin_BindToObject,
467     RecycleBin_BindToStorage,
468     RecycleBin_CompareIDs,
469     RecycleBin_CreateViewObject,
470     RecycleBin_GetAttributesOf,
471     RecycleBin_GetUIObjectOf,
472     RecycleBin_GetDisplayNameOf,
473     RecycleBin_SetNameOf,
474
475     /* IShellFolder2 */
476     RecycleBin_GetDefaultSearchGUID,
477     RecycleBin_EnumSearches,
478     RecycleBin_GetDefaultColumn,
479     RecycleBin_GetDefaultColumnState,
480     RecycleBin_GetDetailsEx,
481     RecycleBin_GetDetailsOf,
482     RecycleBin_MapColumnToSCID
483 };
484
485 static HRESULT WINAPI RecycleBin_IPersistFolder2_QueryInterface(IPersistFolder2 *This, REFIID riid, void **ppvObject)
486 {
487     return RecycleBin_QueryInterface((IShellFolder2 *)impl_from_IPersistFolder(This), riid, ppvObject);
488 }
489
490 static ULONG WINAPI RecycleBin_IPersistFolder2_AddRef(IPersistFolder2 *This)
491 {
492     return RecycleBin_AddRef((IShellFolder2 *)impl_from_IPersistFolder(This));
493 }
494
495 static ULONG WINAPI RecycleBin_IPersistFolder2_Release(IPersistFolder2 *This)
496 {
497     return RecycleBin_Release((IShellFolder2 *)impl_from_IPersistFolder(This));
498 }
499
500 static IPersistFolder2Vtbl recycleBinPersistVtbl =
501 {
502     /* IUnknown */
503     RecycleBin_IPersistFolder2_QueryInterface,
504     RecycleBin_IPersistFolder2_AddRef,
505     RecycleBin_IPersistFolder2_Release,
506
507     /* IPersist */
508     RecycleBin_GetClassID,
509     /* IPersistFolder */
510     RecycleBin_Initialize,
511     /* IPersistFolder2 */
512     RecycleBin_GetCurFolder
513 };