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