windows-nt/Source/XPSP1/NT/shell/shell32/sfvcmpt.cpp
2020-09-26 16:20:57 +08:00

695 lines
19 KiB
C++

#include "shellprv.h"
#include <shellp.h>
#include <sfview.h>
#include "basefvcb.h"
#include "ids.h"
#include "prop.h"
CBaseShellFolderViewCB::CBaseShellFolderViewCB(LPCITEMIDLIST pidl, LONG lEvents)
: _cRef(1), _hwndMain(NULL), _lEvents(lEvents)
{
_pidl = ILClone(pidl);
}
CBaseShellFolderViewCB::~CBaseShellFolderViewCB()
{
ILFree(_pidl); // accpets NULL
}
STDMETHODIMP CBaseShellFolderViewCB::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CBaseShellFolderViewCB, IShellFolderViewCB), // IID_IShellFolderViewCB
QITABENT(CBaseShellFolderViewCB, IObjectWithSite), // IID_IObjectWithSite
QITABENT(CBaseShellFolderViewCB, IServiceProvider), // IID_IServiceProvider
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CBaseShellFolderViewCB::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CBaseShellFolderViewCB::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CBaseShellFolderViewCB::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr = RealMessage(uMsg, wParam, lParam);
if (FAILED(hr))
{
switch (uMsg)
{
case SFVM_HWNDMAIN:
_hwndMain = (HWND)lParam;
hr = S_OK;
break;
case SFVM_GETNOTIFY:
*(LPCITEMIDLIST*)wParam = _pidl;
*(LONG*)lParam = _lEvents;
hr = S_OK;
break;
}
}
return hr;
}
class CWrapOldCallback : public CBaseShellFolderViewCB
{
public:
CWrapOldCallback(LPCSFV pcsfv);
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
// IObjectWithSite
STDMETHODIMP SetSite(IUnknown *punkSite);
private:
~CWrapOldCallback();
LPFNVIEWCALLBACK _pfnCB;
IShellView* _psvOuter;
IShellFolder *_psf;
UINT _fvm;
LPARAM _lSelChangeInfo;
};
CWrapOldCallback::CWrapOldCallback(LPCSFV pcsfv) : CBaseShellFolderViewCB(pcsfv->pidl, pcsfv->lEvents)
{
_psf = pcsfv->pshf;
_psf->AddRef();
_psvOuter = pcsfv->psvOuter;
_fvm = pcsfv->fvm;
_pfnCB = pcsfv->pfnCallback;
}
CWrapOldCallback::~CWrapOldCallback()
{
_psf->Release();
}
// Some older clients may not support IObjectWithSite::SetSite
// For compat send them the old SFVM_SETISFV message
HRESULT CWrapOldCallback::SetSite(IUnknown *punkSite)
{
HRESULT hr = CBaseShellFolderViewCB::SetSite( punkSite );
MessageSFVCB( SFVM_SETISFV, 0, (LPARAM)punkSite );
return hr;
}
STDMETHODIMP CWrapOldCallback::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DVSELCHANGEINFO dvsci;
switch (uMsg)
{
case SFVM_DEFVIEWMODE:
if (_fvm)
*(UINT*)lParam = _fvm;
break;
case SFVM_SELCHANGE:
{
SFVM_SELCHANGE_DATA* pSelChange = (SFVM_SELCHANGE_DATA*)lParam;
dvsci.uNewState = pSelChange->uNewState;
dvsci.uOldState = pSelChange->uOldState;
dvsci.plParam = &_lSelChangeInfo;
dvsci.lParamItem = pSelChange->lParamItem;
lParam = (LPARAM)&dvsci;
break;
}
case SFVM_INSERTITEM:
case SFVM_DELETEITEM:
case SFVM_WINDOWCREATED:
dvsci.plParam = &_lSelChangeInfo;
dvsci.lParamItem = lParam;
lParam = (LPARAM)&dvsci;
break;
case SFVM_REFRESH:
case SFVM_SELECTALL:
case SFVM_UPDATESTATUSBAR:
case SFVM_SETFOCUS:
case SFVM_PRERELEASE:
lParam = _lSelChangeInfo;
break;
default:
break;
}
// NOTE: The DVM_ messages are the same as the SFVM_ message
return _pfnCB(_psvOuter, _psf, _hwndMain, uMsg, wParam, lParam);
}
LRESULT _ShellFolderViewMessage(IShellFolderView* psfv, UINT uMsg, LPARAM lParam)
{
UINT uScratch;
switch (uMsg)
{
case SFVM_REARRANGE:
psfv->Rearrange(lParam);
break;
case SFVM_ARRANGEGRID:
psfv->ArrangeGrid();
break;
case SFVM_AUTOARRANGE:
psfv->AutoArrange();
break;
case SFVM_GETAUTOARRANGE:
return psfv->GetAutoArrange() == S_OK;
case SFVM_GETARRANGEPARAM:
psfv->GetArrangeParam(&lParam);
return lParam;
case SFVM_ADDOBJECT:
if (SUCCEEDED(psfv->AddObject((LPITEMIDLIST)lParam, &uScratch))
&& (int)uScratch >= 0)
{
// New semantics make a copy of the IDList
ILFree((LPITEMIDLIST)lParam);
return uScratch;
}
return -1;
case SFVM_GETOBJECTCOUNT:
return SUCCEEDED(psfv->GetObjectCount(&uScratch)) ? uScratch : -1;
case SFVM_GETOBJECT:
{
LPITEMIDLIST pidl;
return SUCCEEDED(psfv->GetObject(&pidl, (UINT)lParam)) ? (LPARAM)pidl : NULL;
}
case SFVM_REMOVEOBJECT:
return SUCCEEDED(psfv->RemoveObject((LPITEMIDLIST)lParam, &uScratch)) ? uScratch : -1;
case SFVM_UPDATEOBJECT:
{
LPITEMIDLIST *ppidl = (LPITEMIDLIST*)lParam;
if (SUCCEEDED(psfv->UpdateObject(ppidl[0], ppidl[1], &uScratch))
&& (int)uScratch >= 0)
{
// New semantics make a copy of the IDList
ILFree(ppidl[1]);
return uScratch;
}
return -1;
}
case SFVM_REFRESHOBJECT:
{
LPITEMIDLIST *ppidl = (LPITEMIDLIST*)lParam;
return SUCCEEDED(psfv->RefreshObject(ppidl[0], &uScratch)) ? uScratch : -1;
}
case SFVM_SETREDRAW:
psfv->SetRedraw(BOOLFROMPTR(lParam));
break;
case SFVM_GETSELECTEDOBJECTS:
return SUCCEEDED(psfv->GetSelectedObjects((LPCITEMIDLIST**)lParam, &uScratch)) ? uScratch : -1;
case SFVM_GETSELECTEDCOUNT:
return SUCCEEDED(psfv->GetSelectedCount(&uScratch)) ? uScratch : -1;
case SFVM_ISDROPONSOURCE:
return psfv->IsDropOnSource((IDropTarget *)lParam) == S_OK;
case SFVM_MOVEICONS:
psfv->MoveIcons((IDataObject *)lParam);
break;
case SFVM_GETDROPPOINT:
return psfv->GetDropPoint((POINT *)lParam) == S_OK;
case SFVM_GETDRAGPOINT:
return psfv->GetDragPoint((POINT *)lParam) == S_OK;
case SFVM_SETITEMPOS:
{
SFV_SETITEMPOS* psip = (SFV_SETITEMPOS*)lParam;
psfv->SetItemPos(psip->pidl, &psip->pt);
break;
}
case SFVM_ISBKDROPTARGET:
return psfv->IsBkDropTarget((IDropTarget *)lParam) == S_OK;
case SFVM_SETCLIPBOARD:
psfv->SetClipboard(lParam == DFM_CMD_MOVE);
break;
case SFVM_SETPOINTS:
psfv->SetPoints((IDataObject *)lParam);
return 0;
case SFVM_GETITEMSPACING:
return psfv->GetItemSpacing((LPITEMSPACING)lParam) == S_OK;
default:
// -1L is the default return value
return 0;
}
return 1;
}
IShellFolderView* ShellFolderViewFromWindow(HWND hwnd)
{
IShellFolderView* psfv = NULL;
// HPCView sometimes gets confused and passes HWND_BROADCAST as its
// window. We can't let this reach FileCabinet_GetIShellBrowser or
// we end up broadcasting the CWM_GETISHELLBROWSER message and screwing
// up everybody in the system. (Not to mention that it will return TRUE,
// indicating a successful broadcast, and then we fault thinking that
// it's a vtbl.)
if (hwnd && hwnd != HWND_BROADCAST)
{
IShellBrowser* psb = FileCabinet_GetIShellBrowser(hwnd);
// Use !IS_INTRESOURCE() to protect against blatanly bogus values
// that clearly aren't pointers to objects.
if (!IS_INTRESOURCE(psb))
{
IShellView* psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
{
psv->QueryInterface(IID_PPV_ARG(IShellFolderView, &psfv));
psv->Release();
}
}
}
return psfv;
}
STDAPI_(HWND) ShellFolderViewWindow(HWND hwnd)
{
HWND hwndRet = NULL;
IShellBrowser *psb = FileCabinet_GetIShellBrowser(hwnd);
if (psb)
{
IShellView *psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
{
IUnknown_GetWindow(psv, &hwndRet);
psv->Release();
}
}
return hwndRet;
}
// undoced shell32 export
STDAPI_(IShellFolderViewCB *) SHGetShellFolderViewCB(HWND hwnd)
{
ASSERT(0);
return NULL; // no one calls this (search of the NT code finds no callers)
}
// old msg based way of programming defview (pre dates IShellFolderView)
STDAPI_(LRESULT) SHShellFolderView_Message(HWND hwnd, UINT uMsg, LPARAM lParam)
{
LRESULT lret = 0;
IShellFolderView* psfv = ShellFolderViewFromWindow(hwnd);
if (psfv)
{
lret = _ShellFolderViewMessage(psfv, uMsg, lParam);
psfv->Release();
}
return lret;
}
STDAPI SHCreateShellFolderViewEx(LPCSFV pcsfv, IShellView **ppsv)
{
SFV_CREATE sfvc;
sfvc.cbSize = sizeof(sfvc);
sfvc.pshf = pcsfv->pshf;
sfvc.psvOuter = pcsfv->psvOuter;
sfvc.psfvcb = pcsfv->pfnCallback ? new CWrapOldCallback(pcsfv) : NULL;
HRESULT hr = SHCreateShellFolderView(&sfvc, ppsv);
if (sfvc.psfvcb)
sfvc.psfvcb->Release();
return hr;
}
STDAPI_(void) InitializeStatus(IUnknown *psite)
{
IShellBrowser *psb;
if (SUCCEEDED(IUnknown_QueryService(psite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
{
LONG_PTR nParts = 0, n;
psb->SendControlMsg(FCW_STATUS, SB_GETPARTS, 0, 0, &nParts);
for (n = 0; n < nParts; n ++)
{
psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, n, (LPARAM)TEXT(""), NULL);
psb->SendControlMsg(FCW_STATUS, SB_SETICON, n, (LPARAM)NULL, NULL);
}
psb->SendControlMsg(FCW_STATUS, SB_SETPARTS, 0, 0, NULL);
psb->Release();
}
}
//
// The status bar partitioning has undergone several changes. Here's
// what we've got right now:
//
// Pane 0 = Selection - all remaining space
// Pane 1 = Size - just big enough to say 9,999 bytes (11 chars)
// Pane 2 = Zone - just big enough to hold longest zone
//
STDAPI_(void) ResizeStatus(IUnknown *psite, UINT cx)
{
IShellBrowser *psb;
if (SUCCEEDED(IUnknown_QueryService(psite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
{
HWND hwndStatus;
if (SUCCEEDED(psb->GetControlWindow(FCW_STATUS, &hwndStatus)) && hwndStatus)
{
RECT rc;
int ciParts[3];
int ciBorders[3];
int cxPart;
GetClientRect(hwndStatus, &rc);
// Must also take status bar borders into account.
psb->SendControlMsg(FCW_STATUS, SB_GETBORDERS, 0, (LPARAM)ciBorders, NULL);
// We build the panes from right to left.
ciParts[2] = -1;
// The Zones part
cxPart = ciBorders[0] + ZoneComputePaneSize(hwndStatus) + ciBorders[2];
ciParts[1] = rc.right - cxPart;
// The Size part
HDC hdc = GetDC(hwndStatus);
HFONT hfPrev = SelectFont(hdc, GetWindowFont(hwndStatus));
SIZE siz;
GetTextExtentPoint32(hdc, TEXT("0"), 1, &siz);
SelectObject(hdc, hfPrev);
ReleaseDC(hwndStatus, hdc);
cxPart = ciBorders[0] + siz.cx * (11 + 2); // "+2" for slop
ciParts[0] = ciParts[1] - cxPart;
//
// If we underflowed, then give up and just give everybody
// one third.
//
if (ciParts[0] < 0)
{
ciParts[0] = rc.right / 3;
ciParts[1] = 2 * ciParts[0];
}
psb->SendControlMsg(FCW_STATUS, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts, NULL);
}
psb->Release();
}
}
STDAPI_(void) SetStatusText(IUnknown *psite, LPCTSTR *ppszText, int iStart, int iEnd)
{
IShellBrowser *psb;
if (SUCCEEDED(IUnknown_QueryService(psite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
{
for (; iStart <= iEnd; iStart++)
{
LPCTSTR psz;
if (ppszText)
{
psz = *ppszText;
ppszText++;
}
else
psz = c_szNULL;
psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, (WPARAM)iStart, (LPARAM)psz, NULL);
}
psb->Release();
}
}
STDAPI_(void) ViewShowSelectionState(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, ARRAYSIZE(szTemp)));
}
if (pfssci->cNonFolders)
ShortSizeFormat64(pfssci->cbBytes, szBytes, ARRAYSIZE(szBytes));
else
szBytes[0] = 0;
LPCTSTR rgpsz[] = { pszStatus, szBytes };
SetStatusText(psite, rgpsz, 0, 1);
if (pszStatus)
LocalFree(pszStatus);
}
HRESULT _UpdateDiskFreeSpace(LPCITEMIDLIST pidlFolder, FSSELCHANGEINFO *pfssci)
{
IShellFolder2 *psf2;
LPCITEMIDLIST pidlLast;
HRESULT hr = SHBindToIDListParent(pidlFolder, IID_PPV_ARG(IShellFolder2, &psf2), &pidlLast);
if (SUCCEEDED(hr))
{
ULONGLONG ullTotalFreeCaller;
hr = GetLongProperty(psf2, pidlLast, &SCID_FREESPACE, &ullTotalFreeCaller);
if (SUCCEEDED(hr))
{
pfssci->cbFree = ullTotalFreeCaller;
}
else if (!ILIsEmpty(pidlFolder) && !ILIsEmpty(_ILNext(pidlFolder)))
{
// if there are at least 2 segments in the IDList rip off the
// last item and recurse to compute the size
LPITEMIDLIST pidl = ILCloneParent(pidlFolder);
if (pidl)
{
hr = _UpdateDiskFreeSpace(pidl, pfssci);
ILFree(pidl);
}
}
psf2->Release();
}
return hr;
}
void _ShowNoSelectionState(IUnknown *psite, LPCITEMIDLIST pidlFolder, FSSELCHANGEINFO *pfssci)
{
TCHAR szTemp[30], szTempHidden[30], szFreeSpace[30];
UINT ids = IDS_FSSTATUSBASE;
// Assume we don't need freespace info
szFreeSpace[0] = 0;
// See if we need the freespace info (idDrive != -1)
ULONGLONG cbFree = -1;
if (pidlFolder && IsExplorerModeBrowser(psite))
{
if (pfssci->cbFree == -1)
_UpdateDiskFreeSpace(pidlFolder, pfssci);
// cbFree couldstill be -1 if GetDiskFreeSpace didn't get any info
cbFree = pfssci->cbFree;
if (cbFree != -1)
{
ShortSizeFormat64(pfssci->cbFree, szFreeSpace, ARRAYSIZE(szFreeSpace));
ids += DIDS_FSSPACE; // Also show freespace
}
}
// hidden files -> show "and nn hidden".
if (pfssci->cHiddenFiles)
ids += DIDS_FSHIDDEN;
// Get the status string
LPTSTR pszStatus = ShellConstructMessageString(HINST_THISDLL, IntToPtr_(LPCTSTR, ids),
AddCommas(pfssci->cFiles, szTemp, ARRAYSIZE(szTemp)),
AddCommas(pfssci->cHiddenFiles, szTempHidden, ARRAYSIZE(szTempHidden)),
szFreeSpace);
// Get the size portion
StrFormatByteSize64(pfssci->cbSize, szTemp, ARRAYSIZE(szTemp));
LPCTSTR rgpsz[] = { pszStatus, szTemp };
SetStatusText(psite, rgpsz, 0, 1);
LocalFree(pszStatus); // may be NULL
}
STDAPI ViewUpdateStatusBar(IUnknown *psite, LPCITEMIDLIST pidlFolder, FSSELCHANGEINFO *pfssci)
{
HRESULT hr = S_OK;
switch (pfssci->nItems)
{
case 0:
_ShowNoSelectionState(psite, pidlFolder, pfssci);
hr = S_OK;
break;
case 1:
ViewShowSelectionState(psite, pfssci); //Set the Size only.
hr = SFVUSB_INITED; // Make defview set infotip as text
break;
default:
ViewShowSelectionState(psite, pfssci);
hr = S_OK;
break;
}
return hr;
}
STDAPI_(void) ViewInsertDeleteItem(IShellFolder2 *psf, FSSELCHANGEINFO *pfssci, LPCITEMIDLIST pidl, int iMul)
{
ULONGLONG ullSize;
if (SUCCEEDED(GetLongProperty(psf, pidl, &SCID_SIZE, &ullSize)))
{
pfssci->cFiles += iMul;
pfssci->cbSize += iMul * ullSize;
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;
}
}
STDAPI_(void) ViewSelChange(IShellFolder2 *psf, SFVM_SELCHANGE_DATA* pdvsci, FSSELCHANGEINFO *pfssci)
{
ULONGLONG ullSize;
LPCITEMIDLIST pidl = (LPCITEMIDLIST)pdvsci->lParamItem;
if (SUCCEEDED(GetLongProperty(psf, pidl, &SCID_SIZE, &ullSize)))
{
int iMul = -1;
// 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;
pfssci->cbBytes += (iMul * ullSize);
if (!SHGetAttributes(psf, pidl, SFGAO_FOLDER))
pfssci->cNonFolders += iMul;
}
}
STDAPI DefaultGetWebViewTemplateFromHandler(LPCTSTR pszKey, SFVM_WEBVIEW_TEMPLATE_DATA* pvit)
{
HRESULT hr = S_OK;
TCHAR szKey[200];
wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\shellex\\ExtShellFolderViews\\{5984FFE0-28D4-11CF-AE66-08002B2E1262}"), pszKey);
DWORD cbSize = sizeof(pvit->szWebView);
if (ERROR_SUCCESS == SHGetValueW(HKEY_CLASSES_ROOT, szKey, TEXT("PersistMoniker"), NULL, pvit->szWebView, &cbSize))
{
//if the %UserAppData% exists, expand it!
ExpandOtherVariables(pvit->szWebView, ARRAYSIZE(pvit->szWebView));
}
else
{
hr = E_FAIL;
}
return hr;
}
STDAPI DefaultGetWebViewTemplateFromClsid(REFCLSID clsid, SFVM_WEBVIEW_TEMPLATE_DATA* pvit)
{
TCHAR szHandler[6+40] = TEXT("CLSID\\"); // 6 for "CLSID\\", 40 for GUID
SHStringFromGUID(clsid, &szHandler[6], ARRAYSIZE(szHandler)-6);
return DefaultGetWebViewTemplateFromHandler(szHandler, pvit);
}
STDAPI DefaultGetWebViewTemplateFromPath(LPCTSTR pszDir, SFVM_WEBVIEW_TEMPLATE_DATA* pvit)
{
SHFOLDERCUSTOMSETTINGS fcs = {0};
TCHAR szPath[MAX_PATH+40]; // slop for "webview://file://"
fcs.dwSize = sizeof(fcs);
fcs.dwMask = FCSM_WEBVIEWTEMPLATE;
fcs.pszWebViewTemplate = szPath;
fcs.cchWebViewTemplate = ARRAYSIZE(szPath);
HRESULT hr = SHGetSetFolderCustomSettings(&fcs, pszDir, FCS_READ);
if (SUCCEEDED(hr))
{
LPTSTR pszPath = szPath;
// We want to allow relative paths for the file: protocol
//
if (0 == StrCmpNI(TEXT("file://"), pszPath, 7)) // ARRAYSIZE(TEXT("file://"))
{
pszPath += 7; // ARRAYSIZE(TEXT("file://"))
}
// for webview:// compatibility, keep this working:
else if (0 == StrCmpNI(TEXT("webview://file://"), pszPath, 17)) // ARRAYSIZE(TEXT("webview://file://"))
{
pszPath += 17; // ARRAYSIZE(TEXT("webview://file://"))
}
// handle relative references...
PathCombine(pszPath, pszDir, pszPath);
StrCpyN(pvit->szWebView, szPath, ARRAYSIZE(pvit->szWebView));
}
return hr;
}