|
|
#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() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
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!
hr = 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; }
|