#include "shellprv.h" #include #include #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; }