2806 lines
88 KiB
C
2806 lines
88 KiB
C
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "bitbuck.h"
|
|
#include "fstreex.h"
|
|
#include "util.h"
|
|
#include "copy.h"
|
|
#include "mrsw.h"
|
|
#include "prop.h" // for COL_DATA
|
|
|
|
#include "datautil.h"
|
|
|
|
STDAPI_(LPITEMIDLIST) ILResize(LPITEMIDLIST pidl, UINT cbRequired, UINT cbExtra);
|
|
|
|
// mulprsht.c
|
|
void SetDateTimeText(HWND hdlg, int id, const FILETIME* pftUTC);
|
|
|
|
// defext.c
|
|
STDMETHODIMP CCommonShellPropSheetExt_ReplacePage(IShellPropSheetExt* pspx, UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam);
|
|
|
|
// fstreex.c
|
|
int FS_CreateMoveCopyList(IDataObject* pdtobj, void* hNameMappings, LPITEMIDLIST** pppidl);
|
|
void FS_PositionItems(HWND hwndOwner, UINT cidl, const LPITEMIDLIST* ppidl, IDataObject* pdtobj, POINT* pptOrigin, BOOL fMove);
|
|
void FS_FreeMoveCopyList(LPITEMIDLIST* ppidl, UINT cidl);
|
|
HRESULT CFSIDLData_QueryGetData(IDataObject* pdtobj, FORMATETC* pformatetc); // subclass member function to support CF_HDROP and CF_NETRESOURCE
|
|
|
|
|
|
|
|
// Prototypes
|
|
|
|
void BBGetDisplayName(LPCIDFOLDER pidf, LPTSTR pszPath);
|
|
void BBGetItemPath(LPCIDFOLDER pidf, LPTSTR pszPath);
|
|
STDMETHODIMP CBitBucket_PS_AddPages(IShellPropSheetExt* pspx, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
|
|
LPITEMIDLIST DeletedFilePathToBBPidl(LPTSTR lpszPath);
|
|
|
|
|
|
// structs
|
|
|
|
typedef struct _bbpropsheetinfo
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
|
|
int idDrive;
|
|
|
|
BOOL fNukeOnDelete;
|
|
BOOL fOriginalNukeOnDelete;
|
|
|
|
int iPercent;
|
|
int iOriginalPercent;
|
|
|
|
// the following two fields are valid only for the "global" tab, where they represent the state
|
|
// of the "Configure drives independently" / "Use one setting for all drives" checkbox
|
|
BOOL fUseGlobalSettings;
|
|
BOOL fOriginalUseGlobalSettings;
|
|
|
|
// this is a pointer to the global property sheet page after it has been copied somewhere by the
|
|
// CreatePropertySheetPage(), we use this to get to the global state of the % slider and fNukeOnDelete
|
|
// from the other tabs
|
|
struct _bbpropsheetinfo* pGlobal;
|
|
|
|
} BBPROPSHEETINFO, * LPBBPROPSHEETINFO;
|
|
|
|
|
|
const static DWORD aBitBucketPropHelpIDs[] = { // Context Help IDs
|
|
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
|
|
IDC_INDEPENDENT, IDH_RECYCLE_CONFIG_INDEP,
|
|
IDC_GLOBAL, IDH_RECYCLE_CONFIG_ALL,
|
|
IDC_DISKSIZE, IDH_RECYCLE_DRIVE_SIZE,
|
|
IDC_DISKSIZEDATA, IDH_RECYCLE_DRIVE_SIZE,
|
|
IDC_BYTESIZE, IDH_RECYCLE_BIN_SIZE,
|
|
IDC_BYTESIZEDATA, IDH_RECYCLE_BIN_SIZE,
|
|
IDC_NUKEONDELETE, IDH_RECYCLE_PURGE_ON_DEL,
|
|
IDC_BBSIZE, IDH_RECYCLE_MAX_SIZE,
|
|
IDC_BBSIZETEXT, IDH_RECYCLE_MAX_SIZE,
|
|
IDC_CONFIRMDELETE, IDH_DELETE_CONFIRM_DLG,
|
|
IDC_TEXT, NO_HELP,
|
|
0, 0
|
|
};
|
|
|
|
const static DWORD aBitBucketHelpIDs[] = { // Context Help IDs
|
|
IDD_LINE_1, NO_HELP,
|
|
IDD_LINE_2, NO_HELP,
|
|
IDD_ITEMICON, IDH_FPROP_GEN_ICON,
|
|
IDD_NAME, IDH_FPROP_GEN_NAME,
|
|
IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
|
|
IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
|
|
IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
|
|
IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
|
|
IDD_LOCATION_TXT, IDH_FCAB_DELFILEPROP_LOCATION,
|
|
IDD_LOCATION, IDH_FCAB_DELFILEPROP_LOCATION,
|
|
IDD_DELETED_TXT, IDH_FCAB_DELFILEPROP_DELETED,
|
|
IDD_DELETED, IDH_FCAB_DELFILEPROP_DELETED,
|
|
IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
|
|
IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
|
|
IDD_READONLY, IDH_FCAB_DELFILEPROP_READONLY,
|
|
IDD_HIDDEN, IDH_FCAB_DELFILEPROP_HIDDEN,
|
|
IDD_ARCHIVE, IDH_FCAB_DELFILEPROP_ARCHIVE,
|
|
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
|
|
0, 0
|
|
};
|
|
|
|
|
|
// for the pidls
|
|
|
|
enum
|
|
{
|
|
ICOL_NAME = 0,
|
|
ICOL_ORIGINAL,
|
|
ICOL_MODIFIED,
|
|
ICOL_TYPE,
|
|
ICOL_SIZE,
|
|
};
|
|
|
|
const COL_DATA c_bb_cols[] = {
|
|
{ICOL_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT, &SCID_NAME},
|
|
{ICOL_ORIGINAL, IDS_DELETEDFROM_COL, 20, LVCFMT_LEFT, &SCID_ORIGINALLOCATION},
|
|
{ICOL_MODIFIED, IDS_DATEDELETED_COL, 18, LVCFMT_LEFT, &SCID_DATEDELETED},
|
|
{ICOL_TYPE, IDS_TYPE_COL, 20, LVCFMT_LEFT, &SCID_TYPE},
|
|
{ICOL_SIZE, IDS_SIZE_COL, 8, LVCFMT_RIGHT, &SCID_SIZE}
|
|
};
|
|
|
|
|
|
// CBitBucket members
|
|
|
|
STDMETHODIMP CBitBucket_SF_QueryInterface(IShellFolder2* psf, REFIID riid, void** ppvObj)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
|
|
|
|
if (IsEqualIID(riid, &IID_IShellFolder) ||
|
|
IsEqualIID(riid, &IID_IShellFolder2) ||
|
|
IsEqualIID(riid, &IID_IUnknown)) {
|
|
*ppvObj = &this->isf;
|
|
} else if (IsEqualIID(riid, &IID_IPersistFolder) ||
|
|
IsEqualIID(riid, &IID_IPersistFolder2) ||
|
|
IsEqualIID(riid, &IID_IPersist)) {
|
|
*ppvObj = &this->ipf;
|
|
} else if (IsEqualIID(riid, &IID_IContextMenu)) {
|
|
*ppvObj = &this->icm;
|
|
} else if (IsEqualIID(riid, &IID_IShellPropSheetExt)) {
|
|
*ppvObj = &this->ips;
|
|
} else if (IsEqualIID(riid, &IID_IShellExtInit)) {
|
|
*ppvObj = &this->isei;
|
|
} else {
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
this->cRef++;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_SF_Release(IShellFolder2* psf)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
if (InterlockedDecrement(&this->cRef))
|
|
return this->cRef;
|
|
|
|
ILFree(this->pidl);
|
|
LocalFree((HLOCAL)this);
|
|
return 0;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_SF_AddRef(IShellFolder2* psf)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
return InterlockedIncrement(&this->cRef);
|
|
}
|
|
|
|
|
|
BOOL BBGetOriginalPath(LPBBDATAENTRYIDA pbbidl, TCHAR* pszOrig, UINT cch)
|
|
{
|
|
#ifdef UNICODE
|
|
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
|
|
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
|
|
ualstrcpyn(pszOrig, pbbidlw->wszOriginal, cch);
|
|
return (BOOL)*pszOrig;
|
|
}
|
|
#endif
|
|
SHAnsiToTChar(pbbidl->bbde.szOriginal, pszOrig, cch);
|
|
return (BOOL)*pszOrig;
|
|
}
|
|
|
|
|
|
|
|
// We need to be able to compare the names of two bbpidls. Since either of
|
|
// them could be a unicode name, we might have to convert both to unicode.
|
|
|
|
int _BBCompareOriginal(LPBBDATAENTRYIDA lpbbidl1, LPBBDATAENTRYIDA lpbbidl2)
|
|
{
|
|
TCHAR szOrig1[MAX_PATH];
|
|
TCHAR szOrig2[MAX_PATH];
|
|
BBGetOriginalPath(lpbbidl1, szOrig1, ARRAYSIZE(szOrig1));
|
|
BBGetOriginalPath(lpbbidl2, szOrig2, ARRAYSIZE(szOrig2));
|
|
|
|
PathRemoveFileSpec(szOrig1);
|
|
PathRemoveFileSpec(szOrig2);
|
|
return lstrcmpi(szOrig1, szOrig2);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SF_CompareIDs(IShellFolder2* psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hres = ResultFromShort(-1);
|
|
LPBBDATAENTRYIDA pbbidl1, pbbidl2;
|
|
LPCIDFOLDER pidf1 = FS_IsValidID(pidl1);
|
|
LPCIDFOLDER pidf2 = FS_IsValidID(pidl2);
|
|
short nCmp;
|
|
|
|
if (!pidf1 || !pidf2) {
|
|
ASSERT(0);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
pbbidl1 = PIDLTODATAENTRYID(pidl1);
|
|
pbbidl2 = PIDLTODATAENTRYID(pidl2);
|
|
|
|
// don't worry about recursing down because we don't have children.
|
|
switch (lParam) {
|
|
case ICOL_TYPE:
|
|
nCmp = _CompareFileTypes((IShellFolder*)psf, pidf1, pidf2);
|
|
if (nCmp) {
|
|
return ResultFromShort(nCmp);
|
|
} else {
|
|
goto CompareNames;
|
|
}
|
|
break;
|
|
|
|
case ICOL_SIZE:
|
|
{
|
|
ULONGLONG qw1, qw2;
|
|
|
|
// BUGBUG (reinerf) - directories are not dealt with properly
|
|
// when sorting by size.
|
|
|
|
FS_GetSize(NULL, pidf1, &qw1);
|
|
FS_GetSize(NULL, pidf2, &qw2);
|
|
|
|
if (qw1 < qw2) {
|
|
return ResultFromShort(-1);
|
|
}
|
|
if (qw1 > qw2) {
|
|
return ResultFromShort(1);
|
|
}
|
|
} // else fall through
|
|
|
|
CompareNames:
|
|
case ICOL_NAME:
|
|
hres = FS_CompareNamesCase(pidf1, pidf2);
|
|
// compare the real filenames first, if they are different,
|
|
// try comparing the display name
|
|
if ((hres != ResultFromShort(0)) && (BB_IsRealID(pidl1) && BB_IsRealID(pidl2))) {
|
|
HRESULT hresOld = hres;
|
|
TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
|
|
|
|
BBGetDisplayName(pidf1, szName1);
|
|
BBGetDisplayName(pidf2, szName2);
|
|
|
|
hres = ResultFromShort(lstrcmpi(szName1, szName2));
|
|
// if they are from the same location, sort them by delete times
|
|
if (hres == ResultFromShort(0)) {
|
|
// if the items are same in title, sort by drive
|
|
hres = ResultFromShort(pbbidl1->bbde.idDrive - pbbidl2->bbde.idDrive);
|
|
|
|
if (hres == ResultFromShort(0)) {
|
|
// if the items are still the same, sort by index
|
|
hres = ResultFromShort(pbbidl1->bbde.iIndex - pbbidl2->bbde.iIndex);
|
|
|
|
// once we're not equal, we can never be equal again
|
|
ASSERT(hres != ResultFromShort(0));
|
|
if (hres == ResultFromShort(0)) {
|
|
hres = hresOld;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ICOL_ORIGINAL:
|
|
hres = ResultFromShort(_BBCompareOriginal(pbbidl1, pbbidl2));
|
|
break;
|
|
|
|
case ICOL_MODIFIED:
|
|
if (pbbidl1->bbde.ft.dwHighDateTime < pbbidl2->bbde.ft.dwHighDateTime) {
|
|
hres = ResultFromShort(-1);
|
|
} else if (pbbidl1->bbde.ft.dwHighDateTime > pbbidl2->bbde.ft.dwHighDateTime) {
|
|
hres = ResultFromShort(1);
|
|
} else {
|
|
if (pbbidl1->bbde.ft.dwLowDateTime < pbbidl2->bbde.ft.dwLowDateTime) {
|
|
hres = ResultFromShort(-1);
|
|
} else if (pbbidl1->bbde.ft.dwLowDateTime > pbbidl2->bbde.ft.dwLowDateTime) {
|
|
hres = ResultFromShort(1);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SF_GetAttributesOf(IShellFolder2* psf, UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfOut)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
ULONG gfOut = *rgfOut;
|
|
|
|
// asking about the root itself
|
|
if (cidl == 0) {
|
|
gfOut = SFGAO_HASPROPSHEET;
|
|
} else {
|
|
LPCIDFOLDER pidf = FS_IsValidID(apidl[0]);
|
|
|
|
gfOut &= (SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM);
|
|
|
|
if (*rgfOut & SFGAO_LINK) {
|
|
if (SHGetClassFlags(pidf) & SHCF_IS_LINK) {
|
|
gfOut |= SFGAO_LINK;
|
|
}
|
|
}
|
|
}
|
|
|
|
*rgfOut = gfOut;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
int DataObjToFileOpString(IDataObject* pdtobj, LPTSTR* ppszSrc, LPTSTR* ppszDest)
|
|
{
|
|
int cItems = 0;
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
if (pida) {
|
|
LPTSTR lpszSrc, lpszDest;
|
|
int i, cchSrc, cchDest;
|
|
|
|
cItems = pida->cidl;
|
|
|
|
// start with null terminated strings
|
|
cchSrc = cchDest = 1;
|
|
|
|
if (ppszSrc)
|
|
lpszSrc = (void*)LocalAlloc(LPTR, cchSrc * SIZEOF(TCHAR));
|
|
|
|
if (ppszDest)
|
|
lpszDest = (void*)LocalAlloc(LPTR, cchDest * SIZEOF(TCHAR));
|
|
|
|
for (i = 0; i < cItems; i++) {
|
|
LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, i);
|
|
LPBBDATAENTRYIDA pbbidl = PIDLTODATAENTRYID(pidl);
|
|
|
|
if (ppszSrc) {
|
|
TCHAR szSrc[MAX_PATH];
|
|
int cchSrcFile;
|
|
LPTSTR psz;
|
|
|
|
BBGetItemPath((LPIDFOLDER)pidl, szSrc);
|
|
|
|
cchSrcFile = lstrlen(szSrc) + 1;
|
|
psz = (LPTSTR)LocalReAlloc((HLOCAL)lpszSrc, (cchSrc + cchSrcFile) * SIZEOF(TCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
if (!psz) {
|
|
// out of memory!
|
|
// bugbug: do something real
|
|
LocalFree((HLOCAL)lpszSrc);
|
|
if (ppszDest)
|
|
LocalFree((HLOCAL)lpszDest);
|
|
cItems = 0;
|
|
goto Bail;
|
|
}
|
|
lpszSrc = psz;
|
|
lstrcpy(lpszSrc + cchSrc - 1, szSrc);
|
|
cchSrc += cchSrcFile;
|
|
}
|
|
|
|
if (ppszDest) {
|
|
TCHAR szOrig[MAX_PATH];
|
|
int cchDestFile;
|
|
LPTSTR psz;
|
|
|
|
BBGetOriginalPath(pbbidl, szOrig, ARRAYSIZE(szOrig));
|
|
|
|
cchDestFile = lstrlen(szOrig) + 1;
|
|
|
|
psz = (LPTSTR)LocalReAlloc((HLOCAL)lpszDest, (cchDest + cchDestFile) * SIZEOF(TCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
if (!psz) {
|
|
// out of memory!
|
|
LocalFree((HLOCAL)lpszDest);
|
|
if (ppszSrc)
|
|
LocalFree((HLOCAL)lpszSrc);
|
|
cItems = 0;
|
|
goto Bail;
|
|
}
|
|
lpszDest = psz;
|
|
lstrcpy(lpszDest + cchDest - 1, szOrig);
|
|
cchDest += cchDestFile;
|
|
}
|
|
}
|
|
|
|
if (ppszSrc)
|
|
*ppszSrc = lpszSrc;
|
|
|
|
if (ppszDest)
|
|
*ppszDest = lpszDest;
|
|
|
|
Bail:
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
return cItems;
|
|
}
|
|
|
|
|
|
|
|
// restores the list of files in the IDataObject
|
|
|
|
void BBRestoreFileList(CBitBucket* this, HWND hwndOwner, IDataObject* pdtobj)
|
|
{
|
|
LPTSTR pszSrc, pszDest;
|
|
|
|
if (DataObjToFileOpString(pdtobj, &pszSrc, &pszDest)) {
|
|
// now do the actual restore.
|
|
SHFILEOPSTRUCT sFileOp = {hwndOwner,
|
|
FO_MOVE,
|
|
pszSrc,
|
|
pszDest,
|
|
FOF_MULTIDESTFILES | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR,
|
|
FALSE,
|
|
NULL,
|
|
MAKEINTRESOURCE(IDS_BB_RESTORINGFILES)};
|
|
|
|
DECLAREWAITCURSOR;
|
|
|
|
SetWaitCursor();
|
|
|
|
if (SHFileOperation(&sFileOp) == ERROR_SUCCESS) {
|
|
// success!;
|
|
SHChangeNotifyHandleEvents();
|
|
|
|
BBCheckRestoredFiles(pszSrc);
|
|
}
|
|
|
|
LocalFree((HLOCAL)pszSrc);
|
|
LocalFree((HLOCAL)pszDest);
|
|
|
|
ResetWaitCursor();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// nukes the list of files in the IDataObject
|
|
|
|
void BBNukeFileList(CBitBucket* this, HWND hwndOwner, IDataObject* pdtobj)
|
|
{
|
|
LPTSTR lpszSrc, lpszDest;
|
|
int nFiles = DataObjToFileOpString(pdtobj, &lpszSrc, &lpszDest);
|
|
|
|
if (nFiles) {
|
|
// now do the actual nuke.
|
|
int iItems;
|
|
WIN32_FIND_DATA fd;
|
|
CONFIRM_DATA cd = {CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_PROGRAM_FILE | CONFIRM_MULTIPLE, 0};
|
|
SHFILEOPSTRUCT sFileOp = {hwndOwner,
|
|
FO_DELETE,
|
|
lpszSrc,
|
|
NULL,
|
|
FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS,
|
|
FALSE,
|
|
NULL,
|
|
MAKEINTRESOURCE(IDS_BB_DELETINGWASTEBASKETFILES)};
|
|
|
|
DECLAREWAITCURSOR;
|
|
|
|
SetWaitCursor();
|
|
|
|
fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
if (ConfirmFileOp(hwndOwner, NULL, &cd, nFiles, 0, CONFIRM_DELETE_FILE | CONFIRM_WASTEBASKET_PURGE, lpszDest, &fd, NULL, &fd, NULL) == IDYES) {
|
|
SHFileOperation(&sFileOp);
|
|
|
|
SHChangeNotifyHandleEvents();
|
|
|
|
// update the icon if there are objects left in the list
|
|
iItems = (int)ShellFolderView_GetObjectCount(hwndOwner);
|
|
UpdateIcon(iItems);
|
|
}
|
|
|
|
LocalFree((HLOCAL)lpszSrc);
|
|
LocalFree((HLOCAL)lpszDest);
|
|
|
|
ResetWaitCursor();
|
|
}
|
|
}
|
|
|
|
|
|
void EnableTrackbarAndFamily(HWND hDlg, BOOL f)
|
|
{
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BBSIZE), f);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BBSIZETEXT), f);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_TEXT), f);
|
|
}
|
|
|
|
|
|
void BBGlobalPropOnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
LPBBPROPSHEETINFO ppsi = (LPBBPROPSHEETINFO)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
BOOL fNukeOnDelete;
|
|
|
|
switch (id) {
|
|
case IDC_GLOBAL:
|
|
case IDC_INDEPENDENT:
|
|
fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE);
|
|
|
|
ppsi->fUseGlobalSettings = (IsDlgButtonChecked(hDlg, IDC_GLOBAL) == BST_CHECKED) ? TRUE : FALSE;
|
|
EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), ppsi->fUseGlobalSettings);
|
|
EnableTrackbarAndFamily(hDlg, ppsi->fUseGlobalSettings && !fNukeOnDelete);
|
|
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
break;
|
|
|
|
case IDC_NUKEONDELETE:
|
|
fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE);
|
|
|
|
if (fNukeOnDelete) {
|
|
// In order to help protect users, when they turn on "Remove files immedately" we also
|
|
// check the "show delete confimation" box automatically for them. Thus, they will have
|
|
// to explicitly uncheck it if they do not want confimation that their files will be nuked.
|
|
CheckDlgButton(hDlg, IDC_CONFIRMDELETE, BST_CHECKED);
|
|
}
|
|
|
|
EnableTrackbarAndFamily(hDlg, !fNukeOnDelete);
|
|
// fall through
|
|
case IDC_CONFIRMDELETE:
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void RelayMessageToChildren(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hwndChild;
|
|
|
|
for (hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL; hwndChild = GetWindow(hwndChild, GW_HWNDNEXT)) {
|
|
SendMessage(hwndChild, uMessage, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// This is the dlg proc for the "Global" tab on the recycle bin
|
|
|
|
BOOL_PTR CALLBACK BBGlobalPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPBBPROPSHEETINFO ppsi = (LPBBPROPSHEETINFO)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
switch (uMsg) {
|
|
HANDLE_MSG(hDlg, WM_COMMAND, BBGlobalPropOnCommand);
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
|
|
SHELLSTATE ss;
|
|
|
|
// make sure the info we have is current
|
|
RefreshAllBBDriveSettings();
|
|
|
|
ppsi = (LPBBPROPSHEETINFO)lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
|
|
SendMessage(hwndTrack, TBM_SETTICFREQ, 10, 0);
|
|
SendMessage(hwndTrack, TBM_SETRANGE, FALSE, MAKELONG(0, 100));
|
|
SendMessage(hwndTrack, TBM_SETPOS, TRUE, ppsi->iOriginalPercent);
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), ppsi->fUseGlobalSettings);
|
|
EnableTrackbarAndFamily(hDlg, ppsi->fUseGlobalSettings && !ppsi->fNukeOnDelete);
|
|
CheckDlgButton(hDlg, IDC_NUKEONDELETE, ppsi->fNukeOnDelete);
|
|
CheckRadioButton(hDlg, IDC_INDEPENDENT, IDC_GLOBAL, ppsi->fUseGlobalSettings ? IDC_GLOBAL : IDC_INDEPENDENT);
|
|
|
|
SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, FALSE);
|
|
CheckDlgButton(hDlg, IDC_CONFIRMDELETE, !ss.fNoConfirmRecycle);
|
|
}
|
|
// fall through to set iGlobalPercent
|
|
case WM_HSCROLL:
|
|
{
|
|
TCHAR szPercent[20];
|
|
HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
|
|
|
|
ppsi->iPercent = (int)SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
|
|
wsprintf(szPercent, TEXT("%d%%"), ppsi->iPercent);
|
|
SetDlgItemText(hDlg, IDC_BBSIZETEXT, szPercent);
|
|
|
|
if (ppsi->iPercent != ppsi->iOriginalPercent) {
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
|
|
|
|
if (ppsi->iPercent == 0) {
|
|
// In order to help protect users, when they set the % slider to zero we also
|
|
// check the "show delete confimation" box automatically for them. Thus, they will have
|
|
// to explicitly uncheck it if they do not want confimation that their files will be nuked.
|
|
CheckDlgButton(hDlg, IDC_CONFIRMDELETE, BST_CHECKED);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aBitBucketPropHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU,
|
|
(ULONG_PTR)(LPVOID)aBitBucketPropHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_WININICHANGE:
|
|
case WM_SYSCOLORCHANGE:
|
|
case WM_DISPLAYCHANGE:
|
|
RelayMessageToChildren(hDlg, uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
CheckCompactAndPurge();
|
|
SHUpdateRecycleBinIcon();
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR*)lParam)->code) {
|
|
case PSN_APPLY:
|
|
{
|
|
SHELLSTATE ss;
|
|
|
|
ss.fNoConfirmRecycle = !IsDlgButtonChecked(hDlg, IDC_CONFIRMDELETE);
|
|
SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
|
|
|
|
ppsi->fNukeOnDelete = (IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) == BST_CHECKED) ? TRUE : FALSE;
|
|
ppsi->fUseGlobalSettings = (IsDlgButtonChecked(hDlg, IDC_INDEPENDENT) == BST_CHECKED) ? FALSE : TRUE;
|
|
|
|
// if anything on the global tab changed, update all the drives
|
|
if (ppsi->fUseGlobalSettings != ppsi->fOriginalUseGlobalSettings ||
|
|
ppsi->fNukeOnDelete != ppsi->fOriginalNukeOnDelete ||
|
|
ppsi->iPercent != ppsi->iOriginalPercent) {
|
|
int i;
|
|
|
|
// NOTE: We get a PSN_APPLY after all the drive tabs. This has to be this way so that
|
|
// if global settings change, then the global tab will re-apply all the most current settings
|
|
// bassed on the global variables that get set above.
|
|
|
|
// this sets the new global settings in the registry
|
|
if (!PersistGlobalSettings(ppsi->fUseGlobalSettings, ppsi->fNukeOnDelete, ppsi->iPercent)) {
|
|
// we failed, so show the error dialog and bail
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_BB_CANNOTCHANGESETTINGS),
|
|
MAKEINTRESOURCE(IDS_WASTEBASKET),
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
SetDlgMsgResult(hDlg, WM_NOTIFY, PSNRET_INVALID_NOCHANGEPAGE);
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0; i < MAX_BITBUCKETS; i++) {
|
|
if (MakeBitBucket(i)) {
|
|
BOOL bPurge = TRUE;
|
|
|
|
// we need to purge all the drives in this case
|
|
RegSetValueEx(g_pBitBucket[i]->hkeyPerUser, TEXT("NeedToPurge"), 0, REG_DWORD, (LPBYTE)&bPurge, SIZEOF(bPurge));
|
|
|
|
RefreshBBDriveSettings(i);
|
|
}
|
|
}
|
|
|
|
ppsi->fOriginalUseGlobalSettings = ppsi->fUseGlobalSettings;
|
|
ppsi->fOriginalNukeOnDelete = ppsi->fNukeOnDelete;
|
|
ppsi->iOriginalPercent = ppsi->iPercent;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL_PTR CALLBACK BBDriveDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPBBPROPSHEETINFO ppsi = (LPBBPROPSHEETINFO)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
TCHAR szDiskSpace[40];
|
|
HWND hwndTrack;
|
|
|
|
switch (uMsg) {
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
|
|
|
|
ppsi = (LPBBPROPSHEETINFO)lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
|
|
SendMessage(hwndTrack, TBM_SETTICFREQ, 10, 0);
|
|
SendMessage(hwndTrack, TBM_SETRANGE, FALSE, MAKELONG(0, 100));
|
|
SendMessage(hwndTrack, TBM_SETPOS, TRUE, ppsi->iPercent);
|
|
CheckDlgButton(hDlg, IDC_NUKEONDELETE, ppsi->fNukeOnDelete);
|
|
|
|
// set the disk space info
|
|
StrFormatByteSize64(g_pBitBucket[ppsi->idDrive]->qwDiskSize, szDiskSpace, ARRAYSIZE(szDiskSpace));
|
|
SetDlgItemText(hDlg, IDC_DISKSIZEDATA, szDiskSpace);
|
|
wParam = 0;
|
|
}
|
|
// fall through
|
|
|
|
case WM_HSCROLL:
|
|
{
|
|
ULARGE_INTEGER ulBucketSize;
|
|
HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
|
|
ppsi->iPercent = (int)SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
|
|
|
|
wsprintf(szDiskSpace, TEXT("%d%%"), ppsi->iPercent);
|
|
SetDlgItemText(hDlg, IDC_BBSIZETEXT, szDiskSpace);
|
|
|
|
if (ppsi->iPercent != ppsi->iOriginalPercent) {
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
|
|
}
|
|
|
|
// we peg the max size of the recycle bin to 4 gig
|
|
ulBucketSize.QuadPart = (ppsi->pGlobal->fUseGlobalSettings ? ppsi->pGlobal->iPercent : ppsi->iPercent) * (g_pBitBucket[ppsi->idDrive]->qwDiskSize / 100);
|
|
StrFormatByteSize64(ulBucketSize.HighPart ? (DWORD)-1 : ulBucketSize.LowPart, szDiskSpace, ARRAYSIZE(szDiskSpace));
|
|
SetDlgItemText(hDlg, IDC_BYTESIZEDATA, szDiskSpace);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aBitBucketPropHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU,
|
|
(ULONG_PTR)(LPVOID)aBitBucketPropHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
WORD wCommandID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
|
|
if (wCommandID == IDC_NUKEONDELETE) {
|
|
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
|
|
EnableTrackbarAndFamily(hDlg, !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZE), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZEDATA), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR*)lParam)->code) {
|
|
case PSN_APPLY:
|
|
{
|
|
ppsi->fNukeOnDelete = (IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) == BST_CHECKED) ? TRUE : FALSE;
|
|
|
|
// update the info in the registry
|
|
if (!PersistBBDriveSettings(ppsi->idDrive, ppsi->iPercent, ppsi->fNukeOnDelete)) {
|
|
// we failed, so show the error dialog and bail
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_BB_CANNOTCHANGESETTINGS),
|
|
MAKEINTRESOURCE(IDS_WASTEBASKET),
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
SetDlgMsgResult(hDlg, WM_NOTIFY, PSNRET_INVALID_NOCHANGEPAGE);
|
|
return TRUE;
|
|
}
|
|
|
|
// only purge this drive if the user set the slider to a smaller value
|
|
if (ppsi->iPercent < ppsi->iOriginalPercent) {
|
|
BOOL bPurge = TRUE;
|
|
|
|
// since this drive just shrunk, we need to purge the files in it
|
|
RegSetValueEx(g_pBitBucket[ppsi->idDrive]->hkeyPerUser, TEXT("NeedToPurge"), 0, REG_DWORD, (LPBYTE)&bPurge, SIZEOF(bPurge));
|
|
}
|
|
|
|
ppsi->iOriginalPercent = ppsi->iPercent;
|
|
ppsi->fOriginalNukeOnDelete = ppsi->fNukeOnDelete;
|
|
|
|
// update the g_pBitBucket[] for this drive
|
|
|
|
// NOTE: We get a PSN_APPLY before the global tab does. This has to be this way so that
|
|
// if global settings change, then the global tab will re-apply all the most current settings
|
|
// bassed on the global variables that get set in his tab.
|
|
RefreshBBDriveSettings(ppsi->idDrive);
|
|
}
|
|
break;
|
|
|
|
case PSN_SETACTIVE:
|
|
{
|
|
BOOL fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE);
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), !ppsi->pGlobal->fUseGlobalSettings);
|
|
EnableTrackbarAndFamily(hDlg, !ppsi->pGlobal->fUseGlobalSettings && !fNukeOnDelete);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZE), !fNukeOnDelete);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZEDATA), !fNukeOnDelete);
|
|
|
|
// send this to make sure that the "space reserved" field is accurate when using global settings
|
|
SendMessage(hDlg, WM_HSCROLL, 0, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
// this is the property sheet page for a file/folder in the bitbucket
|
|
|
|
BOOL_PTR CALLBACK BBFilePropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BBFILEPROPINFO* pbbfpi = (BBFILEPROPINFO*)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
TCHAR szTemp[MAX_PATH];
|
|
|
|
switch (uMsg) {
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
LPCITEMIDLIST pidl;
|
|
LPBBDATAENTRYIDA pbbidl;
|
|
HICON hIcon;
|
|
ULONGLONG cbSize;
|
|
|
|
pbbfpi = (BBFILEPROPINFO*)lParam;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
|
|
pidl = pbbfpi->pidl;
|
|
pbbidl = PIDLTODATAENTRYID(pidl);
|
|
|
|
BBGetOriginalPath(pbbidl, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
#ifdef WINNT
|
|
|
|
// We don't allow user to change compression attribute on a deleted file
|
|
// but we do show the current compressed state
|
|
|
|
{
|
|
TCHAR szRoot[_MAX_PATH + 1];
|
|
DWORD dwVolumeFlags = 0;
|
|
DWORD dwFileAttributes;
|
|
TCHAR szFSName[12];
|
|
|
|
|
|
// If file's volume doesn't support compression, don't show
|
|
// "Compressed" checkbox.
|
|
// If compression is supported, show the checkbox and check/uncheck
|
|
// it to indicate compression state of the file.
|
|
// Perform this operation while szTemp contains complete path name.
|
|
|
|
lstrcpy(szRoot, szTemp);
|
|
PathQualify(szRoot);
|
|
PathStripToRoot(szRoot);
|
|
|
|
dwFileAttributes = GetFileAttributes(szTemp);
|
|
|
|
if (GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, &dwVolumeFlags, szFSName, ARRAYSIZE(szFSName)) &&
|
|
(dwFileAttributes != (DWORD)-1)) {
|
|
if (dwVolumeFlags & FS_FILE_COMPRESSION) {
|
|
if (dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
|
|
CheckDlgButton(hDlg, IDD_COMPRESS, 1);
|
|
}
|
|
ShowWindow(GetDlgItem(hDlg, IDD_COMPRESS), SW_SHOW);
|
|
}
|
|
|
|
if (g_bRunOnNT5) {
|
|
// BUGBUG (ccteng) - HACK
|
|
// Before NTFS implements FS_FILE_ENCRYPTION, we check the compression
|
|
// flag instead, which works as long as we also check for g_bRunOnNT5.
|
|
// After we switch to FS_FILE_ENCRYPTION,
|
|
// we can also remove if (g_bRunOnNT5).
|
|
|
|
// if (dwVolumeFlags & FS_FILE_ENCRYPTION)
|
|
if (dwVolumeFlags & FS_FILE_COMPRESSION) {
|
|
if (dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
|
CheckDlgButton(hDlg, IDD_ENCRYPT, 1);
|
|
}
|
|
ShowWindow(GetDlgItem(hDlg, IDD_ENCRYPT), SW_SHOW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
|
|
// Win95 doesn't support compression/encryption
|
|
|
|
#endif
|
|
|
|
PathRemoveExtension(szTemp);
|
|
SetDlgItemText(hDlg, IDD_NAME, PathFindFileName(szTemp));
|
|
|
|
// origin
|
|
PathRemoveFileSpec(szTemp);
|
|
SetDlgItemText(hDlg, IDD_LOCATION, PathFindFileName(szTemp));
|
|
|
|
// Type
|
|
FS_GetTypeName((LPIDFOLDER)pidl, szTemp, ARRAYSIZE(szTemp));
|
|
SetDlgItemText(hDlg, IDD_FILETYPE, szTemp);
|
|
|
|
// Size
|
|
if (FS_IsFolder((LPIDFOLDER)pidl))
|
|
cbSize = pbbidl->bbde.dwSize;
|
|
else
|
|
FS_GetSize(NULL, (LPIDFOLDER)pidl, &cbSize);
|
|
|
|
StrFormatByteSize64(cbSize, szTemp, ARRAYSIZE(szTemp));
|
|
SetDlgItemText(hDlg, IDD_FILESIZE, szTemp);
|
|
|
|
// deleted time
|
|
{
|
|
FILETIME ft = pbbidl->bbde.ft;
|
|
SetDateTimeText(hDlg, IDD_DELETED, &ft);
|
|
}
|
|
|
|
{
|
|
HANDLE hfind;
|
|
WIN32_FIND_DATA fd;
|
|
|
|
BBGetItemPath((LPIDFOLDER)pidl, szTemp);
|
|
|
|
hfind = FindFirstFile(szTemp, &fd);
|
|
if (hfind != INVALID_HANDLE_VALUE) {
|
|
SetDateTimeText(hDlg, IDD_CREATED, &fd.ftCreationTime);
|
|
FindClose(hfind);
|
|
}
|
|
|
|
// file attributes
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
|
CheckDlgButton(hDlg, IDD_READONLY, 1);
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
|
|
CheckDlgButton(hDlg, IDD_ARCHIVE, 1);
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|
|
CheckDlgButton(hDlg, IDD_HIDDEN, 1);
|
|
|
|
// icon
|
|
hIcon = SHGetFileIcon(NULL, szTemp, fd.dwFileAttributes, SHGFI_LARGEICON | SHGFI_USEFILEATTRIBUTES);
|
|
if (hIcon) {
|
|
hIcon = (HICON)SendDlgItemMessage(hDlg, IDD_ITEMICON, STM_SETICON, (WPARAM)hIcon, 0L);
|
|
if (hIcon)
|
|
DestroyIcon(hIcon);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_WININICHANGE:
|
|
case WM_SYSCOLORCHANGE:
|
|
case WM_DISPLAYCHANGE:
|
|
RelayMessageToChildren(hDlg, uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
switch (((NMHDR*)lParam)->code) {
|
|
case PSN_APPLY:
|
|
case PSN_SETACTIVE:
|
|
case PSN_KILLACTIVE:
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aBitBucketHelpIDs);
|
|
return TRUE;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU,
|
|
(ULONG_PTR)(LPVOID)aBitBucketHelpIDs);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void BBGetDriveDisplayName(int idDrive, LPTSTR pszName, UINT cchSize)
|
|
{
|
|
TCHAR szDrive[MAX_PATH];
|
|
SHFILEINFO sfi;
|
|
|
|
VDATEINPUTBUF(pszName, TCHAR, cchSize);
|
|
|
|
DriveIDToBBRoot(idDrive, szDrive);
|
|
|
|
if (SHGetFileInfo(szDrive, 0, &sfi, SIZEOF(sfi), SHGFI_DISPLAYNAME)) {
|
|
lstrcpyn(pszName, sfi.szDisplayName, cchSize);
|
|
}
|
|
|
|
// If SERVERDRIVE, attempt to overwrite the default display name with the display
|
|
// name for the mydocs folder on the desktop, since SERVERDRIVE==mydocs for now
|
|
if (idDrive == SERVERDRIVE) {
|
|
GetMyDocumentsDisplayName(pszName, cchSize);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CALLBACK _BB_AddPage(HPROPSHEETPAGE psp, LPARAM lParam)
|
|
{
|
|
LPPROPSHEETHEADER ppsh = (LPPROPSHEETHEADER)lParam;
|
|
|
|
ppsh->phpage[ppsh->nPages++] = psp;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD CALLBACK _BB_PropertiesThread(IDataObject* pdtobj)
|
|
{
|
|
HPROPSHEETPAGE ahpage[MAXPROPPAGES];
|
|
TCHAR szTitle[80];
|
|
PROPSHEETHEADER psh;
|
|
UNIQUESTUBINFO usi;
|
|
LPITEMIDLIST pidlBitBucket = SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE);
|
|
BOOL fUnique;
|
|
|
|
psh.dwSize = SIZEOF(psh);
|
|
psh.dwFlags = PSH_PROPTITLE;
|
|
psh.hInstance = HINST_THISDLL;
|
|
// psh.hwndParent = NULL; // will be filled in later
|
|
psh.nStartPage = 0;
|
|
psh.phpage = ahpage;
|
|
psh.nPages = 0;
|
|
|
|
if (pdtobj) {
|
|
// this is the recycled file properties case,
|
|
// we only show the proeprties for the first file if
|
|
// there is a multiple selection
|
|
BBFILEPROPINFO bbfpi;
|
|
STGMEDIUM medium;
|
|
LPITEMIDLIST pidlSave;
|
|
TCHAR szTemp[MAX_PATH];
|
|
LPBBDATAENTRYIDA lpbbidl;
|
|
LPIDA pida;
|
|
|
|
pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
pidlSave = ILCombine(pidlBitBucket, IDA_GetIDListPtr(pida, 0));
|
|
fUnique = EnsureUniqueStub(pidlSave, STUBCLASS_PROPSHEET, NULL, &usi);
|
|
ILFree(pidlSave);
|
|
|
|
if (!fUnique) // found other window
|
|
goto Cleanup;
|
|
|
|
bbfpi.psp.dwFlags = 0;
|
|
bbfpi.psp.dwSize = SIZEOF(bbfpi);
|
|
bbfpi.psp.hInstance = HINST_THISDLL;
|
|
bbfpi.psp.pszTemplate = MAKEINTRESOURCE(DLG_DELETEDFILEPROP);
|
|
bbfpi.psp.pfnDlgProc = BBFilePropDlgProc;
|
|
bbfpi.psp.pszTitle = szTitle;
|
|
bbfpi.hida = medium.hGlobal;
|
|
bbfpi.pidl = IDA_GetIDListPtr(pida, 0);
|
|
|
|
lpbbidl = PIDLTODATAENTRYID(bbfpi.pidl);
|
|
|
|
BBGetOriginalPath(lpbbidl, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
lstrcpyn(szTitle, PathFindFileName(szTemp), ARRAYSIZE(szTitle));
|
|
PathRemoveExtension(szTitle);
|
|
|
|
psh.phpage[0] = CreatePropertySheetPage(&bbfpi.psp);
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
|
|
psh.nPages = 1;
|
|
psh.pszCaption = szTitle;
|
|
} else {
|
|
// this is the recycle bin property sheet case
|
|
fUnique = EnsureUniqueStub(pidlBitBucket, STUBCLASS_PROPSHEET, NULL, &usi);
|
|
|
|
if (!fUnique)
|
|
goto Cleanup;
|
|
|
|
CBitBucket_PS_AddPages(NULL, _BB_AddPage, (LPARAM)&psh);
|
|
|
|
psh.pszCaption = MAKEINTRESOURCE(IDS_WASTEBASKET);
|
|
}
|
|
|
|
psh.hwndParent = usi.hwndStub;
|
|
PropertySheet(&psh);
|
|
|
|
Cleanup:
|
|
|
|
if (pidlBitBucket)
|
|
ILFree(pidlBitBucket);
|
|
|
|
FreeUniqueStub(&usi);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef struct _bb_threaddata {
|
|
CBitBucket* pbb;
|
|
HWND hwndOwner;
|
|
IDataObject* pdtobj;
|
|
IStream* pstmDataObj;
|
|
ULONG_PTR idCmd;
|
|
POINT ptDrop;
|
|
BOOL fSameHwnd;
|
|
BOOL fDragDrop;
|
|
} BBTHREADDATA;
|
|
|
|
DWORD WINAPI BB_DropThreadInit(BBTHREADDATA* pbbtd)
|
|
{
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
if (SUCCEEDED(pbbtd->pdtobj->lpVtbl->GetData(pbbtd->pdtobj, &fmte, &medium))) {
|
|
// call delete here so that files will be moved in
|
|
// their respective bins, not necessarily this one.
|
|
DRAGINFO di;
|
|
|
|
di.uSize = SIZEOF(DRAGINFO);
|
|
|
|
if (DragQueryInfo(medium.hGlobal, &di)) {
|
|
// Since BBWillRecycle() can return true even when the file will NOT be
|
|
// recycled (eg the file will be nuked), we want to warn the user when we
|
|
// are going to nuke something that they initiall thought that it would
|
|
// be recycled
|
|
UINT fOptions = SD_WARNONNUKE;
|
|
|
|
if (!BBWillRecycle(di.lpFileList, NULL) ||
|
|
(di.lpFileList && (di.lpFileList[lstrlen(di.lpFileList) + 1] == 0)
|
|
&& PathIsShortcutToProgram(di.lpFileList)))
|
|
fOptions = SD_USERCONFIRMATION;
|
|
|
|
if (IsFileInBitBucket(di.lpFileList)) {
|
|
LPITEMIDLIST* ppidl = NULL;
|
|
int cidl = FS_CreateMoveCopyList(pbbtd->pdtobj, NULL, &ppidl);
|
|
if (ppidl) {
|
|
FS_PositionItems(pbbtd->hwndOwner, cidl, ppidl, pbbtd->pdtobj, &pbbtd->ptDrop, pbbtd->fDragDrop);
|
|
FS_FreeMoveCopyList(ppidl, cidl);
|
|
}
|
|
} else {
|
|
TransferDelete(pbbtd->hwndOwner, medium.hGlobal, fOptions);
|
|
}
|
|
|
|
SHChangeNotifyHandleEvents();
|
|
SHFree(di.lpFileList);
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD CALLBACK _BB_DispatchThreadProc(void* pv)
|
|
{
|
|
BBTHREADDATA* pbbtd = (BBTHREADDATA*)pv;
|
|
|
|
if (pbbtd->pstmDataObj) {
|
|
CoGetInterfaceAndReleaseStream(pbbtd->pstmDataObj, &IID_IDataObject, (void**)&pbbtd->pdtobj);
|
|
pbbtd->pstmDataObj = NULL; // this is dead
|
|
}
|
|
|
|
switch (pbbtd->idCmd) {
|
|
case DFM_CMD_MOVE:
|
|
if (pbbtd->pdtobj)
|
|
BB_DropThreadInit(pbbtd);
|
|
break;
|
|
|
|
case DFM_CMD_PROPERTIES:
|
|
case FSIDM_PROPERTIESBG: // NULL pdtobj is valid
|
|
|
|
_BB_PropertiesThread(pbbtd->pdtobj);
|
|
break;
|
|
|
|
case DFM_CMD_DELETE:
|
|
if (pbbtd->pdtobj)
|
|
BBNukeFileList(pbbtd->pbb, pbbtd->hwndOwner, pbbtd->pdtobj);
|
|
break;
|
|
|
|
case FSIDM_RESTORE:
|
|
if (pbbtd->pdtobj)
|
|
BBRestoreFileList(pbbtd->pbb, pbbtd->hwndOwner, pbbtd->pdtobj);
|
|
break;
|
|
}
|
|
|
|
if (pbbtd->pdtobj)
|
|
pbbtd->pdtobj->lpVtbl->Release(pbbtd->pdtobj);
|
|
|
|
if (pbbtd->pbb)
|
|
pbbtd->pbb->isf.lpVtbl->Release(&pbbtd->pbb->isf);
|
|
|
|
LocalFree((HLOCAL)pbbtd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
HRESULT BB_LaunchThread(CBitBucket* this, HWND hwndOwner, IDataObject* pdtobj, WPARAM idCmd)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
BBTHREADDATA* pbbtd = (BBTHREADDATA*)LocalAlloc(LPTR, SIZEOF(*pbbtd));
|
|
if (pbbtd) {
|
|
pbbtd->pbb = this;
|
|
pbbtd->hwndOwner = hwndOwner;
|
|
pbbtd->idCmd = idCmd;
|
|
|
|
if (idCmd == DFM_CMD_MOVE) {
|
|
pbbtd->fDragDrop = (BOOL)ShellFolderView_GetDropPoint(hwndOwner, &pbbtd->ptDrop);
|
|
}
|
|
|
|
if (this)
|
|
this->isf.lpVtbl->AddRef(&this->isf);
|
|
|
|
if (pdtobj)
|
|
CoMarshalInterThreadInterfaceInStream(&IID_IDataObject, (IUnknown*)pdtobj, &pbbtd->pstmDataObj);
|
|
|
|
if (SHCreateThread(_BB_DispatchThreadProc, pbbtd, CTF_COINIT, NULL)) {
|
|
hr = NOERROR;
|
|
} else {
|
|
if (pbbtd->pstmDataObj)
|
|
pbbtd->pstmDataObj->lpVtbl->Release(pbbtd->pstmDataObj);
|
|
|
|
if (this)
|
|
this->isf.lpVtbl->Release(&this->isf);
|
|
|
|
LocalFree((HLOCAL)pbbtd);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
const TCHAR c_szUnDelete[] = TEXT("undelete");
|
|
const TCHAR c_szPurgeAll[] = TEXT("empty");
|
|
|
|
HRESULT GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode)
|
|
{
|
|
HRESULT hres;
|
|
LPCTSTR pszNameT;
|
|
|
|
switch (idCmd) {
|
|
case FSIDM_RESTORE:
|
|
pszNameT = c_szUnDelete;
|
|
break;
|
|
case FSIDM_PURGEALL:
|
|
pszNameT = c_szPurgeAll;
|
|
break;
|
|
default:
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if (bUnicode)
|
|
hres = SHTCharToUnicode(pszNameT, (LPWSTR)pszName, cchMax);
|
|
else
|
|
hres = SHTCharToAnsi(pszNameT, (LPSTR)pszName, cchMax);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// Callback from DefCM
|
|
|
|
HRESULT CALLBACK CBitBucket_DFMCallBack(IShellFolder* psf, HWND hwndOwner,
|
|
IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
HRESULT hres = NOERROR; // assume no error
|
|
|
|
switch (uMsg) {
|
|
case DFM_MERGECONTEXTMENU:
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_BITBUCKET_ITEM, 0, (LPQCMINFO)lParam);
|
|
hres = ResultFromShort(-1); // return 1 so default reg commands won't be added
|
|
break;
|
|
|
|
case DFM_MAPCOMMANDNAME:
|
|
if (lstrcmpi((LPCTSTR)lParam, c_szUnDelete) == 0) {
|
|
*(int*)wParam = FSIDM_RESTORE;
|
|
} else {
|
|
// command not found
|
|
hres = E_FAIL;
|
|
}
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch (wParam) {
|
|
case FSIDM_RESTORE:
|
|
case DFM_CMD_DELETE:
|
|
case DFM_CMD_PROPERTIES:
|
|
hres = BB_LaunchThread(this, hwndOwner, pdtobj, wParam);
|
|
break;
|
|
|
|
default:
|
|
hres = S_FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
|
|
break;
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
|
|
break;
|
|
|
|
case DFM_GETVERBA:
|
|
case DFM_GETVERBW:
|
|
hres = GetVerb((UINT_PTR)(LOWORD(wParam)), (LPSTR)lParam, (UINT)(HIWORD(wParam)), uMsg == DFM_GETVERBW);
|
|
break;
|
|
|
|
default:
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
|
|
// CBitBucketIDLDropTarget::DragEnter
|
|
|
|
// This function puts DROPEFFECT_LINK in *pdwEffect, only if the data object
|
|
// contains one or more net resource.
|
|
|
|
STDMETHODIMP CBitBucketIDLDropTarget_DragEnter(IDropTarget* pdropt, IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
CIDLDropTarget* this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: CBitBucketIDLDropTarget::DragEnter");
|
|
|
|
// Call the base class first
|
|
CIDLDropTarget_DragEnter(pdropt, pDataObj, grfKeyState, pt, pdwEffect);
|
|
|
|
// we don't really care what is in the data object, as long as move
|
|
// is supported by the source we say you can move it to the wastbasket
|
|
// in the case of files we will do the regular recycle bin stuff, if
|
|
// it is not files we will just say it is moved and let the source delete it
|
|
*pdwEffect &= DROPEFFECT_MOVE;
|
|
|
|
this->dwEffectLastReturned = *pdwEffect;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
// CBitBucketIDLDropTarget::Drop
|
|
|
|
// This function creates a connection to a dropped net resource object.
|
|
|
|
STDMETHODIMP CBitBucketIDLDropTarget_Drop(IDropTarget* pdropt, IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
|
|
{
|
|
CIDLDropTarget* this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
HRESULT hres;
|
|
BOOL fWebFoldersHack;
|
|
|
|
// only move operation is allowed
|
|
*pdwEffect &= DROPEFFECT_MOVE;
|
|
fWebFoldersHack = FALSE;
|
|
|
|
if (*pdwEffect) {
|
|
hres = CIDLDropTarget_DragDropMenu(this, DROPEFFECT_MOVE, pDataObj,
|
|
pt, pdwEffect, NULL, NULL, POPUP_NONDEFAULTDD, grfKeyState);
|
|
|
|
if (hres == S_FALSE) {
|
|
// let callers know where this is about to go
|
|
// Defview cares where it went so it can handle non-filesys items
|
|
// SHScrap cares because it needs to close the file so we can delete it
|
|
DataObj_SetDropTarget(pDataObj, &CLSID_RecycleBin);
|
|
|
|
if (DataObj_GetDWORD(pDataObj, g_cfNotRecyclable, 0)) {
|
|
if (ShellMessageBox(HINST_THISDLL, NULL,
|
|
MAKEINTRESOURCE(IDS_CONFIRMNOTRECYCLABLE),
|
|
MAKEINTRESOURCE(IDS_RECCLEAN_NAMETEXT),
|
|
MB_SETFOREGROUND | MB_ICONQUESTION | MB_YESNO) == IDNO) {
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
goto lCancel;
|
|
}
|
|
}
|
|
|
|
if (this->dwData & DTID_HDROP) // CF_HDROP
|
|
{
|
|
BB_LaunchThread(NULL, this->hwndOwner, pDataObj, DFM_CMD_MOVE);
|
|
|
|
// since we will move the file ourself, known as an optimised move,
|
|
// we return zero here. this is per the OLE spec
|
|
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
} else {
|
|
// if it was not files, we just say we moved the data, letting the
|
|
// source deleted it. lets hope they support undo...
|
|
|
|
*pdwEffect = DROPEFFECT_MOVE;
|
|
|
|
// HACK: Put up a "you can't undo this" warning for web folders.
|
|
{
|
|
LPIDA pida;
|
|
STGMEDIUM stgmed;
|
|
pida = DataObj_GetHIDA(pDataObj, &stgmed);
|
|
if (pida) {
|
|
CLSID clsidSource;
|
|
IPersist* pPers;
|
|
LPCITEMIDLIST pidl;
|
|
|
|
pidl = IDA_GetIDListPtr(pida, -1);
|
|
if (pidl) {
|
|
hres = SHBindToIDListParent(pidl, &IID_IPersist, (void**)&pPers, NULL);
|
|
if (FAILED(hres)) {
|
|
IShellFolder* psf;
|
|
hres = SHBindToObject(NULL, &IID_IShellFolder, pidl, (void**)&psf);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = psf->lpVtbl->QueryInterface(psf, &IID_IPersist, (void**)&pPers);
|
|
psf->lpVtbl->Release(psf);
|
|
}
|
|
}
|
|
if (SUCCEEDED(hres)) {
|
|
hres = pPers->lpVtbl->GetClassID(pPers, &clsidSource);
|
|
if (SUCCEEDED(hres) &&
|
|
IsEqualGUID(&clsidSource, &CLSID_WebFolders)) {
|
|
if (ShellMessageBox(HINST_THISDLL, NULL,
|
|
MAKEINTRESOURCE(IDS_CONFIRMNOTRECYCLABLE),
|
|
MAKEINTRESOURCE(IDS_RECCLEAN_NAMETEXT),
|
|
MB_SETFOREGROUND | MB_ICONQUESTION | MB_YESNO) == IDNO) {
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
pPers->lpVtbl->Release(pPers);
|
|
HIDA_ReleaseStgMedium(pida, &stgmed);
|
|
goto lCancel;
|
|
} else {
|
|
fWebFoldersHack = TRUE;
|
|
}
|
|
}
|
|
pPers->lpVtbl->Release(pPers);
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &stgmed);
|
|
}
|
|
}
|
|
}
|
|
lCancel:
|
|
if (!fWebFoldersHack) {
|
|
DataObj_SetDWORD(pDataObj, g_cfPerformedDropEffect, *pdwEffect);
|
|
DataObj_SetDWORD(pDataObj, g_cfLogicalPerformedDropEffect, DROPEFFECT_MOVE);
|
|
} else {
|
|
// Make web folders really delete its source file.
|
|
DataObj_SetDWORD(pDataObj, g_cfPerformedDropEffect, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
CIDLDropTarget_DragLeave(pdropt);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
const IDropTargetVtbl c_CBBDropTargetVtbl =
|
|
{
|
|
CIDLDropTarget_QueryInterface, CIDLDropTarget_AddRef, CIDLDropTarget_Release,
|
|
CBitBucketIDLDropTarget_DragEnter,
|
|
CIDLDropTarget_DragOver,
|
|
CIDLDropTarget_DragLeave,
|
|
CBitBucketIDLDropTarget_Drop,
|
|
};
|
|
|
|
|
|
void BBInitializeViewWindow(HWND hwndView)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_BITBUCKETS; i++) {
|
|
SHChangeNotifyEntry fsne;
|
|
|
|
fsne.fRecursive = FALSE;
|
|
|
|
// make it if it's there so that we'll get any events
|
|
if (MakeBitBucket(i)) {
|
|
UINT u;
|
|
fsne.pidl = g_pBitBucket[i]->pidl;
|
|
|
|
u = SHChangeNotifyRegister(hwndView, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel, SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
|
|
SHGetPathFromIDList(fsne.pidl, szTemp);
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: SHChangeNotifyRegister returned %d on path %s", u, szTemp);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT BBHandleFSNotify(HWND hwnd, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
TCHAR szPath[MAX_PATH];
|
|
LPTSTR pszFileName;
|
|
|
|
// pidls must be child of drives or network
|
|
// (actually only drives work for right now)
|
|
// that way we won't get duplicate events
|
|
if ((!ILIsParent((LPCITEMIDLIST)&c_idlDrives, pidl1, FALSE) && !ILIsParent((LPCITEMIDLIST)&c_idlNet, pidl1, FALSE)) ||
|
|
(pidl2 && !ILIsParent((LPCITEMIDLIST)&c_idlDrives, pidl2, FALSE) && !ILIsParent((LPCITEMIDLIST)&c_idlNet, pidl2, FALSE))) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
SHGetPathFromIDList(pidl1, szPath);
|
|
pszFileName = PathFindFileName(szPath);
|
|
|
|
if (!lstrcmpi(pszFileName, c_szInfo2) ||
|
|
!lstrcmpi(pszFileName, c_szInfo) ||
|
|
!lstrcmpi(pszFileName, c_szDesktopIni)) {
|
|
// we ignore changes to these files because they mean we were simply doing bookeeping
|
|
// (eg updating the info file, re-creating the desktop.ini, etc)
|
|
return S_FALSE;
|
|
}
|
|
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: BBHandleFSNotify event %x on path %s", lEvent, szPath);
|
|
|
|
|
|
switch (lEvent) {
|
|
case SHCNE_RENAMEFOLDER:
|
|
case SHCNE_RENAMEITEM:
|
|
{
|
|
int idDrive;
|
|
|
|
// if the rename's target is in a bitbucket, then do a create.
|
|
// otherwise, return NOERROR..
|
|
|
|
idDrive = DriveIDFromBBPath(szPath);
|
|
|
|
if (MakeBitBucket(idDrive) && ILIsParent(g_pBitBucket[idDrive]->pidl, pidl1, TRUE)) {
|
|
hres = BBHandleFSNotify(hwnd, (lEvent == SHCNE_RENAMEITEM) ? SHCNE_DELETE : SHCNE_RMDIR, pidl1, NULL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SHCNE_CREATE:
|
|
case SHCNE_MKDIR:
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
|
|
pidl = DeletedFilePathToBBPidl(szPath);
|
|
|
|
if (pidl) {
|
|
ShellFolderView_AddObject(hwnd, pidl);
|
|
hres = S_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SHCNE_DELETE:
|
|
case SHCNE_RMDIR:
|
|
{
|
|
// if this was a delete into the recycle bin, pidl2 will exist
|
|
if (pidl2) {
|
|
hres = BBHandleFSNotify(hwnd, (lEvent == SHCNE_DELETE) ? SHCNE_CREATE : SHCNE_MKDIR, pidl2, NULL);
|
|
} else {
|
|
ShellFolderView_RemoveObject(hwnd, ILFindLastID(pidl1));
|
|
hres = S_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SHCNE_UPDATEDIR:
|
|
{
|
|
// we recieved an updatedir, which means we probably had more than 10 fsnotify events come in,
|
|
// so we just refresh our brains out.
|
|
ShellFolderView_RefreshAll(hwnd);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
// didn't handle this message
|
|
hres = S_FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
void BBSort(HWND hwndOwner, int id)
|
|
{
|
|
switch (id) {
|
|
case FSIDM_SORTBYNAME:
|
|
ShellFolderView_ReArrange(hwndOwner, 0);
|
|
break;
|
|
|
|
case FSIDM_SORTBYORIGIN:
|
|
ShellFolderView_ReArrange(hwndOwner, 1);
|
|
break;
|
|
|
|
case FSIDM_SORTBYDELETEDDATE:
|
|
ShellFolderView_ReArrange(hwndOwner, 2);
|
|
break;
|
|
|
|
case FSIDM_SORTBYTYPE:
|
|
ShellFolderView_ReArrange(hwndOwner, 3);
|
|
break;
|
|
|
|
case FSIDM_SORTBYSIZE:
|
|
ShellFolderView_ReArrange(hwndOwner, 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CALLBACK CBitBucket_DFMCallBackBG(IShellFolder* psf, HWND hwndOwner,
|
|
IDataObject* pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
HRESULT hres = NOERROR;
|
|
|
|
switch (uMsg) {
|
|
case DFM_MERGECONTEXTMENU:
|
|
if (!(wParam & CMF_DVFILE)) //In the case of the file menu
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_BITBUCKET_BACKGROUND, POPUP_BITBUCKET_POPUPMERGE, (LPQCMINFO)lParam);
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch (wParam) {
|
|
case FSIDM_SORTBYNAME:
|
|
case FSIDM_SORTBYORIGIN:
|
|
case FSIDM_SORTBYDELETEDDATE:
|
|
case FSIDM_SORTBYTYPE:
|
|
case FSIDM_SORTBYSIZE:
|
|
BBSort(hwndOwner, (int)wParam);
|
|
break;
|
|
|
|
case FSIDM_PROPERTIESBG:
|
|
hres = BB_LaunchThread(NULL, hwndOwner, NULL, FSIDM_PROPERTIESBG);
|
|
break;
|
|
|
|
case DFM_CMD_PASTE:
|
|
case DFM_CMD_PROPERTIES:
|
|
// GetAttributesOf cidl==0 has SFGAO_HASPROPSHEET,
|
|
// let defcm handle this
|
|
hres = S_FALSE;
|
|
break;
|
|
|
|
|
|
default:
|
|
// GetAttributesOf cidl==0 does not set _CANMOVE, _CANDELETE, etc,
|
|
// BUT accelerator keys will get these unavailable menu items...
|
|
// so we need to return failure here
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SF_CreateViewObject(IShellFolder2* psf, HWND hwnd, REFIID riid, void** ppvOut)
|
|
{
|
|
HRESULT hres;
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
|
|
if (IsEqualIID(riid, &IID_IShellView)) {
|
|
SFV_CREATE sSFV;
|
|
|
|
sSFV.cbSize = sizeof(sSFV);
|
|
sSFV.pshf = (IShellFolder*)psf;
|
|
sSFV.psvOuter = NULL;
|
|
sSFV.psfvcb = BitBucket_CreateSFVCB((IShellFolder*)psf, this);
|
|
|
|
hres = SHCreateShellFolderView(&sSFV, (IShellView**)ppvOut);
|
|
|
|
if (sSFV.psfvcb)
|
|
sSFV.psfvcb->lpVtbl->Release(sSFV.psfvcb);
|
|
|
|
} else if (IsEqualIID(riid, &IID_IDropTarget)) {
|
|
hres = CIDLDropTarget_Create(hwnd, &c_CBBDropTargetVtbl, this->pidl, (IDropTarget**)ppvOut);
|
|
} else if (IsEqualIID(riid, &IID_IContextMenu)) {
|
|
hres = CDefFolderMenu_Create(NULL, hwnd, 0, NULL, (IShellFolder*)psf, CBitBucket_DFMCallBackBG,
|
|
NULL, NULL, (IContextMenu**)ppvOut);
|
|
} else {
|
|
*ppvOut = NULL;
|
|
hres = E_NOINTERFACE;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SF_ParseDisplayName(IShellFolder2* psf, HWND hwnd, LPBC pbc,
|
|
LPOLESTR pwszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes)
|
|
{
|
|
*ppidl = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBBIDLData_QueryGetData(IDataObject* pdtobj, FORMATETC* pformatetc)
|
|
{
|
|
ASSERT(g_cfFileNameMap);
|
|
|
|
if (pformatetc->cfFormat == g_cfFileNameMap && (pformatetc->tymed & TYMED_HGLOBAL)) {
|
|
return NOERROR; // same as S_OK
|
|
}
|
|
return CFSIDLData_QueryGetData(pdtobj, pformatetc);
|
|
}
|
|
|
|
// subclass member function to support CF_HDROP and CF_NETRESOURCE
|
|
// in:
|
|
// hida bitbucket id array
|
|
|
|
// out:
|
|
// HGLOBAL with double NULL terminated string list of destination names
|
|
|
|
|
|
HGLOBAL BuildDestSpecs(LPIDA pida)
|
|
{
|
|
LPCITEMIDLIST pidl;
|
|
LPBBDATAENTRYIDA pbbidl;
|
|
LPTSTR pszRet;
|
|
TCHAR szTemp[MAX_PATH];
|
|
UINT i, cbAlloc = SIZEOF(TCHAR); // for double NULL termination
|
|
|
|
for (i = 0; pidl = IDA_GetIDListPtr(pida, i); i++) {
|
|
pbbidl = PIDLTODATAENTRYID(pidl);
|
|
|
|
BBGetOriginalPath(pbbidl, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
cbAlloc += lstrlen(PathFindFileName(szTemp)) * SIZEOF(TCHAR) + SIZEOF(TCHAR);
|
|
}
|
|
pszRet = LocalAlloc(LPTR, cbAlloc);
|
|
if (pszRet) {
|
|
LPTSTR pszDest = pszRet;
|
|
for (i = 0; pidl = IDA_GetIDListPtr(pida, i); i++) {
|
|
pbbidl = PIDLTODATAENTRYID(pidl);
|
|
BBGetOriginalPath(pbbidl, szTemp, ARRAYSIZE(szTemp));
|
|
lstrcpy(pszDest, PathFindFileName(szTemp));
|
|
pszDest += lstrlen(pszDest) + 1;
|
|
|
|
ASSERT((ULONG_PTR)((LPBYTE)pszDest - (LPBYTE)pszRet) < cbAlloc);
|
|
ASSERT(*(pszDest) == 0); // zero init alloc
|
|
}
|
|
ASSERT((LPTSTR)((LPBYTE)pszRet + cbAlloc - SIZEOF(TCHAR)) == pszDest);
|
|
ASSERT(*pszDest == 0); // zero init alloc
|
|
}
|
|
return pszRet;
|
|
}
|
|
|
|
extern HRESULT CFSIDLData_GetData(IDataObject* pdtobj, FORMATETC* pformatetcIn, STGMEDIUM* pmedium);
|
|
|
|
STDMETHODIMP CBBIDLData_GetData(IDataObject* pdtobj, FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
ASSERT(g_cfFileNameMap);
|
|
|
|
if (pformatetcIn->cfFormat == g_cfFileNameMap && (pformatetcIn->tymed & TYMED_HGLOBAL)) {
|
|
STGMEDIUM medium;
|
|
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (medium.hGlobal) {
|
|
pmedium->hGlobal = BuildDestSpecs(pida);
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->pUnkForRelease = NULL;
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
|
|
hres = pmedium->hGlobal ? NOERROR : E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
hres = CFSIDLData_GetData(pdtobj, pformatetcIn, pmedium);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
const IDataObjectVtbl c_CBBIDLDataVtbl = {
|
|
CIDLData_QueryInterface, CIDLData_AddRef, CIDLData_Release,
|
|
CBBIDLData_GetData,
|
|
CIDLData_GetDataHere,
|
|
CBBIDLData_QueryGetData,
|
|
CIDLData_GetCanonicalFormatEtc,
|
|
CIDLData_SetData,
|
|
CIDLData_EnumFormatEtc,
|
|
CIDLData_Advise,
|
|
CIDLData_Unadvise,
|
|
CIDLData_EnumAdvise
|
|
};
|
|
|
|
|
|
HRESULT _CreateDefExtIcon(LPCIDFOLDER pidf, REFIID riid, void** ppxicon)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
|
|
if (FS_IsFileFolder(pidf)) {
|
|
return SHCreateDefExtIcon(NULL, II_FOLDER, II_FOLDEROPEN, GIL_PERCLASS, riid, ppxicon);
|
|
} else {
|
|
DWORD dwFlags = SHGetClassFlags(pidf);
|
|
if (dwFlags & SHCF_ICON_PERINSTANCE) {
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
BBGetItemPath(pidf, szPath);
|
|
|
|
if (dwFlags & SHCF_HAS_ICONHANDLER) {
|
|
LPITEMIDLIST pidlFull = SHSimpleIDListFromPath(szPath);
|
|
if (pidlFull) {
|
|
// We hit this case for .lnk files in the recycle bin. We used to call FSLoadHandler
|
|
// but guz decided he only wanted the fstree code using that function (and it takes a fs psf
|
|
// nowdays anyway). So we just bind our brains out.
|
|
hres = SHGetUIObjectFromFullPIDL(pidlFull, NULL, riid, ppxicon);
|
|
ILFree(pidlFull);
|
|
} else {
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
DWORD uid = FS_GetUID(pidf);
|
|
|
|
hres = SHCreateDefExtIcon(szPath, uid, uid, GIL_PERINSTANCE | GIL_NOTFILENAME, riid, ppxicon);
|
|
}
|
|
} else {
|
|
UINT iIcon = (dwFlags & SHCF_ICON_INDEX);
|
|
hres = SHCreateDefExtIcon(c_szStar, iIcon, iIcon, GIL_PERCLASS | GIL_NOTFILENAME, riid, ppxicon);
|
|
}
|
|
return hres;
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_GetUIObjectOf(IShellFolder2* psf, HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void** ppvOut)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
HRESULT hres;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
if (cidl && IsEqualIID(riid, &IID_IDataObject)) {
|
|
hres = CIDLData_CreateFromIDArray2(&c_CBBIDLDataVtbl, this->pidl, cidl, apidl, (IDataObject**)ppvOut);
|
|
} else if ((cidl == 1) &&
|
|
(IsEqualIID(riid, &IID_IExtractIconA) || IsEqualIID(riid, &IID_IExtractIconW))) {
|
|
hres = _CreateDefExtIcon(FS_IsValidID(apidl[0]), riid, ppvOut);
|
|
} else if (IsEqualIID(riid, &IID_IContextMenu)) {
|
|
hres = CDefFolderMenu_Create(this->pidl, hwnd, cidl, apidl,
|
|
(IShellFolder*)psf, CBitBucket_DFMCallBack, NULL, NULL, (IContextMenu**)ppvOut);
|
|
} else {
|
|
hres = E_NOTIMPL;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT FindDataFromBBPidl(LPCITEMIDLIST bbpidl, WIN32_FIND_DATAW* pfd)
|
|
{
|
|
LPBBDATAENTRYIDA pbbdei = PIDLTODATAENTRYID(bbpidl);
|
|
LPIDFOLDER pidf = (LPIDFOLDER)bbpidl;
|
|
ULONGLONG cbSize; // 64 bits
|
|
|
|
ZeroMemory(pfd, sizeof(*pfd));
|
|
|
|
if (bbpidl->mkid.cb < sizeof(BBDATAENTRYIDA))
|
|
return E_INVALIDARG;
|
|
|
|
// this code copied from the size column in details view, so webview will match that
|
|
if (FS_IsFolder(pidf)) {
|
|
cbSize = pbbdei->bbde.dwSize; // we cache the size of the folder, rounded to cluster
|
|
} else {
|
|
FS_GetSize(NULL, pidf, &cbSize);
|
|
}
|
|
|
|
pfd->nFileSizeHigh = HIDWORD(cbSize);
|
|
pfd->nFileSizeLow = LODWORD(cbSize);
|
|
pfd->dwFileAttributes = pidf->fs.wAttrs; // right?
|
|
|
|
DosDateTimeToFileTime(pidf->fs.dateModified, pidf->fs.timeModified, &pfd->ftLastWriteTime);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
LPITEMIDLIST BBDataEntryToPidl(int idDrive, LPBBDATAENTRYW pbbde)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
TCHAR chDrive;
|
|
TCHAR szPath[MAX_PATH];
|
|
TCHAR szDeletedPath[MAX_PATH];
|
|
TCHAR szOriginalFileName[MAX_PATH];
|
|
LPITEMIDLIST pidl = NULL;
|
|
LPITEMIDLIST pidlRet = NULL;
|
|
BBDATAENTRYIDW bbpidl;
|
|
void* lpv;
|
|
|
|
if (g_pBitBucket[idDrive]->fIsUnicode) {
|
|
WCHAR szTemp[MAX_PATH];
|
|
|
|
// save off the original filename so we have the extension (needed to construct the delete file name)
|
|
SHUnicodeToTChar(pbbde->szOriginal, szOriginalFileName, ARRAYSIZE(szOriginalFileName));
|
|
|
|
bbpidl.bbde.iIndex = pbbde->iIndex;
|
|
bbpidl.bbde.idDrive = pbbde->idDrive;
|
|
bbpidl.bbde.ft = pbbde->ft;
|
|
bbpidl.bbde.dwSize = pbbde->dwSize;
|
|
|
|
SHAnsiToUnicode(pbbde->szShortName, szTemp, ARRAYSIZE(szTemp));
|
|
|
|
if (StrCmpW(pbbde->szOriginal, szTemp) == 0) {
|
|
// The short and long names match, so we can use an ansi pidl
|
|
bbpidl.cb = SIZEOF(BBDATAENTRYIDA);
|
|
lpv = &bbpidl.cb;
|
|
} else {
|
|
// The short and long names DO NOT match, so create a full
|
|
// blown unicode pidl (both ansi and unicode names)
|
|
bbpidl.cb = SIZEOF(BBDATAENTRYIDW);
|
|
StrCpyW(bbpidl.wszOriginal, pbbde->szOriginal);
|
|
lpv = &bbpidl;
|
|
}
|
|
|
|
bbpidl.bbde = *((LPBBDATAENTRYA)pbbde);
|
|
} else {
|
|
LPBBDATAENTRYA pbbdea = (LPBBDATAENTRYA)pbbde;
|
|
|
|
// save off the original filename so we have the extension (needed to construct the delete file name)
|
|
SHAnsiToTChar(pbbdea->szOriginal, szOriginalFileName, ARRAYSIZE(szOriginalFileName));
|
|
|
|
// just create an ansi pidl
|
|
bbpidl.cb = SIZEOF(BBDATAENTRYIDA);
|
|
bbpidl.bbde = *pbbdea;
|
|
lpv = &bbpidl.cb;
|
|
}
|
|
|
|
chDrive = TEXT('a') + pbbde->idDrive;
|
|
|
|
if (chDrive == (TEXT('a') + SERVERDRIVE)) {
|
|
chDrive = TEXT('@');
|
|
}
|
|
|
|
DriveIDToBBPath(idDrive, szPath);
|
|
lstrcpyn(szDeletedPath, szPath, ARRAYSIZE(szDeletedPath));
|
|
|
|
// create the full path to the delete file so we can get its attributes
|
|
wsprintf(&szDeletedPath[lstrlen(szDeletedPath)], TEXT("\\D%c%d%s"), chDrive, pbbde->iIndex, PathFindExtension(szOriginalFileName));
|
|
|
|
fd.dwFileAttributes = GetFileAttributes(szDeletedPath);
|
|
|
|
if (fd.dwFileAttributes == -1) {
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: unable to get file attributes for path %s , cannot create pidl!!", szDeletedPath);
|
|
return NULL;
|
|
}
|
|
|
|
fd.ftCreationTime = pbbde->ft;
|
|
fd.ftLastAccessTime = pbbde->ft;
|
|
fd.ftLastWriteTime = pbbde->ft;
|
|
fd.nFileSizeHigh = 0;
|
|
fd.nFileSizeLow = pbbde->dwSize;
|
|
fd.dwReserved0 = 0;
|
|
fd.dwReserved1 = 0;
|
|
lstrcpyn(fd.cFileName, PathFindFileName(szDeletedPath), ARRAYSIZE(fd.cFileName));
|
|
fd.cAlternateFileName[0] = TEXT('\0'); // no one uses this anyway...
|
|
|
|
SHCreateFSIDList(szPath, &fd, &pidl);
|
|
|
|
if (pidl) {
|
|
UINT cbSize = ILGetSize(pidl);
|
|
pidlRet = ILResize(pidl, cbSize + bbpidl.cb, 0);
|
|
if (pidlRet) {
|
|
// Append this BBDATAENTRYID (A or W) onto the end
|
|
memcpy(_ILSkip(pidlRet, cbSize - SIZEOF(pidl->mkid.cb)), lpv, bbpidl.cb);
|
|
// And 0 terminate the thing
|
|
_ILSkip(pidlRet, cbSize + bbpidl.cb - SIZEOF(pidl->mkid.cb))->mkid.cb = 0;
|
|
// Now edit it into one larger id
|
|
pidlRet->mkid.cb += bbpidl.cb;
|
|
ASSERT(ILGetSize(pidlRet) == cbSize + bbpidl.cb);
|
|
}
|
|
}
|
|
return pidlRet;
|
|
}
|
|
|
|
|
|
__inline int CALLBACK BBFDCompare(void* p1, void* p2, LPARAM lParam)
|
|
{
|
|
return ((LPBBFINDDATA)p1)->iIndex - ((LPBBFINDDATA)p2)->iIndex;
|
|
}
|
|
|
|
|
|
__inline int CALLBACK BBFDIndexCompare(void* iIndex, void* p2, LPARAM lParam)
|
|
{
|
|
return (int)((INT_PTR)iIndex - ((LPBBFINDDATA)p2)->iIndex);
|
|
}
|
|
|
|
BOOL CALLBACK BBEnumDPADestroyCallback(LPVOID pidl, LPVOID pData)
|
|
{
|
|
ILFree(pidl);
|
|
return TRUE;
|
|
}
|
|
|
|
LPITEMIDLIST BBEnum_GetNextPidl(LPENUMDELETED ped)
|
|
{
|
|
LPITEMIDLIST pidlRet = FALSE;
|
|
|
|
ASSERT(NULL != ped);
|
|
if (NULL == ped->hdpa) {
|
|
// This is the first Next() call - so snapshot the info files:
|
|
if (NULL != (ped->hdpa = DPA_CreateEx(0, NULL))) {
|
|
int iBitBucket;
|
|
int nItem = 0;
|
|
// loop through the bitbucket drives to find an info file
|
|
for (iBitBucket = 0; iBitBucket < MAX_BITBUCKETS; iBitBucket++) {
|
|
if (MakeBitBucket(iBitBucket)) {
|
|
HANDLE hFile;
|
|
int cDeleted = 0;
|
|
// since we are going to start reading this bitbucket, we take the mrsw
|
|
#ifdef BB_USE_MRSW
|
|
MRSW_EnterRead(g_pBitBucket[iBitBucket]->pmrsw);
|
|
#endif // BB_USE_MRSW
|
|
hFile = OpenBBInfoFile(iBitBucket, OPENBBINFO_WRITE, 0);
|
|
|
|
if (INVALID_HANDLE_VALUE != hFile) {
|
|
BBDATAENTRYW bbdew;
|
|
DWORD dwDataEntrySize = g_pBitBucket[iBitBucket]->fIsUnicode ? SIZEOF(BBDATAENTRYW) : SIZEOF(BBDATAENTRYA);
|
|
|
|
while (ReadNextDataEntry(hFile, &bbdew, FALSE, dwDataEntrySize, iBitBucket)) {
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
if (IsDeletedEntry(&bbdew)) {
|
|
cDeleted++;
|
|
} else {
|
|
pidl = BBDataEntryToPidl(iBitBucket, &bbdew);
|
|
}
|
|
|
|
if (pidl) {
|
|
DPA_SetPtr(ped->hdpa, nItem++, pidl);
|
|
}
|
|
}
|
|
|
|
if (cDeleted > BB_DELETED_ENTRY_MAX) {
|
|
BOOL bTrue = TRUE;
|
|
|
|
// set the registry key so that we will compact the info file after the next delete operation
|
|
RegSetValueEx(g_pBitBucket[iBitBucket]->hkeyPerUser, TEXT("NeedToCompact"), 0, REG_DWORD, (LPBYTE)&bTrue, SIZEOF(bTrue));
|
|
}
|
|
CloseBBInfoFile(hFile, iBitBucket);
|
|
}
|
|
#ifdef BB_USE_MRSW
|
|
MRSW_LeaveRead(g_pBitBucket[iBitBucket]->pmrsw);
|
|
#endif // BB_USE_MRSW
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != ped->hdpa) {
|
|
pidlRet = DPA_GetPtr(ped->hdpa, ped->nItem);
|
|
if (NULL != pidlRet) {
|
|
// We're returning an allocated pidl, so replace the pointer
|
|
// in the DPA with NULL so that we don't free it later:
|
|
DPA_SetPtr(ped->hdpa, ped->nItem, NULL);
|
|
} else {
|
|
// We've reached the end, so destroy our snapshot:
|
|
DPA_DestroyCallback(ped->hdpa, BBEnumDPADestroyCallback, NULL);
|
|
ped->hdpa = NULL;
|
|
}
|
|
ped->nItem++;
|
|
}
|
|
|
|
return pidlRet;
|
|
}
|
|
|
|
|
|
// To be called back from within SHCreateEnumObjects
|
|
|
|
HRESULT CALLBACK CBitBucket_EnumCallBack(LPARAM lParam, void* pvData, UINT ecid, UINT index)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
ENUMDELETED* ped = (ENUMDELETED*)pvData;
|
|
|
|
switch (ecid) {
|
|
case ECID_SETNEXTID:
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
|
|
if (!(ped->grfFlags & SHCONTF_NONFOLDERS))
|
|
return S_FALSE; // "no more element"
|
|
|
|
pidl = BBEnum_GetNextPidl(ped);
|
|
|
|
if (!pidl) {
|
|
hres = S_FALSE; // "no more element"
|
|
} else {
|
|
CDefEnum_SetReturn(lParam, pidl);
|
|
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: EnumCallBack, returns %S", PIDLTODATAENTRYID(pidl)->bbde.szOriginal);
|
|
|
|
//hres = NOERROR; // in success
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ECID_RESET:
|
|
if (NULL != ped->hdpa) {
|
|
DPA_DestroyCallback(ped->hdpa, BBEnumDPADestroyCallback, NULL);
|
|
ped->hdpa = NULL;
|
|
}
|
|
ped->nItem = 0;
|
|
break;
|
|
|
|
case ECID_RELEASE:
|
|
if (NULL != ped->hdpa) {
|
|
DPA_DestroyCallback(ped->hdpa, BBEnumDPADestroyCallback, NULL);
|
|
}
|
|
|
|
LocalFree((HLOCAL)ped);
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SF_EnumObjects(IShellFolder2* psf, HWND hwndOwner,
|
|
DWORD grfFlags, LPENUMIDLIST* ppenumUnknown)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
ENUMDELETED* ped = (void*)LocalAlloc(LPTR, SIZEOF(ENUMDELETED));
|
|
if (ped) {
|
|
ped->grfFlags = grfFlags;
|
|
ped->pbb = this;
|
|
return SHCreateEnumObjects(hwndOwner, ped, CBitBucket_EnumCallBack, ppenumUnknown);
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_BindToObject(IShellFolder2* psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppvOut)
|
|
{
|
|
*ppvOut = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_BindToStorage(IShellFolder2* psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void** ppvOut)
|
|
{
|
|
*ppvOut = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
// get the path name of the file in the file system
|
|
void BBGetItemPath(LPCIDFOLDER pidf, LPTSTR pszPath)
|
|
{
|
|
TCHAR szFile[MAX_PATH];
|
|
LPBBDATAENTRYIDA pbbidl = PIDLTODATAENTRYID(pidf);
|
|
|
|
DriveIDToBBPath(pbbidl->bbde.idDrive, pszPath);
|
|
PathAppend(pszPath, FS_CopyName(pidf, szFile, ARRAYSIZE(szFile)));
|
|
}
|
|
|
|
|
|
// get the friendly looking name of this file
|
|
void BBGetDisplayName(LPCIDFOLDER pidf, LPTSTR pszName)
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
LPBBDATAENTRYIDA pbbid = PIDLTODATAENTRYID(pidf);
|
|
|
|
BBGetOriginalPath(pbbid, szTemp, ARRAYSIZE(szTemp));
|
|
lstrcpy(pszName, PathFindFileName(szTemp));
|
|
|
|
if (!FS_ShowExtension(pidf))
|
|
PathRemoveExtension(pszName);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SF_GetDisplayNameOf(IShellFolder2* psf, LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET pStrRet)
|
|
{
|
|
LPCIDFOLDER pidf;
|
|
|
|
pStrRet->uType = STRRET_CSTR;
|
|
pStrRet->cStr[0] = 0;
|
|
|
|
pidf = FS_IsValidID(pidl);
|
|
if (pidf) {
|
|
TCHAR szName[MAX_PATH];
|
|
|
|
if (uFlags & SHGDN_FORPARSING)
|
|
BBGetItemPath(pidf, szName);
|
|
else
|
|
BBGetDisplayName(pidf, szName);
|
|
|
|
return StringToStrRet(szName, pStrRet);
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SetNameOf(IShellFolder2* psf, HWND hwnd,
|
|
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwReserved, LPITEMIDLIST* ppidlOut)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_EnumSearches(IShellFolder2* psf, LPENUMEXTRASEARCH* ppenum)
|
|
{
|
|
*ppenum = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_GetDefaultColumn(IShellFolder2* psf, DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_GetDefaultColumnState(IShellFolder2* psf, UINT iColumn, DWORD* pbState)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_GetDetailsEx(IShellFolder2* psf, LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
HRESULT hres = E_NOTIMPL;
|
|
if (IsEqualSCID(*pscid, SCID_FINDDATA)) {
|
|
WIN32_FIND_DATAW wfd;
|
|
hres = FindDataFromBBPidl(pidl, &wfd);
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
hres = InitVariantFromBuffer(pv, (PVOID)&wfd, sizeof(wfd));
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_GetDetailsOf(IShellFolder2* psf, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isf, psf);
|
|
HRESULT hres = NOERROR;
|
|
|
|
if (iColumn >= ARRAYSIZE(c_bb_cols))
|
|
return E_NOTIMPL;
|
|
|
|
pDetails->str.uType = STRRET_CSTR;
|
|
pDetails->str.cStr[0] = 0;
|
|
|
|
if (!pidl) {
|
|
// getting the headers
|
|
hres = ResToStrRet(c_bb_cols[iColumn].ids, &pDetails->str);
|
|
pDetails->fmt = c_bb_cols[iColumn].iFmt;
|
|
pDetails->cxChar = c_bb_cols[iColumn].cchCol;
|
|
} else {
|
|
TCHAR szTemp[MAX_PATH];
|
|
LPCIDFOLDER pidf = FS_IsValidID(pidl);
|
|
UNALIGNED BBDATAENTRYIDA* pbbidl = PIDLTODATAENTRYID(pidl);
|
|
|
|
switch (iColumn) {
|
|
case ICOL_NAME:
|
|
BBGetDisplayName(pidf, szTemp);
|
|
hres = StringToStrRet(szTemp, &pDetails->str);
|
|
break;
|
|
|
|
case ICOL_SIZE:
|
|
{
|
|
ULONGLONG cbSize;
|
|
|
|
if (FS_IsFolder(pidf))
|
|
cbSize = pbbidl->bbde.dwSize;
|
|
else
|
|
FS_GetSize(NULL, pidf, &cbSize);
|
|
|
|
StrFormatKBSize(cbSize, szTemp, ARRAYSIZE(szTemp));
|
|
hres = StringToStrRet(szTemp, &pDetails->str);
|
|
}
|
|
break;
|
|
|
|
case ICOL_ORIGINAL:
|
|
BBGetOriginalPath(pbbidl, szTemp, ARRAYSIZE(szTemp));
|
|
PathRemoveFileSpec(szTemp);
|
|
hres = StringToStrRet(szTemp, &pDetails->str);
|
|
break;
|
|
|
|
case ICOL_TYPE:
|
|
FS_GetTypeName(pidf, szTemp, ARRAYSIZE(szTemp));
|
|
hres = StringToStrRet(szTemp, &pDetails->str);
|
|
break;
|
|
|
|
case ICOL_MODIFIED:
|
|
{
|
|
// need stack ft since pbbidl is UNALIGNED
|
|
FILETIME ft = pbbidl->bbde.ft;
|
|
DWORD dwFlags = FDTF_DEFAULT;
|
|
|
|
switch (pDetails->fmt) {
|
|
case LVCFMT_LEFT_TO_RIGHT:
|
|
dwFlags |= FDTF_LTRDATE;
|
|
break;
|
|
|
|
case LVCFMT_RIGHT_TO_LEFT:
|
|
dwFlags |= FDTF_RTLDATE;
|
|
break;
|
|
}
|
|
|
|
SHFormatDateTime(&ft, &dwFlags, szTemp, ARRAYSIZE(szTemp));
|
|
hres = StringToStrRet(szTemp, &pDetails->str);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_MapColumnToSCID(IShellFolder2* psf, UINT iColumn, SHCOLUMNID* pscid)
|
|
{
|
|
return MapColumnToSCIDImpl(c_bb_cols, ARRAYSIZE(c_bb_cols), iColumn, pscid);
|
|
}
|
|
|
|
|
|
IShellFolder2Vtbl c_CBitBucketSFVtbl =
|
|
{
|
|
CBitBucket_SF_QueryInterface, CBitBucket_SF_AddRef, CBitBucket_SF_Release,
|
|
CBitBucket_SF_ParseDisplayName,
|
|
CBitBucket_SF_EnumObjects,
|
|
CBitBucket_BindToObject,
|
|
CBitBucket_BindToStorage,
|
|
CBitBucket_SF_CompareIDs,
|
|
CBitBucket_SF_CreateViewObject,
|
|
CBitBucket_SF_GetAttributesOf,
|
|
CBitBucket_GetUIObjectOf,
|
|
CBitBucket_SF_GetDisplayNameOf,
|
|
CBitBucket_SetNameOf,
|
|
FindFileOrFolders_GetDefaultSearchGUID,
|
|
CBitBucket_EnumSearches,
|
|
CBitBucket_GetDefaultColumn,
|
|
CBitBucket_GetDefaultColumnState,
|
|
CBitBucket_GetDetailsEx,
|
|
CBitBucket_GetDetailsOf,
|
|
CBitBucket_MapColumnToSCID,
|
|
};
|
|
|
|
|
|
// CBitBucket's PersistFile members
|
|
|
|
|
|
STDMETHODIMP CBitBucket_PF_QueryInterface(IPersistFolder2* ppf, REFIID riid, void** ppvObj)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ipf, ppf);
|
|
return CBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_PF_Release(IPersistFolder2* ppf)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ipf, ppf);
|
|
return CBitBucket_SF_Release(&this->isf);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_PF_AddRef(IPersistFolder2* ppf)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ipf, ppf);
|
|
return CBitBucket_SF_AddRef(&this->isf);
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_PF_GetClassID(IPersistFolder2* ppf, LPCLSID lpClassID)
|
|
{
|
|
*lpClassID = CLSID_RecycleBin;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_PF_Initialize(IPersistFolder2* ppf, LPCITEMIDLIST pidl)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ipf, ppf);
|
|
ASSERT(this->pidl == NULL);
|
|
this->pidl = ILClone(pidl);
|
|
return this->pidl ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_PF_GetCurFolder(IPersistFolder2* ppf, LPITEMIDLIST* ppidl)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ipf, ppf);
|
|
return GetCurFolderImpl(this->pidl, ppidl);
|
|
}
|
|
|
|
IPersistFolder2Vtbl c_CBitBucketPFVtbl =
|
|
{
|
|
CBitBucket_PF_QueryInterface, CBitBucket_PF_AddRef, CBitBucket_PF_Release,
|
|
CBitBucket_PF_GetClassID,
|
|
CBitBucket_PF_Initialize,
|
|
CBitBucket_PF_GetCurFolder
|
|
};
|
|
|
|
|
|
STDMETHODIMP CBitBucket_SEI_QueryInterface(IShellExtInit* psei, REFIID riid, void** ppvObj)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isei, psei);
|
|
return CBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_SEI_Release(IShellExtInit* psei)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isei, psei);
|
|
return CBitBucket_SF_Release(&this->isf);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_SEI_AddRef(IShellExtInit* psei)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, isei, psei);
|
|
return CBitBucket_SF_AddRef(&this->isf);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_SEI_Initialize(IShellExtInit* psei,
|
|
LPCITEMIDLIST pidlFolder,
|
|
IDataObject* pdtobj, HKEY hkeyProgID)
|
|
{
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CBitBucket_CM_QueryInterface(IContextMenu* pcm, REFIID riid, void** ppvObj)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, icm, pcm);
|
|
return CBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_CM_Release(IContextMenu* pcm)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, icm, pcm);
|
|
return CBitBucket_SF_Release(&this->isf);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_CM_AddRef(IContextMenu* pcm)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, icm, pcm);
|
|
return CBitBucket_SF_AddRef(&this->isf);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CBitBucket_QueryContextMenu(IContextMenu* pcm,
|
|
HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast,
|
|
UINT uFlags)
|
|
{
|
|
int idMax = idCmdFirst;
|
|
HMENU hmMerge = SHLoadPopupMenu(HINST_THISDLL, POPUP_BITBUCKET_POPUPMERGE);
|
|
|
|
if (hmMerge) {
|
|
idMax = Shell_MergeMenus(hmenu, hmMerge, indexMenu, idCmdFirst, idCmdLast, 0);
|
|
|
|
if (IsRecycleBinEmpty()) {
|
|
EnableMenuItem(hmenu, idCmdFirst + FSIDM_PURGEALL, MF_GRAYED | MF_BYCOMMAND);
|
|
}
|
|
|
|
DestroyMenu(hmMerge);
|
|
}
|
|
|
|
return ResultFromShort(idMax - idCmdFirst);
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_InvokeCommand(IContextMenu* pcm,
|
|
LPCMINVOKECOMMANDINFO pici)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, icm, pcm);
|
|
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: BitBucket_invokeCommand %d %d", pici->lpVerb, FSIDM_PURGEALL);
|
|
|
|
switch ((ULONG_PTR)pici->lpVerb) {
|
|
case FSIDM_PURGEALL:
|
|
BBPurgeAll(this, pici->hwnd, 0);
|
|
break;
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_GetCommandString(IContextMenu* pcm,
|
|
UINT_PTR idCmd, UINT wFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax)
|
|
{
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: GetCommandString, idCmd = %d", idCmd);
|
|
|
|
switch (wFlags) {
|
|
case GCS_VERBA:
|
|
case GCS_VERBW:
|
|
return GetVerb(idCmd, pszName, cchMax, wFlags == GCS_VERBW);
|
|
|
|
case GCS_HELPTEXTA:
|
|
return LoadStringA(HINST_THISDLL,
|
|
(UINT)(idCmd + IDS_MH_FSIDM_FIRST),
|
|
pszName, cchMax) ? NOERROR : E_OUTOFMEMORY;
|
|
case GCS_HELPTEXTW:
|
|
return LoadStringW(HINST_THISDLL,
|
|
(UINT)(idCmd + IDS_MH_FSIDM_FIRST),
|
|
(LPWSTR)pszName, cchMax) ? NOERROR : E_OUTOFMEMORY;
|
|
default:
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_PS_QueryInterface(LPSHELLPROPSHEETEXT pps, REFIID riid,
|
|
void** ppvObj)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ips, pps);
|
|
return CBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_PS_Release(LPSHELLPROPSHEETEXT pps)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ips, pps);
|
|
return CBitBucket_SF_Release(&this->isf);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CBitBucket_PS_AddRef(LPSHELLPROPSHEETEXT pps)
|
|
{
|
|
CBitBucket* this = IToClass(CBitBucket, ips, pps);
|
|
return CBitBucket_SF_AddRef(&this->isf);
|
|
}
|
|
|
|
|
|
// Callback function that saves the location of the HPROPSHEETPAGE's
|
|
// LPPROPSHEETPAGE so we can pass it to other propsheet pages.
|
|
|
|
UINT CALLBACK BBGlobalSettingsCalback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
|
|
{
|
|
LPBBPROPSHEETINFO ppsiTemplate;
|
|
LPBBPROPSHEETINFO ppsiGlobal;
|
|
|
|
switch (uMsg) {
|
|
case PSPCB_ADDREF:
|
|
// we save off the address of the "real" ppsi in the pGlobal param of the
|
|
// the template, so that the other drives can get to the global page information
|
|
ppsiGlobal = (LPBBPROPSHEETINFO)ppsp;
|
|
ppsiTemplate = (LPBBPROPSHEETINFO)ppsp->lParam;
|
|
ppsiTemplate->pGlobal = ppsiGlobal;
|
|
ppsiGlobal->pGlobal = ppsiGlobal;
|
|
break;
|
|
|
|
case PSPCB_CREATE:
|
|
return TRUE; // Yes, please create me
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP CBitBucket_PS_AddPages(IShellPropSheetExt* pspx,
|
|
LPFNADDPROPSHEETPAGE lpfnAddPage,
|
|
LPARAM lParam)
|
|
{
|
|
HPROPSHEETPAGE hpage;
|
|
int idDrive;
|
|
int iPage;
|
|
BBPROPSHEETINFO bbpsp;
|
|
TCHAR szTitle[MAX_PATH];
|
|
DWORD dwSize1, dwSize2, dwSize3;
|
|
|
|
// read in the global settings
|
|
dwSize1 = SIZEOF(bbpsp.fUseGlobalSettings);
|
|
dwSize2 = SIZEOF(bbpsp.iOriginalPercent);
|
|
dwSize3 = SIZEOF(bbpsp.fOriginalNukeOnDelete);
|
|
if (RegQueryValueEx(g_hkBitBucket, TEXT("UseGlobalSettings"), NULL, NULL, (LPBYTE)&bbpsp.fOriginalUseGlobalSettings, &dwSize1) != ERROR_SUCCESS ||
|
|
RegQueryValueEx(g_hkBitBucket, TEXT("Percent"), NULL, NULL, (LPBYTE)&bbpsp.iOriginalPercent, &dwSize2) != ERROR_SUCCESS ||
|
|
RegQueryValueEx(g_hkBitBucket, TEXT("NukeOnDelete"), NULL, NULL, (LPBYTE)&bbpsp.fOriginalNukeOnDelete, &dwSize3) != ERROR_SUCCESS) {
|
|
ASSERTMSG(FALSE, "Bitbucket: could not read global settings from the registry, re-regsvr32 shell32.dll!!");
|
|
bbpsp.fUseGlobalSettings = TRUE;
|
|
bbpsp.iOriginalPercent = 10;
|
|
bbpsp.fOriginalNukeOnDelete = FALSE;
|
|
}
|
|
|
|
bbpsp.fUseGlobalSettings = bbpsp.fOriginalUseGlobalSettings;
|
|
bbpsp.fNukeOnDelete = bbpsp.fOriginalNukeOnDelete;
|
|
bbpsp.iPercent = bbpsp.iOriginalPercent;
|
|
|
|
bbpsp.psp.dwSize = SIZEOF(bbpsp);
|
|
bbpsp.psp.dwFlags = PSP_DEFAULT | PSP_USECALLBACK;
|
|
bbpsp.psp.hInstance = HINST_THISDLL;
|
|
bbpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_BITBUCKET_GENCONFIG);
|
|
bbpsp.psp.pfnDlgProc = BBGlobalPropDlgProc;
|
|
bbpsp.psp.lParam = (LPARAM)&bbpsp;
|
|
// the callback will fill the bbpsp.pGlobal with the pointer to the "real" psp after it has been copied
|
|
// so that the other drive pages can get to the global information
|
|
bbpsp.psp.pfnCallback = BBGlobalSettingsCalback;
|
|
|
|
// add the "Global" settings page
|
|
hpage = CreatePropertySheetPage(&bbpsp.psp);
|
|
|
|
#ifdef UNICODE
|
|
// If this assertion fires, it means that comctl32 lost
|
|
// backwards-compatibility with Win95 shell, WinNT4 shell,
|
|
// and IE4 shell, all of which relied on this undocumented
|
|
// behavior.
|
|
ASSERT(bbpsp.pGlobal == (LPBBPROPSHEETINFO)((LPBYTE)hpage + 2 * sizeof(LPVOID)));
|
|
#else
|
|
ASSERT(bbpsp.pGlobal == (LPBBPROPSHEETINFO)hpage);
|
|
#endif
|
|
|
|
lpfnAddPage(hpage, lParam);
|
|
|
|
// now create the pages for the individual drives
|
|
bbpsp.psp.dwFlags = PSP_USETITLE;
|
|
bbpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_BITBUCKET_CONFIG);
|
|
bbpsp.psp.pfnDlgProc = BBDriveDlgProc;
|
|
bbpsp.psp.pszTitle = szTitle;
|
|
|
|
for (idDrive = 0, iPage = 1; (idDrive < MAX_BITBUCKETS) && (iPage < MAXPROPPAGES); idDrive++) {
|
|
if (MakeBitBucket(idDrive)) {
|
|
dwSize1 = SIZEOF(bbpsp.iOriginalPercent);
|
|
dwSize2 = SIZEOF(bbpsp.fOriginalNukeOnDelete);
|
|
if (RegQueryValueEx(g_pBitBucket[idDrive]->hkey, TEXT("Percent"), NULL, NULL, (LPBYTE)&bbpsp.iOriginalPercent, &dwSize1) != ERROR_SUCCESS ||
|
|
RegQueryValueEx(g_pBitBucket[idDrive]->hkey, TEXT("NukeOnDelete"), NULL, NULL, (LPBYTE)&bbpsp.fOriginalNukeOnDelete, &dwSize2) != ERROR_SUCCESS) {
|
|
TraceMsg(TF_BITBUCKET, "Bitbucket: could not read settings from the registry for drive %d, using lame defaults", idDrive);
|
|
bbpsp.iOriginalPercent = 10;
|
|
bbpsp.fNukeOnDelete = FALSE;
|
|
}
|
|
|
|
bbpsp.iPercent = bbpsp.iOriginalPercent;
|
|
bbpsp.fNukeOnDelete = bbpsp.fOriginalNukeOnDelete;
|
|
|
|
bbpsp.idDrive = idDrive;
|
|
|
|
BBGetDriveDisplayName(idDrive, szTitle, ARRAYSIZE(szTitle));
|
|
hpage = CreatePropertySheetPage(&bbpsp.psp);
|
|
lpfnAddPage(hpage, lParam);
|
|
}
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
IContextMenuVtbl c_CBitBucketCMVtbl =
|
|
{
|
|
CBitBucket_CM_QueryInterface,
|
|
CBitBucket_CM_AddRef,
|
|
CBitBucket_CM_Release,
|
|
CBitBucket_QueryContextMenu,
|
|
CBitBucket_InvokeCommand,
|
|
CBitBucket_GetCommandString
|
|
};
|
|
|
|
IShellPropSheetExtVtbl c_CBitBucketPSVtbl =
|
|
{
|
|
CBitBucket_PS_QueryInterface,
|
|
CBitBucket_PS_AddRef,
|
|
CBitBucket_PS_Release,
|
|
CBitBucket_PS_AddPages,
|
|
CCommonShellPropSheetExt_ReplacePage,
|
|
};
|
|
|
|
IShellExtInitVtbl c_CBitBucketSEIVtbl =
|
|
{
|
|
CBitBucket_SEI_QueryInterface,
|
|
CBitBucket_SEI_AddRef,
|
|
CBitBucket_SEI_Release,
|
|
CBitBucket_SEI_Initialize
|
|
};
|
|
|
|
HRESULT CBitBucket_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppvOut)
|
|
{
|
|
HRESULT hres;
|
|
CBitBucket* pbb = (void*)LocalAlloc(LPTR, SIZEOF(CBitBucket));
|
|
if (pbb && InitBBGlobals()) {
|
|
pbb->isf.lpVtbl = &c_CBitBucketSFVtbl;
|
|
pbb->ipf.lpVtbl = &c_CBitBucketPFVtbl;
|
|
pbb->icm.lpVtbl = &c_CBitBucketCMVtbl;
|
|
pbb->isei.lpVtbl = &c_CBitBucketSEIVtbl;
|
|
pbb->ips.lpVtbl = &c_CBitBucketPSVtbl;
|
|
pbb->cRef = 1;
|
|
hres = CBitBucket_SF_QueryInterface(&pbb->isf, riid, ppvOut);
|
|
CBitBucket_SF_Release(&pbb->isf);
|
|
} else {
|
|
*ppvOut = NULL;
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
// takes a full path to a file in a bucket and creates a pidl for it.
|
|
|
|
LPITEMIDLIST DeletedFilePathToBBPidl(LPTSTR pszPath)
|
|
{
|
|
BBDATAENTRYW bbdew;
|
|
|
|
LPITEMIDLIST pidl = NULL;
|
|
int idDrive = DriveIDFromBBPath(pszPath);
|
|
int iIndex;
|
|
DWORD dwDataEntrySize;
|
|
HANDLE hFile;
|
|
|
|
ASSERT(idDrive >= 0); // general UNC case will generate -1
|
|
|
|
iIndex = BBPathToIndex(pszPath);
|
|
|
|
if (iIndex == -1)
|
|
return NULL;
|
|
|
|
dwDataEntrySize = g_pBitBucket[idDrive]->fIsUnicode ? SIZEOF(BBDATAENTRYW) : SIZEOF(BBDATAENTRYA);
|
|
|
|
#ifdef BB_USE_MRSW
|
|
MRSW_EnterRead(g_pBitBucket[idDrive]->pmrsw);
|
|
#endif // BB_USE_MRSW
|
|
|
|
hFile = OpenBBInfoFile(idDrive, OPENBBINFO_WRITE, 0);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
// read records (skipping deleted)
|
|
// until we find an index match
|
|
while (ReadNextDataEntry(hFile, &bbdew, TRUE, dwDataEntrySize, idDrive)) {
|
|
if (bbdew.iIndex == iIndex) {
|
|
pidl = BBDataEntryToPidl(idDrive, &bbdew);
|
|
break;
|
|
}
|
|
}
|
|
CloseBBInfoFile(hFile, idDrive);
|
|
}
|
|
|
|
#ifdef BB_USE_MRSW
|
|
MRSW_LeaveRead(g_pBitBucket[idDrive]->pmrsw);
|
|
#endif // BB_USE_MRSW
|
|
|
|
return pidl;
|
|
}
|
|
|
|
|
|
BOOL_PTR CALLBACK NewDiskFullDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wMsg) {
|
|
case WM_INITDIALOG:
|
|
{
|
|
int idDrive = (int)lParam;
|
|
TCHAR szNewText[MAX_PATH];
|
|
TCHAR szText[MAX_PATH];
|
|
|
|
GetDlgItemText(hDlg, IDD_TEXT, szText, ARRAYSIZE(szText));
|
|
wsprintf(szNewText, szText, TEXT('A') + idDrive);
|
|
SetDlgItemText(hDlg, IDD_TEXT, szNewText);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
|
case IDC_DISKFULL_CLEANUP:
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void WINAPI HandleDiskFull(HWND hwnd, int idDrive)
|
|
{
|
|
INT_PTR ret;
|
|
if ((idDrive >= 0) && (idDrive < MAX_DRIVES)) {
|
|
if (IsBitBucketableDrive(idDrive)) {
|
|
ret = DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DISKFULL_NEW),
|
|
hwnd, NewDiskFullDlgProc, (LPARAM)idDrive);
|
|
switch (ret) {
|
|
case IDC_DISKFULL_CLEANUP:
|
|
LaunchDiskCleanup(hwnd, idDrive);
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
STDAPI_(void) SHHandleDiskFull(HWND hwnd, int idDrive)
|
|
{
|
|
// We will only do anything if noone has created the following named event
|
|
HANDLE hDisable = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("DisableLowDiskWarning"));
|
|
if (!hDisable) {
|
|
if (GetDiskCleanupPath(NULL, 0) && IsBitBucketableDrive(idDrive)) {
|
|
HandleDiskFull(hwnd, idDrive);
|
|
}
|
|
} else {
|
|
CloseHandle(hDisable);
|
|
}
|
|
}
|