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