Windows2000/private/shell/shell32/fstree.cpp
2020-09-30 17:12:32 +02:00

1275 lines
37 KiB
C++

#include "shellprv.h"
extern "C" {
#include <shellp.h>
#include <shguidp.h>
#include "idlcomm.h"
#include "pidl.h"
#include "fstreex.h"
#include "views.h"
#include "ids.h"
#include "shitemid.h"
#include "brfcasep.h"
};
#include "recdocs.h"
#include "sfviewp.h"
#include "brfcase.h"
#include "datautil.h"
void _UpdateDiskFreeSpace(FSSELCHANGEINFO *pfssci)
{
char szPath[10];
ULARGE_INTEGER qwTotalFreeCaller, qwDontCare1, qwDontCare2;
PathBuildRootA(szPath, pfssci->idDrive);
if (SHGetDiskFreeSpaceExA(szPath, &qwTotalFreeCaller, &qwDontCare1, &qwDontCare2))
pfssci->cbFree = qwTotalFreeCaller.QuadPart;
}
BOOL IsExplorerModeBrowser(IUnknown *psite)
{
BOOL bRes = FALSE;
IShellBrowser *psb;
if (SUCCEEDED(IUnknown_QueryService(psite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)))) {
HWND hwndTree;
bRes = SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree;
psb->Release();
}
return bRes;
}
void FSShowNoSelectionState(IUnknown *psite, FSSELCHANGEINFO *pfssci)
{
TCHAR szTemp[30], szTempHidden[30], szFreeSpace[30];
UINT ids = IDS_FSSTATUSBASE;
// Assume we don't need freespace info
szFreeSpace[0] = TEXT('\0');
// See if we need the freespace info (idDrive != -1)
ULONGLONG cbFree = -1;
if (pfssci->idDrive != -1 && IsExplorerModeBrowser(psite)) {
if (pfssci->cbFree == -1)
_UpdateDiskFreeSpace(pfssci);
// cbFree couldstill be -1 if GetDiskFreeSpace didn't get any info
cbFree = pfssci->cbFree;
if (cbFree != -1) {
ShortSizeFormat64(pfssci->cbFree, szFreeSpace);
ids += DIDS_FSSPACE; // Also show freespace
}
}
// If there are hidden files, then show "and nn hidden".
if (pfssci->cHiddenFiles)
ids += DIDS_FSHIDDEN;
// Get the status string
LPTSTR pszStatus = ShellConstructMessageString(HINST_THISDLL, (LPCTSTR)ids,
AddCommas(pfssci->cFiles, szTemp),
AddCommas(pfssci->cHiddenFiles, szTempHidden),
szFreeSpace);
// Get the size portion
StrFormatByteSize64(pfssci->cbSize, szTemp, ARRAYSIZE(szTemp));
LPCTSTR rgpsz[] = {pszStatus, szTemp};
SetStatusText(psite, rgpsz, 0, 1);
if (pszStatus)
LocalFree(pszStatus);
}
void FSShowSelectionState(IUnknown *psite, FSSELCHANGEINFO *pfssci)
{
TCHAR szTemp[20], szBytes[30];
LPTSTR pszStatus = NULL;
if (pfssci->nItems > 1) {
pszStatus = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FSSTATUSSELECTED),
AddCommas(pfssci->nItems, szTemp));
}
if (pfssci->cNonFolders)
ShortSizeFormat64(pfssci->cbBytes, szBytes);
else
szBytes[0] = 0;
LPCTSTR rgpsz[] = {pszStatus, szBytes};
SetStatusText(psite, rgpsz, 0, 1);
if (pszStatus)
LocalFree(pszStatus);
}
HRESULT FSUpdateStatusBar(IUnknown *psite, FSSELCHANGEINFO *pfssci)
{
IShellBrowser *psb;
HRESULT hres = IUnknown_QueryService(psite, SID_STopLevelBrowser, IID_IShellBrowser, (void **)&psb);
if (SUCCEEDED(hres)) {
hres = S_OK;
switch (pfssci->nItems) {
case 0:
FSShowNoSelectionState(psite, pfssci);
hres = S_OK;
break;
case 1:
FSShowSelectionState(psite, pfssci); //Set the Size only.
hres = SFVUSB_INITED; // Make defview set infotip as text
break;
default:
FSShowSelectionState(psite, pfssci);
hres = S_OK;
break;
}
psb->Release();
}
return hres;
}
void FSOnInsertDeleteItem(LPCITEMIDLIST pidlParent, FSSELCHANGEINFO *pfssci, LPCITEMIDLIST pidl, int iMul)
{
LPCIDFOLDER pidf = FS_IsValidID(pidl);
if (pidf) {
ULONGLONG ull;
FS_GetSize(pidlParent, pidf, &ull);
pfssci->cFiles += iMul;
pfssci->cbSize += iMul * ull;
if (pfssci->cFiles <= 0) {
pfssci->cbSize = 0;
pfssci->cFiles = 0;
}
} else {
// means a delete all
pfssci->cFiles = 0;
pfssci->cbSize = 0;
pfssci->nItems = 0;
pfssci->cbBytes = 0;
pfssci->cNonFolders = 0;
pfssci->cHiddenFiles = 0;
}
}
void FSOnSelChange(LPCITEMIDLIST pidlParent, SFVM_SELCHANGE_DATA* pdvsci, FSSELCHANGEINFO *pfssci)
{
LPCIDFOLDER pidf = FS_IsValidID((LPCITEMIDLIST)pdvsci->lParamItem);
if (pidf) {
int iMul = -1;
ULONGLONG cbSize;
// Update selection count
if (pdvsci->uNewState & LVIS_SELECTED)
iMul = 1;
else
ASSERT(0 != pfssci->nItems);
// assert that soemthing changed
ASSERT((pdvsci->uOldState & LVIS_SELECTED) != (pdvsci->uNewState & LVIS_SELECTED));
pfssci->nItems += iMul;
FS_GetSize(pidlParent, pidf, &cbSize);
pfssci->cbBytes += (iMul * cbSize);
if (!FS_IsFolder(pidf))
pfssci->cNonFolders += iMul;
}
}
STDAPI CFSFolder_GetCCHMax(CFSFolder *that, LPCIDFOLDER pidf, UINT *pcchMax)
{
TCHAR szPath[MAX_PATH];
DWORD dwMaxLength;
// Get the maximum file name length.
// MAX_PATH - ('\\' + '\0' + 1) - lstrlen(szParent)
// (the -1 above is b/c the copyengine only supports MAX_PATH-2).
// And of course if the file system does not support
// long file names we should also restrict it somemore.
CFSFolder_GetPathForItem(that, NULL, szPath);
*pcchMax = ARRAYSIZE(szPath) - 3 - lstrlen(szPath);
// Now make sure that that size is valid for the
// type of drive that we are talking to
PathStripToRoot(szPath);
if (GetVolumeInformation(szPath, NULL, 0, NULL, &dwMaxLength, NULL, NULL, 0)) {
if (*pcchMax > (int)dwMaxLength) {
// can't be loner than the FS allows
*pcchMax = (int)dwMaxLength;
}
} else {
dwMaxLength = 255; // Assume LFN for now...
}
// Adjust the cchMax if we are hiding the extension
if (pidf) {
FS_CopyName(pidf, szPath, ARRAYSIZE(szPath));
UINT cchCur = lstrlen(szPath);
// If our code above restricted smaller than current size reset
// back to current size...
if (*pcchMax < cchCur) {
// NOTE: this is bullshit. if we ended up with a max smaller than what
// we currently have, the we are clearly doing something wrong!
*pcchMax = cchCur;
}
if (!FS_ShowExtension(pidf)) {
*pcchMax -= lstrlen(PathFindExtension(szPath));
if ((dwMaxLength <= 12) && (*pcchMax > 8)) {
// if max filesys path is <=12, then assume we are dealing w/ 8.3 names
// and since we are not showing the extension, cap the filename at 8.
*pcchMax = 8;
}
}
if (FS_IsFolder(pidf)) {
#ifdef WINNT
// On NT, a directory must be able to contain an
// 8.3 name and STILL be less than MAX_PATH. The
// "12" below is the length of an 8.3 name (8+1+3).
*pcchMax -= 12;
#else
// on win9x, we make sure that there is enough room
// to append \*.* in the directory case.
*pcchMax -= 4;
#endif
}
ASSERT((int)*pcchMax > 0);
}
return S_OK;
}
class CFSFolderShellFolderViewCB : public CBaseShellFolderViewCB
{
public:
CFSFolderShellFolderViewCB(IShellFolder* psf, CFSFolder *pFSFolder, LONG lEvents)
: CBaseShellFolderViewCB(psf, pFSFolder->_pidl, lEvents), m_pFSFolder(pFSFolder)
{
memset(&m_fssci, 0, sizeof(m_fssci));
m_fssci.idDrive = -1; // these fields use -1 to mean
m_fssci.cbFree = -1; // "unknown" / "not available"
}
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
FSSELCHANGEINFO m_fssci;
CFSFolder* m_pFSFolder;
private:
HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP)
{
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_FSVIEW_POPUPMERGE, lP);
return S_OK;
}
HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
{
ResizeStatus(_punkSite, cx);
return S_OK;
}
HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
{
if (PANE_ZONE == dwPaneID)
*pdwPane = 2;
return S_OK;
}
HRESULT OnInvokeCommand(DWORD pv, UINT wParam)
{
DebugMsg(DM_TRACE, TEXT("sh TR - FS_FSNCallBack DVN_INVOKECOMMAND (id=%x)"), wParam);
switch (wParam) {
case FSIDM_SORTBYNAME:
case FSIDM_SORTBYSIZE:
case FSIDM_SORTBYTYPE:
case FSIDM_SORTBYDATE:
ShellFolderView_ReArrange(m_hwndMain, FSSortIDToICol(wParam));
break;
}
return S_OK;
}
HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax)
{
return CFSFolder_GetCCHMax(m_pFSFolder, FS_IsValidID(pidlItem), pcchMax);
}
HRESULT OnGetHelpText(DWORD pv, UINT wPl, UINT wPh, LPTSTR psz)
{
LoadString(HINST_THISDLL, wPl + IDS_MH_FSIDM_FIRST, psz, wPh);
return S_OK;
}
HRESULT OnWindowCreated(DWORD pv, HWND wP)
{
TCHAR szPath[MAX_PATH];
CFSFolder_GetPathForItem(m_pFSFolder, NULL, szPath);
m_fssci.idDrive = PathGetDriveNumber(szPath); // keep track of this for later
m_fssci.cbFree = -1; // not known yet
InitializeStatus(_punkSite);
return S_OK;
}
HRESULT OnInsertDeleteItem(int iMul, LPCITEMIDLIST wP)
{
FSOnInsertDeleteItem(m_pFSFolder->_pidl, &m_fssci, wP, iMul);
// Tell the FSFolder that it needs to update the extended columns
// when we get an insert item. This will cause the next call to
// IColumnProvider::GetItemData to flush it's row-wise cache.
if (1 == iMul) {
m_pFSFolder->_bUpdateExtendedCols = TRUE;
}
return S_OK;
}
HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP)
{
FSOnSelChange(m_pFSFolder->_pidl, lP, &m_fssci);
return S_OK;
}
HRESULT OnUpdateStatusBar(DWORD pv, BOOL wP)
{
// if initializing, force refresh of disk free space
if (wP)
m_fssci.cbFree = -1;
return FSUpdateStatusBar(_punkSite, &m_fssci);
}
HRESULT OnRefresh(DWORD pv, BOOL fPreRefresh)
{
// pre refresh...
if (fPreRefresh) {
// in case our attributes change invalidate our notion of our own
// attributes
m_pFSFolder->_dwAttributes = -1;
} else {
m_fssci.cHiddenFiles = m_pFSFolder->cHiddenFiles;
m_fssci.cbSize = m_pFSFolder->cbSize;
}
return S_OK;
}
HRESULT OnSelectAll(DWORD pv)
{
HRESULT hres = S_OK;
if (m_fssci.cHiddenFiles > 0) {
if (ShellMessageBox(HINST_THISDLL, m_hwndMain,
MAKEINTRESOURCE(IDS_SELECTALLBUTHIDDEN),
MAKEINTRESOURCE(IDS_SELECTALL), MB_OKCANCEL | MB_SETFOREGROUND | MB_ICONWARNING,
m_fssci.cHiddenFiles) == IDCANCEL) {
hres = S_FALSE;
}
}
return hres;
}
HRESULT OnGetWorkingDir(DWORD pv, UINT wP, LPTSTR lP)
{
return CFSFolder_GetPathForItem(m_pFSFolder, NULL, lP);
}
HRESULT OnGetColSaveStream(DWORD pv, WPARAM wP, IStream**lP)
{
// 99/05/14 vtan: As of Windows 2000 each PIDL persists column information
// individually rather than globally. This is the general trend in the shell.
// Should the old global behavior be desired uncomment the code below. Note
// the code below used to test for aggregation using the presence of
// punkOuter which is no longer valid. punkOuter always exists and is
// NON-NULL. Therefore the test should be against the internal IUnknown.
return E_FAIL;
#if 0
// Don't provide a default stream if we are aggregated
// Added to prevent favorites folder (with 9 columns) from using FS data (with 5 columns)
if (m_pFSFolder->punkOuter != &m_pFSFolder->iunk)
return E_FAIL;
*lP = OpenRegStream(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, TEXT("DirectoryColsX"), (DWORD)wP);
return *lP ? S_OK : E_FAIL;
#endif
}
HRESULT OnGetViews(DWORD pv, SHELLVIEWID *pvid, IEnumSFVViews **ppObj);
HRESULT OnSupportsIdentity(DWORD pv)
{
return S_OK;
}
HRESULT OnQueryReuseExtView(DWORD pv, BOOL *pfReuseAllowed)
{
if (pfReuseAllowed) {
*pfReuseAllowed = TRUE;
return NOERROR;
}
return E_INVALIDARG;
}
};
HRESULT CFSFolderShellFolderViewCB::OnGetViews(DWORD pv, SHELLVIEWID *pvid, IEnumSFVViews **ppObj)
{
*ppObj = NULL;
CViewsList cViews;
// Add base class stuff
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("folder"));
// Add this class stuff
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory"));
// Add this instance stuff
TCHAR szHere[MAX_PATH];
if (SUCCEEDED(CFSFolder_GetPath(m_pFSFolder, szHere))) {
BOOL fForceIni = FALSE;
// Add root-of-drive stuff
if (PathIsRoot(szHere)) {
fForceIni = TRUE;
if (PathIsUNC(szHere)) {
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory\\net"));
} else {
int idDrive = (int)(szHere[0] - 'A');
int type = RealDriveType(idDrive, FALSE);
if (type == DRIVE_REMOVABLE) {
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory\\removeable"));
} else if (type == DRIVE_FIXED) {
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory\\fixed"));
} else if (type == DRIVE_REMOTE) {
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory\\net"));
} else if (type == DRIVE_CDROM) {
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory\\cdrom"));
// win95 cdrom drivers don't support the read/system bits. ???
fForceIni = TRUE;
} else if (type == DRIVE_RAMDISK) {
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory\\ramdisk"));
}
}
} else {
switch (CFSFolder_GetCSIDL(m_pFSFolder)) {
case CSIDL_SYSTEM:
case CSIDL_WINDOWS:
case CSIDL_PERSONAL:
fForceIni = TRUE;
break;
}
}
// Add desktop.ini stuff
if (fForceIni || (CFSFolder_Attributes(m_pFSFolder) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) {
TCHAR szIniFile[MAX_PATH];
PathCombine(szIniFile, szHere, c_szDesktopIni);
cViews.AddIni(szIniFile, szHere);
}
}
cViews.GetDef(pvid);
return CreateEnumCViewList(&cViews, ppObj);
// Note the automatic destructor will free any views still left
}
const CLSID *c_rgFilePages[] = {
&CLSID_FileTypes,
&CLSID_OfflineFilesOptions
};
// add optional pages to Explore/Options.
HRESULT SFVCB_OnAddPropertyPages(DWORD pv, SFVM_PROPPAGE_DATA *ppagedata)
{
for (int i = 0; i < ARRAYSIZE(c_rgFilePages); i++) {
IShellPropSheetExt * pspse;
HRESULT hres = SHCoCreateInstance(NULL, c_rgFilePages[i], NULL, IID_IShellPropSheetExt, (void **)&pspse);
if (SUCCEEDED(hres)) {
pspse->AddPages(ppagedata->pfn, ppagedata->lParam);
pspse->Release();
}
}
return S_OK;
}
STDMETHODIMP CFSFolderShellFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnInvokeCommand);
HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax);
HANDLE_MSG(0, SFVM_GETHELPTEXT, OnGetHelpText);
HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
HANDLE_MSG(1, SFVM_INSERTITEM, OnInsertDeleteItem);
HANDLE_MSG(-1, SFVM_DELETEITEM, OnInsertDeleteItem);
HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange);
HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, OnUpdateStatusBar);
HANDLE_MSG(0, SFVM_REFRESH, OnRefresh);
HANDLE_MSG(0, SFVM_SELECTALL, OnSelectAll);
HANDLE_MSG(0, SFVM_GETWORKINGDIR, OnGetWorkingDir);
HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGetColSaveStream);
HANDLE_MSG(0, SFVM_GETVIEWS, OnGetViews);
HANDLE_MSG(0, SFVM_SUPPORTSIDENTITY, OnSupportsIdentity);
HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
HANDLE_MSG(0, SFVM_QUERYREUSEEXTVIEW, OnQueryReuseExtView);
HANDLE_MSG(0, SFVM_SIZE, OnSize);
HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
default:
return E_FAIL;
}
return NOERROR;
}
STDAPI CFSFolderCallback_Create(IShellFolder* psf, CFSFolder *pFSFolder, LONG lEvents, IShellFolderViewCB **ppsfvcb)
{
*ppsfvcb = new CFSFolderShellFolderViewCB(psf, pFSFolder, lEvents);
return *ppsfvcb ? S_OK : E_OUTOFMEMORY;
}
// Briefcase stuff
const TBBUTTON c_tbBrfCase[] = {
{ 0, FSIDM_UPDATEALL, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ 1, FSIDM_UPDATESELECTION, 0, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP , {0,0}, 0L, -1 },
};
void BrfView_OnGetButtons(PBRFVIEW that, HWND hwndMain, UINT idCmdFirst, LPTBBUTTON ptbbutton)
{
IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndMain);
if (psb) {
UINT i;
LRESULT iBtnOffset;
TBADDBITMAP ab;
// add the toolbar button bitmap, get it's offset
ab.hInst = HINST_THISDLL;
ab.nID = IDB_BRF_TB_SMALL; // std bitmaps
psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 2, (LPARAM)&ab, &iBtnOffset);
for (i = 0; i < ARRAYSIZE(c_tbBrfCase); i++) {
ptbbutton[i] = c_tbBrfCase[i];
if (!(c_tbBrfCase[i].fsStyle & TBSTYLE_SEP)) {
ptbbutton[i].idCommand += idCmdFirst;
ptbbutton[i].iBitmap += (int)iBtnOffset;
}
}
}
}
#define BRFVIEW_EVENTS \
SHCNE_DISKEVENTS | \
SHCNE_ASSOCCHANGED | \
SHCNE_GLOBALEVENTS
class CBrfViewSFVCB : public CBaseShellFolderViewCB
{
public:
CBrfViewSFVCB(IShellFolder* psf, PBRFVIEW pbv) : CBaseShellFolderViewCB(psf, pbv->pidl, BRFVIEW_EVENTS)
{
m_bv = *pbv;
// m_pidl is cloned in the base class thus will have the right lifetime
// (although pbv->pidl is stored in the shell folder that has the same life
// as this object)
ASSERT(ILIsEqual(m_bv.pidl, m_pidl));
m_bv.pidl = m_pidl;
}
~CBrfViewSFVCB()
{
if (m_bv.pbrfstg)
m_bv.pbrfstg->Release();
if (m_bv.pidlRoot)
ILFree(m_bv.pidlRoot);
}
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
BrfView m_bv;
HRESULT OnWINDOWCREATED(DWORD pv, HWND wP)
{
BrfView_OnCreate(&m_bv, m_hwndMain, wP);
return S_OK;
}
HRESULT OnWINDOWDESTROY(DWORD pv, HWND wP)
{
BrfView_OnDestroy(&m_bv, wP);
return S_OK;
}
HRESULT OnMERGEMENU(DWORD pv, QCMINFO*lP)
{
BrfView_MergeMenu(&m_bv, lP);
return S_OK;
}
HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP)
{
BrfView_Command(&m_bv, m_pshf, m_hwndMain, wP);
return S_OK;
}
HRESULT OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz)
{
LoadString(HINST_THISDLL, wPl + (bHelp ? IDS_MH_FSIDM_FIRST : IDS_TT_FSIDM_FIRST), psz, cch);
return S_OK;
}
HRESULT OnINITMENUPOPUP(DWORD pv, UINT wPl, UINT wPh, HMENU lP)
{
BrfView_InitMenuPopup(&m_bv, m_hwndMain, wPl, wPh, lP);
return S_OK;
}
HRESULT OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo)
{
ptbinfo->cbuttons = ARRAYSIZE(c_tbBrfCase);
ptbinfo->uFlags = TBIF_PREPEND;
return S_OK;
}
HRESULT OnGETBUTTONS(DWORD pv, UINT wPl, UINT wPh, TBBUTTON*lP)
{
BrfView_OnGetButtons(&m_bv, m_hwndMain, wPl, lP);
return S_OK;
}
HRESULT OnSELCHANGE(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP)
{
return(BrfView_OnSelChange(&m_bv, m_hwndMain, wPl));
}
HRESULT OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry*lP)
{
return(BrfView_OnQueryFSNotify(&m_bv, lP));
}
HRESULT OnFSNOTIFY(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP)
{
return(BrfView_OnFSNotify(&m_bv, m_hwndMain, (LONG)lP, wP));
}
HRESULT OnQUERYCOPYHOOK(DWORD pv)
{
return S_OK;
}
HRESULT OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO*lP)
{
return(BrfView_OnNotifyCopyHook(&m_bv, m_hwndMain, lP));
}
HRESULT OnINSERTITEM(DWORD pv, LPCITEMIDLIST wP)
{
return(BrfView_OnInsertItem(&m_bv, m_hwndMain, wP));
}
HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP)
{
*lP = FVM_DETAILS;
return S_OK;
}
HRESULT OnSupportsIdentity(DWORD pv)
{
return S_OK;
}
HRESULT OnGetViews(DWORD pv, SHELLVIEWID *pvid, IEnumSFVViews **ppObj);
HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd)
{
StrCpyW(phtd->wszHelpFile, L"brief.chm");
return S_OK;
}
};
HRESULT CBrfViewSFVCB::OnGetViews(DWORD pv, SHELLVIEWID *pvid, IEnumSFVViews **ppObj)
{
*ppObj = NULL;
CViewsList cViews;
// Add base class stuff
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("folder"));
// Add 2nd base class stuff
cViews.AddReg(HKEY_CLASSES_ROOT, TEXT("directory"));
// Add this class stuff
cViews.AddCLSID(&CLSID_Briefcase);
// Add this instance stuff
TCHAR szHere[MAX_PATH];
if (SHGetPathFromIDList(m_pidl, szHere)) {
TCHAR szIniFile[MAX_PATH];
PathCombine(szIniFile, szHere, c_szDesktopIni);
cViews.AddIni(szIniFile, szHere);
}
cViews.GetDef(pvid);
return CreateEnumCViewList(&cViews, ppObj);
// Note the automatic destructor will free any views still left
}
STDMETHODIMP CBrfViewSFVCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWINDOWCREATED);
HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWINDOWDESTROY);
HANDLE_MSG(0, SFVM_MERGEMENU, OnMERGEMENU);
HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND);
HANDLE_MSG(TRUE, SFVM_GETHELPTEXT, OnGetHelpOrTooltipText);
HANDLE_MSG(FALSE, SFVM_GETTOOLTIPTEXT, OnGetHelpOrTooltipText);
HANDLE_MSG(0, SFVM_INITMENUPOPUP, OnINITMENUPOPUP);
HANDLE_MSG(0, SFVM_GETBUTTONINFO, OnGETBUTTONINFO);
HANDLE_MSG(0, SFVM_GETBUTTONS, OnGETBUTTONS);
HANDLE_MSG(0, SFVM_SELCHANGE, OnSELCHANGE);
HANDLE_MSG(0, SFVM_QUERYFSNOTIFY, OnQUERYFSNOTIFY);
HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNOTIFY);
HANDLE_MSG(0, SFVM_QUERYCOPYHOOK, OnQUERYCOPYHOOK);
HANDLE_MSG(0, SFVM_NOTIFYCOPYHOOK, OnNOTIFYCOPYHOOK);
HANDLE_MSG(0, SFVM_INSERTITEM, OnINSERTITEM);
HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE);
HANDLE_MSG(0, SFVM_GETVIEWS, OnGetViews);
HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
HANDLE_MSG(0, SFVM_SUPPORTSIDENTITY, OnSupportsIdentity);
HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
default:
return E_FAIL;
}
return NOERROR;
}
IShellFolderViewCB* BrfView_CreateSFVCB(IShellFolder* psf, PBRFVIEW pbv)
{
return new CBrfViewSFVCB(psf, pbv);
}
class CFolderExtractImage : public IExtractImage,
public IPersist,
public IRunnableTask
{
public:
CFolderExtractImage();
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(ULONG, AddRef) ();
STDMETHOD_(ULONG, Release) ();
// IExtractImage/IExtractLogo
STDMETHOD(GetLocation) (LPWSTR pszPathBuffer,
DWORD cch,
DWORD * pdwPriority,
const SIZE * prgSize,
DWORD dwRecClrDepth,
DWORD *pdwFlags);
STDMETHOD(Extract)(HBITMAP * phBmpThumbnail);
// IPersist
STDMETHOD(GetClassID)(LPCLSID lpClassID);
// IRunnableTask
STDMETHOD(Run)(void);
STDMETHOD(Kill)(BOOL fWait);
STDMETHOD(Suspend)(void);
STDMETHOD(Resume)(void);
STDMETHOD_(ULONG, IsRunning)(void);
STDMETHOD(Init)(LPCTSTR pszPath);
private:
~CFolderExtractImage();
LPCTSTR _GetImagePath(UINT cx);
IExtractImage * m_pExtract;
IRunnableTask * m_pRun;
long _cRef;
TCHAR m_szFolder[MAX_PATH];
TCHAR m_szLogo[MAX_PATH];
TCHAR m_szWideLogo[MAX_PATH];
SIZEL m_rgSize;
};
STDAPI CFolderExtractImage_Create(LPCTSTR pszPath, REFIID riid, void **ppvObj)
{
HRESULT hr = E_OUTOFMEMORY;
CFolderExtractImage * pObj = new CFolderExtractImage;
if (pObj) {
hr = pObj->Init(pszPath);
if (SUCCEEDED(hr))
hr = pObj->QueryInterface(riid, ppvObj);
pObj->Release();
}
return hr;
}
CFolderExtractImage::CFolderExtractImage() : _cRef(1)
{
}
CFolderExtractImage::~CFolderExtractImage()
{
ATOMICRELEASE(m_pExtract);
ATOMICRELEASE(m_pRun);
}
STDMETHODIMP CFolderExtractImage::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CFolderExtractImage, IExtractImage),
QITABENTMULTI2(CFolderExtractImage, IID_IExtractLogo, IExtractImage),
QITABENT(CFolderExtractImage, IPersist),
QITABENT(CFolderExtractImage, IRunnableTask),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CFolderExtractImage::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CFolderExtractImage::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
LPCTSTR CFolderExtractImage::_GetImagePath(UINT cx)
{
if (0 == m_szLogo[0]) {
if (_GetFolderString(m_szFolder, NULL, m_szLogo, ARRAYSIZE(m_szLogo), SZ_CANBEUNICODE TEXT("Logo"))) {
if (_GetFolderString(m_szFolder, NULL, m_szWideLogo, ARRAYSIZE(m_szWideLogo), SZ_CANBEUNICODE TEXT("WideLogo")))
PathCombine(m_szWideLogo, m_szFolder, m_szWideLogo); // relative path support
PathCombine(m_szLogo, m_szFolder, m_szLogo); // relative path support
}
#if 0
// this is the auto-folder image code. since the image generated here
// looks too much like a picture we need to turn this off. once we fix
// the overlay code so it is more obvious what is a folder we can re-enable this
else {
TCHAR szFind[MAX_PATH];
PathCombine(szFind, m_szFolder, TEXT("folder.jpg"));
if (PathFileExists(szFind)) {
lstrcpyn(m_szLogo, szFind, ARRAYSIZE(m_szLogo));
} else {
PathCombine(szFind, m_szFolder, TEXT("*.jpg"));
HANDLE hfind;
WIN32_FIND_DATA fd;
if (S_OK == SHFindFirstFile(szFind, &fd, &hfind)) {
PathCombine(m_szLogo, m_szFolder, fd.cFileName);
FindClose(hfind);
}
}
}
#endif
}
LPCTSTR psz = ((cx > 120) && m_szWideLogo[0]) ? m_szWideLogo : m_szLogo;
return *psz ? psz : NULL;
}
STDMETHODIMP CFolderExtractImage::GetLocation(LPWSTR pszPathBuffer, DWORD cch,
DWORD * pdwPriority, const SIZE * prgSize,
DWORD dwRecClrDepth, DWORD *pdwFlags)
{
HRESULT hr;
LPCTSTR pszLogo = _GetImagePath(prgSize->cx);
if (pszLogo) {
ATOMICRELEASE(m_pExtract);
ATOMICRELEASE(m_pRun);
LPITEMIDLIST pidl;
hr = SHILCreateFromPath(pszLogo, &pidl, NULL);
if (SUCCEEDED(hr)) {
hr = SHGetUIObjectFromFullPIDL(pidl, NULL, IID_PPV_ARG(IExtractImage, &m_pExtract));
if (SUCCEEDED(hr)) {
m_pExtract->QueryInterface(IID_PPV_ARG(IRunnableTask, &m_pRun)); // optional
hr = m_pExtract->GetLocation(pszPathBuffer, cch, pdwPriority, prgSize, dwRecClrDepth, pdwFlags);
}
ILFree(pidl);
}
} else
hr = E_FAIL;
return hr;
}
STDMETHODIMP CFolderExtractImage::Extract(HBITMAP * phBmpThumbnail)
{
return m_pExtract ? m_pExtract->Extract(phBmpThumbnail) : E_FAIL;
}
STDMETHODIMP CFolderExtractImage::GetClassID(CLSID *pClassID)
{
return E_NOTIMPL;
}
STDMETHODIMP CFolderExtractImage::Init(LPCTSTR pszPath)
{
lstrcpyn(m_szFolder, pszPath, ARRAYSIZE(m_szFolder));
return S_OK;
}
STDMETHODIMP CFolderExtractImage::Run(void)
{
return m_pRun ? m_pRun->Run() : E_NOTIMPL;
}
STDMETHODIMP CFolderExtractImage::Kill(BOOL fWait)
{
return m_pRun ? m_pRun->Kill(fWait) : E_NOTIMPL;
}
STDMETHODIMP CFolderExtractImage::Suspend(void)
{
return m_pRun ? m_pRun->Suspend() : E_NOTIMPL;
}
STDMETHODIMP CFolderExtractImage::Resume(void)
{
return m_pRun ? m_pRun->Resume() : E_NOTIMPL;
}
STDMETHODIMP_(ULONG) CFolderExtractImage::IsRunning(void)
{
return m_pRun ? m_pRun->IsRunning() : E_NOTIMPL;
}
class CFileSysEnum : public IEnumIDList
{
public:
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IEnumIDList
STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
STDMETHOD(Skip)(ULONG celt);
STDMETHOD(Reset)();
STDMETHOD(Clone)(IEnumIDList **ppenum);
CFileSysEnum(CFSFolder *pfsf, IUnknown *punk, HWND hwnd, DWORD grfFlags);
HRESULT Init();
private:
~CFileSysEnum();
BOOL _FindNextFile();
LONG _cRef;
IUnknown *_punk; // BUGBUG: since this C++ code can't use the C way to hold a ref on CFSFolder use this
CFSFolder *_pfsf;
DWORD _grfFlags;
HWND _hwnd;
HANDLE _hfind;
TCHAR _szFolder[MAX_PATH];
BOOL _fMoreToEnum;
WIN32_FIND_DATA _fd;
int _cHiddenFiles;
ULONGLONG _cbSize;
BOOL _fRecentDocs; // we are in the recent docs mode
int _iIndexMRU;
BOOL _fShowSuperHidden;
};
CFileSysEnum::CFileSysEnum(CFSFolder *pfsf, IUnknown *punk, HWND hwnd, DWORD grfFlags) :
_cRef(1), _pfsf(pfsf), _punk(punk), _hwnd(hwnd), _grfFlags(grfFlags), _hfind(INVALID_HANDLE_VALUE)
{
_fShowSuperHidden = ShowSuperHidden();
_punk->AddRef();
}
CFileSysEnum::~CFileSysEnum()
{
if (_hfind != INVALID_HANDLE_VALUE) {
// this handle can be the find file or MRU list in the case of RECENTDOCSDIR
if (_fRecentDocs)
FreeMRUList(_hfind);
else
FindClose(_hfind);
_hfind = INVALID_HANDLE_VALUE;
}
_punk->Release();
}
HRESULT CFileSysEnum::Init()
{
TCHAR szPath[MAX_PATH];
HRESULT hres = CFSFolder_GetPath(_pfsf, _szFolder);
if (SUCCEEDED(hres) &&
PathCombine(szPath, _szFolder, c_szStarDotStar)) {
// let name mapper see the path/PIDL pair (for UNC root mapping)
NPTRegisterNameToPidlTranslation(_szFolder, _pfsf->_pidlTarget ? _pfsf->_pidlTarget : _pfsf->_pidl);
if (_grfFlags == SHCONTF_FOLDERS) {
// use mask to only find folders, mask is in the hi byte of dwFileAttributes
// algorithm: (((attrib_on_disk & mask) ^ mask) == 0)
_fd.dwFileAttributes = (FILE_ATTRIBUTE_DIRECTORY << 8) |
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
_fd.dwReserved0 = 0x56504347; // signature to tell kernel to use the attribs specified
}
// BUGBUG: We should supply a punkEnableModless in order to go modal during UI.
hres = SHFindFirstFileRetry(_hwnd, NULL, szPath, &_fd, &_hfind, SHPPFW_NONE);
if (SUCCEEDED(hres)) {
ASSERT(hres == S_OK ? (_hfind != INVALID_HANDLE_VALUE) : TRUE);
_fMoreToEnum = (hres == S_OK);
if (!(_grfFlags & SHCONTF_INCLUDEHIDDEN)) {
if (CFSFolder_IsCSIDL(_pfsf, CSIDL_RECENT)) {
_fRecentDocs = TRUE;
// open it now so that each open within the enum is fast.
// close at release time
if (_hfind != INVALID_HANDLE_VALUE)
FindClose(_hfind);
_hfind = CreateSharedRecentMRUList(NULL, NULL, SRMLF_COMPNAME);
}
}
} else {
// SHFindFirstFileRetry() will display the error message so prevent from displaying
// them in the future
hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
} else if (hres == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
// Tracking target doesn't exist; return an empty enumerator
_fMoreToEnum = FALSE;
hres = S_OK;
} else {
// CFSFolder_GetPathForItem & PathCombine() fail when path is too long
if (_hwnd) {
ShellMessageBox(HINST_THISDLL, _hwnd, MAKEINTRESOURCE(IDS_ENUMERR_PATHTOOLONG),
NULL, MB_OK | MB_ICONHAND);
}
// This error value tells callers that we have already displayed error UI so skip
// displaying errors.
hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
return hres;
}
STDMETHODIMP CFileSysEnum::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CFileSysEnum, IEnumIDList), // IID_IEnumIDList
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CFileSysEnum::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CFileSysEnum::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
BOOL CFileSysEnum::_FindNextFile()
{
BOOL fMoreToEnum = FALSE;
if (_fRecentDocs) {
LPITEMIDLIST pidl;
while (-1 != EnumSharedRecentMRUList(_hfind, _iIndexMRU, NULL, &pidl)) {
// confirm that the item stil exists in the file system, fill in the _fd data
TCHAR szPath[MAX_PATH];
HANDLE h;
CFSFolder_GetPathForItem(_pfsf, FS_IsValidID(pidl), szPath);
ILFree(pidl);
h = FindFirstFile(szPath, &_fd);
if (h != INVALID_HANDLE_VALUE) {
fMoreToEnum = TRUE;
_iIndexMRU++;
FindClose(h);
break;
} else {
// WARNING - if the list is corrupt we torch it - ZekeL 19-JUN-98
// we could do some special crap, i guess, to weed out the bad
// items, but it seems simpler to just blow it away.
// the only reason this should happen is if somebody
// has been mushing around with RECENT directory directly,
// which they shouldnt do since it is hidden...
// kill this invalid entry, and then try the same index again...
DelMRUString(_hfind, _iIndexMRU);
}
}
} else
fMoreToEnum = FindNextFile(_hfind, &_fd);
return fMoreToEnum;
}
#define FILE_ATTRIBUTE_SUPERHIDDEN (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)
#define IS_SYSTEM_HIDDEN(dw) ((dw & FILE_ATTRIBUTE_SUPERHIDDEN) == FILE_ATTRIBUTE_SUPERHIDDEN)
STDMETHODIMP CFileSysEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
{
HRESULT hres;
for (; _fMoreToEnum; _fMoreToEnum = _FindNextFile()) {
if (_fMoreToEnum == (BOOL)42)
continue; // we already processed the current item, skip it now
if (PathIsDotOrDotDot(_fd.cFileName))
continue;
if (_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!(_grfFlags & SHCONTF_FOLDERS))
continue; // item is folder but client does not want folders
} else if (!(_grfFlags & SHCONTF_NONFOLDERS))
continue; // item is file, but client only wants folders
// skip hidden and system things unconditionally, don't even count them
if (!_fShowSuperHidden && IS_SYSTEM_HIDDEN(_fd.dwFileAttributes))
continue;
_cbSize += MAKELONGLONG(_fd.nFileSizeLow, _fd.nFileSizeHigh);
if (!(_grfFlags & SHCONTF_INCLUDEHIDDEN) &&
(_fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) {
_cHiddenFiles++;
continue;
}
break;
}
if (_fMoreToEnum) {
hres = CFSFolder_CreateIDList(_pfsf, &_fd, ppidl);
_fMoreToEnum = (BOOL)42; // we have processed the current item, skip it next time
} else {
*ppidl = NULL;
hres = S_FALSE; // no more items
// completed the enum, stash some items back into the folder (lame!)
_pfsf->cHiddenFiles = _cHiddenFiles;
_pfsf->cbSize = _cbSize;
}
if (pceltFetched)
*pceltFetched = (hres == S_OK) ? 1 : 0;
return hres;
}
STDMETHODIMP CFileSysEnum::Skip(ULONG celt)
{
return E_NOTIMPL;
}
STDMETHODIMP CFileSysEnum::Reset()
{
return S_OK;
}
STDMETHODIMP CFileSysEnum::Clone(IEnumIDList **ppenum)
{
*ppenum = NULL;
return E_NOTIMPL;
}
STDAPI CFSFolder_CreateEnum(CFSFolder *pfsf, IUnknown *punk, HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
{
HRESULT hres;
CFileSysEnum *penum = new CFileSysEnum(pfsf, punk, hwnd, grfFlags);
if (penum) {
hres = penum->Init();
if (SUCCEEDED(hres))
penum->QueryInterface(IID_IEnumIDList, (void **)ppenum);
penum->Release();
} else {
hres = E_OUTOFMEMORY;
*ppenum = NULL;
}
return hres;
}