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 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
25
26 typedef struct
27 {
28 ULARGE_INTEGER FreeBytesAvailable;
29 DWORD dwSerial;
30 DWORD dwMaxCapacity;
31 DWORD dwNukeOnDelete;
32 } DRIVE_ITEM_CONTEXT, *PDRIVE_ITEM_CONTEXT;
33
toggleNukeOnDeleteOption(HWND hwndDlg,BOOL bEnable)34 static void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
35 {
36 if (bEnable)
37 {
38 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_UNCHECKED, 0);
39 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
40 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
41 }
42 else
43 {
44 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_CHECKED, 0);
45 EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
46 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
47 }
48
49 // FIXME: Max capacity not implemented yet, disable for now (CORE-13743)
50 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
51 }
52
53 static VOID
InitializeRecycleBinDlg(HWND hwndDlg,WCHAR DefaultDrive)54 InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
55 {
56 WCHAR szDrive[] = L"A:\\";
57 DWORD dwDrives;
58 WCHAR szName[100];
59 WCHAR szVolume[100];
60 DWORD MaxComponent, Flags;
61 DWORD dwSerial;
62 LVCOLUMNW lc;
63 HWND hDlgCtrl;
64 LVITEMW li;
65 INT itemCount;
66 ULARGE_INTEGER TotalNumberOfFreeBytes, TotalNumberOfBytes, FreeBytesAvailable;
67 RECT rect;
68 int columnSize;
69 int defIndex = 0;
70 DWORD dwSize;
71 PDRIVE_ITEM_CONTEXT pItem = NULL, pDefault = NULL, pFirst = NULL;
72
73 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
74
75 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_LOCATION, szVolume, _countof(szVolume)))
76 szVolume[0] = 0;
77
78 GetClientRect(hDlgCtrl, &rect);
79
80 ZeroMemory(&lc, sizeof(lc));
81 lc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
82
83 columnSize = 140; //FIXME
84 lc.iSubItem = 0;
85 lc.fmt = LVCFMT_FIXED_WIDTH;
86 lc.cx = columnSize;
87 lc.cchTextMax = wcslen(szVolume);
88 lc.pszText = szVolume;
89 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&lc);
90
91 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_DISKSPACE, szVolume, _countof(szVolume)))
92 szVolume[0] = 0;
93
94 lc.iSubItem = 1;
95 lc.cx = rect.right - rect.left - columnSize;
96 lc.cchTextMax = wcslen(szVolume);
97 lc.pszText = szVolume;
98 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&lc);
99
100 dwDrives = GetLogicalDrives();
101 itemCount = 0;
102 do
103 {
104 if (dwDrives & 0x1)
105 {
106 UINT Type = GetDriveTypeW(szDrive);
107 if (Type == DRIVE_FIXED) //FIXME
108 {
109 if (!GetVolumeInformationW(szDrive, szName, _countof(szName), &dwSerial, &MaxComponent, &Flags, NULL, 0))
110 {
111 szName[0] = 0;
112 dwSerial = -1;
113 }
114
115 swprintf(szVolume, L"%s (%c:)", szName, szDrive[0]);
116 ZeroMemory(&li, sizeof(li));
117 li.mask = LVIF_TEXT | LVIF_PARAM;
118 li.iSubItem = 0;
119 li.pszText = szVolume;
120 li.iItem = itemCount;
121 SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&li);
122 if (GetDiskFreeSpaceExW(szDrive, &FreeBytesAvailable, &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
123 {
124 if (StrFormatByteSizeW(TotalNumberOfFreeBytes.QuadPart, szVolume, _countof(szVolume)))
125 {
126 pItem = (DRIVE_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DRIVE_ITEM_CONTEXT));
127 if (pItem)
128 {
129 pItem->FreeBytesAvailable = FreeBytesAvailable;
130 pItem->dwSerial = dwSerial;
131
132 swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial));
133
134 dwSize = sizeof(DWORD);
135 if (RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize))
136 pItem->dwMaxCapacity = ~0;
137
138 /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
139 FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
140 pItem->dwMaxCapacity = min(pItem->dwMaxCapacity, FreeBytesAvailable.LowPart);
141
142 dwSize = sizeof(DWORD);
143 RegGetValueW(HKEY_CURRENT_USER, szName, L"NukeOnDelete", RRF_RT_DWORD, NULL, &pItem->dwNukeOnDelete, &dwSize);
144
145 li.mask = LVIF_PARAM;
146 li.lParam = (LPARAM)pItem;
147 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
148 if (szDrive[0] == DefaultDrive)
149 {
150 defIndex = itemCount;
151 pDefault = pItem;
152 }
153 }
154 if (!pFirst)
155 pFirst = pItem;
156
157 li.mask = LVIF_TEXT;
158 li.iSubItem = 1;
159 li.pszText = szVolume;
160 li.iItem = itemCount;
161 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
162 }
163 }
164 itemCount++;
165 }
166 }
167 szDrive[0]++;
168 dwDrives = (dwDrives >> 1);
169 } while (dwDrives);
170
171 if (!pDefault)
172 pDefault = pFirst;
173 if (pDefault)
174 {
175 toggleNukeOnDeleteOption(hwndDlg, pDefault->dwNukeOnDelete);
176 SetDlgItemInt(hwndDlg, 14002, pDefault->dwMaxCapacity, FALSE);
177 }
178 ZeroMemory(&li, sizeof(li));
179 li.mask = LVIF_STATE;
180 li.stateMask = (UINT)-1;
181 li.state = LVIS_FOCUSED | LVIS_SELECTED;
182 li.iItem = defIndex;
183 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
184 }
185
StoreDriveSettings(HWND hwndDlg)186 static BOOL StoreDriveSettings(HWND hwndDlg)
187 {
188 int iCount, iIndex;
189 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
190 LVITEMW li;
191 PDRIVE_ITEM_CONTEXT pItem;
192 HKEY hKey, hSubKey;
193 WCHAR szSerial[20];
194 DWORD dwSize;
195
196 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
197 return FALSE;
198
199 iCount = ListView_GetItemCount(hDlgCtrl);
200
201 ZeroMemory(&li, sizeof(li));
202 li.mask = LVIF_PARAM;
203
204 for (iIndex = 0; iIndex < iCount; iIndex++)
205 {
206 li.iItem = iIndex;
207 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
208 {
209 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
210 swprintf(szSerial, L"%04X-%04X", LOWORD(pItem->dwSerial), HIWORD(pItem->dwSerial));
211 if (RegCreateKeyExW(hKey, szSerial, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
212 {
213 dwSize = sizeof(DWORD);
214 RegSetValueExW(hSubKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&pItem->dwMaxCapacity, dwSize);
215 dwSize = sizeof(DWORD);
216 RegSetValueExW(hSubKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&pItem->dwNukeOnDelete, dwSize);
217 RegCloseKey(hSubKey);
218 }
219 }
220 }
221 RegCloseKey(hKey);
222 return TRUE;
223 }
224
FreeDriveItemContext(HWND hwndDlg)225 static VOID FreeDriveItemContext(HWND hwndDlg)
226 {
227 int iCount, iIndex;
228 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
229 LVITEMW li;
230
231 iCount = ListView_GetItemCount(hDlgCtrl);
232
233 ZeroMemory(&li, sizeof(li));
234 li.mask = LVIF_PARAM;
235
236 for (iIndex = 0; iIndex < iCount; iIndex++)
237 {
238 li.iItem = iIndex;
239 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
240 {
241 HeapFree(GetProcessHeap(), 0, (PVOID)li.lParam);
242 }
243 }
244 }
245
246 static INT
GetSelectedDriveItem(HWND hwndDlg,LVITEMW * li)247 GetSelectedDriveItem(HWND hwndDlg, LVITEMW* li)
248 {
249 HWND hDlgCtrl;
250 UINT iItemCount, iIndex;
251
252 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
253 if (!hDlgCtrl)
254 return -1;
255
256 iItemCount = ListView_GetItemCount(hDlgCtrl);
257 if (!iItemCount)
258 return -1;
259
260 ZeroMemory(li, sizeof(LVITEMW));
261 li->mask = LVIF_PARAM | LVIF_STATE;
262 li->stateMask = (UINT)-1;
263 for (iIndex = 0; iIndex < iItemCount; iIndex++)
264 {
265 li->iItem = iIndex;
266 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)li))
267 {
268 if (li->state & LVIS_SELECTED)
269 return iIndex;
270 }
271 }
272 return -1;
273 }
274
275 static INT_PTR CALLBACK
RecycleBinDlg(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)276 RecycleBinDlg(
277 HWND hwndDlg,
278 UINT uMsg,
279 WPARAM wParam,
280 LPARAM lParam)
281 {
282 enum { WM_NEWDRIVESELECTED = WM_APP, WM_UPDATEDRIVESETTINGS };
283 LPPSHNOTIFY lppsn;
284 LPNMLISTVIEW lppl;
285 LVITEMW li;
286 PDRIVE_ITEM_CONTEXT pItem;
287 BOOL bSuccess;
288 UINT uResult;
289 PROPSHEETPAGE * page;
290 DWORD dwStyle;
291 ULARGE_INTEGER FreeBytesAvailable;
292
293 switch(uMsg)
294 {
295 case WM_INITDIALOG:
296 page = (PROPSHEETPAGE*)lParam;
297 InitializeRecycleBinDlg(hwndDlg, (WCHAR)page->lParam);
298 SendDlgItemMessage(hwndDlg, 14004, BM_SETCHECK, !SHELL_GetSetting(SSF_NOCONFIRMRECYCLE, fNoConfirmRecycle), 0);
299 dwStyle = (DWORD) SendDlgItemMessage(hwndDlg, 14000, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
300 dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
301 SendDlgItemMessage(hwndDlg, 14000, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
302 if (GetDlgCtrlID((HWND)wParam) != 14000)
303 {
304 SetFocus(GetDlgItem(hwndDlg, 14000));
305 return FALSE;
306 }
307 return TRUE;
308 case WM_COMMAND:
309 switch(LOWORD(wParam))
310 {
311 case 14001:
312 toggleNukeOnDeleteOption(hwndDlg, FALSE);
313 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
314 break;
315 case 14002:
316 if (HIWORD(wParam) == EN_CHANGE)
317 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
318 break;
319 case 14003:
320 toggleNukeOnDeleteOption(hwndDlg, TRUE);
321 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
322 break;
323 case 14004:
324 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
325 break;
326 }
327 break;
328 case WM_NOTIFY:
329 lppsn = (LPPSHNOTIFY) lParam;
330 lppl = (LPNMLISTVIEW) lParam;
331 if (lppsn->hdr.code == PSN_APPLY)
332 {
333 SHELLSTATE ss;
334 ss.fNoConfirmRecycle = SendDlgItemMessage(hwndDlg, 14004, BM_GETCHECK, 0, 0) == BST_UNCHECKED;
335 SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
336
337 if (GetSelectedDriveItem(hwndDlg, &li) > -1)
338 {
339 SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, li.lParam);
340 }
341 if (StoreDriveSettings(hwndDlg))
342 {
343 SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR );
344 return TRUE;
345 }
346 }
347 else if (lppl->hdr.code == LVN_ITEMCHANGING)
348 {
349 ZeroMemory(&li, sizeof(li));
350 li.mask = LVIF_PARAM;
351 li.iItem = lppl->iItem;
352 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&li))
353 return TRUE;
354
355 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
356 if (!pItem)
357 return TRUE;
358
359 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
360 {
361 // New focused item, delay handling until after kill focus has been processed
362 PostMessage(hwndDlg, WM_NEWDRIVESELECTED, 0, (LPARAM)pItem);
363 }
364 else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED))
365 {
366 // Kill focus
367 SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, (LPARAM)pItem);
368 }
369 return TRUE;
370
371 }
372 break;
373 case WM_NEWDRIVESELECTED:
374 if (lParam)
375 {
376 pItem = (PDRIVE_ITEM_CONTEXT)lParam;
377 toggleNukeOnDeleteOption(hwndDlg, pItem->dwNukeOnDelete);
378 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
379 }
380 break;
381 case WM_UPDATEDRIVESETTINGS:
382 if (lParam)
383 {
384 pItem = (PDRIVE_ITEM_CONTEXT)lParam;
385 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
386 if (bSuccess)
387 {
388 /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
389 FreeBytesAvailable = pItem->FreeBytesAvailable;
390 FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
391 pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart);
392 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
393 }
394 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
395 pItem->dwNukeOnDelete = TRUE;
396 else
397 pItem->dwNukeOnDelete = FALSE;
398 }
399 break;
400 case WM_DESTROY:
401 FreeDriveItemContext(hwndDlg);
402 break;
403 }
404 return FALSE;
405 }
406
RecycleBin_AddPropSheetPages(LPFNSVADDPROPSHEETPAGE pfnAddPage,LPARAM lParam)407 HRESULT RecycleBin_AddPropSheetPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
408 {
409 HPROPSHEETPAGE hpsp = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, 0, NULL);
410 return AddPropSheetPage(hpsp, pfnAddPage, lParam);
411 }
412