[SHELL32] Mark the halfplementation as such.
[reactos.git] / reactos / dll / win32 / shell32 / folders / CRecycleBin.cpp
1 /*
2 * Trash virtual folder support. The trashing engine is implemented in trash.c
3 *
4 * Copyright (C) 2006 Mikolaj Zalewski
5 * Copyright (C) 2009 Andrew Hill
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include <precomp.h>
23
24 #include <ntquery.h>
25
26 #define MAX_PROPERTY_SHEET_PAGE 32
27
28 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
29
30 typedef struct
31 {
32 int column_name_id;
33 const GUID *fmtId;
34 DWORD pid;
35 int pcsFlags;
36 int fmt;
37 int cxChars;
38 } columninfo;
39
40 static const columninfo RecycleBinColumns[] =
41 {
42 {IDS_SHV_COLUMN1, &FMTID_Storage, PID_STG_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
43 {IDS_SHV_COLUMN_DELFROM, &FMTID_Displaced, PID_DISPLACED_FROM, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
44 {IDS_SHV_COLUMN_DELDATE, &FMTID_Displaced, PID_DISPLACED_DATE, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
45 {IDS_SHV_COLUMN2, &FMTID_Storage, PID_STG_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 20},
46 {IDS_SHV_COLUMN3, &FMTID_Storage, PID_STG_STORAGETYPE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
47 {IDS_SHV_COLUMN4, &FMTID_Storage, PID_STG_WRITETIME, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
48 /* {"creation time", &FMTID_Storage, PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20}, */
49 /* {"attribs", &FMTID_Storage, PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 20}, */
50 };
51
52 #define COLUMN_NAME 0
53 #define COLUMN_DELFROM 1
54 #define COLUMN_DATEDEL 2
55 #define COLUMN_SIZE 3
56 #define COLUMN_TYPE 4
57 #define COLUMN_MTIME 5
58
59 #define COLUMNS_COUNT 6
60
61 /*
62 * Recycle Bin folder
63 */
64
65 class CRecycleBinEnum :
66 public CEnumIDListBase
67 {
68 private:
69 public:
70 CRecycleBinEnum();
71 ~CRecycleBinEnum();
72 HRESULT WINAPI Initialize(DWORD dwFlags);
73 static BOOL WINAPI CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile);
74 BOOL WINAPI CBEnumRecycleBin(IN HANDLE hDeletedFile);
75
76 BEGIN_COM_MAP(CRecycleBinEnum)
77 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
78 END_COM_MAP()
79 };
80
81 class CRecycleBinItemContextMenu :
82 public CComObjectRootEx<CComMultiThreadModelNoCS>,
83 public IContextMenu2
84 {
85 private:
86 LPITEMIDLIST apidl;
87 public:
88 CRecycleBinItemContextMenu();
89 ~CRecycleBinItemContextMenu();
90 HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
91
92 // IContextMenu
93 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
94 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
95 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
96
97 // IContextMenu2
98 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
99
100 BEGIN_COM_MAP(CRecycleBinItemContextMenu)
101 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
102 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
103 END_COM_MAP()
104 };
105
106 typedef struct
107 {
108 PIDLRecycleStruct *pFileDetails;
109 HANDLE hDeletedFile;
110 BOOL bFound;
111 } SEARCH_CONTEXT, *PSEARCH_CONTEXT;
112
113 typedef struct
114 {
115 DWORD dwNukeOnDelete;
116 DWORD dwSerial;
117 DWORD dwMaxCapacity;
118 } DRIVE_ITEM_CONTEXT, *PDRIVE_ITEM_CONTEXT;
119
120 BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
121 {
122 PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context;
123
124 PDELETED_FILE_DETAILS_W pFileDetails;
125 DWORD dwSize;
126 BOOL ret;
127
128 if (!GetDeletedFileDetailsW(hDeletedFile,
129 0,
130 NULL,
131 &dwSize) &&
132 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
133 {
134 ERR("GetDeletedFileDetailsW failed\n");
135 return FALSE;
136 }
137
138 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
139 if (!pFileDetails)
140 {
141 ERR("No memory\n");
142 return FALSE;
143 }
144
145 if (!GetDeletedFileDetailsW(hDeletedFile,
146 dwSize,
147 pFileDetails,
148 NULL))
149 {
150 ERR("GetDeletedFileDetailsW failed\n");
151 SHFree(pFileDetails);
152 return FALSE;
153 }
154
155 ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize);
156 if (!ret)
157 {
158 pContext->hDeletedFile = hDeletedFile;
159 pContext->bFound = TRUE;
160 }
161 else
162 CloseRecycleBinHandle(hDeletedFile);
163
164 SHFree(pFileDetails);
165 return ret;
166 }
167
168 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl)
169 {
170 LPPIDLDATA pdata = _ILGetDataPointer(pidl);
171
172 if (pdata && pdata->type == 0x00)
173 return (PIDLRecycleStruct*) & (pdata->u.crecycle);
174
175 return NULL;
176 }
177
178 CRecycleBinEnum::CRecycleBinEnum()
179 {
180 }
181
182 CRecycleBinEnum::~CRecycleBinEnum()
183 {
184 }
185
186 HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
187 {
188 static LPCWSTR szDrive = L"C:\\";
189
190 if (dwFlags & SHCONTF_NONFOLDERS)
191 {
192 TRACE("Starting Enumeration\n");
193
194 if (!EnumerateRecycleBinW(szDrive /* FIXME */ , CBEnumRecycleBin, (PVOID)this))
195 {
196 WARN("Error: EnumerateCRecycleBinW failed\n");
197 return E_FAIL;
198 }
199 }
200 else
201 {
202 // do nothing
203 }
204 return S_OK;
205 }
206
207 static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails)
208 {
209 PIDLDATA tmp;
210 LPITEMIDLIST pidl;
211 PIDLRecycleStruct * p;
212 int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle;
213 int size = size0;
214
215 tmp.type = 0x00;
216 size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR);
217
218 pidl = (LPITEMIDLIST)SHAlloc(size + 4);
219 if (!pidl)
220 return pidl;
221
222 pidl->mkid.cb = size + 2;
223 memcpy(pidl->mkid.abID, &tmp, 2 + size0);
224
225 p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle;
226 RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W));
227 wcscpy(p->szName, pFileDetails->FileName);
228 *(WORD*)((char*)pidl + (size + 2)) = 0;
229 return pidl;
230 }
231
232 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
233 {
234 return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile);
235 }
236
237 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HANDLE hDeletedFile)
238 {
239 PDELETED_FILE_DETAILS_W pFileDetails;
240 DWORD dwSize;
241 LPITEMIDLIST pidl = NULL;
242 BOOL ret;
243
244 if (!GetDeletedFileDetailsW(hDeletedFile,
245 0,
246 NULL,
247 &dwSize) &&
248 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
249 {
250 ERR("GetDeletedFileDetailsW failed\n");
251 return FALSE;
252 }
253
254 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
255 if (!pFileDetails)
256 {
257 ERR("No memory\n");
258 return FALSE;
259 }
260
261 if (!GetDeletedFileDetailsW(hDeletedFile,
262 dwSize,
263 pFileDetails,
264 NULL))
265 {
266 ERR("GetDeletedFileDetailsW failed\n");
267 SHFree(pFileDetails);
268 return FALSE;
269 }
270
271 pidl = _ILCreateRecycleItem(pFileDetails);
272 if (!pidl)
273 {
274 SHFree(pFileDetails);
275 return FALSE;
276 }
277
278 ret = AddToEnumList(pidl);
279
280 if (!ret)
281 SHFree(pidl);
282 SHFree(pFileDetails);
283 TRACE("Returning %d\n", ret);
284 CloseRecycleBinHandle(hDeletedFile);
285 return ret;
286 }
287
288 /**************************************************************************
289 * IContextMenu2 Bitbucket Item Implementation
290 */
291
292 CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
293 {
294 apidl = NULL;
295 }
296
297 CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
298 {
299 ILFree(apidl);
300 }
301
302 HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
303 {
304 apidl = ILClone(pidl);
305 if (apidl == NULL)
306 return E_OUTOFMEMORY;
307 return S_OK;
308 }
309
310 HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
311 {
312 WCHAR szBuffer[30] = {0};
313 ULONG Count = 1;
314
315 TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
316
317 if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
318 {
319 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
320 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_ENABLED);
321 Count++;
322 }
323
324 if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
325 {
326 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
327 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
328 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
329 }
330
331 if (LoadStringW(shell32_hInstance, IDS_DELETE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
332 {
333 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
334 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
335 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
336 }
337
338 if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
339 {
340 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
341 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
342 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_DEFAULT);
343 }
344
345 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
346 }
347
348 HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
349 {
350 SEARCH_CONTEXT Context;
351 static LPCWSTR szDrive = L"C:\\";
352
353 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
354
355 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb == MAKEINTRESOURCEA(5))
356 {
357 Context.pFileDetails = _ILGetRecycleStruct(apidl);
358 Context.bFound = FALSE;
359
360 EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context);
361 if (!Context.bFound)
362 return E_FAIL;
363
364 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1))
365 {
366 /* restore file */
367 if (RestoreFile(Context.hDeletedFile))
368 return S_OK;
369 else
370 return E_FAIL;
371 }
372 else
373 {
374 DeleteFileHandleToRecycleBin(Context.hDeletedFile);
375 return E_NOTIMPL;
376 }
377 }
378 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3))
379 {
380 FIXME("implement cut\n");
381 return E_NOTIMPL;
382 }
383 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7))
384 {
385 FIXME("implement properties\n");
386 return E_NOTIMPL;
387 }
388
389 return S_OK;
390 }
391
392 HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
393 {
394 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
395
396 return E_FAIL;
397 }
398
399 HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
400 {
401 TRACE("CRecycleBin_IContextMenu2Item_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
402
403 return E_NOTIMPL;
404 }
405
406 /**************************************************************************
407 * registers clipboardformat once
408 */
409 void CRecycleBin::SF_RegisterClipFmt()
410 {
411 TRACE ("(%p)\n", this);
412
413 if (!cfShellIDList)
414 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
415 }
416
417 CRecycleBin::CRecycleBin()
418 {
419 pidl = NULL;
420 iIdEmpty = 0;
421 cfShellIDList = 0;
422 SF_RegisterClipFmt();
423 fAcceptFmt = FALSE;
424 }
425
426 CRecycleBin::~CRecycleBin()
427 {
428 /* InterlockedDecrement(&objCount);*/
429 SHFree(pidl);
430 }
431
432 /*************************************************************************
433 * RecycleBin IPersistFolder2 interface
434 */
435
436 HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
437 {
438 TRACE("(%p, %p)\n", this, pClassID);
439 if (pClassID == NULL)
440 return E_INVALIDARG;
441 memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
442 return S_OK;
443 }
444
445 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidl)
446 {
447 TRACE("(%p, %p)\n", this, pidl);
448
449 SHFree((LPVOID)this->pidl);
450 this->pidl = ILClone(pidl);
451 if (this->pidl == NULL)
452 return E_OUTOFMEMORY;
453 return S_OK;
454 }
455
456 HRESULT WINAPI CRecycleBin::GetCurFolder(LPITEMIDLIST *ppidl)
457 {
458 TRACE("\n");
459 *ppidl = ILClone(pidl);
460 return S_OK;
461 }
462
463 /*************************************************************************
464 * RecycleBin IShellFolder2 interface
465 */
466
467 HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
468 LPOLESTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl,
469 ULONG *pdwAttributes)
470 {
471 FIXME("stub\n");
472 return E_NOTIMPL;
473 }
474
475
476 PDELETED_FILE_DETAILS_W
477 UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
478 {
479 return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
480 }
481
482 HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
483 {
484 return ShellObjectCreatorInit<CRecycleBinEnum>(dwFlags, IID_IEnumIDList, ppEnumIDList);
485 }
486
487 HRESULT WINAPI CRecycleBin::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
488 {
489 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
490 return E_NOTIMPL;
491 }
492
493 HRESULT WINAPI CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
494 {
495 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
496 return E_NOTIMPL;
497 }
498
499 HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
500 {
501 /* TODO */
502 TRACE("(%p, %p, %p, %p)\n", this, (void *)lParam, pidl1, pidl2);
503 if (pidl1->mkid.cb != pidl2->mkid.cb)
504 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, pidl1->mkid.cb - pidl2->mkid.cb);
505 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb));
506 }
507
508 HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
509 {
510 CComPtr<IShellView> pShellView;
511 HRESULT hr = E_NOINTERFACE;
512
513 TRACE("(%p, %p, %s, %p)\n", this, hwndOwner, debugstr_guid(&riid), ppv);
514
515 if (!ppv)
516 return hr;
517
518 *ppv = NULL;
519
520 if (IsEqualIID (riid, IID_IDropTarget))
521 {
522 hr = this->QueryInterface (IID_IDropTarget, ppv);
523 }
524 else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2))
525 {
526 hr = this->QueryInterface(riid, ppv);
527 }
528 else if (IsEqualIID (riid, IID_IShellView))
529 {
530 hr = IShellView_Constructor ((IShellFolder *)this, &pShellView);
531 if (pShellView)
532 {
533 hr = pShellView->QueryInterface(riid, ppv);
534 }
535 }
536 else
537 return hr;
538 TRACE ("-- (%p)->(interface=%p)\n", this, ppv);
539 return hr;
540
541 }
542
543 HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
544 SFGAOF *rgfInOut)
545 {
546 TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut);
547 *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
548 return S_OK;
549 }
550
551 HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
552 REFIID riid, UINT *prgfInOut, void **ppv)
553 {
554 IUnknown *pObj = NULL;
555 HRESULT hr = E_INVALIDARG;
556
557 TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
558 hwndOwner, cidl, apidl, prgfInOut, ppv);
559
560 if (!ppv)
561 return hr;
562
563 *ppv = NULL;
564
565 if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
566 {
567 hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
568 }
569 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
570 {
571 IDropTarget * pDt = NULL;
572 hr = QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
573 pObj = pDt;
574 }
575 else if(IsEqualIID(riid, IID_IExtractIconA) && (cidl == 1))
576 {
577 // FIXME: This is not correct, it does not show the right icons
578 LPITEMIDLIST pidlItem = ILCombine(pidl, apidl[0]);
579 pObj = IExtractIconA_Constructor(pidlItem);
580 SHFree(pidlItem);
581 hr = S_OK;
582 }
583 else if (IsEqualIID(riid, IID_IExtractIconW) && (cidl == 1))
584 {
585 // FIXME: This is not correct, it does not show the right icons
586 LPITEMIDLIST pidlItem = ILCombine(pidl, apidl[0]);
587 pObj = IExtractIconW_Constructor(pidlItem);
588 SHFree(pidlItem);
589 hr = S_OK;
590 }
591 else
592 hr = E_NOINTERFACE;
593
594 if (SUCCEEDED(hr) && !pObj)
595 hr = E_OUTOFMEMORY;
596
597 *ppv = pObj;
598 TRACE ("(%p)->hr=0x%08x\n", this, hr);
599 return hr;
600 }
601
602 HRESULT WINAPI CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pName)
603 {
604 PIDLRecycleStruct *pFileDetails;
605 LPWSTR pFileName;
606
607 TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName);
608
609
610 if (_ILIsBitBucket (pidl))
611 {
612 WCHAR pszPath[100];
613
614 if (HCR_GetClassNameW(CLSID_RecycleBin, pszPath, MAX_PATH))
615 {
616 pName->uType = STRRET_WSTR;
617 pName->pOleStr = StrDupW(pszPath);
618 return S_OK;
619 }
620 }
621
622 pFileDetails = _ILGetRecycleStruct(pidl);
623 if (!pFileDetails)
624 {
625 pName->cStr[0] = 0;
626 pName->uType = STRRET_CSTR;
627 return E_INVALIDARG;
628 }
629
630 pFileName = wcsrchr(pFileDetails->szName, L'\\');
631 if (!pFileName)
632 {
633 pName->cStr[0] = 0;
634 pName->uType = STRRET_CSTR;
635 return E_UNEXPECTED;
636 }
637
638 pName->pOleStr = StrDupW(pFileName + 1);
639 if (pName->pOleStr == NULL)
640 return E_OUTOFMEMORY;
641
642 pName->uType = STRRET_WSTR;
643 return S_OK;
644 }
645
646 HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pszName,
647 SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
648 {
649 TRACE("\n");
650 return E_FAIL; /* not supported */
651 }
652
653 HRESULT WINAPI CRecycleBin::GetDefaultSearchGUID(GUID *pguid)
654 {
655 FIXME("stub\n");
656 return E_NOTIMPL;
657 }
658
659 HRESULT WINAPI CRecycleBin::EnumSearches(IEnumExtraSearch **ppEnum)
660 {
661 FIXME("stub\n");
662 *ppEnum = NULL;
663 return E_NOTIMPL;
664 }
665
666 HRESULT WINAPI CRecycleBin::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
667 {
668 TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved, pSort, pDisplay);
669 *pSort = 0;
670 *pDisplay = 0;
671 return S_OK;
672 }
673
674 HRESULT WINAPI CRecycleBin::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
675 {
676 TRACE("(%p, %d, %p)\n", this, iColumn, pcsFlags);
677 if (iColumn >= COLUMNS_COUNT)
678 return E_INVALIDARG;
679 *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
680 return S_OK;
681 }
682
683 HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
684 {
685 FIXME("stub\n");
686 return E_NOTIMPL;
687 }
688
689 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
690 {
691 FILETIME lft;
692 SYSTEMTIME time;
693 int ret;
694
695 FileTimeToLocalFileTime(ft, &lft);
696 FileTimeToSystemTime(&lft, &time);
697
698 ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
699 if (ret > 0 && ret < size)
700 {
701 /* Append space + time without seconds */
702 buffer[ret-1] = ' ';
703 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
704 }
705
706 return (ret != 0 ? E_FAIL : S_OK);
707 }
708
709 HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetails)
710 {
711 PIDLRecycleStruct * pFileDetails;
712 WCHAR buffer[MAX_PATH];
713 WCHAR szTypeName[100];
714 LPWSTR pszBackslash;
715 UINT Length;
716
717 TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
718 if (iColumn >= COLUMNS_COUNT)
719 return E_FAIL;
720 pDetails->fmt = RecycleBinColumns[iColumn].fmt;
721 pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
722 if (pidl == NULL)
723 {
724 pDetails->str.uType = STRRET_WSTR;
725 LoadStringW(shell32_hInstance, RecycleBinColumns[iColumn].column_name_id, buffer, MAX_PATH);
726 return SHStrDupW(buffer, &pDetails->str.pOleStr);
727 }
728
729 if (iColumn == COLUMN_NAME)
730 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
731
732 pFileDetails = _ILGetRecycleStruct(pidl);
733 switch (iColumn)
734 {
735 case COLUMN_DATEDEL:
736 FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
737 break;
738 case COLUMN_DELFROM:
739 pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
740 Length = (pszBackslash - pFileDetails->szName);
741 memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
742 buffer[Length] = L'\0';
743 break;
744 case COLUMN_SIZE:
745 StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
746 break;
747 case COLUMN_MTIME:
748 FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
749 break;
750 case COLUMN_TYPE:
751 szTypeName[0] = L'\0';
752 wcscpy(buffer, PathFindExtensionW(pFileDetails->szName));
753 if (!( HCR_MapTypeToValueW(buffer, buffer, sizeof(buffer) / sizeof(WCHAR), TRUE) &&
754 HCR_MapTypeToValueW(buffer, szTypeName, sizeof(szTypeName) / sizeof(WCHAR), FALSE )))
755 {
756 wcscpy (szTypeName, PathFindExtensionW(pFileDetails->szName));
757 wcscat(szTypeName, L"-");
758 Length = wcslen(szTypeName);
759 if (LoadStringW(shell32_hInstance, IDS_SHV_COLUMN1, &szTypeName[Length], (sizeof(szTypeName) / sizeof(WCHAR)) - Length))
760 szTypeName[(sizeof(szTypeName)/sizeof(WCHAR))-1] = L'\0';
761 }
762 pDetails->str.uType = STRRET_WSTR;
763 return SHStrDupW(szTypeName, &pDetails->str.pOleStr);
764 break;
765 default:
766 return E_FAIL;
767 }
768
769 pDetails->str.uType = STRRET_WSTR;
770 return SHStrDupW(buffer, &pDetails->str.pOleStr);
771 }
772
773 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
774 {
775 TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
776 if (iColumn >= COLUMNS_COUNT)
777 return E_INVALIDARG;
778 pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
779 pscid->pid = RecycleBinColumns[iColumn].pid;
780 return S_OK;
781 }
782
783 /*************************************************************************
784 * RecycleBin IContextMenu interface
785 */
786
787 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
788 {
789 WCHAR szBuffer[100];
790 MENUITEMINFOW mii;
791 int id = 1;
792
793 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
794
795 if (!hMenu)
796 return E_INVALIDARG;
797
798 memset(&mii, 0, sizeof(mii));
799 mii.cbSize = sizeof(mii);
800 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
801 mii.fState = MFS_ENABLED;
802 szBuffer[0] = L'\0';
803 LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, sizeof(szBuffer) / sizeof(WCHAR));
804 mii.dwTypeData = szBuffer;
805 mii.cch = wcslen(mii.dwTypeData);
806 mii.wID = idCmdFirst + id++;
807 mii.fType = MFT_STRING;
808 iIdEmpty = 1;
809
810 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
811 return E_FAIL;
812
813 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
814 }
815
816 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
817 {
818 HRESULT hr;
819 LPSHELLBROWSER lpSB;
820 IShellView * lpSV = NULL;
821
822 TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
823
824 if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
825 {
826 // FIXME
827 // path & flags
828 hr = SHEmptyRecycleBinW(lpcmi->hwnd, L"C:\\", 0);
829 TRACE("result %x\n", hr);
830 if (hr != S_OK)
831 return hr;
832
833 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
834 if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
835 lpSV->Refresh();
836 }
837 return S_OK;
838 }
839
840 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
841 {
842 FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
843
844 return E_NOTIMPL;
845 }
846
847 /*************************************************************************
848 * RecycleBin IShellPropSheetExt interface
849 */
850
851 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
852 {
853 FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
854
855 return E_NOTIMPL;
856 }
857
858 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
859 {
860 FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
861
862 return E_NOTIMPL;
863 }
864
865 /*************************************************************************
866 * RecycleBin IShellExtInit interface
867 */
868
869 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
870 {
871 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
872 return S_OK;
873 }
874
875 void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
876 {
877 if (bEnable)
878 {
879 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_UNCHECKED, 0);
880 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
881 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
882 }
883 else
884 {
885 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_CHECKED, 0);
886 EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
887 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
888 }
889 }
890
891
892 static VOID
893 InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
894 {
895 WCHAR CurDrive = L'A';
896 WCHAR szDrive[] = L"A:\\";
897 DWORD dwDrives;
898 WCHAR szName[100];
899 WCHAR szVolume[100];
900 DWORD MaxComponent, Flags;
901 DWORD dwSerial;
902 LVCOLUMNW lc;
903 HWND hDlgCtrl;
904 LVITEMW li;
905 INT itemCount;
906 ULARGE_INTEGER TotalNumberOfFreeBytes, TotalNumberOfBytes, FreeBytesAvailable;
907 RECT rect;
908 int columnSize;
909 int defIndex = 0;
910 DWORD dwSize;
911 PDRIVE_ITEM_CONTEXT pItem = NULL, pDefault = NULL, pFirst = NULL;
912
913 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
914
915 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_LOCATION, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
916 szVolume[0] = 0;
917
918 GetClientRect(hDlgCtrl, &rect);
919
920 memset(&lc, 0, sizeof(LV_COLUMN) );
921 lc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
922
923 columnSize = 140; //FIXME
924 lc.iSubItem = 0;
925 lc.fmt = LVCFMT_FIXED_WIDTH;
926 lc.cx = columnSize;
927 lc.cchTextMax = wcslen(szVolume);
928 lc.pszText = szVolume;
929 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&lc);
930
931 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_DISKSPACE, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
932 szVolume[0] = 0;
933
934 lc.iSubItem = 1;
935 lc.cx = rect.right - rect.left - columnSize;
936 lc.cchTextMax = wcslen(szVolume);
937 lc.pszText = szVolume;
938 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&lc);
939
940 dwDrives = GetLogicalDrives();
941 itemCount = 0;
942 do
943 {
944 if ((dwDrives & 0x1))
945 {
946 UINT Type = GetDriveTypeW(szDrive);
947 if (Type == DRIVE_FIXED) //FIXME
948 {
949 if (!GetVolumeInformationW(szDrive, szName, sizeof(szName) / sizeof(WCHAR), &dwSerial, &MaxComponent, &Flags, NULL, 0))
950 {
951 szName[0] = 0;
952 dwSerial = -1;
953 }
954
955 swprintf(szVolume, L"%s (%c)", szName, szDrive[0]);
956 memset(&li, 0x0, sizeof(LVITEMW));
957 li.mask = LVIF_TEXT | LVIF_PARAM;
958 li.iSubItem = 0;
959 li.pszText = szVolume;
960 li.iItem = itemCount;
961 SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&li);
962 if (GetDiskFreeSpaceExW(szDrive, &FreeBytesAvailable , &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
963 {
964 if (StrFormatByteSizeW(TotalNumberOfFreeBytes.QuadPart, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
965 {
966
967 pItem = (DRIVE_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DRIVE_ITEM_CONTEXT));
968 if (pItem)
969 {
970 swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial));
971 dwSize = sizeof(DWORD);
972 RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize);
973 dwSize = sizeof(DWORD);
974 RegGetValueW(HKEY_CURRENT_USER, szName, L"NukeOnDelete", RRF_RT_DWORD, NULL, &pItem->dwNukeOnDelete, &dwSize);
975 pItem->dwSerial = dwSerial;
976 li.mask = LVIF_PARAM;
977 li.lParam = (LPARAM)pItem;
978 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
979 if (CurDrive == DefaultDrive)
980 {
981 defIndex = itemCount;
982 pDefault = pItem;
983 }
984 }
985 if (!pFirst)
986 pFirst = pItem;
987
988 li.mask = LVIF_TEXT;
989 li.iSubItem = 1;
990 li.pszText = szVolume;
991 li.iItem = itemCount;
992 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
993 }
994 }
995 itemCount++;
996 }
997 }
998 CurDrive++;
999 szDrive[0] = CurDrive;
1000 dwDrives = (dwDrives >> 1);
1001 } while(dwDrives);
1002
1003 if (!pDefault)
1004 pDefault = pFirst;
1005 if (pDefault)
1006 {
1007 toggleNukeOnDeleteOption(hwndDlg, pDefault->dwNukeOnDelete);
1008 SetDlgItemInt(hwndDlg, 14002, pDefault->dwMaxCapacity, FALSE);
1009 }
1010 ZeroMemory(&li, sizeof(li));
1011 li.mask = LVIF_STATE;
1012 li.stateMask = (UINT) - 1;
1013 li.state = LVIS_FOCUSED | LVIS_SELECTED;
1014 li.iItem = defIndex;
1015 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
1016
1017 }
1018
1019 static BOOL StoreDriveSettings(HWND hwndDlg)
1020 {
1021 int iCount, iIndex;
1022 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1023 LVITEMW li;
1024 PDRIVE_ITEM_CONTEXT pItem;
1025 HKEY hKey, hSubKey;
1026 WCHAR szSerial[20];
1027 DWORD dwSize;
1028
1029
1030 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
1031 return FALSE;
1032
1033 iCount = ListView_GetItemCount(hDlgCtrl);
1034
1035 ZeroMemory(&li, sizeof(li));
1036 li.mask = LVIF_PARAM;
1037
1038 for(iIndex = 0; iIndex < iCount; iIndex++)
1039 {
1040 li.iItem = iIndex;
1041 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
1042 {
1043 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1044 swprintf(szSerial, L"%04X-%04X", LOWORD(pItem->dwSerial), HIWORD(pItem->dwSerial));
1045 if (RegCreateKeyExW(hKey, szSerial, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
1046 {
1047 dwSize = sizeof(DWORD);
1048 RegSetValueExW(hSubKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&pItem->dwNukeOnDelete, dwSize);
1049 dwSize = sizeof(DWORD);
1050 RegSetValueExW(hSubKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&pItem->dwMaxCapacity, dwSize);
1051 RegCloseKey(hSubKey);
1052 }
1053 }
1054 }
1055 RegCloseKey(hKey);
1056 return TRUE;
1057
1058 }
1059
1060 static VOID FreeDriveItemContext(HWND hwndDlg)
1061 {
1062 int iCount, iIndex;
1063 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1064 LVITEMW li;
1065
1066 iCount = ListView_GetItemCount(hDlgCtrl);
1067
1068 ZeroMemory(&li, sizeof(li));
1069 li.mask = LVIF_PARAM;
1070
1071 for(iIndex = 0; iIndex < iCount; iIndex++)
1072 {
1073 li.iItem = iIndex;
1074 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
1075 {
1076 HeapFree(GetProcessHeap(), 0, (LPVOID)li.lParam);
1077 }
1078 }
1079 }
1080
1081 static INT
1082 GetDefaultItem(HWND hwndDlg, LVITEMW * li)
1083 {
1084 HWND hDlgCtrl;
1085 UINT iItemCount, iIndex;
1086
1087 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1088 if (!hDlgCtrl)
1089 return -1;
1090
1091 iItemCount = ListView_GetItemCount(hDlgCtrl);
1092 if (!iItemCount)
1093 return -1;
1094
1095 ZeroMemory(li, sizeof(LVITEMW));
1096 li->mask = LVIF_PARAM | LVIF_STATE;
1097 li->stateMask = (UINT) - 1;
1098 for (iIndex = 0; iIndex < iItemCount; iIndex++)
1099 {
1100 li->iItem = iIndex;
1101 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)li))
1102 {
1103 if (li->state & LVIS_SELECTED)
1104 return iIndex;
1105 }
1106 }
1107 return -1;
1108
1109 }
1110
1111 static INT_PTR CALLBACK
1112 RecycleBinDlg(
1113 HWND hwndDlg,
1114 UINT uMsg,
1115 WPARAM wParam,
1116 LPARAM lParam
1117 )
1118 {
1119 LPPSHNOTIFY lppsn;
1120 LPNMLISTVIEW lppl;
1121 LVITEMW li;
1122 PDRIVE_ITEM_CONTEXT pItem;
1123 BOOL bSuccess;
1124 UINT uResult;
1125 PROPSHEETPAGE * page;
1126 DWORD dwStyle;
1127
1128 switch(uMsg)
1129 {
1130 case WM_INITDIALOG:
1131 page = (PROPSHEETPAGE*)lParam;
1132 InitializeRecycleBinDlg(hwndDlg, (WCHAR)page->lParam);
1133 dwStyle = (DWORD) SendDlgItemMessage(hwndDlg, 14000, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1134 dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
1135 SendDlgItemMessage(hwndDlg, 14000, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
1136 if (GetDlgCtrlID((HWND)wParam) != 14000)
1137 {
1138 SetFocus(GetDlgItem(hwndDlg, 14000));
1139 return FALSE;
1140 }
1141 return TRUE;
1142 case WM_COMMAND:
1143 switch(LOWORD(wParam))
1144 {
1145 case 14001:
1146 toggleNukeOnDeleteOption(hwndDlg, FALSE);
1147 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1148 break;
1149 case 14003:
1150 toggleNukeOnDeleteOption(hwndDlg, TRUE);
1151 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1152 break;
1153 case 14004:
1154 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1155 break;
1156 }
1157 break;
1158 case WM_NOTIFY:
1159 lppsn = (LPPSHNOTIFY) lParam;
1160 lppl = (LPNMLISTVIEW) lParam;
1161 if (lppsn->hdr.code == PSN_APPLY)
1162 {
1163 if (GetDefaultItem(hwndDlg, &li) > -1)
1164 {
1165 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1166 if (pItem)
1167 {
1168 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
1169 if (bSuccess)
1170 pItem->dwMaxCapacity = uResult;
1171 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1172 pItem->dwNukeOnDelete = TRUE;
1173 else
1174 pItem->dwNukeOnDelete = FALSE;
1175 }
1176 }
1177 if (StoreDriveSettings(hwndDlg))
1178 {
1179 SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR );
1180 return TRUE;
1181 }
1182 }
1183 else if (lppl->hdr.code == LVN_ITEMCHANGING)
1184 {
1185 ZeroMemory(&li, sizeof(li));
1186 li.mask = LVIF_PARAM;
1187 li.iItem = lppl->iItem;
1188 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&li))
1189 return TRUE;
1190
1191 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1192 if (!pItem)
1193 return TRUE;
1194
1195 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
1196 {
1197 /* new focused item */
1198 toggleNukeOnDeleteOption(lppl->hdr.hwndFrom, pItem->dwNukeOnDelete);
1199 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
1200 }
1201 else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED))
1202 {
1203 /* kill focus */
1204 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
1205 if (bSuccess)
1206 pItem->dwMaxCapacity = uResult;
1207 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1208 pItem->dwNukeOnDelete = TRUE;
1209 else
1210 pItem->dwNukeOnDelete = FALSE;
1211 }
1212 return TRUE;
1213
1214 }
1215 break;
1216 case WM_DESTROY:
1217 FreeDriveItemContext(hwndDlg);
1218 break;
1219 }
1220 return FALSE;
1221 }
1222
1223 BOOL SH_ShowRecycleBinProperties(WCHAR sDrive)
1224 {
1225 HPROPSHEETPAGE hpsp[1];
1226 PROPSHEETHEADERW psh;
1227 HPROPSHEETPAGE hprop;
1228
1229 BOOL ret;
1230
1231
1232 ZeroMemory(&psh, sizeof(PROPSHEETHEADERW));
1233 psh.dwSize = sizeof(PROPSHEETHEADERW);
1234 psh.dwFlags = PSP_DEFAULT | PSH_PROPTITLE;
1235 psh.pszCaption = MAKEINTRESOURCEW(IDS_RECYCLEBIN_FOLDER_NAME);
1236 psh.hwndParent = NULL;
1237 psh.phpage = hpsp;
1238 psh.hInstance = shell32_hInstance;
1239
1240 hprop = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, (LPARAM)sDrive, NULL);
1241 if (!hprop)
1242 {
1243 ERR("Failed to create property sheet\n");
1244 return FALSE;
1245 }
1246 hpsp[psh.nPages] = hprop;
1247 psh.nPages++;
1248
1249
1250 ret = PropertySheetW(&psh);
1251 if (ret < 0)
1252 return FALSE;
1253 else
1254 return TRUE;
1255 }
1256
1257 BOOL
1258 TRASH_CanTrashFile(LPCWSTR wszPath)
1259 {
1260 LONG ret;
1261 DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
1262 DWORD FileSystemFlags, dwSize, dwDisposition;
1263 HKEY hKey;
1264 WCHAR szBuffer[10];
1265 WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\";
1266
1267 if (wszPath[1] != L':')
1268 {
1269 /* path is UNC */
1270 return FALSE;
1271 }
1272
1273 if (GetDriveTypeW(wszPath) != DRIVE_FIXED)
1274 {
1275 /* no bitbucket on removable media */
1276 return FALSE;
1277 }
1278
1279 if (!GetVolumeInformationW(wszPath, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
1280 {
1281 ERR("GetVolumeInformationW failed with %u\n", GetLastError());
1282 return FALSE;
1283 }
1284
1285 swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
1286 wcscat(szKey, szBuffer);
1287
1288 if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
1289 {
1290 ERR("RegCreateKeyExW failed\n");
1291 return FALSE;
1292 }
1293
1294 if (dwDisposition & REG_CREATED_NEW_KEY)
1295 {
1296 /* per default move to bitbucket */
1297 dwNukeOnDelete = 0;
1298 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1299 /* per default unlimited size */
1300 dwSize = -1;
1301 RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
1302 RegCloseKey(hKey);
1303 return TRUE;
1304 }
1305 else
1306 {
1307 dwSize = sizeof(dwNukeOnDelete);
1308 ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
1309 if (ret != ERROR_SUCCESS)
1310 {
1311 if (ret == ERROR_FILE_NOT_FOUND)
1312 {
1313 /* restore key and enable bitbucket */
1314 dwNukeOnDelete = 0;
1315 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1316 }
1317 RegCloseKey(hKey);
1318 return TRUE;
1319 }
1320 else if (dwNukeOnDelete)
1321 {
1322 /* do not delete to bitbucket */
1323 RegCloseKey(hKey);
1324 return FALSE;
1325 }
1326 /* FIXME
1327 * check if bitbucket is full
1328 */
1329 RegCloseKey(hKey);
1330 return TRUE;
1331 }
1332 }
1333
1334 BOOL
1335 TRASH_TrashFile(LPCWSTR wszPath)
1336 {
1337 TRACE("(%s)\n", debugstr_w(wszPath));
1338 return DeleteFileToRecycleBin(wszPath);
1339 }
1340
1341 /*************************************************************************
1342 * SHUpdateCRecycleBinIcon [SHELL32.@]
1343 *
1344 * Undocumented
1345 */
1346 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
1347 {
1348 FIXME("stub\n");
1349
1350
1351
1352 return S_OK;
1353 }
1354
1355 /****************************************************************************
1356 * IDropTarget implementation
1357 */
1358 BOOL CRecycleBin::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1359 {
1360 /* TODO on shift we should delete, we should update the cursor manager to show this. */
1361
1362 DWORD dwEffect = DROPEFFECT_COPY;
1363
1364 *pdwEffect = DROPEFFECT_NONE;
1365
1366 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1367 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1368
1369 if (*pdwEffect == DROPEFFECT_NONE)
1370 *pdwEffect = dwEffect;
1371
1372 /* ... matches the desired effect ? */
1373 if (dwEffect & *pdwEffect) {
1374 return TRUE;
1375 }
1376 }
1377 return FALSE;
1378 }
1379
1380 HRESULT WINAPI CRecycleBin::DragEnter(IDataObject *pDataObject,
1381 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1382 {
1383 TRACE("Recycle bin drag over (%p)\n", this);
1384 /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
1385 fAcceptFmt = TRUE;
1386
1387 QueryDrop(dwKeyState, pdwEffect);
1388 return S_OK;
1389 }
1390
1391 HRESULT WINAPI CRecycleBin::DragOver(DWORD dwKeyState, POINTL pt,
1392 DWORD *pdwEffect)
1393 {
1394 TRACE("(%p)\n", this);
1395
1396 if (!pdwEffect)
1397 return E_INVALIDARG;
1398
1399 QueryDrop(dwKeyState, pdwEffect);
1400
1401 return S_OK;
1402 }
1403
1404 HRESULT WINAPI CRecycleBin::DragLeave()
1405 {
1406 TRACE("(%p)\n", this);
1407
1408 fAcceptFmt = FALSE;
1409
1410 return S_OK;
1411 }
1412
1413 HRESULT WINAPI CRecycleBin::Drop(IDataObject *pDataObject,
1414 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1415 {
1416 TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect);
1417
1418 /* TODO: pdwEffect should be read and make the drop object be permanently deleted in the move case (shift held) */
1419
1420 FORMATETC fmt;
1421 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1422 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1423
1424 /* Handle cfShellIDList Drop objects here, otherwise send the approriate message to other software */
1425 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) {
1426 IStream *s;
1427 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &s);
1428 SHCreateThread(DoDeleteThreadProc, s, NULL, NULL);
1429 }
1430 else
1431 {
1432 /*
1433 * TODO call SetData on the data object with format CFSTR_TARGETCLSID
1434 * set to the Recycle Bin's class identifier CLSID_RecycleBin.
1435 */
1436 }
1437 return S_OK;
1438 }
1439
1440 DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter)
1441 {
1442 CoInitialize(NULL);
1443 CComPtr<IDataObject> pDataObject;
1444 HRESULT hr = CoGetInterfaceAndReleaseStream (static_cast<IStream*>(lpParameter), IID_PPV_ARG(IDataObject, &pDataObject));
1445 if (SUCCEEDED(hr))
1446 {
1447 DoDeleteDataObject(pDataObject);
1448 }
1449 CoUninitialize();
1450 return 0;
1451 }
1452
1453 HRESULT WINAPI DoDeleteDataObject(IDataObject *pda)
1454 {
1455 TRACE("performing delete");
1456 HRESULT hr;
1457
1458 STGMEDIUM medium;
1459 FORMATETC formatetc;
1460 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
1461 hr = pda->GetData(&formatetc, &medium);
1462 if (FAILED(hr))
1463 return hr;
1464
1465 /* lock the handle */
1466 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1467 if (!lpcida)
1468 {
1469 ReleaseStgMedium(&medium);
1470 return E_FAIL;
1471 }
1472
1473 /* convert the data into pidl */
1474 LPITEMIDLIST pidl;
1475 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1476 if (!apidl)
1477 {
1478 ReleaseStgMedium(&medium);
1479 return E_FAIL;
1480 }
1481
1482 CComPtr<IShellFolder> psfDesktop;
1483 CComPtr<IShellFolder> psfFrom = NULL;
1484
1485 /* Grab the desktop shell folder */
1486 hr = SHGetDesktopFolder(&psfDesktop);
1487 if (FAILED(hr))
1488 {
1489 ERR("SHGetDesktopFolder failed\n");
1490 SHFree(pidl);
1491 _ILFreeaPidl(apidl, lpcida->cidl);
1492 ReleaseStgMedium(&medium);
1493 return E_FAIL;
1494 }
1495
1496 /* Find source folder, this is where the clipboard data was copied from */
1497 if (_ILIsDesktop(pidl))
1498 {
1499 psfFrom = psfDesktop;
1500 }
1501 else
1502 {
1503 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
1504 if (FAILED(hr))
1505 {
1506 ERR("no IShellFolder\n");
1507 SHFree(pidl);
1508 _ILFreeaPidl(apidl, lpcida->cidl);
1509 ReleaseStgMedium(&medium);
1510 return E_FAIL;
1511 }
1512 }
1513
1514 STRRET strTemp;
1515 hr = psfFrom->GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strTemp);
1516 if (FAILED(hr))
1517 {
1518 ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
1519 SHFree(pidl);
1520 _ILFreeaPidl(apidl, lpcida->cidl);
1521 ReleaseStgMedium(&medium);
1522 return hr;
1523 }
1524
1525 WCHAR wszPath[MAX_PATH];
1526 hr = StrRetToBufW(&strTemp, apidl[0], wszPath, _countof(wszPath));
1527 if (FAILED(hr))
1528 {
1529 ERR("StrRetToBufW failed with %x\n", hr);
1530 SHFree(pidl);
1531 _ILFreeaPidl(apidl, lpcida->cidl);
1532 ReleaseStgMedium(&medium);
1533 return hr;
1534 }
1535
1536 /* Only keep the base path */
1537 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
1538 *pwszFilename = L'\0';
1539
1540 /* Build paths list */
1541 LPWSTR pwszPaths = BuildPathsList(wszPath, lpcida->cidl, (LPCITEMIDLIST*) apidl);
1542 if (!pwszPaths)
1543 {
1544 SHFree(pidl);
1545 _ILFreeaPidl(apidl, lpcida->cidl);
1546 ReleaseStgMedium(&medium);
1547 return E_FAIL;
1548 }
1549
1550 /* Delete them */
1551 SHFILEOPSTRUCTW FileOp;
1552 ZeroMemory(&FileOp, sizeof(FileOp));
1553 FileOp.wFunc = FO_DELETE;
1554 FileOp.pFrom = pwszPaths;
1555 FileOp.fFlags = FOF_ALLOWUNDO;
1556
1557 if (SHFileOperationW(&FileOp) != 0)
1558 {
1559 ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(), debugstr_w(pwszPaths));
1560 hr = E_FAIL;
1561 }
1562
1563 HeapFree(GetProcessHeap(), 0, pwszPaths);
1564 SHFree(pidl);
1565 _ILFreeaPidl(apidl, lpcida->cidl);
1566 ReleaseStgMedium(&medium);
1567
1568 return hr;
1569 }